gcc 编译 汇编 链接

要想研究使用 gcc, gcc-multilib 这个包是一定要安装的, 它允许通过 -m32 和 -m64 选项来选择生成 32 位或者 64 的 ELF 文件.

我们知道程序的默认起点是 _start, 该函数做了一些未知/初始化的工作, 然后调用 main 函数, 如果 main 函数返回, 则由 _start 函数销毁进程.

我们可以使用 -e<symbol> 来重新设置该入口点.

 

观察上面的程序, 无论在32位下还是64位下均出现错误, 是什么原因?

没错, 就是因为没有销毁进程, 导致 ret(q) 指令(x86-64)继续执行, 该指令从调用者栈帧中取指令地址, 导致 main 函数返回到未知的内存地址取指令, 可能这个内存地址无法访问, 当然, 即使成功取址, 也极可能是无效的指令, 导致崩溃. 这件事告诉我们: 有些函数是不能返回的(实际上是计算机指令必须严格有序地按人类设计执行, 差之毫厘, 谬之千里).

编译

cpp 预处理, gcc -S 生成特定体系结构的汇编代码, 这个过程称为编译.

参数主要配置头文件搜索路径, 选择体系结构(-m32 -m64), 生成位置无关代码(-fPIC, 共享库必须要使用位置无关的目标文件, 而不是可重定位目标文件, 共享库不能重定位, 因为不知道, 也不能假设共享库的加载位置.)

汇编

as. 识别汇编代码, 生成可重定位或位置无关的目标文件, 什么区别? 毕竟复杂, 一言难弊.

链接

组织各目标文件, 生成可执行文件或共享库, 修改需要重定位的指令, 使其地址从0x0变为对应的线性地址, 共享库基本使用偏移寻址, 对外部符号的访问则采取 PLT 技术, 该技术使用称为 GOT 的偏移表, GOT 是运行时数据.

运行

现在我们有必要来研究一下什么是 "位置无关目标文件" 了

现在我们正常编译为 a.s, 查看之:

加上 -fPIC 选项, 编译为 b.s, 查看之:

 使用 diff 查看差异:

编译后的目标文件具有显著差异:

请看一下汇编代码:

可见, 涉及地址的指令均需要链接器"细细商榷", 我们再看可执行文件:

想也知道, 直接寻址比偏移寻址快些, 这项技术主要用于共享库.

盗了一张图, 惊天大秘密:

 

图片来源:

https://eli.thegreenplace.net/2011/11/03/position-independent-code-pic-in-shared-libraries/

本文皆小儿之见, 文思迂腐, 切莫计较.

原文地址:https://www.cnblogs.com/develon/p/9690641.html