zoukankan      html  css  js  c++  java
  • 一个连接器的实现:「二」

    Elf的文件格式

    我实现的简单C链接器,MiniLinker下载:

    git clone git@github.com:youzhonghui/MiniLinker.git

    在windows或者在linux上的可执行文件,除了记录了程序运行需要的指令和数据以外,还需要提供一些载入的信息。比如文件中那个部分是指令,那个部分是数据等等。windows下的可执行文件是PE格式,linux下的则是Elf格式。

    Elf格式在文件上是这个样子的:

    对于Elf文件格式的解析,在《程序员的自我修养》里写的很清楚了,我就不再重复。记录一些对写链接器必要而书中没有涉及的东西。当然,没事闲着研究链接器的人也是少数,所以资料不好找,大多数是靠自己的实验得出的。

    猜想与证明

    链接器的最终目标是生成可执行文件,让我们想一想这个可执行文件的组成。

    1. Elf文件头
    2. 节表
    3. 段表
    4. 已经重新定位好的指令和数据

    我们再猜想,转载Elf可执行文件的过程是什么。

    1. 读取文件头,进而得到表头,程序头
    2. 根据程序头的信息,将对应的文件片段载入到对应的内存地址
    3. 根据文件头中记录的入口地址,将ip指针指向它,开始运行程序

    细节[1]

    从以上的猜想可以看出,转载过程起作用的是程序头。那么节表是不是可有可无呢?

    文件头里有一个e_shnum元素,记录文件中的节表数量。用二进制修改软件(例如ghex),把这个值修改为0,再运行程序,一切正常!

    说明,在最后的可执行文件中,节表是可有可无的。那么,我们就不需要为生成节表设计算法了

    还有两个重要的需要注意的细节,都是在我实现了一个链接器原型,发现链接出来的东西运行的时候,要不是

    已杀死
    

    就是

    段错误
    

    细节[2]

    对于已杀死的情况,用edb都无法加载。说明在载入初期就出现错误,甚至是没有载入,那么应该就是文件头或者段表出了问题。

    将ld链接出来的正常程序与我的细细对比,发现要说什么差异,那么最大的可能就是我程序头的第一个offset并不是从0x0开始的。

    如果真是这样,理论上,我用ghex修改了程序表的第一个offset之后,应该能够装载(当然不可能正确运行)

    试了以后,edb果然装载了。

    所以,这是第二个很重要的细节:程序头的第一条记录的offset应该是从0开始的

    细节[3]

    解决了一进来就已杀死的问题,还有段错误

    用edb运行,会在最后的几条语句,因为违法的地址访问(一个解决于0的地址),而弹出段错误。

    当时这个错误郁闷了很久,因为这个现象可能是我的链接器算法写错误,但是我又找不到bug。

    使用《程序员的自我修养》中 最小程序 的那个例子来调试,猛然醒悟。

    这个例子中不但用int 0x80写了一个print,还写了一个exit

    也就是程序在执行main的ret是不能正常结束的。必须要有一个系统调用来终止程序。

    把这个exit函数拷贝出来,链接上,正常了。这也就是为什么要_start在main之前运行的原因之一,之前竟然没有注意到。

    第三个细节,程序需要系统调用来终止

    大胆猜想,小心求证

  • 相关阅读:
    ReportMachine打印条形码的问题
    性能测试基础知识
    jmeter请求参数中文乱码及无法读取CSV文件解决办法
    soapui学习
    java环境变量和查看安装路径
    python字典中显示中文
    Jmeter做webservices接口测试
    windows 上robot framework 读取sqlite3提示:OperationalError: unable to open database file错误
    c++ 复习练习
    笔记草稿。
  • 原文地址:https://www.cnblogs.com/nanshu/p/3240156.html
Copyright © 2011-2022 走看看