LD_PRELOAD - Andrew1013/TestCode GitHub Wiki

如果你希望的不僅僅是替換掉原有庫函數,而且還希望最終將函數邏輯傳遞到原有系統函數,那麼你可能需要用到RTLD_NEXT。系統可能提示RTLD_NEXT未定義,這裡給出了解決方案:

define _GNU_SOURCE

or

*#define RTLD_NEXT ((void ) -1l)

使用dlsym時候可能遇到連結錯誤,提示找不到dlsym。解決方法是編譯的時候加上-ldl編譯選項:

我的過程記錄 fork.c,最後編譯成fork.so

1.	#include <stdio.h>  
2.	#include <sys/types.h>  
3.	#include <unistd.h>  
4.	#include <stdlib.h>  
5.	#include <sys/types.h>  
6.	#include <dlfcn.h>  
7.	/* 即使按照參考文獻中的方法,定義__USE_GNU 
8.	 * 還是被提示RTLD_NEXT未定義 
9.	 * 只能用這種猥瑣的,不可移植的方法了 
10.	 * 實驗目的,無傷大雅。 
11.	 */  
12.	# define RTLD_NEXT      ((void *) -1l)  
13.	static pid_t (*real_fork)(void);  
14.	pid_t fork(void)  
15.	{  
16.	        printf("Fork is called/n");  
17.	        if (real_fork == NULL)  
18.	        {  
19.	                real_fork = (pid_t (*)(void))dlsym( RTLD_NEXT, "fork" );  
20.	        }  
21.	        return real_fork();  
22.	}  
 

strlen.c,最後編譯成strlen.so 。 需要說明的是,最後這個so沒有用到,因為我遇到了一個詭異的事情,詳細見後文。

[cpp] view plain copy
1.	#include <stdio.h>  
2.	size_t strlen(const char *s)  
3.	{  
4.	        size_t i = 0;  
5.	        printf("strlen is called, return x/n");  
6.	        while(*s)  
7.	        {  
8.	                i++;  
9.	                s++;  
10.	        }  
11.	        return i;  
12.	}  
 

測試代碼。測試了strlen和fork。 但是,我並沒有為strlen加鉤子,因為,我發現,無論是否掛鉤strlen,這個測試函數總是沒有反應,根本沒有進入到strlen.c的代碼 中。不過呢,當我用export LD_PRELOAD="./strlen.so"後,vi、ls等應用程式都能夠進入到strlen.c中,唯獨我的./hello不能進入 strlen.c。詭異!

fork函數測試有效。

[cpp] view plain copy
1.	#include <string.h>  
2.	#include <stdio.h>  
3.	#include <sys/types.h>  
4.	#include <unistd.h>  
5.	int main(int argc,char** argv)  
6.	{  
7.	        pid_t m;  
8.	        if(argc < 2)  
9.	                exit(0);  
10.	        const char* hello = argv[1];  
11.	        printf("%s length is %d/n",hello, strlen(hello));  
12.	        m = fork();  
13.	        printf("pid=%d/n", (pid_t)m);  
14.	        return 0;  
15.	}  

編譯過程(只涉及到 fork.c 和 hello.c )

gcc -fPIC -shared -ldl -o fork.so fork.c ; note: 如果沒有家ldl,則提示找不到符號dlsym,沒有家-fPIC,則編譯器會提示添加此選項。跟網上的一些資料有點出入。

gcc -o hello hello.c

export LD_PRELOAD="./fork.so"

./hello abcd abcd length is 4 Fork is called pid=0 pid=12828

export LD_PRELOAD="" or unset LD_PRELOAD ; clear preload path

上面使用LD_PRELOAD的方法有個問題,它是一個持久的全域設置,可能會影響到./hello之外的程式的正常行為。一個更好的使用方法是直接執行:

LD_PRELOAD="./fork.so" ./hello abcd

後記:

關於dlsym
[cpp] view plain copy
1.	#include <dlfcn.h>  
2.	void *dlopen(const char *filename, int flag);  
3.	char *dlerror(void);  
4.	void *dlsym(void *handle, const char *symbol);  
5.	int dlclose(void *handle);  

LD_PRELOAD能夠很方便地實現應用層的二進位相容,值得推廣。同時謹記LD_PRELOAD不可濫用,否則會干擾正常系統的功能。一般,用完後需要將LD_PRELOAD的內容清空。

For GDB

set environment LD_PRELOAD

⚠️ **GitHub.com Fallback** ⚠️