0 概述
有了上面两篇文章的铺垫,再看一遍ELF1.1规范,就已经简单的入门了
这篇文章,主要讲静态链接中的符号重定向过程和ELF处理机制
2 变量内存分布
我们知道,C语言的变量按作用域来分,包括:
- 全局作用域变量
 未加static修饰的变量,这种变量会标记为PUBLIC,并对外可见
- 文件作用域变量
 static修饰的变量,这种变量会标记为LOCAK,对外不可见
- 局部作用域变量
 block里面的变量
 什么是block呢,就是包含在{}里面的,未被static修饰的变量,这种变量会放在栈中,没有任何标记,对外不可见
也许你会问,C语言中有很多类型的变量,比如int,char,enum等,你为什么只提变量,而不提具体类型?
这是因为,变量类型仅仅对高级语言有效,机器码里面并没有数据类型,CPU只认识特定宽度的数据,然后做基本运算。
真的,在ELF里面,只有OBJECT,FUNC两种类型符号,我们来尝试一下
首先,查看一下a.out文件的符号表1
root@raspberrypi:/home/pi/tmp# readelf -s a.out
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: 00000000     0 FUNC    GLOBAL DEFAULT  UND printf@GLIBC_2.4 (2)
     3: 00000000     0 FUNC    GLOBAL DEFAULT  UND abort@GLIBC_2.4 (2)
     4: 00000000     0 FUNC    GLOBAL DEFAULT  UND __libc_start_main@GLIBC_2.4 (2)
