链接装载与库 第3章 目标文件里有什么

PC平台流行的可执行文件格式: linux ELF(executable linkable format) windows PE(portable executable)
它们都是coff格式的变种。
ELF文件可分为如下4类

  • 可重定位文件 包含了代码和数据块,可以被用来链接成可执行文件或共享目标文件。静态库也可以归为这一类(为什么?)
  • 可执行文件 windows下的exe linux下无后缀
  • 共享目标文件 包含了代码和数据,可以跟其他可重定位文件和共享目标文件链接,产生新的目标文件。也可以被动态链接器将共享目标文件和可执行文件结合起来,作为进程映像的一部分来运行
  • 核心转储文件 ,进程意外终止,系统可以将进程地址空间的内容及一些其他信息转储到核心转储文件。

目标文件将信息按不同的属性,以段的形式存储,比如.code, .data。最开始是文件头,描述整个文件的属性,并且还包含一个段表,用来描述各个段的信息。
分段存储而不是整个一起存储的好处:

  1. 分别设置权限
  2. 相同属性的放一起,提高缓存命中
  3. 节约空间。一个进程运行多个进程时,可以共享代码段等可以共用的段。

挖掘SimpleSection.o
源码:

SimpleSection.c
linux: 
gcc -c SimpleSection.c
*/

int printf(const char* format, ...);

int global_init_var = 84;
int global_uninit_var ;

void func1(int i)
{
    print("%d\n", i);
}

int main(void)
{
    static int static_var = 85;
    static int static_var2;
    
    int a=1;
    int b;
    
    func1(static_var + static_var2+ a + b);
    return a;
}

各个段基本信息:

root@debian:~/compileLinkLoad# objdump -h SimpleSection.o

SimpleSection.o:     file format elf32-i386

Sections:
Idx Name          Size      VMA       LMA       File off  Algn
  0 .group        00000008  00000000  00000000  00000034  2**2
                  CONTENTS, READONLY, EXCLUDE, GROUP, LINK_ONCE_DISCARD
  1 .text         0000007f  00000000  00000000  0000003c  2**0
                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
  2 .data         00000008  00000000  00000000  000000bc  2**2
                  CONTENTS, ALLOC, LOAD, DATA
  3 .bss          00000004  00000000  00000000  000000c4  2**2
                  ALLOC
  4 .rodata       00000004  00000000  00000000  000000c4  2**0
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  5 .text.__x86.get_pc_thunk.ax 00000004  00000000  00000000  000000c8  2**0
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
  6 .comment      0000002e  00000000  00000000  000000cc  2**0
                  CONTENTS, READONLY
  7 .note.GNU-stack 00000000  00000000  00000000  000000fa  2**0
                  CONTENTS, READONLY
  8 .eh_frame     0000007c  00000000  00000000  000000fc  2**2
                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, DATA

file off 表示段相对于文件起始地址的偏移,size表示段的大小。

BSS段
.bss段存储的是未初始化的全局变量和局部静态变量。但是bss段并未实际分配空间存储空间,而只是预留。等到真正链接成可执行文件时,再在.bss段分配空间。(那是不是相应的要修改段表?)

3.4ELF文件结构描述

3.4.1文件头

root@debian:~/compileLinkLoad# readelf -h SimpleSection.o
ELF Header:
  Magic:   7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
  Class:                             ELF32
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              REL (Relocatable file)
  Machine:                           Intel 80386
  Version:                           0x1
  Entry point address:               0x0
  Start of program headers:          0 (bytes into file)
  Start of section headers:          1072 (bytes into file)
  Flags:                             0x0
  Size of this header:               52 (bytes)
  Size of program headers:           0 (bytes)
  Number of program headers:         0
  Size of section headers:           40 (bytes)
  Number of section headers:         15
  Section header string table index: 14

ELF 文件头及相关常数被定义在/usr/include/elf.h中。

