.data format: .ascii "%d\n" .text .global main main: mov $format,%rbx mov (%rbx),%rdi mov $1,%rsi call printf ret
我使用GCC来组装和链接:
gcc -o main main.s
我用这个命令运行它:
./main
当我运行程序时,我得到一个seg错误.通过使用gdb,它说找不到printf.我试过“.extern printf”,这不起作用.有人建议我应该在调用printf之前存储堆栈指针并在RET之前恢复,我该怎么做?
解决方法
The end of the input argument area shall be aligned on a 16 (32,if __m256 is
passed on stack) byte boundary.
在C运行时调用主函数之后,堆栈未对齐8,因为返回指针被CALL放在堆栈上.要重新对齐到16字节边界,您可以简单地将任何通用寄存器推入堆栈并在最后将其POP关闭.
%al is used to indicate the number of vector arguments passed to a function requiring a variable number of arguments
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中了解到,调用约定还需要您编写的函数以保护某些寄存器.登记册清单及是否应予保留如下:
任何在Preserved中说“是”的寄存器
注册列是您必须确保在您的功能中保留的列.函数main与任何其他C函数一样.
如果你有你知道的字符串/数据只读,你可以用.section .rodata而不是.data将它们放在.rodata部分.
在64位模式下:如果您的目标操作数是32位寄存器,则cpu将在整个64位寄存器中对寄存器进行零扩展.这可以节省指令编码上的字节.
您的可执行文件可能被编译为与位置无关的代码.您可能会收到类似于以下错误:
relocation R_X86_64_PC32 against symbol `printf@@GLIBC_2.2.5′ can not be used when making a shared object; recompile with -fPIC
call printf@plt