ELF动态符号表

时间:2015-09-23 10:38:33

标签: gcc elf

我对ELF动态符号表有疑问。对于FUNC类型的符号,我注意到某些二进制文件中的值为0。但在其他二进制文件中,它具有一些非零值。这两个二进制文件都是由gcc生成的,我想知道为什么会有这种区别?是否有任何编译器选项来控制它?

编辑:这是readelf --dyn-syms prog1

的输出
Symbol table '.dynsym' contains 5 entries:
Num:    Value  Size Type    Bind   Vis      Ndx Name
 0: 00000000     0 NOTYPE  LOCAL  DEFAULT  UND 
 1: 00000000     0 NOTYPE  WEAK   DEFAULT  UND __gmon_start__
 2: 000082f0     0 FUNC    GLOBAL DEFAULT  UND printf@GLIBC_2.4 (2)
 3: 00008314     0 FUNC    GLOBAL DEFAULT  UND abort@GLIBC_2.4 (2)
 4: 000082fc     0 FUNC    GLOBAL DEFAULT  UND __libc_start_main@GLIBC_2.4 

" printf"符号是82f0,它恰好是printf的plt表条目的地址。

readelf的输出--dyn-syms prog2

Symbol table '.dynsym' contains 6 entries:
Num:    Value  Size Type    Bind   Vis      Ndx Name
 0: 00000000     0 NOTYPE  LOCAL  DEFAULT  UND 
 1: 00000000     0 NOTYPE  WEAK   DEFAULT  UND __gmon_start__
 2: 00000000     0 FUNC    GLOBAL DEFAULT  UND puts@GLIBC_2.4 (2)
 3: 00000000     0 FUNC    GLOBAL DEFAULT  UND printf@GLIBC_2.4 (2)
 4: 00000000     0 FUNC    GLOBAL DEFAULT  UND abort@GLIBC_2.4 (2)
 5: 00000000     0 FUNC    GLOBAL DEFAULT  UND __libc_start_main@GLIBC_2.4 

此处所有符号的值均为零。

2 个答案:

答案 0 :(得分:2)

x86_64 SV ABI强制要求(强调我的):

  

允许比较函数地址按预期工作,   如果可执行文件引用共享对象中定义的函数,   链接编辑器将放置过程链接表的地址   该函数在其关联的符号表条目中的条目。   这将导致符号表条目的节索引为   SHN_UNDEF但是一种STT_FUNC和非零st_value 。   从共享内引用函数的地址   图书馆会满意的   通过可执行文件中的这种定义。

我的GCC,这个程序:

#include <stdio.h>

int main()
{
  printf("hello %i\n", 42);
  return 0;
}

直接编译成可执行文件时会生成一个空值:

 1: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND printf@GLIBC_2.2.5 (2)

但是这个程序与printf函数的比较:

#include <stdio.h>

int main()
{
  printf("hello %i\n", 42);
  if (printf == puts)
    return 1;
  return 0;
}

生成非空值:

 3: 0000000000400410     0 FUNC    GLOBAL DEFAULT  UND printf@GLIBC_2.2.5 (2)

在.o文件中,第一个程序生成:

000000000014  000a00000002 R_X86_64_PC32     0000000000000000 printf - 4

和第二个:

000000000014  000a00000002 R_X86_64_PC32     0000000000000000 printf - 4
000000000019  000a0000000a R_X86_64_32       0000000000000000 printf + 0

差异是由获取函数地址的额外R_X86_64_32重定位引起的。

答案 1 :(得分:1)

在某些二进制文件上运行readelf进行观察

所有未完成的功能都为零。

这些未定义的函数是通过库调用的函数。在我的小型ELF二进制文件中,所有对GLIBc的引用都未定义,大小为零

第21页的http://docs.oracle.com/cd/E19457-01/801-6737/801-6737.pdf

很明显,符号表可以有三种类型的符号。在这三种中,两种类型的UNDEFINED和TENTATIVE符号是那些没有分配存储的符号。在后面的例子中,您可以在readelf输出中看到一些未定义的函数(具有索引)并且没有存储。

为清楚起见,未定义的符号是那些被引用但未分配存储(尚未创建)的符号,而暂定符号是那些被创建但没有分配存储的符号。例如未初始化的符号

编辑

如果你在谈论.plt,共享库符号bind是懒惰的。

如何控制绑定请参阅http://www.linuxjournal.com/article/1060

  

此功能称为延迟符号绑定。这个想法是,如果你有很多共享库,它可能需要动态加载器很多时间来查找所有函数来初始化所有.plt插槽,所以最好将绑定地址推迟到函数,直到我们确实需要它们。如果您最终只使用共享库中的一小部分功能,那么这将是一个巨大的胜利。在将控制权转移到应用程序之前,可以指示动态加载程序将地址绑定到所有.plt插槽 - 这是通过在运行程序之前设置环境变量 LD_BIND_NOW = 1 来完成的。例如,在调试程序时,这在某些情况下非常有用。另外,我应该指出.plt在只读内存中。因此,用于跳转目标的地址实际上存储在.got部分中。 .got还包含一组指针,用于指向来自共享库的程序中使用的所有全局变量。