编译技术

编译技术

概念

编译:严格意义上讲,指的是将高级语言编写的源代码翻译成低级语言描述的代码的过程。
交叉编译:如果代码在同一个平台上进行编译,生成的代码可以在其他平台上执行,那么这种编译过程就称为交叉编译。

gcc编译器可以用于完整的编译(其中包括预处理、汇编和编译阶段),该过程中会生成二进制目标文件(标准扩展名是.o),其结构遵循ELF格式规范。
如果只需要执行编译过程(不执行链接),可以使用下面的命令: gcc -c <input file> -o <output file>.o

在Linux上,可执行和可链接的格式(ELF)已经得到了普遍运用。而在Windows上,二进制文件通常遵循PE/COFF格式规范。

是什么原因让链接如此复杂?
链接阶段:重定位、解析引用。

因此,在运行时每个进程的内存映射中将存在多份动态库的副本。对于短期目标实现来说我们认为这种方式可行,但从长远目标来看我们还希望能够实现更为高效的一种机制:动态库仅需要加载一次(无论哪个应用程序先加载它),而且对于其他需要加载这个动态库的应用程序来说同样适用。我们可以利用位置无关代码(position independent code, PIC)这一概念来实现这个目标。
由于PIC概念的出现,动态库也被设计成共享库。现如今PIC概念被广泛使用,而且在64位系统中被编译器大量使用,因此我们不再区分动态库和共享库,而这两个名称或多或少可以互换使用。

构建动态库生成的二进制文件本质上与可执行文件是相同的,唯一的区别在于动态库缺少了让其独立执行的启动例程(startup routines)。

遵循“只构建一个,其他依靠加载”的设计模式。

动态库的特点

  1. 创建动态库需要完整的构建过程。
  2. 动态库可以链接其他库
    可执行文件和动态库都可以加载和链接动态库。

ABI:应用程序二进制接口
API:应用程序编程接口

使用静态库

静态库是通过编译器编译源代码文件并将生成的目标文件打包生成后的归档文件。

FAQ

只有在编译动态链接库时才会使用-fPIC编译器选项吗?能否在静态编译库的情况下使用呢?

分两种情况:

如果静态库是链接到可执行文件的,那么编译时可以指定也可以不指定-fPIC编译器选项。
如果静态链接库是链接到动态库中的,那么就必须使用-fPIC选项编译(如果不适用-fPIC选项,你可以指定-mcmodel=large编译器选项)。

如果静态库编译时没有使用上述两种选项之一,在试图将其链接到动态库中时将会引发链接器错误。

'.rodata' can not be used when making a shared object; recompile with -fPIC

C++的动态库

C++使用了更加复杂的符号命名规则,给链接器的设计带来了巨大的挑战。

第三方包一般都是怎么交付的?

第三方包中会包含库文件以及对外提供的头文件,也可能包含少量额外文件(比如文档、联机帮助、软件包的图标文件、应用软件、代码和多媒体示例等)。

查看二进制的常用命令

readelf -d libbootdrv.so 可以获取到Library soname,以及它依赖了哪些so二进制。
file libbootdrv.so 可以用于查看几乎任何类型文件的详细信息。由于可以查看二进制文件的绝大多数基本信息,因此该工具迟早会派上用场。
size libbootdrv.so 能够快速地获取ELF节的字节长度信息。
ldd libbootdrv.so 该工具可以显示出客户二进制文件启动时需要静态加载的动态库的完整列表(即加载时依赖项)。使用readelf工具可以达到同样的效果:readelf -d /path/to/program | grep NEEDED
nm libbootdrv.so nm实用程序可以列出二进制文件的符号列表。该工具可以输出符号并显示出对应的符号类型。
objdump可以算得上是功能最丰富的二进制分析工具了。objdump的优势在于不仅仅支持ELF格式,还支持大概50种其他格式。同时其反汇编的功能也强于readelf。常用选项:

-f  获取目标文件头信息
-h  列出所有二进制文件的节
-t  列出所有符号,与nm命令的执行结果完全相同
-T  只列出动态符号,与nm -D命令的执行结果完全相同
-p  查看动态节信息
-R  查看重定位节的信息

构建过程中库文件定位规则

在Linux中使用-L和-l选项来指定构建过程中库文件的路径。

将完整的库文件路径分成两个部分:目录路径和库文件名。
目录路径添加到-L链接器选项后面,并传递给链接器。
将库文件名添加到-l参数后面,并传递给链接器。

重复的符号定义

在解析引用的过程中,最常发生的问题就是会出现重复符号,该问题发生在链接的最后阶段,可能在所有可用符号列表中包含两个甚至更多同名的符号。
C语言仅仅看函数名,C++只有函数的名称和参数列表都相同时函数才是重复的。

如何判断一个可执行文件是否使用了PIE?

方法一

用elf header里面的 type来判断:

readelf -h program|grep Type

如果是

Type:                              EXEC (Executable file)

说明是没有使用-fPIE的。

如果是

Type:                              DYN (Shared object file)

说明是有使用-fPIE的。

方法二

size --format=sysv program

用size命令看里面的地址,PIE是以base地址0开始的。非PIE的地址是很大的。

$size --format=sysv program
program  :
section          size      addr
.interp            28   4194816
.note.ABI-tag      32   4194844
.hash              68   4194880
....

$size --format=sysv program_pie
program_pie  :
section          size      addr
.interp            28       512
.note.ABI-tag      32       540
.hash              76       576
posted @ 2019-07-04 17:53  琅琊散人  阅读(745)  评论(1编辑  收藏  举报