typedef struct
{
  unsigned char e_ident[EI_NIDENT];     /* Magic number and other info */
  Elf32_Half    e_type;                 /* Object file type */
  Elf32_Half    e_machine;              /* Architecture */
  Elf32_Word    e_version;              /* Object file version */
  Elf32_Addr    e_entry;                /* Entry point virtual address */
  Elf32_Off     e_phoff;                /* Program header table file offset */
  Elf32_Off     e_shoff;                /* Section header table file offset */
  Elf32_Word    e_flags;                /* Processor-specific flags */
  Elf32_Half    e_ehsize;               /* ELF header size in bytes */
  Elf32_Half    e_phentsize;            /* Program header table entry size */
  Elf32_Half    e_phnum;                /* Program header table entry count */
  Elf32_Half    e_shentsize;            /* Section header table entry size */
  Elf32_Half    e_shnum;                /* Section header table entry count */
  Elf32_Half    e_shstrndx;             /* Section header string table index */
} Elf32_Ehdr;

readelf -h的输出与Elf32_Ehdr一一对应。
e_type 三种类型,可重定位文件,可执行文件,共享目标文件,不知道为何不包括核心转储文件
e_entry表示elf程序的入口虚拟地址,操作系统在加载完该程序之后从这个地址开始执行进程的指令。可重定位文件一般没有入口地址,则这个值为0.
e_shoff section header off 段表在文件中的偏移
e_ehsize elf header size
elf_shentsize elf section header entry size 段表描述符每一个条目的大小
elf_shnum number of section headers
e_shstrndx 段表字符串表所在的段在段表中的下标

3.4.2 段表

段表是保存段的属性的基本结构,比如段名,长度,段在文件中的偏移,读写权限等等。。
段表是一个以Elf32_Shdr结构体为元素的数组,每个Elf32_Shdr结构体对应一个段。数据第一个元素为无效段描述符。
Elf32_Shdr:

typedef struct
{
  Elf32_Word    sh_name;                /* Section name (string tbl index) */
  Elf32_Word    sh_type;                /* Section type */
  Elf32_Word    sh_flags;               /* Section flags */
  Elf32_Addr    sh_addr;                /* Section virtual addr at execution */
  Elf32_Off     sh_offset;              /* Section file offset */
  Elf32_Word    sh_size;                /* Section size in bytes */
  Elf32_Word    sh_link;                /* Link to another section */
  Elf32_Word    sh_info;                /* Additional section information */
  Elf32_Word    sh_addralign;           /* Section alignment */
  Elf32_Word    sh_entsize;             /* Entry size if section holds table */
} Elf32_Shdr;

sh_name 段名。段名是一个字符串,位于一个叫做.shstrtab的字符串表,sh_name是段名字符串在.shstrtab中的偏移
sh_type 段的名字不能真正表示段的属性,主要决定段的属性的是段的类型和标志位。

SHT_NULL          0   /* Section header table entry unused */
SHT_PROGBITS      1   /* Program data */
SHT_SYMTAB        2   /* Symbol table */
SHT_STRTAB        3   /* String table */
SHT_RELA          4   /* Relocation entries with addends */
SHT_HASH          5   /* Symbol hash table */
SHT_DYNAMIC       6   /* Dynamic linking information */
SHT_NOTE          7   /* Notes */
SHT_NOBITS        8   /* Program space with no data (bss) */
SHT_REL           9   /* Relocation entries, no addends */
SHT_SHLIB         10  /* Reserved */
SHT_DYNSYM        11  /* Dynamic linker symbol table */

sh_flag 段的标志位 表示该段在进程虚拟地址空间中的属性
最常见的几种

SHF_WRITE            (1 << 0)   /* Writable */
SHF_ALLOC            (1 << 1)   /* Occupies memory during execution */
SHF_EXECINSTR        (1 << 2)   /* Executable */

sh_link sh_info 段的链接信息。如果段的类型是与链接相关的,比如重定位表,符号表等,那么这两个成员包含了一些链接相关的信息(具体含义有待补充)。对于其他段,没有意义。

