Linux下的链接器支持一个强大的库打桩(library interpositioning),允许你阻拦对系统标准库中某个目标函数的调用,取而代之执行自己的包装函数。它可以给我们带来两个好处,一是通过增加某些语句,可以追踪自己的程序对某些库函数的调用情况;二是可以在你自己的程序中,对某些库函数偷天换日,替换成一个完全不同的实现。

打桩可以发生在编译,链接和运行的任意一个阶段,相应的代码编写和编译也有少量区别,下文将分别做一阐述:

需求:假设需要在主程序myprog.c中跟踪对库函数malloc和free的使用情况。

1.编译时打桩

1.1 建立包装函数

建立mymalloc.c文件,定义需要的包装函数mymalloc和myfree.#ifdef COMPILETIME#include #include //定义malloc 包装函数void *mymalloc(size_t size){ void *ptr = malloc(size); printf("malloc(%d) = %p\n", (int)size, ptr); return ptr;}//定义free 包装函数void *myfree(void *ptr){ free(ptr); printf("free(%p) = %p\n", ptr);}#endif

1.2 建立头文件malloc.h

该文件向预解决器指明用mymalloc.c中的包装函数替换库里的目标函数#define malloc(size) mymalloc(size)#define free(ptr) myfree(ptr)void *mymalloc(size_t size);void *myfree(void *ptr);

1.3 建立自己的程序文件

建立文件myprog.c,并在其中正常调用malloc函数.#include #include int main(void){ int *p = malloc(32); free(p); return 0;}

1.4 编译链接gcc -DCOMPILETIME -c mymalloc.cgcc -I. -o myprog myprog.c mymalloc.c

-I.:指示C预解决器在搜索通常的系统目录前,先在当前目录中查找malloc.h

1.5 运行结果./myprogmalloc(32) = 0x9ee010free(0x9ee010)

2.链接时打桩

linux利用静态链接器完成库打桩。先看它的一个命令行参数:--wrap f:指示链接器把对符号f的引用解析成__wrap_f,把对__real_f的引用解析成符号f。

此处f代表任意的库函数名或者变量名

-Wl,option:将option传递给链接器, option中的每个逗号都要用空格来替换。

2.1 建立包装函数

创立mymalloc.c文件,定义需要的包装函数.#ifdef LIKETIME#include void *__real_malloc(size_t size);void __real_free(void *ptr);//定义malloc 包装函数void *__wrap_malloc(size_t size){ void *ptr = *__real_malloc(size); //调用标准库里的malloc printf("malloc(%d) = %p\n", (int)size, ptr); return ptr;}//定义free 包装函数void *__wrap_free(void *ptr){ __real_free(ptr); //调用标准库里的free printf("free(%p) = %p\n", ptr);}#endif

2.2 建立自己的程序文件

建立文件myprog.c,并在其中正常调用malloc函数.#include int main(void){ int *p = malloc(32); free(p); return 0;}

2.3 编译链接gcc -DLINKTIME -c mymalloc.cgcc -c myprog.cgcc -Wl,--wrap,malloc -Wl,--wrap,free -o myprog myprog.o mymalloc.o-Wl,option:将option传递给链接器。 option中的每个逗号都要用空格来替换, 所以-Wl,--wrap,malloc意味着把--wrap malloc传递给链接器。

2.4 运行结果./myprogmalloc(32) = 0x18cf010free(0x18cf010)

3 运行时打桩

编译时打桩需要访问程序的源代码,而链接时需要访问可重定位目标文件。那有没有一种办法让仅仅访问可执行目标就能达到同样的目的呢?我们可以利用基于动态链接器的LD_PRELOAD环境变量来实现。

当你将LD_PRELOAD环境变量设置为一个共享路径名的列表(以空格或者分号分开),那么在运行一个程序时,动态链接器(LD-LINUX.SO)会先搜索列表里的库,而后才搜素系统其它库。

