2

我正在为 Linux 编写“Hello World”程序的编译 AArch64 程序集文件。

我已经成功地将它从 504 字节缩短到 124 字节。我能想到的唯一更“优化”的是找到一些在单个指令中执行两个或多个任务的指令。

目前文件中的机器代码(以 asm 表示)是这样的:

  mov x8, 64     // __NR_write
  adr x1, hello  //the string, I know the exact address
  mov x2, 10     //string length (actually only "HelloWorld")

j:
  mov x0, 0      // write to stdin happens to work
  svc 0
  mov x8, 93     // __NR_exit
  b j    //the branching saves me one instruction to exit with status = 0

有什么指令可以缩短这里的内容吗?

4

1 回答 1

1

如果你不介意在你的字符串后面写一堆二进制字节(甚至是其他垃圾),它可能会ldp x0, x2, [sp], #16从堆栈中弹出前两个单词,argc并且,到 x0 和 x2 中。argv[0]\0

Linux 进程启动环境的堆栈指针指向argc,并高于argv[]数组值。(不是像gets那样指向argv的指针main;它的第一个双字是argv[0]。上面argv[]env[]。)

  • argc将为 1,因此它适用于标准输出 fd,如果从没有 args 的 shell 正常运行。
  • argv是指向堆栈内存的指针,因此是一个比 10 大得多的大整数,因此write()将读取字节直到它到达未映射的页面。
    (Linuxwrite确实将前面的字节复制到 fd,-EFAULT如果在遇到错误之前可以写入非零数量的字节,则不返回。它似乎只在到达后面的页面时检查它们的可读性。这是一个实现细节这没有记录,但是当前的 Linux 实际是这样做的,至少在 x86-64 上是这样。)

假设它在没有参数的情况下运行,它甚至可能仍然以 0 状态退出。后增量寻址将使ldp下一次迭代加载 x0 = argv[1]= NULL。(env[0]进入 x2;我们知道我们不会因为 env[] 就在堆栈区域的顶部读取过去而导致段错误。)

但是没有必要exit(0)打印文本;任何退出状态都可以工作。(如果您不介意 shell 发出的噪音,您甚至可以安排您的程序使其发生段错误,而不是进行退出系统调用,在第一个指令之后保存所有指令svc 0!)


如果您通过手动 execve 运行没有 args 的程序,那么argv[0] = 0它会调用write(0, hello, 0),因此不会打印任何内容。

但是如果你用一个 arg 运行它(不计算 shell 隐式传递的 argv[0]),它会打印到 stderr。使用 2 个或更多 args,它会尝试写入未打开的 fd 并且 write 会返回-EBADF,如您在strace.

于 2021-11-09T11:40:40.757 回答