编译链接基本素养笔记(一)ELF文件结构

源代码:

int printf(const char * format,...);
int global_init_var=84;
int global_uninit_var;

void func1(int i){
    printf("%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;
}

头文件结构:

karl@ubuntu:~/c/static$ objdump -h SimpleSection.o 

SimpleSection.o:     file format elf32-i386

Sections:
Idx Name          Size      VMA       LMA       File off  Algn
  0 .text         00000053  00000000  00000000  00000034  2**0
                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
  1 .data         00000008  00000000  00000000  00000088  2**2
                  CONTENTS, ALLOC, LOAD, DATA
  2 .bss          00000004  00000000  00000000  00000090  2**2
                  ALLOC
  3 .rodata       00000004  00000000  00000000  00000090  2**0
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  4 .comment      0000002a  00000000  00000000  00000094  2**0
                  CONTENTS, READONLY
  5 .note.GNU-stack 00000000  00000000  00000000  000000be  2**0
                  CONTENTS, READONLY
  6 .eh_frame     00000058  00000000  00000000  000000c0  2**2
                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, DATA

生成的汇编语言和代码段内容:

karl@ubuntu:~/c/static$ objdump -s -d SimpleSection.o

SimpleSection.o:     file format elf32-i386

Contents of section .text:
 0000 5589e583 ec188b45 08894424 04c70424  U......E..D$...$
 0010 00000000 e8fcffff ffc9c355 89e583e4  ...........U....
 0020 f083ec20 c7442418 01000000 8b150400  ... .D$.........
 0030 0000a100 00000001 c28b4424 1801c28b  ..........D$....
 0040 44241c01 d0890424 e8fcffff ff8b4424  D$.....$......D$
 0050 18c9c3                               ...             
Contents of section .data:
 0000 54000000 55000000                    T...U...        
Contents of section .rodata:
 0000 25640a00                             %d..            
Contents of section .comment:
 0000 00474343 3a202855 62756e74 7520342e  .GCC: (Ubuntu 4.
 0010 382e342d 32756275 6e747531 7e31342e  8.4-2ubuntu1~14.
 0020 30342920 342e382e 3400               04) 4.8.4.      
Contents of section .eh_frame:
 0000 14000000 00000000 017a5200 017c0801  .........zR..|..
 0010 1b0c0404 88010000 1c000000 1c000000  ................
 0020 00000000 1b000000 00410e08 8502420d  .........A....B.
 0030 0557c50c 04040000 1c000000 3c000000  .W..........<...
 0040 1b000000 38000000 00410e08 8502420d  ....8....A....B.
 0050 0574c50c 04040000                    .t......        

Disassembly of section .text:

00000000 <func1>:
   0:   55                      push   %ebp
   1:   89 e5                   mov    %esp,%ebp
   3:   83 ec 18                sub    $0x18,%esp
   6:   8b 45 08                mov    0x8(%ebp),%eax
   9:   89 44 24 04             mov    %eax,0x4(%esp)
   d:   c7 04 24 00 00 00 00    movl   $0x0,(%esp)
  14:   e8 fc ff ff ff          call   15 <func1+0x15>
  19:   c9                      leave  
  1a:   c3                      ret    

0000001b <main>:
  1b:   55                      push   %ebp
  1c:   89 e5                   mov    %esp,%ebp
  1e:   83 e4 f0                and    $0xfffffff0,%esp
  21:   83 ec 20                sub    $0x20,%esp
  24:   c7 44 24 18 01 00 00    movl   $0x1,0x18(%esp)
  2b:   00 
  2c:   8b 15 04 00 00 00       mov    0x4,%edx
  32:   a1 00 00 00 00          mov    0x0,%eax
  37:   01 c2                   add    %eax,%edx
  39:   8b 44 24 18             mov    0x18(%esp),%eax
  3d:   01 c2                   add    %eax,%edx
  3f:   8b 44 24 1c             mov    0x1c(%esp),%eax
  43:   01 d0                   add    %edx,%eax
  45:   89 04 24                mov    %eax,(%esp)
  48:   e8 fc ff ff ff          call   49 <main+0x2e>
  4d:   8b 44 24 18             mov    0x18(%esp),%eax
  51:   c9                      leave  
  52:   c3                      ret    

段说明:

  • 代码段第一列为偏移量,中间代码段中的内容(16进制),往下为代码段的汇编指令。可以看到代码段中的内容和会汇编指令的序列一样。代码段的长度也和头文件结构中的长度(0x53)相同。
  • 数据段(data)保存的是初始化了的全局变量和局部静态变量。程序中有两个变量,global_init_varstatic_var,共八个字节。和头文件结构中的data段大小一样。
  • rodata段存放的是只读数据,const修饰的变量和字符串常量(%d\n)。
  • bss段存放的是未初始化的全局变量和局部静态变量。

观察文件头:

karl@ubuntu:~/c/static$ 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:          376 (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:         13
  Section header string table index: 10

32位文件头数据结构:/usr/include/elf.h

#define EI_NIDENT (16)

typedef struct
{

  unsigned char e_ident[EI_NIDENT]; 
  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;
  • Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 ,这个16字节魔术数对应了ELF文件的平台属性,字长,字节序,版本等。操作系统在加载的时候会确认魔术数是否正确,如果不正确,会拒绝加载。
  • e_type ELF文件类型。
/* Legal values for e_type (object file type).  */

#define ET_NONE     0       /* No file type */
#define ET_REL      1       /* Relocatable file */
#define ET_EXEC     2       /* Executable file */
#define ET_DYN      3       /* Shared object file */
#define ET_CORE     4       /* Core file */

段表

karl@ubuntu:~/c/static$   readelf -S SimpleSection.o
There are 13 section headers, starting at offset 0x178://注意在文件头中,Start of section headers的值为十进制。

Section Headers:
  [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al
  [ 0]                   NULL            00000000 000000 000000 00      0   0  0
  [ 1] .text             PROGBITS        00000000 000034 000053 00  AX  0   0  1
  [ 2] .rel.text         REL             00000000 0004e8 000028 08     11   1  4
  [ 3] .data             PROGBITS        00000000 000088 000008 00  WA  0   0  4
  [ 4] .bss              NOBITS          00000000 000090 000004 00  WA  0   0  4
  [ 5] .rodata           PROGBITS        00000000 000090 000004 00   A  0   0  1
  [ 6] .comment          PROGBITS        00000000 000094 00002a 01  MS  0   0  1
  [ 7] .note.GNU-stack   PROGBITS        00000000 0000be 000000 00      0   0  1
  [ 8] .eh_frame         PROGBITS        00000000 0000c0 000058 00   A  0   0  4
  [ 9] .rel.eh_frame     REL             00000000 000510 000010 08     11   8  4
  [10] .shstrtab         STRTAB          00000000 000118 00005f 00      0   0  1
  [11] .symtab           SYMTAB          00000000 000380 000100 10     12  11  4
  [12] .strtab           STRTAB          00000000 000480 000066 00      0   0  1
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings)
  I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown)
  O (extra OS processing required) o (OS specific), p (processor specific)
  • 段表其实是一个段结构体的数组。
  • 段表描述符结构:
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_type)和段的标志位(sh_flags).

  • 段类型:

/* Legal values for sh_type (section type).  */

#define SHT_NULL      0     /* Section header table entry unused */
#define SHT_PROGBITS      1     /* Program data */
#define SHT_SYMTAB    2     /* Symbol table */
#define SHT_STRTAB    3     /* String table */
#define SHT_RELA      4     /* Relocation entries with addends */
#define SHT_HASH      5     /* Symbol hash table */
#define SHT_DYNAMIC   6     /* Dynamic linking information */
#define SHT_NOTE      7     /* Notes */
#define SHT_NOBITS    8     /* Program space with no data (bss) */
#define SHT_REL       9     /* Relocation entries, no addends */
#define SHT_SHLIB     10        /* Reserved */
#define SHT_DYNSYM    11        /* Dynamic linker symbol table */
#define SHT_INIT_ARRAY    14        /* Array of constructors */
#define SHT_FINI_ARRAY    15        /* Array of destructors */
#define SHT_PREINIT_ARRAY 16        /* Array of pre-constructors */
#define SHT_GROUP     17        /* Section group */
#define SHT_SYMTAB_SHNDX  18        /* Extended section indeces */
#define SHT_NUM       19        /* Number of defined types.  */
  • 段标志:段的标志位(sh_flag) 段的标志位表示该段在进程虚拟地址空间中的属性,比如是否可写,是否可执行等。
/* Legal values for sh_flags (section flags).  */

#define SHF_WRITE        (1 << 0)   /* Writable */
#define SHF_ALLOC        (1 << 1)   /* Occupies memory during execution */
#define SHF_EXECINSTR        (1 << 2)   /* Executable */
#define SHF_MERGE        (1 << 4)   /* Might be merged */
#define SHF_STRINGS      (1 << 5)   /* Contains nul-terminated strings */
  • 分析文件头,能够得到短表和短表字符串表的位置,从而了解整个ELF文件。

符号表

  • 查看目标文件的符号表:ELF文件中的符号表往往是文件中的一个段,段名一般叫”.symtab”。
karl@ubuntu:~/c/static$ nm SimpleSection.o
00000000 T func1
00000000 D global_init_var
00000004 C global_uninit_var
0000001b T main
         U printf
00000004 d static_var.1378
00000000 b static_var2.1379
  • 符号表的结构很简单,它是一个Elf32_Sym结构(32位ELF文件)的数组,每个Elf32_Sym结构对应一个符号。
/* Symbol table entry.  */

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_info) 该成员低4位表示符号的类型(Symbol Type),高28位表示符号绑定信息(Symbol Binding)
  • 绑定信息:
