zoukankan      html  css  js  c++  java
  • 《程序员的自我修养》第三章学习笔记

    1,  编译器编译源代码生成的文件叫做目标文件。

    从结构上说,是编译后的可执行文件,只不过还没有经过链接

     

    3.1 目标文件的格式

    1,可执行文件的格式: Windows下的PE     Linux下的ELF

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

    3,可执行文件,动态链接库,静态链接库都按照可执行文件格式存储(Windows下是 PE-COFF格式,Linux下是ELF格式)。

    4Linux下命令: $: file   ***   显示出对应文件的类型

    目标文件是什么样的

    1,目标文件包含的内容:编译后的机器指令代码、数据,还有链接时要的一些信息(比如符号表、调试信息、字符串等)。

    1,  一般目标文件把上述信息按不同的属性,以节(section)的形式存储。有时也叫段(segment

    2,  代码段常见的名字有 “.code”  “.text”,编译后的机器指令就放在代码段,

    数据段:一般名字都是 “.data”,已初始化全局变量和局部静态变量数据放这

    3,  ELF文件的开头是一个文件头,它描述了整个文件的文件属性(是否可读可写可执行,是静态链接还是动态链接及入口地址)、目标硬件、目标操作系统等信息。

    4,  文件头还有一个段表(section table)。描述文件各个段的数组(各个段在文件中的偏移和属性)。

    5,  关于bss段:未初始化的全局变量和静态局部变量一般放在一个”.bss”段的地方。

    它只是为未初始化的全局变量和静态局部变量预留位置而已,并没有内容,在文件中不占空间。

    6,  总体来说:程序源代码被编译后主要分成两种段:程序指令(代码段),程序数据(数据段,bss段)。

    3.3挖掘simplesection.o

    1objdump 查看各种目标文件的结构和内容, objdump –h main.o  -h 表示把ELF文件的各个段的基本信息打出来

       readelf 专门针对ELF文件格式的解析器

       size 可以用来查看ELF文件的代码段、数据段和bss段的长度   size main.o

     

    3.3.1代码段

    1objdump 的参数 –s 将所有的段的内容以十六进制的方式打印出来 

                        -d 将所有包含指令的段反汇编    比如 objdump  –s  –d  main.o

                        -h 把关键的段显示了出来,忽略了辅助性段

    3.3.2数据段和只读数据段

    1 “.data” :保存的是已初始化全局变量和局部静态变量

    2,”rodata”:存放的是只读数据,一般是程序中的只读变量(如用const修饰的)和字符串常量。

     

    3.3.3 bss

    1,存放的是未初始化的全局变量和静态局部变量。不占磁盘空间

     

    3.3.4 其他段

     

      还可以自定义段GCC提供的扩展机制

    3.4 ELF文件结构描述

     

    3.4.1 文件头

    1,查看 readelf –h main.o

    2ELF文件中定义了:ELF魔数、文件机器字节长度、数据存储方式、版本、运行平台、ABI版本、

    ELF重定位类型、硬件平台、硬件平台版本、入口地址、程序头入口和长度、段表的位置和长度及段的数量等。

     

    3ELF魔数:文件头最开始的4个字节是所有ELF都必须相同的标识码。又称为魔数。

       魔数用来确认文件类型,操作系统在加载可执行文件的时候会确认魔数是否正确,若不正确则拒绝加载。

    4ELF文件类型:分为 可重定位、可执行、共享目标文件、核心转储文件

    3.4.2 段表(除了文件头以外最重要的结构)

    1,描述了ELF各个段的信息(比如每个段的段名、段的长度、在文件中的偏移,读写权限及其他属性)。

    2,编译器、链接器和装载器都是靠段表来定位和访问各个段的属性的。

    3readelf –S main.o 显示真正的段表结构

    4,上述结果是一个以 “ELF32_Shdr” 结构体为元素的数组。

     

    3.4.3 重定位表

    1,一个叫”rel.text”的段,类型是”SHT_REL”,就是重定位表

    2,链接器在处理目标文件时,需要对目标文件中的某些部位进行重定位(即代码段和数据段中那些对绝对地址的引用的位置)。

    3,这些重定位信息都记录在ELF文件的重定位表里。对于每个需要重定位的代码段或数据段,都会有一个相应的重定位表。

    3.4.3 字符串表

    1,把ELF文件中用到的字符串(段名、变量名等)集中起来存放到一个表中。然后使用字符串在表中的偏移来引用字符串。这个表就是字符串表。

    2,一般字符串表在ELF文件中也以段的形式保存。常见的段名有”.strtab” ”.shstrtab”

    3”.strtab”:字符串表:保存普通的字符串

    4”.shstrtab”:段表字符串表:保存段表中用到的字符串,最常见的就是段名。

    3.5 链接的接口——符号

    1,链接过程的本质就是要把多个不同的目标文件之间相互粘滞到一起。

    2,为了使不同目标文件之间能够相互粘合,这写目标文件必须要有固定的规则才行。

    3,在链接中,目标文件之间相互拼合实际上是目标文件之间对地址的引用,即对函数和变量的地址的引用。

    4,在链接中,我们将函数和变量统称为符号(symbol),函数名或变量名就是符号名(symbol name)。

    5,每一个目标文件都会有一个相应的符号表(symbol table),记录着目标文件中所用到的所有的符号。

    6,每个定义的符号有一个对应的值叫做符号值(symbol value)。对于变量或函数来说,符号值就是它们的地址。

    8,  符号的分类: 1)和(2)比较需要关注

    (1)       定义在本目标文件的全局符号,可被其他目标文件引用。

    (2)       在本目标文件中引用的全局符号,在别的目标文件中定义。一般叫做外部符号(external symbol)。

    (3)       段名,这种符号往往由编译器产生,它的值就是该段的起始地址。

    (4)       局部符号,这类符号只在编译单元内部可见,

    (5)       行号信息,即目标文件指令与源代码中代码行的对应关系,是可选的。

     

    3.5.1 ELF符号表结构

    1ELF文件中的符号表往往是文件中的一个段,y一般叫做 “.symtab”。是一个Elf32_Sym的数组,数组中每个元素对应一个符号。

    2,结构体定义如下

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

    3.5.2 特殊符号

    1ld链接器产生可执行文件时,会给我们定义很多符号(没有在自己的程序中定义),但是可以直接声明并且引用它,我们称之为特殊符号。

     

    3.5.3 符号修饰与函数签名

    1c++增加了名称空间(namespace)的方法来解决多模块之间的符号冲突问题。

     

    2c++符号修饰

    (1)       函数签名:包含了一个函数的信息(函数名、参数类型、所在的类和名称空间和其他信息)。函数签名用于识别不同的函数。

     

    3.5.4 extern”C”

    1c++为了与c兼容,在符号的管理上,c++有一个用来声明或定义一个C的符号extern”C”的关键字用法。

    2,可以让c++的名称修饰机制不作用。

    3.5.5 弱符号与强符号

    1,多个目标文件含有相同名字的全局符号的定义,在链接时将会出现符号重复定义的错误。

    2,编译器默认函数和初始化了的全局变量为强符号,未初始化的全局变量为弱符号。

    3,强弱符号都是针对定义来说的,不是针对符号的引用。 假如 extern int ext; ext不是强符号也不是弱符号,因为它是一个外部变量的引用。

    4,链接器按下面规则处理与选择被多次定义的全局符号。

    (1)       不允许强符号被多次定义,否则报错。

    (2)       若一个符号在某个目标文件中是强符号,在其他文件中都是弱符号,那么选择强符号。

    (3)       若一个符号早所有的目标文件中都是若符号,那么选择其中占用空间最大的那一个。

    5,弱引用与强引用:

    强引用:假如链接时没找到该符号的定义,链接器就会报符号未定义错误。

      弱引用:假如链接时没找到该符号的定义,链接器不会报错,默认其为0或是一个特殊值。

    6GCC中可以通过 “__attribute__((weakref))”这个扩展关键字来声明一个外部函数为弱引用  p93

      用法: __attribute__((weakref)) void foo();  这样就说明foo为弱符号。

    7,弱符号和弱引用对库来说非常有用,比如库中定义的弱符号可以被用户定义的强符号所覆盖。

    8,补充: Linux中支持多线程时需要在编译时加上 –lpthread 选项, 比如 gcc test.c -lpthread

    3.6 调试信息

    1,在gcc编译时加上 –g 参数就会在产生的目标文件里面加上调试信息。   目标文件会多些 debug段。

    2,假设有个目标文件 test Linux下可以用 ”strip” 命令来去除调试信息, 用法: $ strip test

     

    3.7 本章小结

    参考《程序员的自我修养》

  • 相关阅读:
    203. Remove Linked List Elements
    86. Partition List
    143. Reorder List
    876. Middle of the Linked List
    246. Strobogrammatic Number
    202. Happy Number
    数据类型转换
    表达式
    面向对象
    对齐
  • 原文地址:https://www.cnblogs.com/xcywt/p/4957427.html
Copyright © 2011-2022 走看看