Symbol table '.symtab' contains 122 entries:
   Num:    Value  Size Type    Bind   Vis      Ndx Name
     0: 00000000     0 NOTYPE  LOCAL  DEFAULT  UND 
     1: 00010134     0 SECTION LOCAL  DEFAULT    1 
     2: 00010150     0 SECTION LOCAL  DEFAULT    2 
     3: 00010170     0 SECTION LOCAL  DEFAULT    3 
     4: 00010194     0 SECTION LOCAL  DEFAULT    4 
     5: 000101c0     0 SECTION LOCAL  DEFAULT    5 
     6: 00010210     0 SECTION LOCAL  DEFAULT    6 
     7: 00010254     0 SECTION LOCAL  DEFAULT    7 
     8: 00010260     0 SECTION LOCAL  DEFAULT    8 
     9: 00010280     0 SECTION LOCAL  DEFAULT    9 
    10: 00010288     0 SECTION LOCAL  DEFAULT   10 
    11: 000102a8     0 SECTION LOCAL  DEFAULT   11 
    12: 000102b4     0 SECTION LOCAL  DEFAULT   12 
    13: 000102f8     0 SECTION LOCAL  DEFAULT   13 
    14: 00010500     0 SECTION LOCAL  DEFAULT   14 
    15: 00010508     0 SECTION LOCAL  DEFAULT   15 
    16: 00010518     0 SECTION LOCAL  DEFAULT   16 
    17: 00010520     0 SECTION LOCAL  DEFAULT   17 
    18: 00020524     0 SECTION LOCAL  DEFAULT   18 
    19: 00020528     0 SECTION LOCAL  DEFAULT   19 
    20: 0002052c     0 SECTION LOCAL  DEFAULT   20 
    21: 00020530     0 SECTION LOCAL  DEFAULT   21 
    22: 00020618     0 SECTION LOCAL  DEFAULT   22 
    23: 00020638     0 SECTION LOCAL  DEFAULT   23 
    24: 00020640     0 SECTION LOCAL  DEFAULT   24 
    25: 00000000     0 SECTION LOCAL  DEFAULT   25 
    26: 00000000     0 SECTION LOCAL  DEFAULT   26 
    27: 00000000     0 SECTION LOCAL  DEFAULT   27 
    28: 00000000     0 SECTION LOCAL  DEFAULT   28 
    29: 00000000     0 SECTION LOCAL  DEFAULT   29 
    30: 00000000     0 SECTION LOCAL  DEFAULT   30 
    31: 00000000     0 SECTION LOCAL  DEFAULT   31 
    32: 00000000     0 SECTION LOCAL  DEFAULT   32 
    33: 00000000     0 FILE    LOCAL  DEFAULT  ABS /usr/lib/gcc/arm-linux-gn
    34: 00010150     0 NOTYPE  LOCAL  DEFAULT    2 $d
    35: 000102f8     0 NOTYPE  LOCAL  DEFAULT   13 $a
    36: 00010518     0 NOTYPE  LOCAL  DEFAULT   16 $d
    37: 00010328     0 NOTYPE  LOCAL  DEFAULT   13 $d
    38: 00010508     0 NOTYPE  LOCAL  DEFAULT   15 $d
    39: 00020638     0 NOTYPE  LOCAL  DEFAULT   23 $d
    40: 00000000     0 FILE    LOCAL  DEFAULT  ABS /usr/lib/gcc/arm-linux-gn
    41: 00010334     0 NOTYPE  LOCAL  DEFAULT   13 $a
    42: 00010334     0 FUNC    LOCAL  DEFAULT   13 call_weak_fn
    43: 00010350     0 NOTYPE  LOCAL  DEFAULT   13 $d
    44: 000102a8     0 NOTYPE  LOCAL  DEFAULT   11 $a
    45: 00010500     0 NOTYPE  LOCAL  DEFAULT   14 $a
    46: 00000000     0 FILE    LOCAL  DEFAULT  ABS /usr/lib/gcc/arm-linux-gn
    47: 000102b0     0 NOTYPE  LOCAL  DEFAULT   11 $a
    48: 00010504     0 NOTYPE  LOCAL  DEFAULT   14 $a
    49: 00000000     0 FILE    LOCAL  DEFAULT  ABS crtstuff.c
    50: 0002052c     0 OBJECT  LOCAL  DEFAULT   20 __JCR_LIST__
    51: 00010358     0 NOTYPE  LOCAL  DEFAULT   13 $a
    52: 00010358     0 FUNC    LOCAL  DEFAULT   13 deregister_tm_clones
    53: 0001037c     0 NOTYPE  LOCAL  DEFAULT   13 $d
    54: 00010388     0 NOTYPE  LOCAL  DEFAULT   13 $a
    55: 00010388     0 FUNC    LOCAL  DEFAULT   13 register_tm_clones
    56: 000103b4     0 NOTYPE  LOCAL  DEFAULT   13 $d
    57: 0002063c     0 NOTYPE  LOCAL  DEFAULT   23 $d
    58: 000103c0     0 NOTYPE  LOCAL  DEFAULT   13 $a
    59: 000103c0     0 FUNC    LOCAL  DEFAULT   13 __do_global_dtors_aux
    60: 000103e4     0 NOTYPE  LOCAL  DEFAULT   13 $d
    61: 00020640     1 OBJECT  LOCAL  DEFAULT   24 completed.9004
    62: 00020528     0 NOTYPE  LOCAL  DEFAULT   19 $d
    63: 00020528     0 OBJECT  LOCAL  DEFAULT   19 __do_global_dtors_aux_fin
    64: 000103e8     0 NOTYPE  LOCAL  DEFAULT   13 $a
    65: 000103e8     0 FUNC    LOCAL  DEFAULT   13 frame_dummy
    66: 00010418     0 NOTYPE  LOCAL  DEFAULT   13 $d
    67: 00020524     0 NOTYPE  LOCAL  DEFAULT   18 $d
    68: 00020524     0 OBJECT  LOCAL  DEFAULT   18 __frame_dummy_init_array_
    69: 00020640     0 NOTYPE  LOCAL  DEFAULT   24 $d
    70: 00000000     0 FILE    LOCAL  DEFAULT  ABS main.c
    71: 0001050c     0 NOTYPE  LOCAL  DEFAULT   15 $d
    72: 00010420     0 NOTYPE  LOCAL  DEFAULT   13 $a
    73: 00010460     0 NOTYPE  LOCAL  DEFAULT   13 $d
    74: 00000010     0 NOTYPE  LOCAL  DEFAULT   31 $d
    75: 00000000     0 FILE    LOCAL  DEFAULT  ABS fun.c
    76: 00010468     0 NOTYPE  LOCAL  DEFAULT   13 $a
    77: 0000003c     0 NOTYPE  LOCAL  DEFAULT   31 $d
    78: 00000000     0 FILE    LOCAL  DEFAULT  ABS elf-init.oS
    79: 00010498     0 NOTYPE  LOCAL  DEFAULT   13 $a
    80: 000104f4     0 NOTYPE  LOCAL  DEFAULT   13 $d
    81: 000104fc     0 NOTYPE  LOCAL  DEFAULT   13 $a
    82: 00000000     0 FILE    LOCAL  DEFAULT  ABS crtstuff.c
    83: 00010520     0 NOTYPE  LOCAL  DEFAULT   17 $d
    84: 00010520     0 OBJECT  LOCAL  DEFAULT   17 __FRAME_END__
    85: 0002052c     0 NOTYPE  LOCAL  DEFAULT   20 $d
    86: 0002052c     0 OBJECT  LOCAL  DEFAULT   20 __JCR_END__
    87: 00000000     0 FILE    LOCAL  DEFAULT  ABS 
    88: 00020528     0 NOTYPE  LOCAL  DEFAULT   18 __init_array_end
    89: 00020530     0 OBJECT  LOCAL  DEFAULT   21 _DYNAMIC
    90: 00020524     0 NOTYPE  LOCAL  DEFAULT   18 __init_array_start
    91: 00020618     0 OBJECT  LOCAL  DEFAULT   22 _GLOBAL_OFFSET_TABLE_
    92: 000102b4     0 NOTYPE  LOCAL  DEFAULT   12 $a
    93: 000102c4     0 NOTYPE  LOCAL  DEFAULT   12 $d
    94: 000102c8     0 NOTYPE  LOCAL  DEFAULT   12 $a
    95: 000104fc     4 FUNC    GLOBAL DEFAULT   13 __libc_csu_fini
    96: 00000000     0 NOTYPE  WEAK   DEFAULT  UND _ITM_deregisterTMCloneTab
    97: 00020638     0 NOTYPE  WEAK   DEFAULT   23 data_start
    98: 00000000     0 FUNC    GLOBAL DEFAULT  UND printf@@GLIBC_2.4
    99: 00020640     0 NOTYPE  GLOBAL DEFAULT   24 __bss_start__
   100: 00020648     0 NOTYPE  GLOBAL DEFAULT   24 _bss_end__
   101: 00020640     0 NOTYPE  GLOBAL DEFAULT   23 _edata
   102: 00010500     0 FUNC    GLOBAL DEFAULT   14 _fini
   103: 00020648     0 NOTYPE  GLOBAL DEFAULT   24 __bss_end__
   104: 00020638     0 NOTYPE  GLOBAL DEFAULT   23 __data_start
   105: 00010468    48 FUNC    GLOBAL DEFAULT   13 f_add
   106: 00000000     0 FUNC    GLOBAL DEFAULT  UND __libc_start_main@@GLIBC_
   107: 00000000     0 NOTYPE  WEAK   DEFAULT  UND __gmon_start__
   108: 0002063c     0 OBJECT  GLOBAL HIDDEN    23 __dso_handle
   109: 00010508     4 OBJECT  GLOBAL DEFAULT   15 _IO_stdin_used
   110: 00010498   100 FUNC    GLOBAL DEFAULT   13 __libc_csu_init
   111: 00020648     0 NOTYPE  GLOBAL DEFAULT   24 _end
   112: 000102f8     0 FUNC    GLOBAL DEFAULT   13 _start
   113: 00020648     0 NOTYPE  GLOBAL DEFAULT   24 __end__
   114: 00020644     4 OBJECT  GLOBAL DEFAULT   24 m                      # 0 变量符号
   115: 00020640     0 NOTYPE  GLOBAL DEFAULT   24 __bss_start
   116: 00010420    72 FUNC    GLOBAL DEFAULT   13 main                   # 1 函数符号
   117: 00000000     0 NOTYPE  WEAK   DEFAULT  UND _Jv_RegisterClasses
   118: 00020640     0 OBJECT  GLOBAL HIDDEN    23 __TMC_END__
   119: 00000000     0 NOTYPE  WEAK   DEFAULT  UND _ITM_registerTMCloneTable
   120: 00000000     0 FUNC    GLOBAL DEFAULT  UND abort@@GLIBC_2.4
   121: 000102a8     0 FUNC    GLOBAL DEFAULT   11 _init
