美文网首页
《程序员的自我修养》读书笔记——目标文件

《程序员的自我修养》读书笔记——目标文件

作者: 纸简书生 | 来源:发表于2018-03-31 18:26 被阅读120次

    上一篇介绍了编译连接的过程,提到了目标文件是通过汇编过程生成的,最终链接生成可执行文件,这篇介绍一下目标文件里面到底有什么。

    本文导图


    格式概述

    目标文件相对于最终的可执行文件而言,结构上已经和可执行文件的结构基本一样了。只是还没有经过链接过程,某些符号、地址还没被重定位,大部分内容都已经具备了。

    可执行文件的格式在Windows(PE-Portable Executable)和Linux(ELF- Executable Linkable Format)、Mac(Mach-O)不同,前两者都是基于COFF(Common file format)格式的,除此之外还有其他不常见的,如Intel/Microsoft的OMF、Unix a.out、MS-Doc .Com格式等。

    广义上将,目标文件与可执行文件格式几乎一样,可以看成同一种类型的文件。

    动态链接库(window .dll、Linux .so)及静态链接库(window lib、Linux .a)、Mac(dylb,tbd)都是按照可执行文件的格式存储。静态库稍微不同,因为他是把很多目标文件集合在一起,需要在头部加上一些索引。也就是包含了很多目标文件的一个目标文件包。

    ELF格式文件更可以做如下归纳(目标文件.o 静态库、可执行文件、动态库):


    查看格式

    file命令查看相应的文件格式。

    Mac下


    Linux下


    下面以ELF结构作为分析

    浅析目标文件内部结构

    目标文件至少包含编译后的机器指令代码、数据、和链接所需要的信息比如、符号表、调试信息、字符串等,下面的内容围绕着这几个部分进行总结。

    按照信息的不同属性以节(Section)也叫段(Segment)的形式存储,表示一个一定长度 的区域。这些区域里面分别存储了上诉编译后的信息。

    • 代码段:编译之后的机器指令放在代码段(Code Section),一般是叫.code.text
    • 数据段:全局变量和静态变量数据放在数据段(Data Section),一般叫.data
    • BBS段:未初始化的全局变量、未初始化静态变量放在.bbs段。

    具体来讲,下面代码与目标文件对应关系:

    • 文件头:ELF文件有个文件头,描述整个文件的文件属性,如是否可执行、静态库还是动态库、及入口地址(可执行文件)、目标硬件、操作系统等。其中还包含一个段表,段表示描述各个段的一个数组,包含各个段在文件的偏移位置、段的属性。文件头后面就是各个段的内容,比如代码段保存指令,数据段保存数据。——其中段表非常重要。

    未初始化的全局变量、局部静态变量虽然默认值都是0,但是没必要在.data段为它们分配空间(浪费),存放0是没必要的。所以放在.bbs段,也就给他们预留个位置而已,没有内容,所以在文件中不占空间。

    总体来说,程序代码被编译后分成两种段,程序指令(代码段)和程序数据(数据端及.BSS

    指令和数据分开的好处:

    • 指令和数据分别映射到两个虚拟区域,数据可读可写,而指令是只读的,可以设值区域的权限,防止恶意修改程序指令。——权限,安全
    • 分开有利于程序的局部性(在一段时间内,整个程序的执行仅限于程序中的某一部分。相应地,执行所访问的存储空间也局限于某个内存区域。)CPU有几大的缓存体系。——局部性原理
    • 复用,如果系统有多个程序的副本,那么指令都是一样的。这个也是动态库非常大的优势,节省内存开销——复用、复用!!!

    实验目标文件内容

    实验代码:



    使用objdump -h 查看目标文件内部结构。

    在Mac上实验结果

    objdump -h  hello.o
    
    hello.o:    file format Mach-O 64-bit x86-64
    
    Sections:
    Idx Name          Size      Address          Type
      0 __text        00000067 0000000000000000 TEXT
      1 __data        00000008 0000000000000068 DATA
      2 __cstring     00000004 0000000000000070 DATA
      3 __bss         00000004 0000000000000120 BSS
      4 __compact_unwind 00000040 0000000000000078 DATA
      5 __eh_frame    00000068 00000000000000b8 DATA
    

    在Linux实验结果


    除了之前讲到的三个段,这里多了只读数据端,注释端,堆栈提示段。其中包含了一些属性关键词,如长度(size)、端的位置(File off)、该段是否在文件中存在(Contents)、段的各种属性(Alloc)。可以看到.bbs段没有contents所以在文件中没有内容。而堆栈提示段size为0

    将上面的内容整理一下:

    可以通过size命令查看可执行文件的各个端大小(注意dec所有段的十进制和,hex是16进制的和)

    Mac上实验(文件格式是Mach-o)


    Linux上实验(文件格式是ELF)


    深入目标文件内部结构

    下面将深入了解目标文件中的各个段,他们的作用及含义。

    核心段

    上面提到过核心段有代码段、数据端、BBS段。下面分别介绍!

    代码段

    代码段可以使用objdump的-s参数打印出来,-d可以将包含指令的段反汇编信息。

    • 最左边是偏移地址,紧跟着的是16进制信息(每两个数字为一个字节,一般都是以字节位单位查看。

    Mac上实验(文件格式是Mach-o):最前面的部分是反汇编内容,接下来是各个段的内容。

    $ objdump -s -d hello.o
    
    hello.o:    file format Mach-O 64-bit x86-64
    
    Disassembly of section __TEXT,__text:
    _fun1:
           0:   55  pushq   %rbp
           1:   48 89 e5    movq    %rsp, %rbp
           4:   48 83 ec 10     subq    $16, %rsp
           8:   48 8d 05 61 00 00 00    leaq    97(%rip), %rax
           f:   89 7d fc    movl    %edi, -4(%rbp)
          12:   8b 75 fc    movl    -4(%rbp), %esi
          15:   48 89 c7    movq    %rax, %rdi
          18:   b0 00   movb    $0, %al
          1a:   e8 00 00 00 00  callq   0 <_fun1+0x1F>
          1f:   89 45 f8    movl    %eax, -8(%rbp)
          22:   48 83 c4 10     addq    $16, %rsp
          26:   5d  popq    %rbp
          27:   c3  retq
          28:   0f 1f 84 00 00 00 00 00     nopl    (%rax,%rax)
    
    _main:
          30:   55  pushq   %rbp
          31:   48 89 e5    movq    %rsp, %rbp
          34:   48 83 ec 10     subq    $16, %rsp
          38:   c7 45 fc 00 00 00 00    movl    $0, -4(%rbp)
          3f:   c7 45 f8 6f 00 00 00    movl    $111, -8(%rbp)
          46:   8b 05 00 00 00 00   movl    (%rip), %eax
          4c:   03 05 00 00 00 00   addl    (%rip), %eax
          52:   03 45 f8    addl    -8(%rbp), %eax
          55:   03 45 f4    addl    -12(%rbp), %eax
          58:   89 c7   movl    %eax, %edi
          5a:   e8 00 00 00 00  callq   0 <_main+0x2F>
          5f:   31 c0   xorl    %eax, %eax
          61:   48 83 c4 10     addq    $16, %rsp
          65:   5d  popq    %rbp
          66:   c3  retq
    Contents of section __text:
     0000 554889e5 4883ec10 488d0561 00000089  UH..H...H..a....
     0010 7dfc8b75 fc4889c7 b000e800 00000089  }..u.H..........
     0020 45f84883 c4105dc3 0f1f8400 00000000  E.H...].........
     0030 554889e5 4883ec10 c745fc00 000000c7  UH..H....E......
     0040 45f86f00 00008b05 00000000 03050000  E.o.............
     0050 00000345 f80345f4 89c7e800 00000031  ...E..E........1
     0060 c04883c4 105dc3                      .H...].
    Contents of section __data:
     0068 54000000 55000000                    T...U...
    Contents of section __cstring:
     0070 25640a00                             %d..
    Contents of section __bss:
    <skipping contents of bss section at [0120, 0124)>
    Contents of section __compact_unwind:
     0078 00000000 00000000 28000000 00000001  ........(.......
     0088 00000000 00000000 00000000 00000000  ................
     0098 30000000 00000000 37000000 00000001  0.......7.......
     00a8 00000000 00000000 00000000 00000000  ................
    Contents of section __eh_frame:
     00b8 14000000 00000000 017a5200 01781001  .........zR..x..
     00c8 100c0708 90010000 24000000 1c000000  ........$.......
     00d8 28ffffff ffffffff 28000000 00000000  (.......(.......
     00e8 00410e10 8602430d 06000000 00000000  .A....C.........
     00f8 24000000 44000000 30ffffff ffffffff  $...D...0.......
     0108 37000000 00000000 00410e10 8602430d  7........A....C.
     0118 06000000 00000000                    ........
    
    # instanza @ InstanzadeMacBook-Pro in ~/Desktop/Temp [12:12:42]
    $
    

    Linux实验(文件格式是ELF)


    • contents of section .text就是数据已十六进制打印出来的内容。中间4列是16进制内容,最右边是.text段的ASCII码形式。
    • .text段包含是.c文件两个函数的指令,比如第一个字节是0x55,就是func1()函数第一条push %ebp指令,最后一个0xc3代表main函数最后一个指令ret

    数据段

    .data段保存了初始化的全局静态变量和局部静态变量。上面代码中有两个这样的变量,每个变量4个字节一共8个字节。所以.data这个段大小为8个字节。——这是个非常准确的计算,不会存在多一个、少一个字节的情况

    注意printf的时候,有一个字符串常量“%d\n”。在linux中放到了.rodata段,分别有四个字符%、d、\n(换行符)、空字符。一个字符一个字节(8位ASCII码),所以一共四个字节。在Mac上放到了字符串常量区。

    以Mac下例子为例:

    Contents of section __cstring:
     0070 25640a00                             %d..
    

    通过查ASCII表,得到对应情况:%——25,d——64,\n(换行符)——0a,空字符(NULL)——00。刚好和字符串常量区对应

    在来看一起.data段(Mac下):

     0060 c04883c4 105dc3                      .H...].
    Contents of section __data:
     0068 54000000 55000000                    T...U...
    

    根据偏移范围可以知道.data端一共8个字节。和前面用size看到的不一样,size看到的是12个字节。

    Linux下

    可以看到Linux也是8个字节。字节从低到高分别是0x54、0x00、0x00、0x00。刚好是十进制的84,特别注意这里的顺序,字节从低到高还是从高到底排列涉及到CPU的字节序问题,也就是大端小端。后面四个字节也刚好是85

    BBS段

    Mac中的bss段如下:

    Contents of section __bss:
    <skipping contents of bss section at [0120, 0124)>
    

    可以知道具体的值放到了0120到0124之间,一共四个字节。

    Linux下


    同样也是四个字节。和global_uninit_varstatic_var2合起来大小8个字节不符。

    BBS段最终是通过符号表决定的。在Linux下只有static_var2存放到了bbs段。而global_uninit_var没有存放到任何段。只是一个未定义的common符号,这和语言及编译器实现有关。有一点可以明确——编译单元内部可见的静态变量是存在bbs段的。

    其他段

    ELF中除了.text、.data、.bbs三个最常用的段还有其他段。具体如下:


    .开头的都是系统保留的。如果想要自定义端,则不能以.开头。

    GCC提供了扩展机制,可以将变量或者代码放到你指定的段中去。用以下方式实现:


    以上只是ELF文件的轮廓,下面用一张图总结:



    几点说明一下:

    • 文件头:描述怎个文件的基本属性,文件版本,目标机器型号,程序入口地址。
    • 段表:描述了ELF文件包含所有段的信息,比如每个段的段名,长度,文件中的偏移,读写权限,和其他属性。

    下面开始从文件头开始

    文件头

    Linux用readelf来查看文件头,Mac用otool查看(otool -h hello.o

    在Linux中


    系统定义文件头数据结构:

    #define EI_NIDENT 16
    typedef struct {
        unsigned char e_ident[EI_NIDENT];
        Elf32_Half e_type;
        Elf32_Half e_machine;
        Elf32_Word e_version;
        Elf32_Addr e_entry;
        Elf32_Off e_phoff;
        Elf32_Off e_shoff;
        Elf32_Word e_flags;
        Elf32_Half e_ehsize;
        Elf32_Half e_phentsize;
        Elf32_Half e_phnum;
        Elf32_Half e_shentsize;
        Elf32_Half e_shnum;
        Elf32_Half e_shstrndx;
    } Elf32_Ehdr;
    

    各个字段代表什么意思根据名称就知道了。关键要知道在哪里定义这些常数的。ELF文件头定义在user/include/elf。mach.o类型的目标文件也有对应的头文件。

    下图是对readelf和ELF头文件定义的各个成员的含义解释。这里先大致了解下,后面会详解!

    ELF魔数(e_ident前四个字节)

    通过readelf之后看到最前面的16个字节刚好是e_ident,这16个ELF标准用来标识ELF文件平台属性,比如ELF字长(32位、64位),字节序、文件版本等。

    下表是对e_ident数组各个成员的说明:


    • 前四个字节是所有ELF文件必须相同的标识码:0x7F、0x45、0x4c、0x46。第一个字节赌赢DEL控制符,后三个对应ELF。所有的可执行文件最开始的几个字节都是魔数,用来确认文件类型,操作系统在加载的时候会确认魔数是否正确,不正确就不会加载。
    • 下一个字节标识文件类型;0x01标识32,0x02标识64
    • 第六个字节是字节序
    • 第七个字节:规定ELF文本版本
    • 后面9个字节:标准还没有定义,一般填0

    文件类型

    e_type表示文件类型,之前提到过3种ELF文件类型。如下表


    机器类型

    e_machine表示平台属性

    段表

    ELF最终段结构由段表决定,编译器、链接器和 装载器都是依赖段表来定位和访问各个端的属性。段表的位置有ELF文件头的e_shoff决定,如上面的例子段表的偏移为0x118

    直接来查看所有的段信息

    这里的数据其实在代码层面来讲都是由对应的数据结构的。无论是mach.o还是ELF。比如这里的Elf32_Shdr结构体是对一个段的封装,段表也就是由Elf32_Shdr组成的数组。对上图而言一个有11个这样的元素。

    段表元素数据结构


     typedef struct {
        Elf32_Word sh_name;
        Elf32_Word sh_type;
        Elf32_Word sh_flags;
        Elf32_Addr sh_addr;
        Elf32_Off sh_offset;
        Elf32_Word sh_size;
        Elf32_Word sh_link;
        Elf32_Word sh_info;
        Elf32_Word sh_
    

    各个字段的含义


    1

    现在把整个.o文件所有段的位置及长度都分析清楚了。

    • 段表长度为0x1b8,一共440个字节,包含11个段描述符,每个段描述符为40个字节,这个长短刚好是结构Elf32_Shdr的长度。
    • 文件最后一段.re.text结束后,长度为0x450也就是1104个字节,刚好是.o文件的大小。

    中间有段表和.rel.text都因为对齐的原因,与前面的短之间有一个字节和两个字节的间隔。注意这里是为了对齐。

    段类型(sh_type、sh_flags)

    段名(sh_name)只有在编译、链接过程有意义,但不能真正表示段的类型,段名(sh_name)其实只是一个索引指向字符串表(SHT_STRTAB)的某个位置,在编译器和链接器中起作用的是段类型字段和段标志位字段。

    段类型:


    段标志位


    系统保留段

    段的链接信息(sh_link、sh_info)

    段的类型与链接相关的话都需要sh_link、sh_info,无论是动态库还是静态库。比如重定位表,符号表。对于其他段这两个成员没意义


    重定位表(段类型SHT_REL)

    在刚才的段中,有一个.rel.text的短,类型(sh_type)为SHT_REL,也就是这个段包含的是重定位表。

    链接器在处理目标文件的时候,需要对目标文件某些部分重定位,虽然在代码段和数据端中用的是绝对地址的引用位置。重定位信息都记录在重定位表里面,每一个需要重定位的代码段或数据段都会有一个相应的重定位表,比如上面的.rel.text就是对.text的重定位表。因为.text段有一个绝对地址的引用,就是printf函数的调用。这里的决定地址引用也就是写死的地址。而.data段没有绝对地址引用。

    字符串表(段类型SHT_STRTAB)

    ELF用到很多字符串 ,如段名、变量名、因为字符串长度不确定,所以固定的解构表示比较困难。一种很常见做法就是把字符串集中放到一个表中,使用字符串的时候在表中的偏移来引用字符串。如下图

    字符串表一般有两种一种用来保存普通的字符串,比如符号的名字;另一种段表字符串用来来保存段表中用到的字符串,比如段表名。字符串表中包含有若干个以’null’结尾的字符序列。

    只要分析ELF头文件,就可以得到段表和段表字符串表的位置,从而解析整个ELF文件。

    符号表(symbol table)

    符号

    假如B文件要用到A文件的函数foo,那么在目标文件A定义了函数foo,目标文件B引用了A中的foo函数。每个函数、变量都有自己的名字,将函数名、变量名统称为符号名。

    每一个目标文件都会一个相应的符号表,记录了目标文件所要用到的符号,每个定义的符号有一个对应的值,叫做符号值对于变量和函数来说,符号值就是他们的地址。出了变量和函数外还有其他几种符号,可以将符号表中的符号进行分类。

    符号表包含的信息用于定位和重定位程序中的符号定义和引用。目标文件的其它部分通过一个符号在这个表中的索引值来使用 该符号。索引值从 0 开始计数,但值为 0 的表项(即第一项)并没有实际的意义, 它表示未定义的符号。这里用常量 STN_UNDEF 来表示未定义的符号。

    符号一般有如下类型:

    • 定义在目标文件的全局符号:可以被其他目标文件引用。如上面的func1,main,global_init_var
    • 引用其他目标文件中的全局符号:一般叫做外部符号,如 printf
    • 段名:由编译器产生,它的值就是该段的其实起始地址。如.text.data
    • 局部符号:只在编译单元内部可见,如static_var、static_var2。调试器可以是用哪个这些符号来分析程序。对于链接没什么作用,链接器也会忽略他们。
    • 行号符号:目标文件指令与源代码中代码行的对应关系。

    可以使用nm查看符号表的内容

    Mac上


    Linux上


    符号表结构(SHT_SYMTAB)

    符号表示文件中的一个段,段名.symtab。符号表数据结构比较简单Elf32_sym(符号表项),每个结构体对应一个符号。

    其结构体如下:

     typedef struct {
      Elf32_Word st_name;
      Elf32_Addr st_value;
      Elf32_Word st_size;
      unsigned char st_info;
      unsigned char st_other;
      Elf32_Half st_shndx;
    

    各个字段的含义


    符号类型和属性(st_info)

    由低4位表示符号类型,高28位表示符号属性信息。

    符号所在段(st_shndx)

    如果符号定义在本目标文件,则这个成员表示符号所载的段在段表中的下标。因为符号是为段而定义,在段中被引用。本数据成员即指明了相关联的段。本数据成员是一个索引值,它指向相关联的段在段表中的索引。在重定位过程中,段的位置会改变,本数据成员的值也随之改变,继续指向段的新位置。

    如果没有定义在本目标文件,则sh_shndx有些特殊。

    符号值(st_value)

    每个符号都有一个对应的值。如果是一个变量或者函数,符号值就是其地址。总的来说有以下几种情况:

    • 如果是符号定义且不是Common块类型(st_shndx不为SHN_COMMON),则st_value表示该符号在段中的偏移,即符号所对应的函数、变量位于st_shndx指定段。这种是最常见的,比如func1、main、global_init_var。
    • 如果是COMMON块的类型,则st_value表示该符号的对齐属性,如global_uninit_var
    • 在可执行文件中,st_value表示符号的虚拟地址。

    符号表内容解析

    内容如下:


    • 第一列表示符号表数组下标,从0开始共15个符号
    • 第二列就是符号值——st_value
    • 第三列Size为符号大小——st_size
    • 第四列、第五列分别为符号类型和绑定信息,对应st_info的低四位和高28位
    • 第六列不知道
    • 第七列表示符号所属的段——st_shndx
    • 第八列表示符号名称

    根据前面的内容做出如下解析:

    • func1、main是函数所有在代码段,代码段Ndx为1,所以.text为1.并且类型是STT_Func,并且是全局可见,所以是STB——GLOBAL。
    • global_uninit_var是一个SHN_COMMON类型的符号,本身并没有在BSS段。
    • ....
    • 依次类推可以把各个符号都解析出来

    需要说明的:static_var和static_var2变成了static_var.1533和static_var.1534,是因为进行符号修正。其次绑定的属性是STB_LOCAL,表示只在编译单元可见。类型是STT_SECTION类型的符号,表示下标为Ndx段的短名。但是符号没有显示。比如2号符号Ndx为1那么就是.text段。那么符号名字就是.text。可以使用objdump -t查看段名符号。

    特殊符号

    当使用链接器链接的时候,链接器会定义很多特殊符号,这些特殊符号没有在程序定义,但是可以直接声明并应用它。链接器将这些特殊符号放在了链接脚本中,链接器会在程序最终链接为可执行文件的时候,将其解析为正确的值。

    常见特殊符号


    符号修饰、函数签名(防止冲突)

    最开始编译器产生目标文件的时候,符号名和相应的变量函数名一样。但是如果已经定义这些符号就会产生目标文件冲突。为了解决目标文件冲突,就在对应的符号名签名加一些字符以示区分。如在符号名前、后加上_

    如果模块较多,命名规范不严格,同样可能导致冲突。于是就增加了命名空间的方法来解决。

    所以看到上面的static_var和static_var2变成了static_var.1533和static_var.1534。也就是进行了一次符号修饰。

    C++符号修饰

    C++强大而复杂,为了支持C++特性,发明了符号修饰和符号改编机制。

    对于不同类,同名函数,引入了一个函数签名的概念。函数签名包含一个函数的信息,函数名,参数类型及所在的命名空间、其他细心,用于识别不同的函数。

    编译器在链接处理符号的时候,会使用名称修饰的方法,使得每个函数签名对应一个修饰后的名称。如下图:


    弱符号、强符号()

    经常在编程中将一个符号重复定义,如果出现定义错误则说明这种事强符号。有些符号可以定义为弱符号,比如未初始化的全局变量,也可以使用__attribut__((weak))定义一个强符号为弱符号。

    它们的规则如下:

    • 不允许强符号被多次定义,也就是不同目标文件不能有相当的强符号(iOS中经常出现的符号冲突就是这个意思)
    • 如果符号在某个目标文件是强符号,其他文件是弱符号,那么选择强符号
    • 如果在所有文件中都是弱符号则选择其中占空间最大的一个

    弱引用、强引用

    注意不是iOS中强弱引用

    如果没有找到该符号的定义,链接器就会报未定义错误这种成为强引用。与之相对的是弱引用,如果是弱引用,链接器不认为是个错误,一般对未定义的弱引用,链接器默认为0,或者其他值,以便程序识别。在动态库中使用到,和COMMON块概念联系很紧密

    可以使用__attribute__((weakref))扩展自声明对一个外部函数为弱引用。如果把它编译为可执行文件,并不会报链接错误。但是当运行的时候,就会发生非法地址访问。

    可以用if加以判断,防止这种情况。

    这种弱符号和弱引用对于动态库来讲非常非常有用,比如动态库中定义的弱符号可以被用户定义的强符号覆盖,使得程序可以使用自定义版的库函数。

    调试信息

    目标文件还可能保存调试信息,比如在函数里面设置断点,可以监视变量变化,可以单步行进等,前提都是编译器必须提前将源代码与目标代码之间的关系(如目标代码中的地址对应源代码中的哪一行、函数和变量的类你先给,结构体的定义,字符串保存到目标文件里面)保存到目标文件。

    如果在gcc 中加入-g参数就可以在目标文件里面加上调试信息。gcc -c -g hello.c

    比如:

    objdump -h hello.o
    
    hello.o:    file format Mach-O 64-bit x86-64
    
    Sections:
    Idx Name          Size      Address          Type
      0 __text        00000067 0000000000000000 TEXT
      1 __data        00000008 0000000000000068 DATA
      2 __cstring     00000004 0000000000000070 DATA
      3 __bss         00000004 00000000000004e4 BSS
      4 __debug_str   0000009e 0000000000000074 DATA
      5 __debug_loc   00000000 0000000000000112 DATA
      6 __debug_abbrev 00000087 0000000000000112 DATA
      7 __debug_info  000000e0 0000000000000199 DATA
      8 __debug_ranges 00000000 0000000000000279 DATA
      9 __debug_macinfo 00000001 0000000000000279 DATA
     10 __apple_names 000000c8 000000000000027a DATA
     11 __apple_objc  00000024 0000000000000342 DATA
     12 __apple_namespac 00000024 0000000000000366 DATA
     13 __apple_types 00000047 000000000000038a DATA
     14 __compact_unwind 00000040 00000000000003d8 DATA
     15 __eh_frame    00000068 0000000000000418 DATA
     16 __debug_line  00000062 0000000000000480 DATA
    

    ELF采用一个DWARF的标准的调试信息格式。在Xcode中也有DWARF的身影。

    调试信息在目标文件和可执行文件中占用很大的空间,往往比程序的代码和数据大很多倍(这一点在iOS开发中用Instrument调试的时候最终就是用得这个文件实现的。

    小结

    这一部分内容比较多,主要是解析目标文件格式。如文件头、段、段表、重定位表、符号表、字符串表各自的数据结构是如何的。其实对ELF文件解析的内容内容远远不止这些,也有专门的官方文档对ELF格式记性详细的说明。

    最重要的是明白各个部分的关系

    扩展阅读

    可执行文件格式
    理解ELF文件格式
    ELF文件格式分析
    Comparison of executable file formats
    MachOView

    相关文章

      网友评论

          本文标题:《程序员的自我修养》读书笔记——目标文件

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