我一直在研究 GCC 与 Clang 生成的 x86 32 位代码的反汇编。
环境是在 VMware 中运行的 Ubuntu Server 16.04.2 32 位。
两者都使用相同的选项编译。没有优化,所以没有省略帧指针或类似的东西。唯一的区别是 GCC 是使用 -mpreferred-stack-boundary=2 编译的,以具有 4 字节对齐的堆栈并避免管理堆栈对齐的额外指令。clang 不提供此选项。
在 GCC 反汇编中,函数局部堆栈变量使用 EBP 的负偏移量来引用,而在调用之前放置在堆栈上的函数参数被推入堆栈或使用 ESP 相对正偏移量移入堆栈内存(第一个参数:ESP,第二个参数) :ESP+4,等等...)。
使用 clang,对于相同的源文件,生成的程序集使用 EBP 相对负偏移量访问函数局部变量和访问堆栈头、堆栈头 + 4 等。用于将调用参数放在堆栈上。除了最初的函数序言之外,几乎从未使用过 ESP。我正在查看的反汇编中有几个函数调用。
我不习惯这种表示法,我觉得它有点奇怪,因为从 EBP 到达堆栈头部的实际负偏移值将始终随着堆栈的增长/收缩而变化。而使用 ESP,更直观地知道在调用之前,ESP 的值将是第一个参数,ESP+4 的值将是第二个,等等......
最终我认为结果是相同的,因为任何本地堆栈帧位置都可以通过 EBP 索引,包括堆栈的头部,前提是您知道在任何给定指令中自堆栈帧开始以来分配了多少堆栈内存。
即 ESP = EBP -(目前帧中分配的总字节数)
有什么不同的原因吗?纯粹是一个不同的实现选择?
(我现在手头没有实际的反汇编,但我稍后会编辑问题以添加它)。
海湾合作委员会 5.4
┌ (fcn) sym.check_authentication 89
│ sym.check_authentication (int arg_8h);
│ ; var int local_14h @ ebp-0x14
│ ; var int local_4h @ ebp-0x4
│ ; arg int arg_8h @ ebp+0x8
│ 0x080484cb 55 push ebp
│ 0x080484cc 89e5 mov ebp, esp
│ 0x080484ce 83ec14 sub esp, 0x14
│ 0x080484d1 c745fc000000. mov dword [ebp - 4], 0
│ 0x080484d8 ff7508 push dword [ebp + 8]
│ 0x080484db 8d45ec lea eax, [ebp - 0x14]
│ 0x080484de 50 push eax
│ 0x080484df e89cfeffff call sym.imp.strcpy
│ 0x080484e4 83c408 add esp, 8
│ 0x080484e7 6820860408 push str.brillig ; "brillig" @ 0x8048620
│ 0x080484ec 8d45ec lea eax, [ebp - 0x14]
│ 0x080484ef 50 push eax
│ 0x080484f0 e86bfeffff call sym.imp.strcmp
│ 0x080484f5 83c408 add esp, 8
│ 0x080484f8 85c0 test eax, eax
│ 0x080484fa 7507 jne 0x8048503
│ 0x080484fc c745fc010000. mov dword [ebp - 4], 1
│ 0x08048503 6828860408 push str.outgrabe ; "outgrabe" @ 0x8048628
│ 0x08048508 8d45ec lea eax, [ebp - 0x14]
│ 0x0804850b 50 push eax
│ 0x0804850c e84ffeffff call sym.imp.strcmp
│ 0x08048511 83c408 add esp, 8
│ 0x08048514 85c0 test eax, eax
│ 0x08048516 7507 jne 0x804851f
│ 0x08048518 c745fc010000. mov dword [ebp - 4], 1
│ 0x0804851f 8b45fc mov eax, dword [ebp - 4]
│ 0x08048522 c9 leave
└ 0x08048523 c3 ret
叮当 3.8.0
┌ (fcn) sym.check_authentication 121
│ sym.check_authentication (int arg_8h);
│ ; var int local_20h @ ebp-0x20
│ ; var int local_1ch @ ebp-0x1c
│ ; var int local_18h @ ebp-0x18
│ ; var int local_14h @ ebp-0x14
│ ; var int local_4h @ ebp-0x4
│ ; arg int arg_8h @ ebp+0x8
│ 0x080484a0 55 push ebp
│ 0x080484a1 89e5 mov ebp, esp
│ 0x080484a3 83ec28 sub esp, 0x28 ; '('
│ 0x080484a6 8b4508 mov eax, dword [ebp + 8] ; [0x8:4]=-1 ; 8(null)
│ 0x080484a9 8d4dec lea ecx, [ebp - 0x14]
│ 0x080484ac 8945fc mov dword [ebp - 4], eax
│ 0x080484af c745e8000000. mov dword [ebp - 0x18], 0
│ 0x080484b6 8b45fc mov eax, dword [ebp - 4]
│ 0x080484b9 89e2 mov edx, esp
│ 0x080484bb 894204 mov dword [edx + 4], eax
│ 0x080484be 890a mov dword [edx], ecx
│ 0x080484c0 894de4 mov dword [ebp - 0x1c], ecx
│ 0x080484c3 e898feffff call sym.imp.strcpy
│ 0x080484c8 89e1 mov ecx, esp
│ 0x080484ca 8b55e4 mov edx, dword [ebp - 0x1c]
│ 0x080484cd 8911 mov dword [ecx], edx
│ 0x080484cf c74104608604. mov dword [ecx + 4], str.brillig ; [0x8048660:4]=0x6c697262 ; "brillig" @ 0x8048660
│ 0x080484d6 8945e0 mov dword [ebp - 0x20], eax
│ 0x080484d9 e862feffff call sym.imp.strcmp
│ 0x080484de 83f800 cmp eax, 0
│ 0x080484e1 0f8507000000 jne 0x80484ee
│ 0x080484e7 c745e8010000. mov dword [ebp - 0x18], 1
│ 0x080484ee 8d45ec lea eax, [ebp - 0x14]
│ 0x080484f1 89e1 mov ecx, esp
│ 0x080484f3 8901 mov dword [ecx], eax
│ 0x080484f5 c74104688604. mov dword [ecx + 4], str.outgrabe ; [0x8048668:4]=0x6774756f ; "outgrabe" @ 0x8048668
│ 0x080484fc e83ffeffff call sym.imp.strcmp
│ 0x08048501 83f800 cmp eax, 0
│ 0x08048504 0f8507000000 jne 0x8048511
│ 0x0804850a c745e8010000. mov dword [ebp - 0x18], 1
│ 0x08048511 8b45e8 mov eax, dword [ebp - 0x18]
│ 0x08048514 83c428 add esp, 0x28 ; '('
│ 0x08048517 5d pop ebp
└ 0x08048518 c3 ret