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修饰,则可以看到变量范围作用变成了LOCAL
1
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 编译器手册