[转] Dangers of using dlsym() with RTLD_NEXT
There are times when you want to wrap a library function in order to provide some additional functionality. A common example of this is wrapping the standard library’s malloc()
and free()
so that you can easily track memory allocations in your program. While there are several techniques for wrapping library functions, one well-known method is using dlsym()
with RTLD_NEXT
to locate the wrapped function’s address so that you can correctly forward calls to it.
Problem
So what can go wrong? Let’s look at an example:
LibWrap.h
void* memAlloc(size_t s); |
LibWrap.c
#define _GNU_SOURCE |
Main.c
#include <malloc.h> |
First compile LibWrap.c
into a shared library:
$ gcc -Wall -Werror -fPIC -shared -o libWrap.so LibWrap.c
Next compile Main.c
and link it against the libWrap.so
that we just created:
$ gcc -Wall -Werror -o Main Main.c ./libWrap.so -ldl
Time to run the program!
$ ./Main
Bytes allocated before malloc: 0
Bytes allocated after malloc: 80
Bytes allocated after free: 0
So far, so good. No surprises. We allocated a bunch of memory and then freed it. The statistics returned by mallinfo()
confirm this.
Out of curiosity, let’s look at ldd
output for the application binary we created.
$ ldd Main
linux-vdso.so.1 => (0x00007fff1b1fe000)
./libWrap.so (0x00007fe7d2755000)
libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007fe7d2542000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fe7d217c000)
/lib64/ld-linux-x86-64.so.2 (0x00007fe7d2959000)
Take note of the relative placement of libWrap.so
with respect to libc.so.6
: libWrap.so
comes before libc.so.6
. Remember this. It will be important later.
Now for fun, let’s re-compile Main.c
with libc.so.6
explicitly specified on the command-line and coming before libWrap.so
:
$ gcc -Wall -Werror -o Main Main.c /lib/x86_64-linux-gnu/libc.so.6 ./libWrap.so -ldl
Re-run:
$ ./Main
Bytes allocated before malloc: 0
Bytes allocated after malloc: 80
Bytes allocated after free: 80
Uh oh, why are we leaking memory all of a sudden? We de-allocate everything we allocate, so why the memory leak?
It turns out that the leak is occurring because we are not actually forwarding malloc()
and free()
calls to libc.so.6
‘s implementations. Instead, we are forwarding them to malloc()
and free()
inside ld-linux-x86-64.so.2
!
“What are you talking about?!” you might be asking.
Well, it just so happens that ld-linux-x86-64.so.2
, which is the dynamic linker/loader, has its own copy of malloc()
and free()
. Why? Because ld-linux
has to allocate memory from the heap before it loads libc.so.6
. But the version of malloc/free
that ld-linux
has does not actually free memory!
[RTLD_NEXT] will find the next occurrence of a function in the search order after the current library. This allows one to provide a wrapper around a function in another shared library.But why does libWrap.so
forward calls to ld-linux
instead of libc
? The answer comes down to how dlsym()
searches for symbols when RTLD_NEXT
is specified. Here’s the relevant excerpt from the dlsym(3)
man page:— dlsym(3)
To understand this better, take a look at ldd
output for the new Main
binary:
$ ldd Main
linux-vdso.so.1 => (0x00007fffe1da0000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f32c2e91000)
./libWrap.so (0x00007f32c2c8f000)
libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f32c2a8a000)
/lib64/ld-linux-x86-64.so.2 (0x00007f32c3267000)
Unlike earlier, libWrap.so
comes after libc.so.6
. So when dlsym()
is called inside libWrap.so
to search for functions, it skips libc.so.6
since it precedes libWrap.so
in the search order list. That means the searches continue through to ld-linux-x86-64.so.2
where they find linker/loader’s malloc/free
and return pointers to those functions. And so, libWrap.so
ends up forwading calls to ld-linux
instead of libc
!
The answer is unfortunately no. At OptumSoft, we recently encountered this very same memory leak with a binary compiled using the standard ./configure && make
on x86-64 Ubuntu 14.04.1 LTS. For reasons we don’t understand, the linking order for the binary was such that using dlsym()
with RTLD_NEXT
to lookup malloc/free
resulted in pointers to implementations inside ld-linux
. It took a ton of effort and invaluable help from Mozilla’s rr tool to root-cause the issue. After the whole ordeal, we decided to write a blog post about this strange behavior in case someone else encounters it in the future.At this point you might be wondering: We ran a somewhat funky command to build our application and then encountered a memory leak due to weird library linking order caused by said command. Isn’t this whole thing a silly contrived scenario?
Solution
If you find dlsym()
with RTLD_NEXT
returning pointers to malloc/free
inside ld-linux
, what can you do?
For starters, you need to detect that a function address indeed does belong to ld-linux
using dladdr()
:
void* func = dlsym(RTLD_NEXT,"malloc"); |
Once you have figured out that a function is inside ld-linux
, you need to decide what to do next. Unfortunately, there is no straightforward way to continue searching for the same function name in all other libraries. But if you know the name of a specific library in which the function exists (e.g. libc), you can use dlopen()
and dlsym()
to fetch the desired pointer:
void* handle = dlopen("libc.so.6",RTLD_LAZY); |
Summary
- One can use
dlsym()
withRTLD_NEXT
to implement wrappers aroundmalloc()
andfree()
. - Due to unexpected linking behavior,
dlsym()
when usingRTLD_NEXT
can return pointers tomalloc/free
implementations insideld-linux
(dynamic linker/loader). Usingld-linux
‘smalloc/free
for general heap allocations leads to memory leaks because that particular version offree()
doesn’t actually release memory. - You can check if an address returned by
dlsym()
belongs told-linux
viadladdr()
. You can also lookup a function in a specific library usingdlopen()
anddlsym()
.
From: http://optumsoft.com/dangers-of-using-dlsym-with-rtld_next/
[转] Dangers of using dlsym() with RTLD_NEXT的更多相关文章
- Android so 文件进阶<二> 从dlsym()源码看android 动态链接过程
0x00 前言 这篇文章其实是我之前学习elf文件关于符号表的学习笔记,网上也有很多关于符号表的文章,怎么说呢,感觉像是在翻译elf文件格式的文档一样,千篇一律,因此把自己的学习笔记分享出来.dls ...
- 加载动态链接库——dlopen dlsym dlclose
DLOPEN DLMOPEN DLCLOSE NAME dlclose, dlopen, dlmopen - 打开/关闭共享对象 SYNOPSIS #include <dlfcn.h&g ...
- 采用dlopen、dlsym、dlclose加载动态链接库【总结】(转)
1.前言 为了使程序方便扩展,具备通用性,可以采用插件形式.采用异步事件驱动模型,保证主程序逻辑不变,将各个业务已动态链接库的形式加载进来,这就是所谓的插件.linux提供了加载和处理动态链接库的系统 ...
- dlopen、dlsym和dlclose的使用
在dlopen()函数以指定模式打开指定的动态链接库文件,并返回一个句柄给dlsym()的调用进程.使用dlclose()来卸载打开的库. dlopen: dlopen() The function ...
- 采用dlopen、dlsym、dlclose加载动态链接库【总结】
摘自http://www.cnblogs.com/Anker/p/3746802.html 采用dlopen.dlsym.dlclose加载动态链接库[总结] 1.前言 为了使程序方便扩展,具备通 ...
- gcc编译出现dlopen、dlerror、dlsym、dlcolse的解决方法
➜ test_sqlite3 gcc *.c -I . -o xixi -pthread /tmp/cckGKTrr.o: In function `unixDlOpen': sqli ...
- 【转】采用dlopen、dlsym、dlclose加载动态链接库
1.前言 为了使程序方便扩展,具备通用性,可以采用插件形式.采用异步事件驱动模型,保证主控制逻辑不变,将各个业务以动态链接库的形式加载进来,这就是所谓的插件.linux提供了加载和处理动态链接库的系统 ...
- 用dlopen,dlsym加载动态链接库.so中函数
代码如下 static void *findSymbol(const char *path, const char *symbol) { void *handle = dlopen(path, RTL ...
- the example of dlsym
void *handle; int i, (*fptr)(int); /* open the needed object */ handle = dlopen("/usr/home/me/l ...
随机推荐
- canvas抛物线运动轨迹
本来是想做一个贝塞尔曲线运动轨迹的 公式太复杂了,懒得算,公式在最后 我先画了一个抛物线,我确定了两个点,起点(0,0),终点(200,200) 用坐标系可算出方程 y=-0.005x^2 现在找出终 ...
- [NOI2012]骑行川藏(未完成)
题解: 满分又是拉格朗日啥的 以后再学 自己对于n=2猜了个三分 然后对拍了一下发现是对的
- Linux 文件系统与挂载详解
https://blog.csdn.net/baidu_34051990/article/details/60963947
- 让我们了解 Ceph 分布式存储
前言 最近在学习 kubernetes 过程中,想实现 pod 数据的持久化.在调研的过程中,发现 ceph 在最近几年发展火热,也有很多案例落地企业.在选型方面,个人更加倾向于社区火热的项目,Glu ...
- HDU 3761 炸碉堡【半平面交(nlogn)】+【二分】
<题目链接> < 转载于 > 题目大意: 给出一个凸多边形,顶点为一些防御塔,保护范围是凸多形内部,不包括边界,在多边形内部选择一点,使得对方至少需要摧毁的塔防数量最多 ...
- 在vue项目中使用canvas-nest.js,报parameter 1 is not of type 'Element'
canvas-nest.js是一款轻量的网页特效,如图: github地址:https://github.com/hustcc/canvas-nest.js 在普通的html项目中,只要将<sc ...
- scss、less 对浏览器兼容的处理方法, css 的单行溢出、多行溢出
1. scss @mixin rounded($param1,$param2) { #{$param1}:$param2; -webkit-#{$param1}: $param2; ...
- linux manjaro 配置 pytorch gpu 环境
manjaro目前中国资料偏少,踩了很多坑. 安装gpu版本就这么几个步骤 1 安装英伟达的驱动cuda 2 安装 cudnn 3 安装支持gpu的pytorch 或者其他的运算框架 manja ...
- 关于supervisor的入门指北
关于supervisor的入门指北 在目前这个时间点(2017/07/25),supervisor还是仅支持python2,所以我们要用版本管理pyenv来隔离环境. pyenv 根据官方文档的讲解, ...
- win10常用快捷命令——运行面板启动
打开运行面板,WIN+R 打开命令提示符面板 ,在运行面板输入cmd,回车 打开控制面板 ,在运行面板输入control,回车 打开IIS管理器面板 , ...