《程序员的自我修养》读书笔记——第四章

244 阅读6分钟

一、空间与地址分配

  • 空间与地址分配 扫描所有目标文件,相同段合并,符号定义与引用收集。
  • 符号解析与重定位

示例代码

/* 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,相当于是一个强符号