源C代码:
int main()
{
int i;
for(i=0, i < 10; i++)
{
printf("Hello World!\n");
}
}
函数main的Intel语法x86汇编程序代码的转储:
1. 0x000055555555463a <+0>: push rbp
2. 0x000055555555463b <+1>: mov rbp,rsp
3. 0x000055555555463e <+4>: sub rsp,0x10
4. 0x0000555555554642 <+8>: mov DWORD PTR [rbp-0x4],0x0
5. 0x0000555555554649 <+15>: jmp 0x55555555465b <main+33>
6. 0x000055555555464b <+17>: lea rdi,[rip+0xa2] # 0x5555555546f4
7. 0x0000555555554652 <+24>: call 0x555555554510 <puts@plt>
8. 0x0000555555554657 <+29>: add DWORD PTR [rbp-0x4],0x1
9. 0x000055555555465b <+33>: cmp DWORD PTR [rbp-0x4],0x9
10. 0x000055555555465f <+37>: jle 0x55555555464b <main+17>
11. 0x0000555555554661 <+39>: mov eax,0x0
12. 0x0000555555554666 <+44>: leave
13. 0x0000555555554667 <+45>: ret
我目前正在研究“哈克,《剥削的艺术》第二版,乔恩·埃里克森(Jon Erickson),而我才刚刚开始着手解决组装问题。
我对将所提供的C代码转换为Assembly有几个问题,但是我主要想知道我的第一个问题。
第一个问题:第6行的目的是什么? (lea rdi,[rip + 0xa2])。
我当前的工作原理是,此方法用于保存下一条指令将跳转到的位置以跟踪发生的情况。我相信这行代码与源C代码中的printf函数相关。
因此,从本质上讲,它将rip + 0xa2(0x5555555546f4)的有效地址加载到寄存器rdi中,以简单地跟踪将跳转到printf函数的位置吗?
第二个问题:第11行的目的是什么? (mov eax,0x0?)我看不到EAX寄存器的先前使用,并且不确定为什么需要将其设置为0。
它将指向字符串文字的指针放入寄存器,作为put的第一个arg。您要查找的搜索词是“呼叫约定”和/或ABI。 (以及相对RIP寻址)。 Why is the address of static variables relative to the Instruction Pointer?
代码和数据之间的小偏移量(仅+0xa2
)是因为.rodata
节与.text
被链接到同一ELF段中,并且您的程序很小。 (较新的gcc + ld版本会将其放在单独的页面中,因此它无法执行。)
在Linux PIE可执行文件中,编译器不能在位置无关的代码中使用更短的更有效的mov edi, address
。可以通过gcc -fno-pie -no-pie
mov eax,0
在C99和C ++保证的return 0
末尾实现隐式main
。 EAX是所有调用约定中的返回值寄存器。
[如果不使用gcc -O2
或更高版本,将不会获得像xor-zeroing(xor eax,eax
)之类的窥视孔优化。