利用这个原理,你可以对任何共享库中的任何函数打桩,包括libc.so。

3.1 建立包装函数文件

下面建立mymalloc.c文件,其中定义了malloc和free的包装函数。每个包装函数中,利用dlsym调用libc中的标准函数。#ifdef RUNTIME#define _GNU_SOURCE#include #include #include //定义malloc 包装函数void *malloc(size_t size){ void *(*mallocp)(size_t size); //取得libc中malloc函数的地址 if( !(mallocp = dlsym(RTLD_NEXT, "malloc")) ){ fputs(dlerror()); exit(1); } char *ptr = *mallocp(size); //利用函数指针间接调用libc中的malloc函数 printf("malloc(%d) = %p\n", (int)size, ptr); return ptr;}//定义free 包装函数void *free(void *ptr){ void (*free)(void *) = NULL; if(!ptr) return; //取得libc中free函数的地址 if( !(freep = dlsym(RTLD_NEXT, "free")) ){ fputs(dlerror()); exit(1); } *freep(ptr); //利用函数指针间接调用libc中的free函数 printf("free(%p)\n", ptr); }#endif

3.2 建立自己的程序文件

建立文件myprog.c,并在其中正常调用malloc函数.#include int main(void){ int *p = malloc(32); free(p); return 0;}

3.3 编译包含包装函数的共享库gcc -DRUNTIME -shared -fpic -o mymalloc.so mymalloc.c -ldl-fpic:(position independent code)指示生成位置无关的目标文件;编译共享库时该参数为必选!

-DRUNTIME:在命令行中定义宏定义RUNTIME,为了与文件头部ifdef RUNTIME呼应。

-shared:指示编译器生成一个共享目标文件。

-ldl:指示链接器链接到libdl.so 共享库。

3.4 编译与运行主程序gcc -o myprog myprog.c //编译

在bash中运行,及其结果:LD_PRELOAD="./mymalloc.so" ./myprog

结果如下malloc(32) = 0x1bf7010 free(0x1bf7010)

在csh或者tcsh中运行方法:(setenv LD_PRELOAD "./mymalloc.so"; ./myprog; unsetenv LD_PRELOAD)

结果如下malloc(32) = 0x2157010 free(0x2157010)

4. 拓展

GNU binutils包有许多实用的工具特别有帮助,而且可以运行在每一个Linux平台上。AR: 创立静态库,插入/删除/列出和提取成员函数。

STRINGS:列出目标文件中所有可打印字符串;

STRIP:从目标文件中删除符号表信息;

NM: 列出目标文件中符号表中定义的符号;

SIZE:列出目标文件中各段的大小;

READELF:显示目标文件的完整结构,包括ELF头中编码的所有信息。包含了NM和SIZE的作用;

OBJDUMP:显示目标文件中所有的信息,最大的左右是反汇编.text段中的二进制指令成汇编指令。

LDD:列出可执行文件运行时需要的所有共享库。

获取更多知识,请点击关注:

嵌入式Linux&ARM

CSDN博客

简书博客

