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 编译器手册