7

链接器如何在 x86-64 ELF 格式的可执行文件中找到 main 函数?

4

2 回答 2

3

一个非常通用的概述,链接器将地址分配给由符号标识的代码块main。就像它对目标文件中的所有符号一样。

实际上,它并没有分配一个真实地址,而是分配一个相对于某个基址的地址,当程序执行时,该基址将被加载程序转换为一个真实地址。

实际的入口点不太可能main,而是 crt 中调用 main 的某个符号。LD 默认情况下会查找符号start,除非您指定不同的内容。

链接的代码最终出现在.text可执行文件的部分,可能看起来像这样(非常简化):

Address | Code
1000      someFunction
...
2000      start
2001        call 3000
...
3000      main
...

当链接器写入 ELF 标头时,它会将入口点指定为地址 2000。

您可以main通过将可执行文件的内容转储为objdump. 要在运行时获取实际地址,您只需读取符号funcptr ptr = main;wherefuncptr被定义为指向具有签名的函数的指针main

typedef int (*funcptr)(int argc, char* argv[]);

int main(int argc, char* argv[])
{
    funcptr ptr = main;
    printf("%p\n", ptr);
    return 0;
}

无论符号是否被剥离,main 的地址都将被正确解析,因为链接器将首先将符号解析main为其相对地址。

像这样使用 objdump:

$ objdump -f funcptr.exe 

funcptr.exe:     file format pei-i386
architecture: i386, flags 0x0000013a:
EXEC_P, HAS_DEBUG, HAS_SYMS, HAS_LOCALS, D_PAGED
start address 0x00401000

具体来说main,在我的机器上我得到了这个:

$ objdump -D funcptr.exe | grep main
  40102c:       e8 af 01 00 00          call   4011e0 <_cygwin_premain0>
  401048:       e8 a3 01 00 00          call   4011f0 <_cygwin_premain1>
  401064:       e8 97 01 00 00          call   401200 <_cygwin_premain2>
  401080:       e8 8b 01 00 00          call   401210 <_cygwin_premain3>
00401170 <_main>:
  401179:       e8 a2 00 00 00          call   401220 <___main>
004011e0 <_cygwin_premain0>:
004011f0 <_cygwin_premain1>:
00401200 <_cygwin_premain2>:
00401210 <_cygwin_premain3>:
00401220 <___main>:

请注意,我在 Windows 上使用 Cygwin,因此您的结果会略有不同。它看起来像main生活在00401170我身上。

于 2013-07-17T20:00:03.577 回答
2

在 Binutils 上,它由以下任一决定:

  • -eCLI 选项
  • 链接器脚本

您可以使用以下命令查看链接器脚本:

ld --verbose

我的包含:

ENTRY(_start)

然后在链接时,glibc 提供crt1.o的包含该_start符号的目标文件与您的main.o.

这些目标文件为你做一些设置argv,然后调用你的main函数。

你可以看到那些额外的目标文件被gcc -v.

这记录在:https ://sourceware.org/binutils/docs/ld/Entry-Point.html#Entry-Point

在程序中执行的第一条指令称为入口点。您可以使用 ENTRY 链接描述文件命令来设置入口点。参数是一个符号名称:

 ENTRY(symbol)

有几种方法可以设置入口点。链接器将通过按顺序尝试以下每个方法来设置入口点,并在其中一个成功时停止:

  • `-e' 进入命令行选项;
  • 链接描述文件中的 ENTRY(symbol) 命令;
  • 目标特定符号的值(如果已定义);对于许多目标,这是开始,但基于 PE 和 BeOS 的系统例如检查可能的条目符号列表,匹配找到的第一个符号。
  • '.text' 部分的第一个字节的地址,如果存在的话;
  • 地址 0。

另请参阅:是否有 GCC 编译器/链接器选项来更改 main 的名称?

于 2015-09-15T20:38:07.993 回答