我正在学习如何将 C 文件编译为机器代码。我知道我可以使用
gcc
标志从 -S
生成程序集,但是它也会生成很多与 main()
和 printf()
相关的代码,我目前对此不感兴趣。
有没有办法让
gcc
或 clang
独立地“编译”函数并输出程序集?
即 单独获取以下 c 的程序集:
int add( int a, int b ) {
return a + b;
}
对于特定的目标文件有两种方法可以执行此操作:
-ffunction-sections
的gcc
选项指示它为正在编译的源文件中的每个函数创建一个单独的ELF部分。objdump
/--start-address
参数输入 --stop-address
。第一个例子:
$ readelf -S t.o | grep '.text.' [1] .text PROGBITS 0000000000000000 00000040 [4] .text.foo PROGBITS 0000000000000000 00000040 [6] .text.bar PROGBITS 0000000000000000 00000060 [9] .text.foo2 PROGBITS 0000000000000000 000000c0 [11] .text.munch PROGBITS 0000000000000000 00000110 [14].text.startup.mai PROGBITS 0000000000000000 00000180
这是用
-ffunction-sections
编译的,在我的目标文件中有四个函数,foo()
、bar()
、foo2()
和munch()
。我可以像这样单独拆卸它们:
$ objdump -w -d --section=.text.foo to.o to.o:文件格式 elf64-x86-64 .text.foo 部分的反汇编: 0000000000000000: 0: 48 83 ec 08 子 $0x8,%rsp 4: 8b 3d 00 00 00 00 mov 0(%rip),%edi # a a: 31 f6 异或 %esi,%esi c: 31 c0 异或 %eax,%eax e: e8 00 00 00 00 呼叫 13 13: 85 c0 测试 %eax,%eax 15: 75 01 jne 18 17:90 没有 18: 48 83 c4 08 添加 $0x8,%rsp 1c: c3 retq
另一个选项可以像这样使用(
nm
转储符号表条目):
$ nm -f sysv t.o | $ nm -f sysv t.o | grep 栏 酒吧|0000000000000020| T | 功能|0000000000000026| |.文本 $ objdump -w -d --start-address=0x20 --stop-address=0x46 to--section=.text to.o:文件格式 elf64-x86-64 .text 节的反汇编: 0000000000000020: 20: 48 83 ec 08 子 $0x8,%rsp 24: 8b 3d 00 00 00 00 mov 0(%rip),%edi # 2a 2a: 31 f6 异或 %esi,%esi 2c: 31 c0 异或 %eax,%eax 2e: e8 00 00 00 00 呼叫 33 33: 85 c0 测试 %eax,%eax 35:75 01 jne 38 37:90 没有 38: bf 3f 00 00 00 移动 $0x3f,%edi 3d: 48 83 c4 08 添加 $0x8,%rsp 41:e9 00 00 00 00 jmpq 46
在这种情况下,
-ffunction-sections
选项尚未使用,因此函数的起始偏移量不为零,并且它不在其单独的部分中(但在.text
中)。
反汇编目标文件时要小心...
这不是完全你想要的,因为,对于目标文件,
call
目标(以及全局变量的地址)没有解析 - 你在这里看不到foo
调用printf
,因为二进制级别的解析仅发生在链接时。不过,汇编源代码中会有 call printf
。这个 callq
实际上是 printf
的信息位于目标文件中,但与代码分开(它位于所谓的 relocation 部分,列出了目标文件中要由链接器“修补”的位置);反汇编程序无法解决这个问题。
最好的方法是将函数复制到单个
temp.c
C 文件中,并使用 -c
标志进行编译,如下所示:gcc -c -S temp.c -o temp.s
它应该生成更紧凑的汇编代码,没有其他干扰(页眉和页脚除外)。