我目前正在用 C 编译一个购买的数据堆栈。我使用他们自己的工具来编译它,在后台使用 gcc。我可以将标志和参数传递给我认为合适的 gcc。我想知道,使用的 main() 来自哪个文件。也就是在项目中,哪个文件是起点。有没有办法告诉 gcc 生成文件列表或类似文件,因为我不知道 main() 是从哪个文件中获取的?谢谢你。
4 回答
您可以反汇编最终的可执行文件以找到起点。尽管您没有提供任何其他信息来为您提供更多帮助。我正在使用示例代码来演示该过程。
#include <stdio.h>
int main() {
printf("hello world\n");
return 0;
}
现在该对象main.o
具有以下内容
[root@s1 sf]# gcc -c main.c
[root@s1 sf]# nm main.o
0000000000000000 T main
U puts
你可以看到 main 没有初始化。因为它会在链接阶段发生变化。现在链接后:
$gcc main.o
$nm a.out
U __libc_start_main@@GLIBC_2.2.5
0000000000600874 A _edata
0000000000600888 A _end
00000000004005b8 T _fini
0000000000400390 T _init
00000000004003e0 T _start
000000000040040c t call_gmon_start
0000000000600878 b completed.6347
0000000000600870 W data_start
0000000000600880 b dtor_idx.6349
00000000004004a0 t frame_dummy
00000000004004c4 T main
您会看到 main 现在有一个地址。但它仍然不是最终的。因为这个 main 将由 C 运行时动态调用。您可以看到谁将参与其中U __libc_start_main@@GLIBC_2.2.5
:
[root@s1 sf]# ldd a.out
linux-vdso.so.1 => (0x00007fff61de1000) /* the linux system call interface */
libc.so.6 => /lib64/libc.so.6 (0x0000003c96000000) /* libc runime , this will invoke your main*/
/lib64/ld-linux-x86-64.so.2 (0x0000003c95c00000) /* dynamic loader */
现在您可以通过查看反汇编来验证这一点:
00000000004003e0 <_start>:
..........
4003fd: 48 c7 c7 c4 04 40 00 mov rdi,0x4004c4 /* address of start of main */
400404: e8 bf ff ff ff call 4003c8 <__libc_start_main@plt> /* this will set up the environment for main, like pushing argc and argv to stack */
...........
如果您没有源代码,那么您可以在可执行文件中搜索对libc_start_main
或main
或的引用,start
以查看您的可执行文件如何初始化并启动main
.
现在所有这些都是在使用默认链接描述文件完成链接时完成的。许多大项目会使用自己的链接器脚本。如果您的项目有自定义链接描述文件,那么根据所使用的链接描述文件查找起点会有所不同。有些项目不使用glibc's
运行时。在这种情况下,仍然可以通过破解目标文件、库档案等来找到起点。
如果您的二进制文件stripped
来自符号,那么您实际上必须依靠您的汇编技能来找到它的开始位置。
我假设您没有源代码,即堆栈仅与一些库和一些标头定义一起分发。(商业软件供应商的常见做法)。
但是,如果您有资源,那么它就太微不足道了。只是grep
你的方式。一些答案已经指出了这一点。
从哪里main()
被调用是依赖于实现的——使用 GCC,它很可能是 /usr/lib 中的存根对象文件被调用crt0.o
或crt1.o
从中调用它。(此文件包含操作系统相关的符号,当您的应用程序加载到内存时,内核会自动调用该符号。在 Linux 和 Mac OS X 上,这称为start
)。
您使用调试符号编译项目,从gdb
可执行文件开始,然后编写list main
,然后是 ' break
' 或直接break main
。
您可以使用objdump -t
列出目标文件中的符号。因此,假设您在 Linux 上,并且还假设目标文件仍在某处,您可以这样做:
find -name '*.o' -print0 \
| xargs -0 objdump -t \
| awk '/\.o:/{f=$1} /\.text\.main/{print f, $6}'
这将打印一个目标文件列表以及main
它们包含的引用。通常应该有一个从目标文件到源文件的简单映射。如果有多个包含该符号的目标文件,那么这取决于其中一个实际链接到您正在查看的二进制文件中,因为main
每个可执行二进制文件不能超过一个(可能除了一些非常奇特的黑魔法)。
链接应用程序并去除调试符号后,通常没有迹象表明特定函数来自哪个源文件。例外情况是包含函数名称作为字符串文字的文件,例如使用__FILE__
宏。在剥离调试符号之前,您可以使用调试器来获取该信息。如果包含调试符号,那就是。