Linux下库打桩机制分析 function Interposition
[时间:2017-08] [状态:Open]
[关键词:linux, libray,打桩,interposition,函数替换,链接器,gcc,malloc,free]
0 引言
本文主要参考《深入理解计算机系统》(原书第三版)ch7.13。作为个人知识整理和后续参考使用。
库打桩(interposition)这个名词比较陌生,这是由Linux链接器所提供的技术,允许用户截获对共享库函数的调用,并执行自己的代码(当然是在普通权限下,管理员权限通常是禁止使用该技术的)。
使用打桩机制,可以追踪某个特殊库函数的调用次数、验证并追踪其输入输出,甚至把它替换成一个完全不同的实现。
2 基本原理
打桩机制的基本实现原理如下:
给定需要打桩的目标函数,常见一个wrapper函数,其原型和目标函数一致。
利用特殊的打桩机制,可以实现让系统调用你的wrapper函数而不是目标函数。
wrapper函数中通常会执行自己的逻辑,然后调用目标函数,再将目标函数的返回值传递给调用者。
打桩可以发生在编译时、链接时或者程序被加载执行的运行时。不同的阶段都有对应的打桩机制,也有其局限性。
下文将以c标准库中的malloc和free函数的打桩来说明不同打桩机制。基本目标是用打桩来追踪程序运行时对malloc和free的调用。
3 示例一:编译时打桩
编译时打桩说白就是将对目标函数的调用替换为对应wrapper的调用。实现方式很简单,通过指定编译指令来实现。
下面代码实现了一个demo,用于说明如何使用预处理器实现编译时打桩。包装函数实现如下:
// malloc.h
#ifndef COMPILE_TIME
#define malloc(size) mymalloc(size)
#define free(ptr) myfree(ptr)
#endif
void * mymalloc(size_t size);
void myfree(void *ptr);
// mymalloc.cpp
#ifdef COMPILE_TIME
#include <stdio.h>
#include <malloc.h>
// malloc wrapper function
void * mymalloc(size_t size) {
void * ptr = ::malloc(size);
printf("malloc %p size %u\n", ptr, size);
return ptr;
}
// free wrapper function
void myfree(void *ptr) {
::free(ptr);
printf("free %p\n", ptr);
}
#endif
在wrapper函数中我们调用目标函数,并打印追踪记录。本地的malloc.h头文件用于替换系统的目标函数调用逻辑,将其切换到对应的包装函数中。
所有源码可以在我的SampleCode[https://git.oschina.net/Tocy/SampleCode.git]的interposition-打桩\compile目录找到,可以使用下面函数指令编译代码:
gcc -DCOMPILE_TIME -c mymalloc.cpp
gcc -I. -o intpos main.cpp mymalloc.o
编译后执行可执行文件,就可以得到将对malloc/free的调用转到我们的wrapper函数中。
4 示例二:链接时打桩
Linux静态链接器支持用--wrap f标志进行链接时打桩。这个标志告诉链接器,把对符号f的引用解析成__wrap_f(前缀是两个下划线),还要对符号__real_f的引用解析成f。
我们的wrap函数实现如下:
#ifdef LINK_TIME
#include <stdio.h>
extern "C" {
void * __real_malloc(size_t size);
void __real_free(void * ptr);
// malloc wrapper function
void * __wrap_malloc(size_t size) {
printf("%s enter %u\n", __FUNCTION__, size);
void * ptr = __real_malloc(size);
printf("malloc %p size %u\n", ptr, size);
return ptr;
}
// free wrapper function
void __wrap_free(void *ptr) {
__real_free(ptr);
printf("free %p\n", ptr);
}
}
#endif
所有源码可以在我的SampleCode[https://git.oschina.net/Tocy/SampleCode.git]的interposition-打桩\link目录找到使用下面命令编译:
gcc -DLINK_TIME -c mymalloc.cpp
gcc -c main.cpp
gcc -Wl,--wrap,malloc -Wl,--wrap,free -o intpos main.o mymalloc.o
注意这里是WL(L小写,不是数字1,Linker option)。-Wl,option标志把option传递给链接器。option中的每个逗号都会替换为一个空格。即-Wl,--wrap,malloc就是把--wrap malloc传递给链接器。上面编译必须分开,否则可能会出错。
通过链接器的命令打桩也可以实现我们的目的,但是有一个缺点,你需要重新连接所有需要监测的模块。
5 示例三:运行时打桩
编译时打桩需要访问程序的源代码,连接时打桩需要能够访问程序的可重定位的对象文件。不过运行时打桩仅需要访问可执行目标文件即可,它的基本原理是基于动态链接器的LD_PRELOAD环境变量的。
如果LD_PRELOAD环境变量被设置为一个共享库路径的列表(以空格或分号分隔),那么当你加载和执行一个程序,需要解析未定义的引用时,动态链接器会先搜做LD_PRELOAD中给定的库,然后才搜索任何其他的库。有了这个机制,当你加载和执行任意可执行文件时,可以对任何共享库中任意函数打桩,包括libc.so中的malloc和free。
我们的wrapper函数实现如下
#ifdef RUNTIME
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>
// malloc wrapper function
void * malloc(size_t size) {
printf("%s enter %u\n", __FUNCTION__, size);
void *(* mallocp)(size_t size);
char * error;
// get address of libc malloc
mallocp = dlsym(RTLD_NEXT, "malloc");
if ((error = dlerror()) != NULL) {
fputs(error, stderr);
exit(1);
}
void * ptr = mallocp(size);
printf("malloc %p size %u\n", ptr, (int)size);
return ptr;
}
// free wrapper function
void free(void *ptr) {
void (* freep)(void *ptr);
char * error;
// get address of libc free
freep = dlsym(RTLD_NEXT, "free");
if ((error = dlerror()) != NULL) {
fputs(error, stderr);
exit(1);
}
freep(ptr);
printf("free %p\n", ptr);
}
#endif
所有源码可以在我的SampleCode[https://git.oschina.net/Tocy/SampleCode.git]的interposition-打桩\runtime目录找到,需要通过下面指令编译:
gcc -DRUNTIME -shared -fpic -o mymalloc.so mymalloc.cpp -ldl -fpermissive
主函数不做修改。但运行时需要使用下面指令:
LD_PRELOAD="./mymalloc.so" ./intpos
如此就可以达到预期的效果,监测对malloc和free函数的调用。
6 补充知识:malloc调试变量——__malloc_hook
如果单纯为了处理malloc/free的调用,可以参考下万能的manual。
__malloc_hook是glibc提供的malloc调试变量中的一个,详情参考MALLOC_HOOK。
只要在代码中添加__malloc_hook= my_malloc_hook;语句,当前程序中关于的malloc调用都会使用my_malloc_hook函数,简单方便。但是这组调试变量不是线程安全的,很多新的编译器已经将该功能废弃。有兴趣的可以参考下。
7 总结
到此,本文开头的问题已经解决。整理此文目的只是为了加深记忆。
同时了解下基本的内存泄露分析方法。
8 参考
Linux下库打桩机制分析 function Interposition的更多相关文章
- CSAPP CH7链接的应用:静动态库制作与神奇的库打桩机制
目录 创建静态库 创建动态库 库打桩机制 编译时打桩: 链接时打桩 运行时打桩 运行时打桩的printf与malloc循环调用debug 使用LD_PRELOAD对任意可执行程序调用运行时打桩 总结 ...
- Linux内核抢占实现机制分析【转】
Linux内核抢占实现机制分析 转自:http://blog.chinaunix.net/uid-24227137-id-3050754.html [摘要]本文详解了Linux内核抢占实现机制.首先介 ...
- linux下epoll实现机制
linux下epoll实现机制 原作者:陶辉 链接:http://blog.csdn.net/russell_tao/article/details/7160071 先简单回顾下如何使用C库封装的se ...
- Linux信号(signal) 机制分析
Linux信号(signal) 机制分析 [摘要]本文分析了Linux内核对于信号的实现机制和应用层的相关处理.首先介绍了软中断信号的本质及信号的两种不同分类方法尤其是不可靠信号的原理.接着分析了内核 ...
- Linux内核态抢占机制分析(转)
Linux内核态抢占机制分析 http://blog.sina.com.cn/s/blog_502c8cc401012pxj.html 摘 要]本文首先介绍非抢占式内核(Non-Preemptive ...
- Linux进程组调度机制分析【转】
转自:http://oenhan.com/task-group-sched 又碰到一个神奇的进程调度问题,在系统重启过程中,发现系统挂住了,过了30s后才重新复位,真正系统复位的原因是硬件看门狗重启的 ...
- linux下常用的日志分析命令
linux下常用的日志分析命令 本文介绍下,在linux中常用的一些分析日志的命令行或脚本,有需要的朋友参考下. 形如下面这样的access.log日志内容: 211.123.23.133 ...
- 2017-2018-1 20155222 《信息安全系统设计基础》第10周 Linux下的IPC机制
2017-2018-1 20155222 <信息安全系统设计基础>第10周 Linux下的IPC机制 IPC机制 在linux下的多个进程间的通信机制叫做IPC(Inter-Process ...
- Linux下的IPC机制
Linux下的IPC机制 IPC(Inter-Process Communication)是多个进程之间相互沟通的一种方法.在linux下有多种进程间通信的方法. 共享内存 Linux内存共享有多种, ...
随机推荐
- vi命令修改文件及保存的使用方法
简单点:vi文件名,按"I"进入insert模式,可以正常文本编辑,编辑好之后按“esc”退出到“命令模式”,再按“shift+:”进入“底行模式”, 按“:wq”保存退出! 还一 ...
- 由自定义事件到vue数据响应
前言 除了大家经常提到的自定义事件之外,浏览器本身也支持我们自定义事件,我们常说的自定义事件一般用于项目中的一些通知机制.最近正好看到了这部分,就一起看了下自定义事件不同的实现,以及vue数据响应的基 ...
- 【Ray Tracing The Next Week 超详解】 光线追踪2-9
我们来整理一下项目的代码 目录 ----include --hit --texture --material ----RTdef.hpp ----ray.hpp ----camera.hpp ---- ...
- Alpha冲刺随笔五:第五天
课程名称:软件工程1916|W(福州大学) 作业要求:项目Alpha冲刺(十天冲刺) 团队名称:葫芦娃队 作业目标:在十天冲刺里对每天的任务进行总结. 随笔汇总:https://www.cnblogs ...
- 什么是mime类型
本文转自:什么是mime类型 - 方法数码 http://www.fangfa.net/webnews/390.html MIME 类型在网站开发中经常碰到,特别是处理非文本数据的请求时(如:文件上传 ...
- 深入理解 Array.prototype.map()
深入理解 Array.prototype.map() map() 方法创建一个新数组,其结果是该数组中的每个元素都调用一个提供的函数后返回的结果. 语法 let new_array = arr.map ...
- unity3d 给游戏添加音源 Unity3d adds a sound source to the game
unity3d 给游戏添加音源 Unity3d adds a sound source to the game 作者:韩梦飞沙 Author:han_meng_fei_sha 邮箱:3131345 ...
- 4572: [Scoi2016]围棋 轮廓线DP KMP
国际惯例的题面:这种题目显然DP了,看到M这么小显然要状压.然后就是具体怎么DP的问题.首先我们可以暴力状压上一行状态,然后逐行转移.复杂度n*3^m+3^(m*2),显然过不去. 考虑状态的特殊性, ...
- Python3基础系列-基本入门语法
本文简单地介绍了python的一些基本入门知识,通过对这些知识的了解,大家可以写一些简单的代码,同时也为后面深入理解打下基础.本文的主要内容如下: 值和类型 值,即value,通常有:1,2,3.14 ...
- 2002 ACM 杭电 计算球体积
题目:http://acm.hdu.edu.cn/showproblem.php?pid=2002 注意,要用double 才能过,float过不了. 体积公式要加括号(优先级别)(4 * Π * r ...