我编译了一个简单的helloworld并使用objdump进行了拆卸。

开始时有_init

0000000000400600 <_init>:
  400600:   48 83 ec 08             sub    rsp,0x8
  400604:   48 8b 05 ed 09 20 00    mov    rax,QWORD PTR [rip+0x2009ed]        # 600ff8 <_DYNAMIC+0x1e0>
  40060b:   48 85 c0                test   rax,rax
  40060e:   74 05                   je     400615 <_init+0x15>
  400610:   e8 1b 00 00 00          call   400630 <__gmon_start__@plt>
  400615:   48 83 c4 08             add    rsp,0x8
  400619:   c3                      ret    


是什么_DYNAMIC
使用-x我可以看到部分详细信息:

Dynamic Section:
  NEEDED               libstdc++.so.6
  NEEDED               libc.so.6
  INIT                 0x0000000000400600
  FINI                 0x0000000000400864
  INIT_ARRAY           0x0000000000600df8
  INIT_ARRAYSZ         0x0000000000000010
  FINI_ARRAY           0x0000000000600e08
  FINI_ARRAYSZ         0x0000000000000008
  GNU_HASH             0x0000000000400298
  STRTAB               0x00000000004003c8
  SYMTAB               0x00000000004002c0
  STRSZ                0x000000000000011c
  SYMENT               0x0000000000000018
  DEBUG                0x0000000000000000
  PLTGOT               0x0000000000601000
  PLTRELSZ             0x0000000000000090
  PLTREL               0x0000000000000007
  JMPREL               0x0000000000400570
  RELA                 0x0000000000400540
  RELASZ               0x0000000000000030
  RELAENT              0x0000000000000018
  VERNEED              0x0000000000400500
  VERNEEDNUM           0x0000000000000002
  VERSYM               0x00000000004004e4


但是,我不确定rip+0x2009ed指的是哪个条目。
考虑下一行是对gmon的调用,它与GPROF挂钩有关系吗?

#1 楼

初始化代码使用此技巧来支持对其进行编译时的监视,并在不进行编译时将其忽略。

当我使用gcc -pg编译小型测试程序时,然后调用objdump -Mintel -d在它上面,我得到:

00000000004004c0 <_init>:
  4004c0:   48 83 ec 08             sub    rsp,0x8
  4004c4:   48 8d 05 c5 00 00 00    lea    rax,[rip+0xc5]        # 400590 <__gmon_start__>
  4004cb:   48 85 c0                test   rax,rax
  4004ce:   74 05                   je     4004d5 <_init+0x15>
  4004d0:   e8 bb 00 00 00          call   400590 <__gmon_start__>
  4004d5:   48 83 c4 08             add    rsp,0x8
  4004d9:   c3                      ret    


如果在编译时省略了-pg,它将变为:

0000000000400418 <_init>:
  400418:   48 83 ec 08             sub    rsp,0x8
  40041c:   48 8b 05 d5 0b 20 00    mov    rax,QWORD PTR [rip+0x200bd5]        # 600ff8 <_DYNAMIC+0x1d0>
  400423:   48 85 c0                test   rax,rax
  400426:   74 05                   je     40042d <_init+0x15>
  400428:   e8 43 00 00 00          call   400470 <__gmon_start__@plt>
  40042d:   48 83 c4 08             add    rsp,0x8
  400431:   c3                      ret    


因此,您看到在启用监视的情况下,代码在调用函数之前检查__gmon_start__是否不为null。在禁用监视的情况下,它将检查一些变量,如果该变量为0,则会跳过对__gmon_start__的调用。

但是请稍候。为什么在第一种情况下有一个lea,在第二种情况下有一个mov?以及为什么名称以@plt结尾?
因为,即使您的程序没有编译性能分析,也许您的某些动态库也可以,并且您正在针对启用了性能分析的C版本运行图书馆。因此,C库可以提供__gmon_start__的动态版本,并提供标记来标记它是否这样做。此功能和标志在GOT和GOTPLT部分中定义。

确实,如果您注意到所使用的地址600FF8,并从部分详细信息中向下滚动objdump -x输出,您将请参见:

 21 .got          00000008  0000000000600ff8  0000000000600ff8  00000ff8  2**3
                  CONTENTS, ALLOC, LOAD, DATA
 22 .got.plt      00000038  0000000000601000  0000000000601000  00001000  2**3
                  CONTENTS, ALLOC, LOAD, DATA


您将看到代码访问GOT表条目(对于小型测试程序,该条目是GOT中唯一的条目,该条目这就是为什么GOT只有8个字节的大小。)