我们看到symtab里面的type一栏,有FUNC,OBJECT,FILE等类型,同时,可以看到我们源代码里面的m被标记为OBJECT,main函数被标记为FUNC类型,GLOBAL属性。
为了验证上面说的理论,现在尝试在main.c中,添加char, unsigned char, struct 类型的变量,修改后的main.c如下所示
| 1 | #include <stdio.h> | 
重新编译,读取符号,如下所示
| 1 | root@raspberrypi:/home/pi/tmp# gcc -c -g -O0 main.c root@raspberrypi:/home/pi/tmp# gcc main.o func.o gcc: error: func.o: No such file or directory root@raspberrypi:/home/pi/tmp# gcc main.o fun.o root@raspberrypi:/home/pi/tmp# readelf -s a.out 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: 00000000 0 FUNC GLOBAL DEFAULT UND printf@GLIBC_2.4 (2) 3: 00000000 0 FUNC GLOBAL DEFAULT UND abort@GLIBC_2.4 (2) 4: 00000000 0 FUNC GLOBAL DEFAULT UND __libc_start_main@GLIBC_2.4 (2) Symbol table '.symtab' contains 126 entries: Num: Value Size Type Bind Vis Ndx Name 0: 00000000 0 NOTYPE LOCAL DEFAULT UND 1: 00010134 0 SECTION LOCAL DEFAULT 1 2: 00010150 0 SECTION LOCAL DEFAULT 2 3: 00010170 0 SECTION LOCAL DEFAULT 3 4: 00010194 0 SECTION LOCAL DEFAULT 4 5: 000101c0 0 SECTION LOCAL DEFAULT 5 6: 00010210 0 SECTION LOCAL DEFAULT 6 7: 00010254 0 SECTION LOCAL DEFAULT 7 8: 00010260 0 SECTION LOCAL DEFAULT 8 9: 00010280 0 SECTION LOCAL DEFAULT 9 10: 00010288 0 SECTION LOCAL DEFAULT 10 11: 000102a8 0 SECTION LOCAL DEFAULT 11 12: 000102b4 0 SECTION LOCAL DEFAULT 12 13: 000102f8 0 SECTION LOCAL DEFAULT 13 14: 00010500 0 SECTION LOCAL DEFAULT 14 15: 00010508 0 SECTION LOCAL DEFAULT 15 16: 00010518 0 SECTION LOCAL DEFAULT 16 17: 00010520 0 SECTION LOCAL DEFAULT 17 18: 00020524 0 SECTION LOCAL DEFAULT 18 19: 00020528 0 SECTION LOCAL DEFAULT 19 20: 0002052c 0 SECTION LOCAL DEFAULT 20 21: 00020530 0 SECTION LOCAL DEFAULT 21 22: 00020618 0 SECTION LOCAL DEFAULT 22 23: 00020638 0 SECTION LOCAL DEFAULT 23 24: 0002064c 0 SECTION LOCAL DEFAULT 24 25: 00000000 0 SECTION LOCAL DEFAULT 25 26: 00000000 0 SECTION LOCAL DEFAULT 26 27: 00000000 0 SECTION LOCAL DEFAULT 27 28: 00000000 0 SECTION LOCAL DEFAULT 28 29: 00000000 0 SECTION LOCAL DEFAULT 29 30: 00000000 0 SECTION LOCAL DEFAULT 30 31: 00000000 0 SECTION LOCAL DEFAULT 31 32: 00000000 0 SECTION LOCAL DEFAULT 32 33: 00000000 0 FILE LOCAL DEFAULT ABS /usr/lib/gcc/arm-linux-gn 34: 00010150 0 NOTYPE LOCAL DEFAULT 2 $d 35: 000102f8 0 NOTYPE LOCAL DEFAULT 13 $a 36: 00010518 0 NOTYPE LOCAL DEFAULT 16 $d 37: 00010328 0 NOTYPE LOCAL DEFAULT 13 $d 38: 00010508 0 NOTYPE LOCAL DEFAULT 15 $d 39: 00020638 0 NOTYPE LOCAL DEFAULT 23 $d 40: 00000000 0 FILE LOCAL DEFAULT ABS /usr/lib/gcc/arm-linux-gn 41: 00010334 0 NOTYPE LOCAL DEFAULT 13 $a 42: 00010334 0 FUNC LOCAL DEFAULT 13 call_weak_fn 43: 00010350 0 NOTYPE LOCAL DEFAULT 13 $d 44: 000102a8 0 NOTYPE LOCAL DEFAULT 11 $a 45: 00010500 0 NOTYPE LOCAL DEFAULT 14 $a 46: 00000000 0 FILE LOCAL DEFAULT ABS /usr/lib/gcc/arm-linux-gn 47: 000102b0 0 NOTYPE LOCAL DEFAULT 11 $a 48: 00010504 0 NOTYPE LOCAL DEFAULT 14 $a 49: 00000000 0 FILE LOCAL DEFAULT ABS crtstuff.c 50: 0002052c 0 OBJECT LOCAL DEFAULT 20 __JCR_LIST__ 51: 00010358 0 NOTYPE LOCAL DEFAULT 13 $a 52: 00010358 0 FUNC LOCAL DEFAULT 13 deregister_tm_clones 53: 0001037c 0 NOTYPE LOCAL DEFAULT 13 $d 54: 00010388 0 NOTYPE LOCAL DEFAULT 13 $a 55: 00010388 0 FUNC LOCAL DEFAULT 13 register_tm_clones 56: 000103b4 0 NOTYPE LOCAL DEFAULT 13 $d 57: 0002063c 0 NOTYPE LOCAL DEFAULT 23 $d 58: 000103c0 0 NOTYPE LOCAL DEFAULT 13 $a 59: 000103c0 0 FUNC LOCAL DEFAULT 13 __do_global_dtors_aux 60: 000103e4 0 NOTYPE LOCAL DEFAULT 13 $d 61: 0002064c 1 OBJECT LOCAL DEFAULT 24 completed.9004 62: 00020528 0 NOTYPE LOCAL DEFAULT 19 $d 63: 00020528 0 OBJECT LOCAL DEFAULT 19 __do_global_dtors_aux_fin 64: 000103e8 0 NOTYPE LOCAL DEFAULT 13 $a 65: 000103e8 0 FUNC LOCAL DEFAULT 13 frame_dummy 66: 00010418 0 NOTYPE LOCAL DEFAULT 13 $d 67: 00020524 0 NOTYPE LOCAL DEFAULT 18 $d 68: 00020524 0 OBJECT LOCAL DEFAULT 18 __frame_dummy_init_array_ 69: 0002064c 0 NOTYPE LOCAL DEFAULT 24 $d 70: 00000000 0 FILE LOCAL DEFAULT ABS main.c 71: 00020642 0 NOTYPE LOCAL DEFAULT 23 $d 72: 0001050c 0 NOTYPE LOCAL DEFAULT 15 $d 73: 00010420 0 NOTYPE LOCAL DEFAULT 13 $a 74: 00010460 0 NOTYPE LOCAL DEFAULT 13 $d 75: 00000010 0 NOTYPE LOCAL DEFAULT 31 $d 76: 00000000 0 FILE LOCAL DEFAULT ABS fun.c 77: 00010468 0 NOTYPE LOCAL DEFAULT 13 $a 78: 0000003c 0 NOTYPE LOCAL DEFAULT 31 $d 79: 00000000 0 FILE LOCAL DEFAULT ABS elf-init.oS 80: 00010498 0 NOTYPE LOCAL DEFAULT 13 $a 81: 000104f4 0 NOTYPE LOCAL DEFAULT 13 $d 82: 000104fc 0 NOTYPE LOCAL DEFAULT 13 $a 83: 00000000 0 FILE LOCAL DEFAULT ABS crtstuff.c 84: 00010520 0 NOTYPE LOCAL DEFAULT 17 $d 85: 00010520 0 OBJECT LOCAL DEFAULT 17 __FRAME_END__ 86: 0002052c 0 NOTYPE LOCAL DEFAULT 20 $d 87: 0002052c 0 OBJECT LOCAL DEFAULT 20 __JCR_END__ 88: 00000000 0 FILE LOCAL DEFAULT ABS 89: 00020528 0 NOTYPE LOCAL DEFAULT 18 __init_array_end 90: 00020530 0 OBJECT LOCAL DEFAULT 21 _DYNAMIC 91: 00020524 0 NOTYPE LOCAL DEFAULT 18 __init_array_start 92: 00020618 0 OBJECT LOCAL DEFAULT 22 _GLOBAL_OFFSET_TABLE_ 93: 000102b4 0 NOTYPE LOCAL DEFAULT 12 $a 94: 000102c4 0 NOTYPE LOCAL DEFAULT 12 $d 95: 000102c8 0 NOTYPE LOCAL DEFAULT 12 $a 96: 000104fc 4 FUNC GLOBAL DEFAULT 13 __libc_csu_fini 97: 00000000 0 NOTYPE WEAK DEFAULT UND _ITM_deregisterTMCloneTab 98: 00020638 0 NOTYPE WEAK DEFAULT 23 data_start 99: 00000000 0 FUNC GLOBAL DEFAULT UND printf@@GLIBC_2.4 100: 0002064c 0 NOTYPE GLOBAL DEFAULT 24 __bss_start__ 101: 00020654 0 NOTYPE GLOBAL DEFAULT 24 _bss_end__ 102: 0002064c 0 NOTYPE GLOBAL DEFAULT 23 _edata 103: 00010500 0 FUNC GLOBAL DEFAULT 14 _fini 104: 00020654 0 NOTYPE GLOBAL DEFAULT 24 __bss_end__ 105: 00020638 0 NOTYPE GLOBAL DEFAULT 23 __data_start 106: 00010468 48 FUNC GLOBAL DEFAULT 13 f_add 107: 00000000 0 FUNC GLOBAL DEFAULT UND __libc_start_main@@GLIBC_ 108: 00000000 0 NOTYPE WEAK DEFAULT UND __gmon_start__ 109: 0002063c 0 OBJECT GLOBAL HIDDEN 23 __dso_handle 110: 00020640 1 OBJECT GLOBAL DEFAULT 23 m_char # 0 变量符号 111: 00010508 4 OBJECT GLOBAL DEFAULT 15 _IO_stdin_used 112: 00010498 100 FUNC GLOBAL DEFAULT 13 __libc_csu_init 113: 00020654 0 NOTYPE GLOBAL DEFAULT 24 _end 114: 00020641 1 OBJECT GLOBAL DEFAULT 23 m_uchar # 1 变量符号 115: 000102f8 0 FUNC GLOBAL DEFAULT 13 _start 116: 00020654 0 NOTYPE GLOBAL DEFAULT 24 __end__ 117: 00020650 4 OBJECT GLOBAL DEFAULT 24 m # 2 变量符号 118: 0002064c 0 NOTYPE GLOBAL DEFAULT 24 __bss_start 119: 00010420 72 FUNC GLOBAL DEFAULT 13 main 120: 00020644 8 OBJECT GLOBAL DEFAULT 23 m_struct # 3 变量符号 121: 00000000 0 NOTYPE WEAK DEFAULT UND _Jv_RegisterClasses 122: 0002064c 0 OBJECT GLOBAL HIDDEN 23 __TMC_END__ 123: 00000000 0 NOTYPE WEAK DEFAULT UND _ITM_registerTMCloneTable 124: 00000000 0 FUNC GLOBAL DEFAULT UND abort@@GLIBC_2.4 125: 000102a8 0 FUNC GLOBAL DEFAULT 11 _init | 
我们可以看到,几种类型的变量,对于ELF来说,除了size不同外,没什么差别
如果我们将几个变量前添加static修饰,则可以看到变量范围作用变成了LOCAL1
71: 00020651     1 OBJECT  LOCAL  DEFAULT   23 m_uchar
而局部变量,则分配在栈里面,完全不会出现在符号表中
看完了变量类型和符号表,我们再看变量分配到了哪里
先反汇编main.o文件,如下所示
| 1 | root@raspberrypi:/home/pi/tmp# objdump -D main.o ... Disassembly of section .data: 00000000 <m_char>: 0: 00000101 andeq r0, r0, r1, lsl #2 00000001 <m_uchar>: 1: 01000001 tsteq r0, r1 00000004 <m_struct>: 4: 00000001 andeq r0, r0, r1 8: 00000002 andeq r0, r0, r2 ... | 
然后看main.o符号表1
root@raspberrypi:/home/pi/tmp# readelf -s main.o
Symbol table '.symtab' contains 27 entries:
   Num:    Value  Size Type    Bind   Vis      Ndx Name
     0: 00000000     0 NOTYPE  LOCAL  DEFAULT  UND 
     1: 00000000     0 FILE    LOCAL  DEFAULT  ABS main.c
     2: 00000000     0 SECTION LOCAL  DEFAULT    1 
     3: 00000000     0 SECTION LOCAL  DEFAULT    3 
     4: 00000000     0 SECTION LOCAL  DEFAULT    4 
     5: 00000001     1 OBJECT  LOCAL  DEFAULT    3 m_uchar
     6: 00000002     0 NOTYPE  LOCAL  DEFAULT    3 $d
     7: 00000000     0 SECTION LOCAL  DEFAULT    5 
     8: 00000000     0 NOTYPE  LOCAL  DEFAULT    5 $d
     9: 00000000     0 NOTYPE  LOCAL  DEFAULT    1 $a
    10: 00000050     0 NOTYPE  LOCAL  DEFAULT    1 $d
    11: 00000000     0 SECTION LOCAL  DEFAULT    6 
    12: 00000000     0 SECTION LOCAL  DEFAULT    8 
    13: 00000000     0 SECTION LOCAL  DEFAULT    9 
    14: 00000000     0 SECTION LOCAL  DEFAULT   11 
    15: 00000000     0 SECTION LOCAL  DEFAULT   13 
    16: 00000000     0 SECTION LOCAL  DEFAULT   15 
    17: 00000010     0 NOTYPE  LOCAL  DEFAULT   16 $d
    18: 00000000     0 SECTION LOCAL  DEFAULT   16 
    19: 00000000     0 SECTION LOCAL  DEFAULT   14 
    20: 00000000     0 SECTION LOCAL  DEFAULT   18 
    21: 00000000     1 OBJECT  GLOBAL DEFAULT    3 m_char
    22: 00000004     8 OBJECT  GLOBAL DEFAULT    3 m_struct
    23: 00000000    88 FUNC    GLOBAL DEFAULT    1 main
    24: 00000000     0 NOTYPE  GLOBAL DEFAULT  UND f_add
    25: 00000000     0 NOTYPE  GLOBAL DEFAULT  UND printf
    26: 00000000     0 NOTYPE  GLOBAL DEFAULT  UND m
