0

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

4

4 回答 4

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_mainmain或的引用,start以查看您的可执行文件如何初始化并启动main.

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

如果您的二进制文件stripped来自符号,那么您实际上必须依靠您的汇编技能来找到它的开始位置。

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

但是,如果您有资源,那么它就太微不足道了。只是grep你的方式。一些答案已经指出了这一点。

于 2012-07-30T11:46:31.990 回答
1

从哪里main()被调用是依赖于实现的——使用 GCC,它很可能是 /usr/lib 中的存根对象文件被调用crt0.ocrt1.o从中调用它。(此文件包含操作系统相关的符号,当您的应用程序加载到内存时,内核会自动调用该符号。在 Linux 和 Mac OS X 上,这称为start)。

于 2012-07-30T10:40:10.393 回答
0

您使用调试符号编译项目,从gdb可执行文件开始,然后编写list main,然后是 ' break' 或直接break main

于 2012-07-30T10:57:32.733 回答
0

您可以使用objdump -t列出目标文件中的符号。因此,假设您在 Linux 上,并且还假设目标文件仍在某处,您可以这样做:

find -name '*.o' -print0 \
| xargs -0 objdump -t \
| awk '/\.o:/{f=$1} /\.text\.main/{print f, $6}'

这将打印一个目标文件列表以及main它们包含的引用。通常应该有一个从目标文件到源文件的简单映射。如果有多个包含该符号的目标文件,那么这取决于其中一个实际链接到您正在查看的二进制文件中,因为main每个可执行二进制文件不能超过一个(可能除了一些非常奇特的黑魔法)。

链接应用程序并去除调试符号后,通常没有迹象表明特定函数来自哪个源文件。例外情况是包含函数名称作为字符串文字的文件,例如使用__FILE__宏。在剥离调试符号之前,您可以使用调试器来获取该信息。如果包含调试符号,那就是。

于 2012-07-30T12:02:17.437 回答