linux程序打桩,一文搞懂linux的库打桩相关推荐

  1. fseek linux 大文件_一文搞懂Linux系统开发

    文章目录 Linux系统开发会用到什么? C语言基础 shell脚本 慢慢学会使用Makefile 常规Linux系统编程知识都有什么?哪些常用?哪些不常用? 常规Linux编程知识 文件IO 文件与 ...

  2. 一文搞懂Linux系统开发

    列一下Linux系统开发要掌握的知识. 欢迎关注我的微信公众号:fensnote 文章目录 Linux系统开发会用到什么? C语言基础 shell脚本 学会使用Makefile 常规Linux系统编程 ...

  3. ac3165 linux驱动_一文读懂Linux系统启动流程

    Linux启动管理 11.1 CentOS 6.x系统启动过程详解 CentOS 6.x系统启动过程发生了较大的变化,使用Upstart启动服务取代了原先的System V init启动服务.Upst ...

  4. 一文搞懂 | Linux 驱动的来龙去脉

    驱动相关的学习资料网上很多,但大部分都是碎片化的记录,很少有系统化的总结整理.本文旨在系统化的讲清楚 Linux 驱动的来龙去脉.先从总线,设备,驱动介绍内核对于驱动的模型设计:然后引入设备树的概念, ...

  5. 一文搞懂Linux内核怎么提升UDP收包的效率

    现在很多人都在诟病Linux内核协议栈收包效率低,不管他们是真的懂还是一点都不懂只是听别人说的,反正就是在一味地怼Linux内核协议栈,他们的武器貌似只有DPDK. 但是,即便Linux内核协议栈收包 ...

  6. Linux内核:一文搞懂外设I/O内存资源的静态映射方式

    Linux内核访问外设I/O内存资源的方式有两种:动态映射(ioremap)和静态映射(map_desc). 动态映射(ioremap)方式 动态映射方式是大家使用了比较多的,也比较简单.即直接通过内 ...

  7. 一文搞懂linux的proc文件

    目录 proc文件夹是干嘛用? proc下都有什么系统信息? /proc/bus /proc/buddyinfo /proc/cgroups /proc/cmdline /proc/consoles ...

  8. 一文搞懂linux时间片,硬件时钟,软件时钟,实时时钟,时间中断,墙上时间

    时间片: ​ 时间片是一个数值,它表明程序在被抢占前所持续运行的时间. 相对时间绝对时间区别: ​ 如果某个时间在5s后呗调度执行,那么系统所需要的不是绝对时间,而是相对时间(比如,相对现在5s后): ...

  9. 一文搞懂Linux 内存管理原理

    导语 linux 内存是后台开发人员,需要深入了解的计算机资源.合理的使用内存,有助于提升机器的性能和稳定性.本文主要介绍 linux 内存组织结构和页面布局,内存碎片产生原因和优化算法,linux ...

最新文章

  1. CSS 单行溢出文本只显示部分内容
  2. IOS单例的两种实现方式
  3. 安妮宝贝的50句经典语句
  4. node to traverse cannot be null!
  5. 活动报名 | 6位技术女神关于智能技术的实践分享
  6. 使用 python 开发 Web Service
  7. CV中的经典网络模型
  8. linux xguest用户,在/etc/passwd中得到普通用户列表
  9. 凸透镜成像动画可拖动_光学实验二:探究凸透镜成像规律
  10. PHP中提问频率最高的11个面试题和答案
  11. UISegmentedControl判断点击第几项
  12. Bill Gates推荐,人工智能必读的三本书 -《终极算法》,《超级智能》和《终极发明》zz
  13. Twaver-HTML5基础学习(1)两点一线
  14. 网络投票的另一面:“刷票”与“防刷” 大PK
  15. 30天自制操作系统 第2天
  16. 【数据分析/挖掘】如何处理类别型特征?常用编码方式?Python实现?
  17. Java“中文”编程-java为什么可以使用中文标识符
  18. Matlab如何删除矩阵中的零元素,重新整理不等行的矩阵
  19. 第 35 届信息学奥林匹克竞赛(NOI 2018)二试赛题
  20. android 如何分析卡顿问题

热门文章

  1. Hibernate SqlQuery
  2. android sdk 帮助文档下载地址
  3. springboot+aop+自定义注解,打造通用的全局异常处理和参数校验切面(通用版)
  4. 【白皮书分享】2020全球数字治理白皮书.pdf(附下载链接)
  5. 推荐系统系列教程之十六:深度和宽度兼具的融合模型
  6. 深度学习福利入门到精通第五讲——ResNet模型
  7. 动态规划-矩阵连乘问题
  8. idea 导入template_如何将静态导入添加到IntelliJ IDEA实时模板
  9. vue data 值如何渲染_vue源码阅读复盘-watcher模块
  10. 计算机作文1000字,丢失的计算机作文1000字