3.4.3 重定位表

重定位表的类型为SHT_REL,链接器在对目标文件进行处理时,需要对代码和数据中那些对绝对地址引用的位置进行重定位,每个需要重定位的代码或数据段,都有一个对应的重定位表。重定位表其实就是ELF文件的一个段。它的sh_link表示符号表的下标,sh_info表示作用于哪个表对应的下标。

3.4.4 字符串表

ELF文件中用到了很多字符串,比如段名,变量名,以及一些常量字符串。因为字符串长度不固定,用固定的结构来表示比较困难,一般做法是将字符串集中起来存储到一个数组中,然后使用字符串在表中的偏移来引用字符串。

字符串在ELF文件中以段的形式保存,常见的段名为 .strtab或.shstrtab. 分别用来存储普通字符串和段表中用到字符串。

3.5 链接的接口——符号

在链接中,将函数和变量统称为符号,函数名和变量名就是符号名。每个目标文件都有一个符号表用来记录目标文件中所用到的所用符号。每个定义的符号都有一个对应的值,叫符号值。
符号可以分为以下几类

  • 定义在本目标文件的全局符号
  • 在本目标文件引用的全局符号
  • 段名
  • 局部符号
  • 行号信息

链接过程中只关注全局符号。

3.5.1 符号表结构

符号表也是作为一个段来存储,段名叫.symtab。符号表是一个Elf32_Sym结构体数组。

typedef struct
{
  Elf32_Word    st_name;                /* Symbol name (string tbl index) */
  Elf32_Addr    st_value;               /* Symbol value */
  Elf32_Word    st_size;                /* Symbol size */
  unsigned char st_info;                /* Symbol type and binding */
  unsigned char st_other;               /* Symbol visibility */
  Elf32_Section st_shndx;               /* Section index */
} Elf32_Sym;

st_name 符号名,值为该符号名在字符串表中的偏移。
st_value 符号值。分以下几种情况:

  • 如果是符号的定义并且不是common块,则表示符号在st_shndx指定的段中的偏移。
  • 如果是common块,则表示对齐属性。
  • 在可执行文件中,表示符号的虚拟地址。
    st_size 数据类型的大小(比如对于函数则表示函数指定所占字节数)
    st_info 符号类型和绑定信息。低4位表示符号类型,高28位表示符号绑定信息
    符号类型:
#define STT_NOTYPE      0               /* Symbol type is unspecified */
#define STT_OBJECT      1               /* Symbol is a data object */
#define STT_FUNC        2               /* Symbol is a code object */
#define STT_SECTION     3               /* Symbol associated with a section */
#define STT_FILE        4               /* Symbol's name is file name */
#define STT_COMMON      5               /* Symbol is a common data object */
#define STT_TLS         6               /* Symbol is thread-local data object*/
#define STT_NUM         7               /* Number of defined types.  */
#define STT_LOOS        10              /* Start of OS-specific */
#define STT_GNU_IFUNC   10              /* Symbol is indirect code object */
#define STT_HIOS        12              /* End of OS-specific */
#define STT_LOPROC      13              /* Start of processor-specific */
#define STT_HIPROC      15              /* End of processor-specific */

符号绑定信息:

#define STB_LOCAL       0               /* Local symbol */
#define STB_GLOBAL      1               /* Global symbol */
#define STB_WEAK        2               /* Weak symbol */
#define STB_NUM         3               /* Number of defined types.  */
#define STB_LOOS        10              /* Start of OS-specific */
#define STB_GNU_UNIQUE  10              /* Unique symbol.  */
#define STB_HIOS        12              /* End of OS-specific */
#define STB_LOPROC      13              /* Start of processor-specific */
#define STB_HIPROC      15              /* End of processor-specific */

st_other 无用
st_shndx 符号所在段

略…

你可能感兴趣的:(编译链接)