可以看到,这几个符号都没有确定地址,下面,我们看a.out里面符号信息
| 1 | root@raspberrypi:/home/pi/tmp# objdump -D a.out ... Disassembly of section .data: 00020648 <__data_start>: 20648: 00000000 andeq r0, r0, r0 0002064c <__dso_handle>: 2064c: 00000000 andeq r0, r0, r0 00020650 <m_char>: 20650: 00000101 andeq r0, r0, r1, lsl #2 00020651 <m_uchar>: 20651: 01000001 tsteq r0, r1 00020654 <m_struct>: 20654: 00000001 andeq r0, r0, r1 20658: 00000002 andeq r0, r0, r2 Disassembly of section .bss: 0002065c <__bss_start>: 2065c: 00000000 andeq r0, r0, r0 00020660 <m>: 20660: 00000000 andeq r0, r0, r0 ... | 
可以看到这几个变量的内存地址已经确定下来了,同时,我们注意到上面反汇编时,会将数据部分进行反编译,这是不对的。
了解了变量的分布和符号表信息,我们看看函数的信息
3 函数内存分布
函数相关的知识,主要考虑
- 接口调用标准
 主要考虑参数分配,入栈,出栈的责任
- 函数地址
 主要是考虑函数对外的地址
- 内部数据调用
 主要考虑函数内部怎么调用外部变量
