美文网首页
程序员自我修养3:目标文件

程序员自我修养3:目标文件

作者: 梦工厂 | 来源:发表于2020-10-10 00:08 被阅读0次

一、相同的文件格式:ELF

  • 目标文件:代码编译后未链接的文件(Linux的.o)
  • 可执行文件
  • 静态链接库(Linux的.a)
  • 动态链接库(Linux的.so)

二、ELF文件的结构

示例代码:simple.c

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;
}

为什么要将程序的指令和数据进行分段呢?

  • 当程序被装载后,数据和指令被映射到两个不同的虚存区域。数据区域对进程是可写可读的,而指令区只是可读的,这样就避免了进程修改指令带来的问题。
  • 当系统中运行着多个该程序的副本时,它们的指令是相同的,数据可能不同。因此在内存中只需保存一份该程序的指令,这样就节省了大量的存储空间。
  • 为了提高缓存的命中率。将数据和指令分离有助于提高程序的局部性;
1. 文件头

ELF文件头描述了整个文件的文件属性,包括文件是否可以执行、是静态链接还是动态链接、入口地址、目标硬件、目标操作系统等信息。

zhoumeng.2019@n224-024-082:~/hello$ readelf -h simple.o
ELF Header:
  Magic:   7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
  Class:                             ELF64
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              REL (Relocatable file)
  Machine:                           Advanced Micro Devices X86-64
  Version:                           0x1
  Entry point address:               0x0
  Start of program headers:          0 (bytes into file)
  Start of section headers:          1104 (bytes into file)
  Flags:                             0x0
  Size of this header:               64 (bytes)
  Size of program headers:           0 (bytes)
  Number of program headers:         0
  Size of section headers:           64 (bytes)
  Number of section headers:         13
  Section header string table index: 12

Entry point address:程序的入口虚拟地址,加载完程序后从这地址开始执行程序的指令。可重定位文件一般没有,为0;
Start of section headers:段表在文件中的偏移;
Start of program headers:segment,装载时的段;

2. 段表

描述了文件中各个段的段名、段大小、偏移位置、读写权限以及段的其他属性等。

zhoumeng.2019@n224-024-082:~/hello$ objdump -h simple.o

simple.o:     file format elf64-x86-64

Sections:
Idx Name          Size      VMA               LMA               File off  Algn
  0 .text         00000057  0000000000000000  0000000000000000  00000040  2**0
                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
  1 .data         00000008  0000000000000000  0000000000000000  00000098  2**2
                  CONTENTS, ALLOC, LOAD, DATA
  2 .bss          00000004  0000000000000000  0000000000000000  000000a0  2**2
                  ALLOC
  3 .rodata       00000004  0000000000000000  0000000000000000  000000a0  2**0
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  4 .comment      0000002e  0000000000000000  0000000000000000  000000a4  2**0
                  CONTENTS, READONLY
  5 .note.GNU-stack 00000000  0000000000000000  0000000000000000  000000d2  2**0
                  CONTENTS, READONLY
  6 .eh_frame     00000058  0000000000000000  0000000000000000  000000d8  2**3
                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, DATA
  • VMA和LMA和虚拟地址有关,装载时才会确定;
  • CONTENTS 该段在文件中存在,.bss就不存在内容;
  • ALLOC 该段在进程空间总要分配空间;
3. 代码段.text
zhoumeng.2019@n224-024-082:~/hello$ objdump -d  simple.o

simple.o:     file format elf64-x86-64

Disassembly of section .text:
0000000000000000 <func1>:
   0:        55                           push   %rbp
   1:        48 89 e5                     mov    %rsp,%rbp
   4:        48 83 ec 10                  sub    $0x10,%rsp
   8:        89 7d fc                     mov    %edi,-0x4(%rbp)
   b:        8b 45 fc                     mov    -0x4(%rbp),%eax
   e:        89 c6                        mov    %eax,%esi
  10:        48 8d 3d 00 00 00 00         lea    0x0(%rip),%rdi        # 17 <func1+0x17>
  17:        b8 00 00 00 00               mov    $0x0,%eax
  1c:        e8 00 00 00 00               callq  21 <func1+0x21>
  21:        90                           nop
  22:        c9                           leaveq
  23:        c3                           retq

0000000000000024 <main>:
  24:        55                           push   %rbp
  25:        48 89 e5                     mov    %rsp,%rbp
  28:        48 83 ec 10                  sub    $0x10,%rsp
  2c:        c7 45 fc 01 00 00 00         movl   $0x1,-0x4(%rbp)
  33:        8b 15 00 00 00 00            mov    0x0(%rip),%edx        # 39 <main+0x15>
  39:        8b 05 00 00 00 00            mov    0x0(%rip),%eax        # 3f <main+0x1b>
  3f:        01 c2                        add    %eax,%edx
  41:        8b 45 fc                     mov    -0x4(%rbp),%eax
  44:        01 c2                        add    %eax,%edx
  46:        8b 45 f8                     mov    -0x8(%rbp),%eax
  49:        01 d0                        add    %edx,%eax
  4b:        89 c7                        mov    %eax,%edi
  4d:        e8 00 00 00 00               callq  52 <main+0x2e>
  52:        8b 45 fc                     mov    -0x4(%rbp),%eax
  55:        c9                           leaveq
  56:        c3                           retq
4. 数据段

已经初始化的全局变量和局部静态变量保存在.data段;
未初始化的全局变量和局部静态变量保存在.bss段(默认都是0,预留位置不占据空间);

