找到起始点,int main()

时间:2012-07-30 10:36:38

标签: c gcc compilation linker main

我目前在C中编译一个已购买的数据堆栈。我使用自己的工具编译它,在后台使用gcc。我可以根据需要将标志和参数传递给gcc。我想知道,从哪个文件是main()使用。也就是说,在项目中,哪个文件是起点。有没有办法告诉gcc生成一个文件列表,或类似的,因为我不知道哪个文件是main()被采取?谢谢。

4 个答案:

答案 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_mainmainstart的引用,以查看您的可执行文件的初始化和启动方式main

现在,当使用默认链接描述文件完成链接时,所有这些都已完成。许多大项目都会使用自己的链接器脚本。如果您的项目具有自定义链接描述文件,则根据使用的链接描述文件,查找起点将有所不同。有些项目不使用glibc's运行时。在这种情况下,仍然可以通过黑客攻击目标文件,库档案等来找到起点。

如果您的二进制符号是stripped符号,那么您必须实际上依靠汇编程序技能来查找它的起始位置。

我假设您没有源代码,即堆栈仅与一些库和一些头定义一起分发。(商业软件供应商的常见做法)。

但如果你有你的消息来源,那么它就太琐碎了。只需grep通过它。一些答案已经指出了这一点。

答案 1 :(得分:1)

调用main()的地方依赖于实现 - 使用GCC,它很可能是/ usr / lib中的一个名为crt0.ocrt1.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__宏。在剥离调试符号之前,您可以使用调试器来获取该信息。如果包含调试符号,那就是。