什么是“__gmon_start__”符号?

28

我使用gcc hello.c -o hello -O3编译此代码:

#include <stdio.h>

int main(void) {
    printf("Hello world\n");
    return 0;
}

当我列出重定位时,我得到:

test@southpark$ readelf -r hello | grep gmon
080495a4  00000106 R_386_GLOB_DAT    00000000   __gmon_start__
080495b4  00000107 R_386_JUMP_SLOT   00000000   __gmon_start__

当我列出这个文件中的符号时,我得到:

test@southpark$ readelf -s hello | grep gmon
     1: 00000000     0 NOTYPE  WEAK   DEFAULT  UND __gmon_start__
    48: 00000000     0 NOTYPE  WEAK   DEFAULT  UND __gmon_start__

gmon_start和gprof有什么关系?为什么它会有一个重定位符号,即使我没有使用-pg-g进行编译/链接?哪个库将解决这个符号?

1个回答

18

我进行了一些谷歌搜索,发现了这个链接的内容:

函数调用 call_gmon_start 初始化 gmon 分析系统。 当二进制文件使用 -pg 标志编译时会启用该系统,并创建供 gprof(1) 使用的输出。 在场景二进制文件中,call_gmon_start 位于 _start 函数直接之前。 call_gmon_start 函数查找全局偏移表(也称为 __gmon_start__)中的最后一个条目, 如果不为 NULL,则将控制权传递给指定地址。__gmon_start__ 元素指向 gmon 初始化函数, 该函数开始记录分析信息并在 atexit() 中注册清理函数。但是,在我们的情况下并未使用 gmon, 因此 __gmon_start__ 为 NULL。

所以...

  1. 是的,它与 gprof 有关。
  2. 我不确定为什么会留下这个符号。也许只是为了在编译为 gprof 时使用的占位符?

更新:

好的,我使用 -pg 和没有使用 -pg 编译了您的代码。 看起来 __gmon_start__ 被映射到编译程序内的地址。 因此,我认为没有库会解析该符号,而是程序本身。

使用 -pg

akyserr@orion:~$ readelf -r hello

Relocation section '.rel.dyn' at offset 0x32c contains 1 entries:
 Offset     Info    Type            Sym.Value  Sym. Name
08049fec  00000806 R_386_GLOB_DAT    08048460   __gmon_start__

Relocation section '.rel.plt' at offset 0x334 contains 6 entries:
 Offset     Info    Type            Sym.Value  Sym. Name
0804a000  00000607 R_386_JUMP_SLOT   080483b0   _mcleanup
0804a004  00000107 R_386_JUMP_SLOT   00000000   __monstartup
0804a008  00000207 R_386_JUMP_SLOT   00000000   mcount
0804a00c  00000307 R_386_JUMP_SLOT   00000000   __cxa_atexit
0804a010  00000407 R_386_JUMP_SLOT   00000000   puts
0804a014  00000507 R_386_JUMP_SLOT   00000000   __libc_start_main

__gmon_start__代码的objdump:

akyserr@orion:~$ objdump -S hello  | grep "460 <__gmon_start__>:" -A 20

08048460 <__gmon_start__>:
 8048460:       83 ec 1c                sub    $0x1c,%esp
 8048463:       a1 20 a0 04 08          mov    0x804a020,%eax
 8048468:       85 c0                   test   %eax,%eax
 804846a:       75 2a                   jne    8048496 <__gmon_start__+0x36>
 804846c:       c7 05 20 a0 04 08 01    movl   $0x1,0x804a020
 8048473:       00 00 00 
 8048476:       c7 44 24 04 36 86 04    movl   $0x8048636,0x4(%esp)
 804847d:       08 
 804847e:       c7 04 24 30 84 04 08    movl   $0x8048430,(%esp)
 8048485:       e8 36 ff ff ff          call   80483c0 <__monstartup@plt>
 804848a:       c7 04 24 b0 83 04 08    movl   $0x80483b0,(%esp)
 8048491:       e8 1a 01 00 00          call   80485b0 <atexit>
 8048496:       83 c4 1c                add    $0x1c,%esp
 8048499:       c3                      ret    
 804849a:       90                      nop
 804849b:       90                      nop
 804849c:       90                      nop
 804849d:       90                      nop

在编译的 hello 程序中加入 __gmon_start__ 后,你可以看到调用了 __monstartup。(monstartup man page

没有使用 -pg 选项:

akyserr@orion:~$ readelf -r hello 

Relocation section '.rel.dyn' at offset 0x290 contains 1 entries:
 Offset     Info    Type            Sym.Value  Sym. Name
08049ff0  00000206 R_386_GLOB_DAT    00000000   __gmon_start__

Relocation section '.rel.plt' at offset 0x298 contains 3 entries:
 Offset     Info    Type            Sym.Value  Sym. Name
0804a000  00000107 R_386_JUMP_SLOT   00000000   puts
0804a004  00000207 R_386_JUMP_SLOT   00000000   __gmon_start__
0804a008  00000307 R_386_JUMP_SLOT   00000000   __libc_start_main

您可以在这里看到,__gmon_start__ 符号的值被设置为 00000000


关于第三个问题呢?我应该把GOT中的最后一个条目视为__gmon_start__的地址吗?此外,检查“hello”的二进制文件时发现有一个入口:gmon_start@plt和另一个(PLT入口)gmon_start@plt-0x10。 - JohnTortugo
gmon_start的偏移量是如何映射到物理地址的? - RouteMapper

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接