代码之家  ›  专栏  ›  技术社区  ›  user4180854

对象文件中的符号引用到底是什么?

  •  8
  • user4180854  · 技术社区  · 7 年前

    对象文件定义和引用符号,其中每个符号对应于函数、全局变量或静态变量(即,使用静态属性声明的任何C变量)。符号解析的目的是将每个符号引用与恰好一个符号定义相关联。

    但他们并没有阐明符号引用的含义,即使他们开始深入描述符号分辨率。那么,在可重新定位的对象文件中,符号究竟是如何被引用的呢?

    2 回复  |  直到 7 年前
        1
  •  9
  •   shallowThought    3 年前

    考虑以下来源:

    static int foo() { return 42; }
    static int bar() { return foo() + 1; }
    
    extern int baz();
    
    int main()
    {
      return foo() + bar() + baz();
    }
    

    之后 gcc -c foo.c objdump -d foo.o 在x86_64 Linux上:

    foo.o:     file format elf64-x86-64
    
    Disassembly of section .text:
    
    0000000000000000 <foo>:
       0:   55                      push   %rbp
       1:   48 89 e5                mov    %rsp,%rbp
       4:   b8 2a 00 00 00          mov    $0x2a,%eax
       9:   5d                      pop    %rbp
       a:   c3                      retq
    
    000000000000000b <bar>:
       b:   55                      push   %rbp
       c:   48 89 e5                mov    %rsp,%rbp
       f:   b8 00 00 00 00          mov    $0x0,%eax
      14:   e8 e7 ff ff ff          callq  0 <foo>
      19:   83 c0 01                add    $0x1,%eax
      1c:   5d                      pop    %rbp
      1d:   c3                      retq
    
    000000000000001e <main>:
      1e:   55                      push   %rbp
      1f:   48 89 e5                mov    %rsp,%rbp
      22:   53                      push   %rbx
      23:   48 83 ec 08             sub    $0x8,%rsp
      27:   b8 00 00 00 00          mov    $0x0,%eax
      2c:   e8 cf ff ff ff          callq  0 <foo>
      31:   89 c3                   mov    %eax,%ebx
      33:   b8 00 00 00 00          mov    $0x0,%eax
      38:   e8 ce ff ff ff          callq  b <bar>
      3d:   01 c3                   add    %eax,%ebx
      3f:   b8 00 00 00 00          mov    $0x0,%eax
      44:   e8 00 00 00 00          callq  49 <main+0x2b>
      49:   01 d8                   add    %ebx,%eax
      4b:   48 83 c4 08             add    $0x8,%rsp
      4f:   5b                      pop    %rbx
      50:   5d                      pop    %rbp
      51:   c3                      retq
    

    这里有几点需要注意:

    1. 注意如何 bar 电话 foo at地址 0 怎么做 objdump 知道这是 有人叫他? 它真的能在地址0吗?(大多数现代系统将虚拟内存的零页映射为 PROT_NONE ,因此无法进行读写访问。)
    2. 注意如何调用 baz main 酒吧 酒吧 巴兹 将。

    那么,考虑到上述信息,链接器如何将其转化为明智之举呢?它不能:这里没有足够的信息。

    为了使链接器能够将引用链接到 (我们还没有看到)呼叫 巴兹 ,它需要其他信息。在ELF系统上,附加信息会写入一个特殊部分 .rela.text

    $ readelf -Wr foo.o
    
    Relocation section '.rela.text' at offset 0x5d0 contains 1 entries:
        Offset             Info             Type               Symbol's Value  Symbol's Name + Addend
    0000000000000045  0000000b00000002 R_X86_64_PC32          0000000000000000 baz - 4
    

    是书中提到的“参考”,但没有定义。它告诉链接器:如果你能找到 (在其他物体中),取其地址,并将其(实际上, &baz - 4 因为 CALL 指令相对于 下一个 之后的说明 .text foo.o

    如果没有这样的定义呢?链接器将产生错误:

    $ gcc foo.o
    foo.o: In function `main':
    foo.c:(.text+0x45): undefined reference to `baz'
    collect2: error: ld returned 1 exit status
    

    最后,谈到上面的第1点: 真的在地址0?

    没有,但是 地址指示 0x14 实际上没有说 CALL 0 0x400501 ,则该调用的目标将是 0x4004ed ,这是 呼叫 .文本 傅。o

        2
  •  4
  •   DimeCadmium    7 年前

    这个答案很好,但也有一个简短的答案:符号引用是指任何时候使用变量(或函数名)。符号定义创建变量(或函数名)。

    所以符号的定义是 int bar; int foo() { ... } . 然后,符号引用将是 foo(bar) (两个参考文件: foo bar ).