编译与链接(一):Linux裁剪库及可执行程序思路

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/zhuyunier/article/details/87877363

  当库和可执行程序在IPC端所占的空间有限制时,就需要考虑对库和可执行程序进行裁剪,以达到功能需求。现采用将所有的库编译成静态库,最后集成为一个可执行程序的方法,整理裁剪过程如下:

一、编译阶段

1、编译静态库为release版本,并添加以下编译选项:

编译参数 参数详解
-fvisibility=hidden 可以将所有导出的符号设置为隐藏,然后在库的代码中通过设置 __ attribute __(visibility(“default”)) 导出想要的类和函数
-ffunction-sections 将每个function创建为一个sections,其中每个sections名与function保持一致
-fdata-sections 将每个data创建为一个sections,其中每个sections名与data名保持一致
-fno-exceptions 禁用异常机制
-fno-unwind-tables 正在运行的程序从给定执行点返回函数调用堆栈的数据放置在.eh_frame部分,此选项会禁这些数据的生成,无法返回函数调用堆栈或使用C ++异常功能。
-fno-asynchronous-unwind-tables 用来不生成CFI指令
-fomit-frame-pointer 开启该选项,主要是用于去掉所有函数SFP(Stack Frame Pointer)的,即在函数调用时不保存栈帧指针SFP,代价是不能通过backtrace进行调试根据堆栈信息了。
-s 把符号表从最终的可执行文件中删除

2、在编译静态库时去掉-fPIC选项;
3、修改代码中的日志等级为warn级别;

二、链接阶段

1、编译可执行程序时,除了添加编译静态库的几个选项外,还需添加以下编译选项:

编译参数 参数详解
-Wl,–gc-sections 指示链接器去掉不用的section
-Wl,–no-export-dynamic 不导出所有的全局符号到动态符号表中

2、链接静态库带到可执行程序时,需要将静态库依赖的静态库全部包含进来,并且使用–start-group和–end-group反复在.a中进行搜索直到所有的未定义字符都被找到为止,不用再担心链接时静态库顺序问题,cmake编译时链接如下:

TARGET_LINK_LIBRARIES(${PROJECT_NAME} -Wl,--start-group ${THIRD_LIB} -Wl,--end-group)

3、编译完可执行程序时,使用strip命令使可执行文件中剥掉一些符号信息和调试信息,使文件变小。

三、段信息

1、使用readelf -S命令查看一个可执行的elf文件的节信息,常见段如下:

段名 说明
.text 存放程序运行代码(机器码)
.data 存放了经过初始化的全局变量和静态变量
.bss 保存了那些用到但未被初始化的数据
.rodata 只读数据(常量、字符串常量)
.shstrtab 段名字符串表
.symtab 保存了连接时所需的符号信息
.strtab 保存了.symtab所需的符号信息。
.init C++编译器生成的用来实现全局构造;该段自动产生名为init的函数,该函数早于main执行
.fini 同.init都为实现全局构造;该段自动产生名为fini的函数,该函数在main函数结束之后执行
.comment 包含编译器版本信息,不重要
.debug 保存调试相关信息,如.debug_info .debug_line等
.dynstr 保存动态链接符号字符串名
.dynsym 保存动态链接符号
.fini_array 保存程序或共享对象退出时的退出函数地址
.hash 符号表的hash表,用于加快符号查找
.init_array 保存程序或共享对象加载时的初始化函数指针
.interp 动态链接库路径
.line 调试时行号信息
.note 编译器、链接器、操作系统加入的平台相关的额外信息
.preinit_array 同init_array 但早于init_array执行
.tbss 线程的未初始化数据
.tdata 线程的初始化数据
.ctors 保存全局构造函数指针
.data.rel.ro 类似.rodata
.dtors 保存了全局析构函数指针
eh_frame C++异常处理内容
.eh_frame_hdr 同eh_frame
.got.plt 保存动态链接的延迟绑定相关信息
.jcr Java语言相关信息
.note.ABI-tag 保存程序ABI信息
.stab 调试信息
.dynamic 动态链接信息,存储了动态链接的符号表地址、字符串表地址及大小、哈希表地址,共享对象的SO-NAME、搜索路径,初始化代码地址,结束代码地址,依赖的共享对象文件名,动态链接重定位表地址、重定位入口数量等。
.gnu.version 动态链接符号版本,.dynsym中的每个符号对应一项(该符号所需版本在.gnu.versiond中的序号)
.gnu.versiond 动态链接符号版本的定义(definitions),每个版本的标志位、序号、共享库名称、主次版本号
.gnu.versionr 动态链接符号版本的需求(requirements),依赖的共享库名称和版本序号
.preinitarray 早于初始化阶段前执行的函数指针,在.initarray之前执行
.rel.data 静态链接文件中,数据段的重定位表
.rel.dyn 动态链接文件中,对数据引用(.got、.data)的重定位表
.rel.plt 动态链接文件中,对函数引用(.got.plt)的重定位表
.rel.text 静态链接文件中,代码段的重定位表

2、可以通过objdump -s 可执行程序命令查看对应段中的具体内容;
3、使用readelf -l 可执行程序 | grep interpreter查看可执行程序所需要的动态链接器的路径;
4、使用readelf --dyn-syms 可执行程序命令查看 .dynsym 表;
5、使用readelf -d 可执行程序命令查看“.dynamic”段的内容;
6、使用objdump -s -j .dynstr 可执行程序命令查看.dynstr的基地址;
7、使用readelf -V 可执行程序 命令输出.gnu.version和.gnu.version_r内容;
8、使用objcopy -R 段名字 可执行程序 命令删除可执行程序中对应的段;

参考文章:
https://lug.ustc.edu.cn/wiki/user/boj/linkers-and-loaders
https://blog.csdn.net/zvvzxzko2006/article/details/48519845

猜你喜欢

转载自blog.csdn.net/zhuyunier/article/details/87877363
今日推荐