$ objdump -x -s -d  simple.o
......
Contents of section .data:
 0000 54000000 55000000                    T...U... //global_init_var和static_var
......
5. 只读数据段.rodata

存放只读数据,一般是程序中的只读变量(如const修饰的变量)和字符串常量;

$ objdump -x -s -d  simple.o
......
Contents of section .rodata:
 0000 25640a00                             %d.. (prinf中的%d)
......
6. 注释信息段.comment
7. 符号表.symtab

记录了目标文件中所用到的所有符号,例如定义的全局和局部符号,引用的全局符号,段名;
每个符号都有个对应的值,对于变量和函数来说,符号值就是他们的地址;

zhoumeng.2019@n224-024-082:~/hello$ readelf -s simple.o

Symbol table '.symtab' contains 17 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND
     1: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS simple.c
     2: 0000000000000000     0 SECTION LOCAL  DEFAULT    1
     3: 0000000000000000     0 SECTION LOCAL  DEFAULT    3
     4: 0000000000000000     0 SECTION LOCAL  DEFAULT    4
     5: 0000000000000000     0 SECTION LOCAL  DEFAULT    5
     6: 0000000000000004     4 OBJECT  LOCAL  DEFAULT    3 static_var.1765
     7: 0000000000000000     4 OBJECT  LOCAL  DEFAULT    4 static_var2.1766
     8: 0000000000000000     0 SECTION LOCAL  DEFAULT    7
     9: 0000000000000000     0 SECTION LOCAL  DEFAULT    8
    10: 0000000000000000     0 SECTION LOCAL  DEFAULT    6
    11: 0000000000000000     4 OBJECT  GLOBAL DEFAULT    3 global_init_var
    12: 0000000000000004     4 OBJECT  GLOBAL DEFAULT  COM global_uninit_var
    13: 0000000000000000    36 FUNC    GLOBAL DEFAULT    1 func1
    14: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND _GLOBAL_OFFSET_TABLE_
    15: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND printf
    16: 0000000000000024    51 FUNC    GLOBAL DEFAULT    1 main
8. 字符串表 .strtab

存储ELF文件中用到的各种字符串,比如段名、变量名等;
如此,引用字符串时只需用表中的偏移即可,而不用考虑字符串长度不定的问题;

9. 重定位段 .rel.text .rel.data

链接器处理目标文件时,需要对目标文件中的某些代码和数据进行重定位,这些信息都会记录在重定位表中;

zhoumeng.2019@n224-024-082:~/hello$ objdump -r simple.o
simple.o:     file format elf64-x86-64

RELOCATION RECORDS FOR [.text]:
OFFSET           TYPE              VALUE
0000000000000013 R_X86_64_PC32     .rodata-0x0000000000000004
000000000000001d R_X86_64_PLT32    printf-0x0000000000000004
0000000000000035 R_X86_64_PC32     .data
000000000000003b R_X86_64_PC32     .bss-0x0000000000000004
000000000000004e R_X86_64_PC32     func1-0x0000000000000004
10. 动态链接信息.dynamic

ELF文件头存储的是静态链接时的相关内容,而.dynamic段可以被视为动态链接下的elf文件头。
保存了依赖于哪些共享对象、动态链接符号表的位置、动态链接重定位表、共享对象初始化代码的地址等;

zhoumeng.2019@n224-024-082:~/dynamix$ readelf -d P1

Dynamic section at offset 0xde0 contains 27 entries:
  Tag        Type                         Name/Value
 0x0000000000000001 (NEEDED)             Shared library: [./Lib.so]
 0x0000000000000001 (NEEDED)             Shared library: [libc.so.6]
 0x000000000000000c (INIT)               0x5f8
 0x000000000000000d (FINI)               0x804
 0x0000000000000019 (INIT_ARRAY)         0x200dc8
 0x000000000000001b (INIT_ARRAYSZ)       8 (bytes)
 0x000000000000001a (FINI_ARRAY)         0x200dd0
 0x000000000000001c (FINI_ARRAYSZ)       8 (bytes)
 0x000000006ffffef5 (GNU_HASH)           0x298
 0x0000000000000005 (STRTAB)             0x408
 0x0000000000000006 (SYMTAB)             0x2d0
 0x000000000000000a (STRSZ)              197 (bytes)
 0x000000000000000b (SYMENT)             24 (bytes)
 0x0000000000000015 (DEBUG)              0x0
 0x0000000000000003 (PLTGOT)             0x201000
 0x0000000000000002 (PLTRELSZ)           24 (bytes)
 0x0000000000000014 (PLTREL)             RELA
 0x0000000000000017 (JMPREL)             0x5e0
 0x0000000000000007 (RELA)               0x508
 0x0000000000000008 (RELASZ)             216 (bytes)
 0x0000000000000009 (RELAENT)            24 (bytes)
 0x000000006ffffffb (FLAGS_1)            Flags: PIE
 0x000000006ffffffe (VERNEED)            0x4e8
 0x000000006fffffff (VERNEEDNUM)         1
 0x000000006ffffff0 (VERSYM)             0x4ce
 0x000000006ffffff9 (RELACOUNT)          3
 0x0000000000000000 (NULL)               0x0

相关文章

网友评论

      本文标题:程序员自我修养3:目标文件

      本文链接:https://www.haomeiwen.com/subject/ixmkpktx.html