我目前在C中编译一个已购买的数据堆栈。我使用自己的工具编译它,在后台使用gcc。我可以根据需要将标志和参数传递给gcc。我想知道,从哪个文件是main()使用。也就是说,在项目中,哪个文件是起点。有没有办法告诉gcc生成一个文件列表,或类似的,因为我不知道哪个文件是main()被采取?谢谢。
答案 0 :(得分:3)
您可以反汇编最终的可执行文件以找到起点。虽然您没有提供任何其他信息来帮助您。我正在使用示例代码来演示该过程。
#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
通过它。一些答案已经指出了这一点。
答案 1 :(得分:1)
调用main()
的地方依赖于实现 - 使用GCC,它很可能是/ usr / lib中的一个名为crt0.o
或crt1.o
的存根目标文件叫做。 (此文件包含依赖于操作系统的符号,当您的应用程序加载到内存时,内核会自动调用该符号。在Linux和Mac OS X上,这称为start
)。
答案 2 :(得分:0)
使用调试符号编译项目,使用可执行文件启动gdb
,然后编写list main
,后跟“break
”或直接break main
。
答案 3 :(得分:0)
您可以使用objdump -t
列出目标文件中的符号。因此,假设您使用的是Linux,并且还假设目标文件仍在某处,您可以这样做:
find -name '*.o' -print0 \
| xargs -0 objdump -t \
| awk '/\.o:/{f=$1} /\.text\.main/{print f, $6}'
这将打印目标文件列表以及对它们包含的main
的引用。通常应该有一个从目标文件到源文件的简单映射。如果有多个包含该符号的目标文件,那么它取决于实际链接到您正在查看的二进制文件中的哪一个,因为每个可执行二进制文件只能有一个main
(除了可能用于一些非常奇特的黑魔法。)
链接应用程序并删除调试符号后,通常没有指示特定功能来自哪个源文件。例外情况是包含函数名称作为字符串文字的文件,例如使用__FILE__
宏。在剥离调试符号之前,您可以使用调试器来获取该信息。如果包含调试符号,那就是。