/* Legal values for ST_BIND subfield of st_info (symbol binding).  */

#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 */
  • 符号的类型
/* Legal values for ST_TYPE subfield of st_info (symbol type).  */

#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 该符号表示一个段,这种符号必须是STB_LOCAL的*/
#define STT_FILE    4       
/* Symbol's name is file name 该符号表示文件名,一般都是该目标文件所对应的源文件名,它一定是STB_LOCAL类型的,
并且它的st_shndx一定是SHN_ABS*/
#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 */
  • 符号所在段 st_shndx )如果符号定义在本目标文件中,那么这个成员表示符号所在的段在段表中的下标,但是如果符号不是定义在本目标文件中,或者对于有些特殊符号,sh_shndx的值有些特殊。
#define SHN_UNDEF   0       
/* Undefined section
符号块未定义,在本目标文件被引用到,但是定义在其他目标文件中*/
#define SHN_ABS     0xfff1      
/* Associated symbol is absolute 
表示该符号包含了一个绝对的值。比如表示文件名的符号就属于这种类型的*/
#define SHN_COMMON  0xfff2  
/* Associated symbol is common 
表示该符号是一个“COMMON块”类型的符号,一般来说,未初始化的全局符号定义就是这种类型的
*/

查看SimpleSection.o中的符号表详细信息

