連結使用的符號

aron1992發表於2019-04-04

連結使用的符號

在連結的過程中,函式名和變數名統稱為符號,連結的過程就是目標檔案之間的對地址的引用,即對函式和變數地址的引用。

使用 nm 命令檢視目標檔案的符號:

[root@localhost simple-secion-linux-elf]# nm SimpleSection.o
0000000000000000 T func1
0000000000000000 D global_init_var
0000000000000004 C global_uninit_var
0000000000000024 T main
                 U printf
0000000000000004 d static_var.2058
0000000000000000 b static_var2.2059
複製程式碼

ELF符號的結構體,定義於“/usr/include”

/* Symbol table entry.  */
typedef struct
{
  Elf32_Word	st_name;		/* Symbol name (string tbl index) */
  Elf32_Addr	st_value;		/* Symbol value */
  Elf32_Word	st_size;		/* Symbol size */
  unsigned char	st_info;		/* Symbol type and binding */
  unsigned char	st_other;		/* Symbol visibility */
  Elf32_Section	st_shndx;		/* Section index */
} Elf32_Sym;
複製程式碼

Elf32_Sym成員的定義解釋:

Elf32_Sym成員的定義
Elf32_Sym成員的定義

st_info(符號繫結資訊),低4位表示符號型別(Symbol Type),高28位表示符號繫結資訊(Symbol Binding) 

符號繫結資訊
符號繫結資訊

符號型別
符號型別

st_shndx(符號所在段) 如果符號是定義在目標檔案中,那麼符號的意思就是符號所在的段的在段表中的下標 ,此外還有幾種特殊的定義如下:

符號所在段
符號所在段

st_value(符號值) 符號如果是一個函式或者變數,符號值表示函式或者變數的地址,特殊地st_value還有以下幾種可能:

  • 在目標檔案中,符號型別不是COMMON型別(st_shndx!=SHN_COMMON),st_value表示符號在段中的偏移,即符號位於st_shndx所在的段偏移st_value,在該例子中的global_init_var、func1、main都屬於這種型別。
  • 在目標檔案中,符號型別是COMMON型別(st_shndx=SHN_COMMON),st_value表示符號的對其屬性,在該例子中的global_uninit_var的Value值為0000000000000004,表示對其長度為4位元組,即為global_uninit_var型別(int)的位元組長度
  • 在可執行檔案中,st_value表示符號的虛擬地址,動態連結需要使用到

使用 readelf -s 命令檢視目標檔案的所有符號詳細資訊

[root@localhost simple-secion-linux-elf]# readelf -s SimpleSection.o

Symbol table '.symtab' contains 16 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND 
     1: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS SimpleSection.c
     2: 0000000000000000     0 SECTION LOCAL  DEFAULT    1 
     3: 0000000000000000     0 SECTION LOCAL  DEFAULT    3 
     4: 0000000000000000     0 SECTION LOCAL  DEFAULT    4 
     5: 0000000000000000     0 SECTION LOCAL  DEFAULT    5 
     6: 0000000000000004     4 OBJECT  LOCAL  DEFAULT    3 static_var.2058
     7: 0000000000000000     4 OBJECT  LOCAL  DEFAULT    4 static_var2.2059
     8: 0000000000000000     0 SECTION LOCAL  DEFAULT    7 
     9: 0000000000000000     0 SECTION LOCAL  DEFAULT    8 
    10: 0000000000000000     0 SECTION LOCAL  DEFAULT    6 
    11: 0000000000000000     4 OBJECT  GLOBAL DEFAULT    3 global_init_var
    12: 0000000000000004     4 OBJECT  GLOBAL DEFAULT  COM global_uninit_var
    13: 0000000000000000    36 FUNC    GLOBAL DEFAULT    1 func1
    14: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND printf
    15: 0000000000000024    57 FUNC    GLOBAL DEFAULT    1 main
複製程式碼

使用 readelf -S 檢視目標檔案的所有段資訊,後面分析符號資訊的時候會作為參照使用到。

[root@localhost simple-secion-linux-elf]# readelf -S SimpleSection.o
There are 13 section headers, starting at offset 0x1a0:

Section Headers:
  [Nr] Name              Type             Address           Offset
       Size              EntSize          Flags  Link  Info  Align
  [ 0]                   NULL             0000000000000000  00000000
       0000000000000000  0000000000000000           0     0     0
  [ 1] .text             PROGBITS         0000000000000000  00000040
       000000000000005d  0000000000000000  AX       0     0     4
  [ 2] .rela.text        RELA             0000000000000000  000006c8
       0000000000000078  0000000000000018          11     1     8
  [ 3] .data             PROGBITS         0000000000000000  000000a0
       0000000000000008  0000000000000000  WA       0     0     4
  [ 4] .bss              NOBITS           0000000000000000  000000a8
       0000000000000004  0000000000000000  WA       0     0     4
  [ 5] .rodata           PROGBITS         0000000000000000  000000a8
       0000000000000004  0000000000000000   A       0     0     1
  [ 6] .comment          PROGBITS         0000000000000000  000000ac
       000000000000002d  0000000000000001  MS       0     0     1
  [ 7] .note.GNU-stack   PROGBITS         0000000000000000  000000d9
       0000000000000000  0000000000000000           0     0     1
  [ 8] .eh_frame         PROGBITS         0000000000000000  000000e0
       0000000000000058  0000000000000000   A       0     0     8
  [ 9] .rela.eh_frame    RELA             0000000000000000  00000740
       0000000000000030  0000000000000018          11     8     8
  [10] .shstrtab         STRTAB           0000000000000000  00000138
       0000000000000061  0000000000000000           0     0     1
  [11] .symtab           SYMTAB           0000000000000000  000004e0
       0000000000000180  0000000000000018          12    11     8
  [12] .strtab           STRTAB           0000000000000000  00000660
       0000000000000066  0000000000000000           0     0     1
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings)
  I (info), L (link order), G (group), x (unknown)
  O (extra OS processing required) o (OS specific), p (processor specific)
複製程式碼

該例子中的幾個重要的符號解釋:

  • func1 和 main 定義與目標檔案中,Ndx值為1表示位於程式碼段中(參照 readelf -S 結果中的 [Nr] 值 )
    Bind值為GLOBAL,表示符號繫結資訊為全域性符號,外部可見
    Type值為FUNC,表示符號類四個函式或其他可執行程式碼
    Size值表示符號大小
    Value符號所在的地址

  • printf符號在目標檔案中沒有定義,所以Ndx值為UND

  • global_init_var符號是已初始化的全域性變數,Ndx值為3表示位於.data資料段,
    Bind值為OBJECT,表示符號型別為STT_OBJECT,變數陣列的符號型別都為該值
    Size為4,表示符號大小佔用4位元組
    Value為0,表示該符號位於Ndx段的第0個位置

  • global_uninit_var Ndx值為COM表示位於COMMON段,全域性未初始化的符號是這種型別的

  • 有部分Name未定義的符號,符號名就是段名,比如Num為2、Ndx為1的的符號表示.text段,他的符號名就是段名

  • SimpleSection.c符號的Ndx為ABS、Type為FILE,表示檔名的符號

相關文章