保留寄存器?

Preserving Registers?

好的,所以在 C 代码中,我让它循环遍历命令行参数并打印出每个参数。我编译它并在 GDB 中打开它以查看主要功能的样子,因为我试图在汇编中做同样的事情。我最终弄清楚了我的问题所在——我的打印函数使用的寄存器与主函数使用的寄存器相同。我最终只是在函数调用之前将每个推入堆栈,然后将它们弹出。我唯一不明白的是为什么这段代码似乎没有那样做,为什么它没有 运行 遇到和我一样的问题。

   0x000000000040052d <+0>:     push   %rbp
   0x000000000040052e <+1>:     mov    %rsp,%rbp
   0x0000000000400531 <+4>:     sub    [=10=]x20,%rsp
   0x0000000000400535 <+8>:     mov    %edi,-0x14(%rbp)
   0x0000000000400538 <+11>:    mov    %rsi,-0x20(%rbp)
   0x000000000040053c <+15>:    jmp    0x400561 <main+52>
   0x000000000040053e <+17>:    mov    -0x4(%rbp),%eax
   0x0000000000400541 <+20>:    cltq   
   0x0000000000400543 <+22>:    lea    0x0(,%rax,8),%rdx
   0x000000000040054b <+30>:    mov    -0x20(%rbp),%rax
   0x000000000040054f <+34>:    add    %rdx,%rax
   0x0000000000400552 <+37>:    mov    (%rax),%rax
   0x0000000000400555 <+40>:    mov    %rax,%rdi
   0x0000000000400558 <+43>:    callq  0x400410 <puts@plt>
   0x000000000040055d <+48>:    addl   [=10=]x1,-0x4(%rbp)
   0x0000000000400561 <+52>:    mov    -0x4(%rbp),%eax
   0x0000000000400564 <+55>:    cmp    -0x14(%rbp),%eax
   0x0000000000400567 <+58>:    jl     0x40053e <main+17>
   0x0000000000400569 <+60>:    leaveq 
   0x000000000040056a <+61>:    retq   

欢迎任何意见,谢谢。

(gdb) disass 0x400410
Dump of assembler code for function puts@plt:
   0x0000000000400410 <+0>:     jmpq   *0x200c02(%rip)        # 0x601018 <puts@got.plt>
   0x0000000000400416 <+6>:     pushq  [=11=]x0
   0x000000000040041b <+11>:    jmpq   0x400400
End of assembler dump.
(gdb) disass 0x601018
Dump of assembler code for function puts@got.plt:
   0x0000000000601018 <+0>:     (bad)  
   0x0000000000601019 <+1>:     add    [=11=]x40,%al
   0x000000000060101b <+3>:     add    %al,(%rax)
   0x000000000060101d <+5>:     add    %al,(%rax)
   0x000000000060101f <+7>:     add    %ah,(%rsi)
End of assembler dump.

事实上,我什至无法找到它在 puts 中打印的位置。我一定是错过了什么,只是不知道是什么。

您为 puts 显示的反汇编不正确。动态加载库的库符号是动态解析的。编译器生成对存根的调用(过程链接 tablePLT),加载程序在 运行 解析该调用时间,您第二次调用该函数时,地址已解析并且速度 运行s。在第 2 次迭代时反汇编它,您将看到实际的 puts 代码为 运行,并且您将看到寄存器被压入。

更多信息here

这条指令:

 jmpq   *0x200c02(%rip)        # 0x601018 <puts@got.plt>

从指令指针的偏移量给定的地址读取四字(8 字节)并跳转到那里。所以要看这是怎么回事,你不想用disas 0x601018,你想用x /1xg 0x601018看看那些字节里有什么(读取指针),然后调用disas 在该值上查看 puts

的实际代码

这玩意儿都是动态链接的玩意儿,就是为了调用动态库中的函数而设置的。 plt 是 "program linkage table" 的缩写,是每当对象调用其他动态库中的函数时由链接器创建的一组蹦床。 got是"global object table"的缩写,是链接器构建的函数指针table,程序加载时由动态链接器填充。