karl@ubuntu:~/c/static$ readelf -s SimpleSection.o

Symbol table '.symtab' contains 16 entries:
   Num:    Value  Size Type    Bind   Vis      Ndx Name
     0: 00000000     0 NOTYPE  LOCAL  DEFAULT  UND 
     1: 00000000     0 FILE    LOCAL  DEFAULT  ABS SimpleSection.c
     2: 00000000     0 SECTION LOCAL  DEFAULT    1 
     3: 00000000     0 SECTION LOCAL  DEFAULT    3 
     4: 00000000     0 SECTION LOCAL  DEFAULT    4 
     5: 00000000     0 SECTION LOCAL  DEFAULT    5 
     6: 00000004     4 OBJECT  LOCAL  DEFAULT    3 static_var.1378
     7: 00000000     4 OBJECT  LOCAL  DEFAULT    4 static_var2.1379
     8: 00000000     0 SECTION LOCAL  DEFAULT    7 
     9: 00000000     0 SECTION LOCAL  DEFAULT    8 
    10: 00000000     0 SECTION LOCAL  DEFAULT    6 
    11: 00000000     4 OBJECT  GLOBAL DEFAULT    3 global_init_var
    12: 00000004     4 OBJECT  GLOBAL DEFAULT  COM global_uninit_var
    13: 00000000    27 FUNC    GLOBAL DEFAULT    1 func1
    14: 00000000     0 NOTYPE  GLOBAL DEFAULT  UND printf
    15: 0000001b    56 FUNC    GLOBAL DEFAULT    1 main
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值