C 保存寄存器?
好的,在C代码中,我让它循环通过命令行参数,并打印出每个参数。我编译它并在GDB中打开它,以查看主函数的外观,因为我试图在汇编中执行相同的操作。我最终发现了我的问题所在——我的打印函数使用了与主函数相同的寄存器。最后,我只是在函数调用之前将它们推到堆栈上,然后在函数调用之后将它们弹出。我唯一不明白的是,为什么这段代码似乎没有做到这一点,为什么它没有遇到和我一样的问题C 保存寄存器?,c,linux,assembly,gdb,x86-64,C,Linux,Assembly,Gdb,X86 64,好的,在C代码中,我让它循环通过命令行参数,并打印出每个参数。我编译它并在GDB中打开它,以查看主函数的外观,因为我试图在汇编中执行相同的操作。我最终发现了我的问题所在——我的打印函数使用了与主函数相同的寄存器。最后,我只是在函数调用之前将它们推到堆栈上,然后在函数调用之后将它们弹出。我唯一不明白的是,为什么这段代码似乎没有做到这一点,为什么它没有遇到和我一样的问题 0x000000000040052d <+0>: push %rbp 0x0000000000
0x000000000040052d <+0>: push %rbp
0x000000000040052e <+1>: mov %rsp,%rbp
0x0000000000400531 <+4>: sub $0x20,%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 $0x1,-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
0x000000000040052d:推送%rbp
0x000000000040052e:mov%rsp,%rbp
0x0000000000400531:子$0x20,%rsp
0x0000000000400535:mov%edi,-0x14(%rbp)
0x0000000000400538:mov%rsi,-0x20(%rbp)
0x000000000040053c:jmp 0x400561
0x000000000040053e:mov-0x4(%rbp),%eax
0x0000000000400541:cltq
0x0000000000400543:lea0x0(,%rax,8),%rdx
0x000000000040054b:mov-0x20(%rbp),%rax
0x000000000040054f:添加%rdx,%rax
0x0000000000400552:mov(%rax),%rax
0x0000000000400555:mov%rax,%rdi
0x0000000000400558:callq 0x400410
0x000000000040055d:添加$0x1,-0x4(%rbp)
0x0000000000400561:mov-0x4(%rbp),%eax
0x0000000000400564:cmp-0x14(%rbp),%eax
0x0000000000400567:jl 0x40053e
0x0000000000400569:LEVEQ
0x000000000040056a:retq
欢迎您的任何意见,谢谢
(gdb) disass 0x400410
Dump of assembler code for function puts@plt:
0x0000000000400410 <+0>: jmpq *0x200c02(%rip) # 0x601018 <puts@got.plt>
0x0000000000400416 <+6>: pushq $0x0
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 $0x40,%al
0x000000000060101b <+3>: add %al,(%rax)
0x000000000060101d <+5>: add %al,(%rax)
0x000000000060101f <+7>: add %ah,(%rsi)
End of assembler dump.
(gdb)disass 0x400410
函数的汇编代码转储puts@plt:
0x000000000040410:jmpq*0x200c02(%rip)#0x601018
0x000000000040416:pushq$0x0
0x00000000004041B:jmpq 0x400400
汇编程序转储结束。
(gdb)disass 0x601018
函数的汇编代码转储puts@got.plt:
0x00000000000601018:(坏)
0x00000000000601019:添加$0x40,%al
0x0000000000060101B:添加%al,(%rax)
0x0000000000060101D:添加%al,(%rax)
0x0000000000060101F:添加%ah,(%rsi)
汇编程序转储结束。
事实上,我甚至找不到它的打印位置。我一定是遗漏了什么,只是不知道是什么。您为puts显示的拆解不正确。动态加载库的库符号是动态解析的。编译器生成对存根的调用(过程链接表或PLT),加载程序在运行时解析该地址,第二次调用该函数时,地址已解析,运行速度更快。在第二次迭代中反汇编它,您将看到实际的puts代码正在运行,您将看到寄存器正在被推送 更多信息。此说明:
jmpq *0x200c02(%rip) # 0x601018 <puts@got.plt>
jmpq*0x200c02(%rip)#0x601018
从指令指针的偏移量给定的地址读取四字(8字节),并跳到那里。因此,要查看这是怎么回事,您不想使用disas 0x601018
,而是想使用x/1xg 0x601018
查看这些字节中的内容(读取指针),然后对该值调用disas
,查看put
这些东西都是动态链接的东西,用来调用动态库中的函数
plt
是“程序链接表”的缩写,是链接器在对象调用其他动态库中的函数时创建的一组蹦床got
是“全局对象表”的缩写,是链接器生成的函数指针表,在加载程序时由动态链接器填写。检查puts@plt
没有。一个明智的猜测是,它首先推送它将要使用的寄存器。这是有道理的,我检查了,但它看起来不像,我会在一秒钟内更新我的帖子,以显示我发现了什么。非常感谢,这现在是有道理的。我知道我不明白什么。