一、空间与地址分配
- 空间与地址分配 扫描所有目标文件,相同段合并,符号定义与引用收集。
- 符号解析与重定位
示例代码
/* a.c */
extern int shared;
extern void swap(int* a, int* b);
int main()
{
int a = 100;
swap(&a, &shared);
}
/* b.c */
int shared = 1;
void swap(int* a, int* b)
{
*a ^= *b ^= *a ^= *b;
}
链接前后
a.o: file format elf32-i386
Sections:
Idx Name Size VMA LMA File off Algn
0 .text 00000039 00000000 00000000 00000034 2**0
CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
1 .data 00000000 00000000 00000000 0000006d 2**0
CONTENTS, ALLOC, LOAD, DATA
2 .bss 00000000 00000000 00000000 0000006d 2**0
ALLOC
3 .comment 00000036 00000000 00000000 0000006d 2**0
CONTENTS, READONLY
4 .note.GNU-stack 00000000 00000000 00000000 000000a3 2**0
CONTENTS, READONLY
5 .eh_frame 00000044 00000000 00000000 000000a4 2**2
CONTENTS, ALLOC, LOAD, RELOC, READONLY, DATA
b.o: file format elf32-i386
Sections:
Idx Name Size VMA LMA File off Algn
0 .text 00000039 00000000 00000000 00000034 2**0
CONTENTS, ALLOC, LOAD, READONLY, CODE
1 .data 00000004 00000000 00000000 00000070 2**2
CONTENTS, ALLOC, LOAD, DATA
2 .bss 00000000 00000000 00000000 00000074 2**0
ALLOC
3 .comment 00000036 00000000 00000000 00000074 2**0
CONTENTS, READONLY
4 .note.GNU-stack 00000000 00000000 00000000 000000aa 2**0
CONTENTS, READONLY
5 .eh_frame 00000038 00000000 00000000 000000ac 2**2
CONTENTS, ALLOC, LOAD, RELOC, READONLY, DATA
ab: file format elf32-i386
Sections:
Idx Name Size VMA LMA File off Algn
0 .text 00000072 08048094 08048094 00000094 2**0
CONTENTS, ALLOC, LOAD, READONLY, CODE
1 .eh_frame 00000064 08048108 08048108 00000108 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
2 .data 00000004 0804916c 0804916c 0000016c 2**2
CONTENTS, ALLOC, LOAD, DATA
3 .comment 00000035 00000000 00000000 00000170 2**0
CONTENTS, READONLY
链接后的VMA就是进程空间中的虚拟地址。 可执行文件会为BSS段分配空间,static_uninit_var 地址 0x08049170 紧跟在 .data 段后面
SYMBOL TABLE:
08048094 l d .text 00000000 .text
08048108 l d .eh_frame 00000000 .eh_frame
0804916c l d .data 00000000 .data
08049170 l d .bss 00000000 .bss
00000000 l d .comment 00000000 .comment
00000000 l df *ABS* 00000000 a.c
08049170 l O .bss 00000004 static_uninit_var.1485
00000000 l df *ABS* 00000000 b.c
080480cd g F .text 00000039 swap
0804916c g O .data 00000004 shared
08049170 g .bss 00000000 __bss_start
08049174 g O .bss 00000004 global_uninit_var
08048094 g F .text 00000039 main
08049170 g .data 00000000 _edata
08049178 g .bss 00000000 _end
二、符号解析与重定位
Symbol table '.symtab' contains 16 entries:
Num: Value Size Type Bind Vis Ndx Name
9: 080480cd 57 FUNC GLOBAL DEFAULT 1 swap
10: 0804916c 4 OBJECT GLOBAL DEFAULT 3 shared
可执行文件的符号表信息,经过段拼合之后,各个段的虚拟空间地址已经被分配,然后经过各个符号原先在目标文件段中的偏移地址计算出虚拟空间地址。
# 目标文件 a.o 的重定位表
a.o: file format elf32-i386
RELOCATION RECORDS FOR [.text]:
OFFSET TYPE VALUE
0000001c R_386_32 shared
00000025 R_386_PC32 swap
重定位表中记录了目标文件中需要重定位的符号在段中的偏移地址,例如 share 变量的偏移地址位 0x0000001c,已经知道 .text 段的虚拟空间地址为 0x08048094,那么需要重定位的地址在 0x080480b0。
Disassembly of section .text:
08048094 <main>:
······
80480af: 68 6c 91 04 08 push $0x804916c
······
0x080480b0 开始的4个字节对 shared 的引用被修正为正确的地址 0x804916c。
如果一个全局符号在编译单元内出现了多次,每一处引用他的地方都会有一条重定位信息在重定位表中。
a.o: file format elf64-x86-64
RELOCATION RECORDS FOR [.text]:
OFFSET TYPE VALUE
0000000000000018 R_X86_64_PC32 shared-0x0000000000000004 # shared 被引用了多次 有多条重定位信息
0000000000000049 R_X86_64_PC32 shared-0x0000000000000004
0000000000000051 R_X86_64_PLT32 swap-0x0000000000000004
000000000000006a R_X86_64_PLT32 __stack_chk_fail-0x0000000000000004
三、关于COMMON块
为初始化的全局变量,在目标文件中为弱符号,不会在BSS段分配空间而是标记为COMMON,这是因为他是全局变量,可能会在其他编译单元有定义,甚至在别的编译单元中该符号的类型大小更大,以至于只有到了最后链接的阶段才可以确认他的大小,才会在输出文件的BSS段为其分配空间。
- 如果有弱符号的大小大于强符号,链接会报警告。
- 可以添加属性强制让为初始化全局变量不被标记为COMMON,相当于是一个强符号。