zoukankan      html  css  js  c++  java
  • 程序员的自我修养 学习心得(2)

    编译和连接:预编译(Prepressing)、编译(Compilation)、汇编(Assembly)、链接(Linking)

    image

    静态语言的C/C++模块之间通信有两种方式,一个是模块间的函数调用,另一个是模块间的变量访问。他们都需要知道目标函数或目标变量的地址,统一起来,就是模块间符号的引用。类似于拼图,定义符号的模块多出一块区域,引用符号的模块刚刚好缺少那块区域,两者结合就形成一个整体,这就是链接。

    可执行文件格式

        PC机上的可执行文件格式,Windows下面是PE(Portable Executable),Linux下面的ELF(Executable Linkable Format),他们都是COFF(Commom file format)格式的变种。

        目标文件,就是源代码编译后,但未进行链接的那些中间文件(Windows下是.obj,Linux下是.o),他们和可执行文件的内容和结构很相似,所以都采用一个格式存储。

        动态链接库(DLL,Dynamic Linking Library),Windows下是.dll,Linux下是.so

        静态链接库(Static Linking library),Windows的.llib,Linux下的.a。

    程序源代码编译后的机器指令被放在代码段(Code Section),常见命名有.code或者.text

    已初始化的全局变量和局部静态变量经常放在数据段(Data Section),常见命名有.data

    未初始化的全局变量和局部静态变量一般放在称为.bss段。(默认值都为0)

    为什么要分开存放呢?

    1. 保护代码段

    2. 增加CPU缓冲(数据缓冲、指令缓冲)的命中率

    2. 代码复用(代码区、资源区公用,数据区各自保存副本)

    以一个简单的simplesection.c的例子来分析ELF文件格式:

    image

    objdump -h打印出目标文件的段信息。

    CONTENTS属性表示该段在文件中存在。SIZE为段长度,FIle Off为段所在的位置。

    .text段开头是ELF文件格式的头信息。占据长度为0x34。

    size命令用于查看各个段的长度

     image

    objdump命令和readelf -h命令

     image

    GCC提供一个扩展命令, 在全局变量或函数之前加上 __attribute__((section(“name”)))的属性,可以把相应的变量或函数放在以”name”作为段名的段中。  

    ELF文件格式被设计成可以在多个平台下使用,这并不表示同一个ELF文件,可以在不同平台下使用(就将java的字节码文件),而是表示不同平台下的ELF文件,都遵循同一套ELF标准。

    ELF文件中,各段的名字只在编译和链接时,才有意义。段的属性中,段的类型和段的标志位这两个属性最为重要。

    段中的调试信息,ELF文件有DWARF标准的调试信息格式,现在已经发展到第三版,有DWARF标准委员会管理。Microsoft有它自己的调试信息格式标准,叫CodeView。值得一提的是,增加了调试信息的目标文件比程序本身大好几倍,当发布最终程序时,需要去掉这些调试信息。Linux下有strip工具来去除调试信息。

    查看静态库中的工具,ar -t,readelf,objdump -t

    静态链接

    不同的输入目标文件,结合相关的库文件,经过链接器链接,形成一个可执行文件。

    主要有两步步骤:空间与地址分配,符号解析与重定位。

    空间与地址分配:扫描所有的输入目标文件,获取各个段长度,收集符号表到一个全局符号表中去。

    符号解析与重定位:第一步收集到的信息,读取输入文件中的数据与重定位信息,进行符号解析与重定位,

    VMA为虚拟地址,LMA为加载地址。一般情况下,两者值是相同的。但在某些嵌入式系统中,有些程序存在ROM中,VMA与LMA不一致,在加载时,需要指出。

    在Linux下面,ELF可执行文件的默认从地址0x0804_8000开始分配的,一般程序的入口地址为_start,这个函数是Linux系统库(Glibc)中的一部分,当我们的程序与Glibc库链接在一起时,形成最终的可执行文件时,这个函数就是程序的初始化部分的入口地址。它在完成一系列初始化后,会调用main函数来执行程序的主体。当main函数执行完成之后,返回到初始化部分,进行一些后续的清理工作,然后结束进程。例如,C++的全局构造函数与解析函数就在main函数执行之前和之后进行执行的。

    ABI与API

    有没有可能,不同编译器编译出来的目标文件,经过链接后,可以形成一个可执行文件。

    MSVC编译的目标文件时PE/COFF格式,GCC编译的是ELF格式,链接器必须同时认识这两种格式才行。

    如果两个编译器编译出来的目标文件,能够相互链接,那么两个目标文件,必须满足下列条件:采用相同规定目标文件格式、相同的符号修饰标准、变量的内存分布方式、函数的调用方式等等,这一系列规范称之为ABI(Application Binary Interface)。

    API和ABI都是应用程序接口标准,只是描述的接口层面不一样。

    API是源码级别的接口,比如POSIX是一个API标准,Windows所规定的应用程序接口是一个API标准。举个例子,POSIX的printf函数,它(POSIX)能保证这个函数定义在所有POSIX标准的系统之间是一样的,但是它不保证printf在实际系统(Intel X86 和 MIPS 都装Linux系统)中执行时,不同入栈顺序、参数的堆栈分布等实际运行的二进制级别是相同的。

    ABI是二进制层面的接口,比如C++的对象内存分布(Object Memory Layout)是C++ ABI的一部分。

    静态链接库的组织,每一个功能有一个源文件来负责,比如,printf.o由printf.c负责。链接器链接时,是以目标文件为最小单位了,为了避免引入无用函数,每个功能都划分到以文件为单位,而不是以函数为单位,在最终结果中,节省空间浪费。

    链接器提供的默认配置足够一般程序使用,在一些特殊要求的程序,比如操作系统内核、BIOS、嵌入式系统等等,则需要额外指出链接过程,如指定输出文件的各个段虚拟地址、段名称等等。链接器生成不同类型的文件时,使用不同的链接脚本。

  • 相关阅读:
    How to install VXDIAG Honda, Toyota and JLR SDD software
    16% off MPPS V16 ECU tuning tool for EDC15 EDC16 EDC17
    Cummins INSITE locked and ask for verification code
    How to use BMW Multi Tool 7.3 to replace lost key for BMW X1
    Bleed Brake Master Cylinder with Intelligent Tester IT2
    Porsche Piwis Tester II “No VCI has been detected”,how to do?
    Creader VIII VS. Creader VII+
    How to solve GM MDI cannot complete the installation
    汽车OBD2诊断程序开发 (原文转载,思路很清晰!)
    汽车节温器单片机开发思路
  • 原文地址:https://www.cnblogs.com/cherishui/p/3778458.html
Copyright © 2011-2022 走看看