【发布时间】:2020-10-27 16:54:53
【问题描述】:
我是 C 和 GCC 编译器的新手,并试图通过反汇编生成的二进制文件来研究如何将 C 编译成机器代码,但是编译然后反汇编一个非常简单的函数的结果似乎过于复杂。
我有 basic.c 文件:
int my_function(){
int a = 0xbaba;
int b = 0xffaa;
return a + b;
}
然后我使用 gcc -ffreestanding -c basic.c -o basic.o
编译它当我分解 basic.o 目标文件时,我得到了相当预期的输出:
0000000000000000 <my_function>:
0: 55 push %rbp
1: 48 89 e5 mov %rsp,%rbp
4: c7 45 fc ba ba 00 00 movl $0xbaba,-0x4(%rbp)
b: c7 45 f8 aa ff 00 00 movl $0xffaa,-0x8(%rbp)
12: 8b 55 fc mov -0x4(%rbp),%edx
15: 8b 45 f8 mov -0x8(%rbp),%eax
18: 01 d0 add %edx,%eax
1a: 5d pop %rbp
1b: c3 retq
看起来很棒。但后来我使用链接器生成原始二进制文件:ld -o basic.bin -Ttext 0x0 --oformat binary basic.o
所以在使用命令 ndisasm -b 32 basic.bin > basic.dis 反汇编这个 basic.bin 文件后,我得到了一些有趣的东西:
00000000 55 push ebp
00000001 48 dec eax
00000002 89E5 mov ebp,esp
00000004 C745FCBABA0000 mov dword [ebp-0x4],0xbaba
0000000B C745F8AAFF0000 mov dword [ebp-0x8],0xffaa
00000012 8B55FC mov edx,[ebp-0x4]
00000015 8B45F8 mov eax,[ebp-0x8]
00000018 01D0 add eax,edx
0000001A 5D pop ebp
0000001B C3 ret
0000001C 0000 add [eax],al
0000001E 0000 add [eax],al
00000020 1400 adc al,0x0
00000022 0000 add [eax],al
00000024 0000 add [eax],al
00000026 0000 add [eax],al
00000028 017A52 add [edx+0x52],edi
0000002B 0001 add [ecx],al
0000002D 7810 js 0x3f
0000002F 011B add [ebx],ebx
00000031 0C07 or al,0x7
00000033 08900100001C or [eax+0x1c000001],dl
00000039 0000 add [eax],al
0000003B 001C00 add [eax+eax],bl
0000003E 0000 add [eax],al
00000040 C0FFFF sar bh,byte 0xff
00000043 FF1C00 call far [eax+eax]
00000046 0000 add [eax],al
00000048 00410E add [ecx+0xe],al
0000004B 108602430D06 adc [esi+0x60d4302],al
00000051 57 push edi
00000052 0C07 or al,0x7
00000054 0800 or [eax],al
00000056 0000 add [eax],al
我真的不知道像 SAR、JS、DEC 这样的命令来自哪里以及为什么需要它们。我猜,那是因为我为编译器或链接器指定了无效的参数。
【问题讨论】:
-
它们不是命令(指令),它们是您作为指令反汇编的数据。它们不是必需的,您的目标文件中可能有除
.text之外的其他部分。 -
使用
objdump -D打印出这些部分。但您看到的可能是与.eh_frame部分相关的数据。这些部分只是数据,但 ndiasm 将所有内容解码为指令,因为二进制格式不会区分实际代码和数据,因此默认情况下所有内容都被解码为指令。 -
如果您删除
.eh_frame部分或根本不生成它们,那么您应该看到您想要的。尝试将-fno-asynchronous-unwind-tables选项添加到 GCC 命令行。评论不会进入二进制文件,但.eh_frame会。您生成了 64 位代码,因此您需要使用-b64反汇编以获得您想要的解码。 -
另外,您编译为 64 位机器代码,但随后您将其反汇编为 32 位。这就是为什么
mov rbp, rsp变成dec eax; mov ebp, esp的原因。 -
那么您的函数以
ret结束,因此永远不会执行其他数据。ret下面结束函数的所有东西都不会被执行。这只是数据。.comment部分位于 ELF 文件(对象)中,但未标记为可分配,因此在生成二进制文件时将它们排除在外。.eh_frame部分是可分配的,因此它们出现在二进制文件中。