此代码存在许多问题。 Linux 使用的AMD64 System V ABI 调用约定需要做一些事情。它要求在 CALL 之前堆栈至少 16 字节(或 32 字节)对齐:
输入参数区域的末尾应对齐在 16(32,如果 __m256 是
在堆栈上传递)字节边界。
C 运行时调用您的main 函数后,堆栈未对齐8,因为返回指针被CALL 放置在堆栈上。要重新对齐到 16 字节边界,您可以简单地将 PUSH any 通用寄存器放入堆栈并在最后 POP 将其关闭。
调用约定还要求 AL 包含用于变量参数函数的向量寄存器的数量:
%al 用于表示传递给需要可变参数数量的函数的向量参数的数量
printf是可变参数函数,所以需要设置AL。在这种情况下,您不会在向量寄存器中传递任何参数,因此您可以将 AL 设置为 0。
当 $format 指针已经是地址时,您还可以取消引用它。所以这是错误的:
mov $format, %rbx
mov (%rbx), %rdi
这会获取格式的地址并将其放在RBX中。然后,您将 RBX 中该地址的 8 个字节放入 RDI 中。 RDI 需要是一个指向字符串的指针,而不是字符本身。这两行可以替换为:
lea format(%rip), %rdi
这使用 RIP 相对寻址。
您还应该 NUL 终止您的字符串。您可以在 x86 平台上使用 .asciz,而不是使用 .ascii。
您的程序的工作版本可能如下所示:
# global data #
.data
format: .asciz "%d\n"
.text
.global main
main:
push %rbx
lea format(%rip), %rdi
mov $1, %esi # Writing to ESI zero extends to RSI.
xor %eax, %eax # Zeroing EAX is efficient way to clear AL.
call printf
pop %rbx
ret
其他建议/建议
您还应该从 64 位 Linux ABI 中了解到,调用约定还需要您编写的函数来尊重某些寄存器的保留。寄存器列表及是否保留如下:
任何在 中写着 Yes 的寄存器都保留
函数调用列是您必须确保在函数中保留的列。函数 main 与任何其他 C 函数一样。
如果您知道字符串/数据是只读的,您可以将它们放在.rodata 部分中,使用.section .rodata 而不是.data
在 64 位模式下:如果您的目标操作数是 32 位寄存器,CPU 会将寄存器零扩展至整个 64 位寄存器。这样可以节省指令编码的字节数。
您的可执行文件可能被编译为与位置无关的代码。您可能会收到类似于以下内容的错误:
在制作共享对象时,不能使用针对符号`printf@@GLIBC_2.2.5'的重定位R_X86_64_PC32;用 -fPIC 重新编译
要解决此问题,您必须以这种方式调用外部函数 printf:
call printf@plt
这通过Procedure Linkage Table (PLT)调用外部库函数