
一次debug遇到的疑惑
某天發現一個程式有點問題,祭上print大法,在關鍵的 lib_func() 函式里添加 print 除錯資訊,重新編譯運行,
期望 print 出的資訊一點都沒有,但是程式確確實實又執行過了 libfunc() ,因為除了添加的除錯 print 沒有執行,libfunc() 該有的功能都執行了,這真是奇怪了,
程式不會騙人,執行的 libfunc() 肯定不是我們修改后的那個 libfunc() ,一定是別的地方有原版的 lib_func() 被執行了,一番調查,果然如此,為了便于說明,把程式和現象簡化說明如下:
程式包含如下代碼檔案——
main.c # 主程式
plugin.c # 插件程式
lib.c lib.h # 一個庫
Makefile如下:
all:main plugin
main:
cc -o main main.c lib.c -ldl -rdynamic
plugin:
cc -shared -fPIC -o plugin.so plugin.c lib.c
編譯后,生成可執行程式main, 和動態庫檔案 plugin.so,其中主程式運行的時候,會動態加載插件 plugin.so (呼叫了 lib.c 里的程式)并執行,
懷疑出問題的地方在lib.c里,修改后的lib.c內容如下,添加了debug字樣,
void lib_func() {
// fprintf(stderr, "%s()\n", __func__);
fprintf(stderr, "debug:%s()\n", __func__);
}
因為主程式沒問題,就只重新編譯了 plugin.so ,重新運行,
期望得到的print的結果是 debug:libfunc() ,實際得到卻是 libfunc(),
看起來,一個程式里的確有兩份 lib_func() 代碼,從Makefile可以看出,一份在main程式里,一份在plugin.so里,實際運行的是main里的那份,
事情忽然就有意思了:如果一個程式里包含多個相同的函式,實際執行的是哪一個?
TIPS:可以簡單的使用linux的命令 nm <程式檔案> 查看程式里有哪些函式
動態庫和符號表
盡管程式各不相同,但總有些功能很常見,每個程式都為他們寫一遍代碼很不劃算,于是獨立出來成了庫,在多個程式之間共享,一個庫也可以使用別的庫,有兩種共享的辦法:靜態的,動態的,
在編譯時,把庫的代碼復制一份合并到可執行檔案里的,是靜態庫,
在運行時,把庫的代碼加載一份到記憶體里的,是動態庫,
動態庫更節省資源,不用被復制很多次,更新也方便,
負責鏈接的東西,叫做聯結器(linker),負責加載的叫做加載器(loader),
然而計算機是根據地址來執行的,每條指令執行前都要先確定地址,動態庫加載之前,誰都不知道它會被加載到哪里,也就不知道動態庫里的指令的地址,只能通過符號(名稱)來記錄它提供給別人用的函式串列(匯出表),以及它期望別人提供給他的函式串列(匯入表),庫被加載后,就獲得了地址,程式運行前,需要先決議符號表,確定每個符號的實際地址,
我們開頭的例子,兩個相同名字的 libfunc() ,一個在main程式里,一個在plugin.so里,main先加載,plugin.so使用的 libfunc() 就被決議到了main的 libfunc() ,執行了老的 libfunc() ,而不是我們修改過的帶有debug版本,
TIPS:對程式鏈接和加載有興趣的同學可以看看 Linkers and Loaders 這本書,非常詳細,
和符號有關的編譯器選項和環境變數選項
如果條件允許,盡量不要在同一個程式中出現兩份代碼,出現相同符號的情況,造成沖突,
如果出現了符號沖突一定要解決:如本例中,假設 main 不可變,已經包含了 lib 的代碼,plugin.so 可通過 gcc 的 -Wl,-Bsymbolic選項告訴加載器優先使用自己的符號,而不優先用全域的符號,該選項可以解決符號沖突,
TIPS: 如果想觀察加載器的作業,可以使用環境變數 LD_DEBUG=all ./main 來執行程式,會獲得詳細的決議程序,manpage的 ld.so(8) 有更多詳細的說明,
最后附上示例用的代碼:
? sample cat lib.h
#ifndef UNTITLED_LIB_H
#define UNTITLED_LIB_H
void lib_func();
#endif //UNTITLED_LIB_H
? sample cat lib.c
#include <stdio.h>
#include "lib.h"
void lib_func() {
//fprintf(stderr, "%s()\n", __func__);
fprintf(stderr, "debug:%s()\n", __func__);
}
? sample cat main.c
#include <stdio.h>
#include <unistd.h>
#include <dlfcn.h>
#include "lib.h"
int main() {
void *handle = dlopen("./plugin.so", RTLD_NOW);
void (*plugin_func)() = (void (*)()) dlsym(handle, "plugin");
plugin_func();
return 0;
}
? sample cat plugin.c
#include "lib.h"
void plugin() {
lib_func();
}
? sample cat Makefile
all:main plugin
main:
cc -o main main.c lib.c -ldl -rdynamic
plugin:plugin.c lib.c
#cc -shared -fPIC -o plugin.so plugin.c lib.c -Wl,-Bsymbolic
cc -shared -fPIC -o plugin.so plugin.c lib.c
clean:
rm -f main plugin.so
? sample
(陳國 | 天存資訊)
轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/286086.html
標籤:其它
