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之外的程式的正常行為。一個更好的使用方法是直接執行:
後記:
關於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