[转] 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.socomes 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-linuxhas 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.2where 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-linuxusing 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_NEXTto implement wrappers aroundmalloc()andfree(). - Due to unexpected linking behavior,
dlsym()when usingRTLD_NEXTcan return pointers tomalloc/freeimplementations insideld-linux(dynamic linker/loader). Usingld-linux‘smalloc/freefor 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-linuxviadladdr(). 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 ...
随机推荐
- ElasticSearch - How to search for a part of a word with ElasticSearch
Search a part of word with ElasticSearch 来自stackoverflow https://stackoverflow.com/questions/6467067 ...
- POJ 1328 Radar Installation【贪心】
POJ 1328 题意: 将一条海岸线看成X轴,X轴上面是大海,海上有若干岛屿,给出雷达的覆盖半径和岛屿的位置,要求在海岸线上建雷达,在雷达能够覆盖全部岛屿情况下,求雷达的最少使用量. 分析: 贪心法 ...
- mongo3.x配置说明
Mongodb 3.x配置说明,本文内容忽略了Enterprise版和一些不常用的配置. 一.配置说明 在Mongod安装包中,包含2个进程启动文件:mongod和mongos:其中mongd是核心基 ...
- [转]notepad++ java编码,输出中文字符时,编译出错
呆在公司中,最近受开发手机app的几个同事影响,想学android的开发,心血来潮,挡也挡不住,说干就干,直接看教程,发现有很多关于java的语法知识不懂,于是又来学java,学习的过程中难免出现问题 ...
- java加密工具类,可设置对应的加解密key
public class AesEncryptUtil { //使用AES-128-CBC加密模式,key需要为16位,key和iv可以相同! private static String KEY =& ...
- FastAdmin 在 Nginx 中的配置
FastAdmin 使用的是 ThinkPHP 5 框架. 在 Apache 下很简单,但是在 nginx 下就需要自行配置一下了. 在网上找了很多,都没有找到很好的. 刚刚在 QQ 里群里找到一个, ...
- VSCode从非根目录编译golang程序(转)
1.问题提出 “习惯在项目目录里建src放源码文件,根目录里放配置文件或者别的什么,在交付时直接忽视掉src目录就行了,但vscode好像不能这样愉快的玩耍...”??? 要实现把源码放到src目录下 ...
- springboot mail+Thymeleaf模板
compile 'org.springframework.boot:spring-boot-starter-thymeleaf' compile 'io.ratpack:ratpack-thymele ...
- Git学习笔记:基础篇
git可以说是所有开发者出开发语言之外的最基本的基本功了,熟悉git可以方便的进行代码版本控制,以及与其他开发者进行合作开发.本文内容是我以往学习git时做的笔记,主要是关于git最基本的操作,但 只 ...
- IdentityServer4目录
一.介绍 二.快速入门 三.主题 四.端点 五.参考 IdentityServer4是ASP.NET Core 2的OpenID Connect和OAuth 2.0框架. 它可以在您的应用程序中启用以 ...