在分析前,我们将main.c代码恢复成最初的那样,如下所示1
2
3
4
5
6
7
8
9
10
11#include <stdio.h>
extern int m;
extern int f_add(int a, int b);
int main(void)
{
    m = f_add(1, 2);
    printf("sum = %d\r\n", m);
    return 0;
}
反汇编main.o,main部分的代码如下
| 1 | root@raspberrypi:/home/pi/tmp# objdump -d main.o main.o: file format elf32-littlearm Disassembly of section .text: 00000000 <main>: 0: e92d4800 push {fp, lr} 4: e28db004 add fp, sp, #4 8: e3a00001 mov r0, #1 c: e3a01002 mov r1, #2 10: ebfffffe bl 0 <f_add> # 0 函数调用 14: e1a02000 mov r2, r0 18: e59f3020 ldr r3, [pc, #32] ; 40 <main+0x40> 1c: e5832000 str r2, [r3] 20: e59f3018 ldr r3, [pc, #24] ; 40 <main+0x40> 24: e5933000 ldr r3, [r3] 28: e59f0014 ldr r0, [pc, #20] ; 44 <main+0x44> 2c: e1a01003 mov r1, r3 30: ebfffffe bl 0 <printf> # 1 函数调用 34: e3a03000 mov r3, #0 38: e1a00003 mov r0, r3 3c: e8bd8800 pop {fp, pc} | 
看到0/1处的函数调用,函数地址均为0
再来看看a.out里面的汇编
| 1 | 00010420 <main>: 10420: e92d4800 push {fp, lr} 10424: e28db004 add fp, sp, #4 10428: e3a00001 mov r0, #1 1042c: e3a01002 mov r1, #2 10430: eb00000c bl 10468 <f_add> 10434: e1a02000 mov r2, r0 10438: e59f3020 ldr r3, [pc, #32] ; 10460 <main+0x40> 1043c: e5832000 str r2, [r3] 10440: e59f3018 ldr r3, [pc, #24] ; 10460 <main+0x40> 10444: e5933000 ldr r3, [r3] 10448: e59f0014 ldr r0, [pc, #20] ; 10464 <main+0x44> 1044c: e1a01003 mov r1, r3 10450: ebffff9c bl 102c8 <printf@plt> 10454: e3a03000 mov r3, #0 10458: e1a00003 mov r0, r3 1045c: e8bd8800 pop {fp, pc} 10460: 00020644 .word 0x00020644 10464: 0001050c .word 0x0001050c | 
函数地址已经分配
OK,铺垫已经全部介绍完,下面重点介绍ELF怎么解决变量地址绑定和函数地址绑定的问题
2 重定向需要处理的问题
链接器读取ELF文件和链接脚本,并尝试解决下面的几个问题
- 解决未定义符号地址
- 分配已有符号的地址
- 分配函数地址
3 变量符号重定向
ELF通过符号表来表示文件中所有的符号(函数和变量),并将外部符号定义未UND
同时,将引用到的,需要重定向的符号放置到相对符号表中,从而允许外部工具进行修正
下面实例来分析,在main.c函数中,我们引用了一个外部变量m,但在编译成.o文件时,我们并不知道m的地址,反汇编main函数,得到1
root@raspberrypi:/home/pi/tmp# objdump -d main.o
main.o:     file format elf32-littlearm
Disassembly of section .text:
00000000 <main>:
   0:	e92d4800 	push	{fp, lr}
   4:	e28db004 	add	fp, sp, #4
   8:	e3a00001 	mov	r0, #1
   c:	e3a01002 	mov	r1, #2
  10:	ebfffffe 	bl	0 <f_add>
  14:	e1a02000 	mov	r2, r0                             #得到返回值
  18:	e59f3020 	ldr	r3, [pc, #32]	; 40 <main+0x40>   #加载m的间接地址信息
  1c:	e5832000 	str	r2, [r3]                           #通过间接取地址,来获取m的地址信息,并将返回值存在到对应ddd地址中
  20:	e59f3018 	ldr	r3, [pc, #24]	; 40 <main+0x40>
  24:	e5933000 	ldr	r3, [r3]
  28:	e59f0014 	ldr	r0, [pc, #20]	; 44 <main+0x44>
  2c:	e1a01003 	mov	r1, r3
  30:	ebfffffe 	bl	0 <printf>
  34:	e3a03000 	mov	r3, #0
  38:	e1a00003 	mov	r0, r3
  3c:	e8bd8800 	pop	{fp, pc}
重点关注第14,18,20行,我已经做了注释,可以知道PC+32处存放着m的地址信息,也就是说,程序代码里面间接的表格方式来获取对应变量地址,从而便于统一管理,
上面的反汇编注释,已经说明了 PC+32 =
main+0x40,所以,我们直接找到main函数所在的.text段,然后加上0x40偏移,就找到了m所在的地址,可以看到为00000000
这里,我们将main.o用hex形式表示,如下所示1
root@raspberrypi:/home/pi/tmp# objdump -s main.o
main.o:     file format elf32-littlearm
Contents of section .text:
 0000 00482de9 04b08de2 0100a0e3 0210a0e3  .H-.............
 0010 feffffeb 0020a0e1 20309fe5 002083e5  ..... .. 0... ..
 0020 18309fe5 003093e5 14009fe5 0310a0e1  .0...0..........
 0030 feffffeb 0030a0e3 0300a0e1 0088bde8  .....0..........
 0040 00000000 00000000                    ........
同时,ELF为了便于链接器修改符号偏移地址,专门设置了一个相对偏移表段.rel.text,也就是说明符号相对于text段所在的偏移地址1
Relocation section '.rel.text' at offset 0x598 contains 4 entries:
 Offset     Info    Type            Sym.Value  Sym. Name
00000010  0000141c R_ARM_CALL        00000000   f_add
00000030  0000151c R_ARM_CALL        00000000   printf
00000040  00001602 R_ARM_ABS32       00000000   m
00000044  00000502 R_ARM_ABS32       00000000   .rodata
我们可以看到m符号相对于text的偏移刚好是0x40,和我们的计算结果一样
4 函数符号重定向
查看相对符号表中的函数偏移地址,然后修改对应的跳转指令地址
5 注意事项
- 重定向机制为编译器提供了极大的方便,但我们也看到,在汇编级别,其实效率并不高,有很大的优化空间,比如:可以将相对取地址改为绝对取地址,从而省去了一条指令
- 如果一个段中出现多次函数调用,则必须多次修改函数调用处的地址
- 如果一个段中出现多次变量调用,只需要修正一次变量地址表即可
示例:我们修改main.c代码,如下所示
| 1 | #include <stdio.h> | 
再次查看相对符号表,如下所示
| 1 | Relocation section '.rel.text' at offset 0x5d4 contains 7 entries: Offset Info Type Sym.Value Sym. Name 00000010 0000141c R_ARM_CALL 00000000 f_add 00000030 0000151c R_ARM_CALL 00000000 printf 00000044 0000151c R_ARM_CALL 00000000 printf 00000058 0000151c R_ARM_CALL 00000000 printf 0000006c 0000151c R_ARM_CALL 00000000 printf 0000007c 00001602 R_ARM_ABS32 00000000 m 00000080 00000502 R_ARM_ABS32 00000000 .rodata | 
6 总结
链接器先找到.rel.text段,得到obj文件中所有修改修正的符号偏移地址,然后根据链接脚本和各个段的综合,计算出符号的正确分配地址,然后再修正对应节中相应的符号偏移地址
这样,就完成了静态链接过程中的地址修正过程
5 参考资料
- ELF规范1.1
- ELF规范1.2
- ARM ELF 规范
- MDK 编译器手册
- IAR 编译器手册
- GCC 编译器手册