小心兩個共享庫共用同一個靜態庫
阿新 • • 發佈:2019-01-01
)
問1:如果測試中的全域性變數global_var是個帶構造和析構的類物件,會如何?(答案在最後)
問2:如果使用-fPIE替代-fPIC編譯連結,會是什麼結果了?
位置無關程式碼(PIC)對常量和函式入口地址的操作都是採用基於基暫存器(base register)BASE+ 偏移量的相對地址的定址方式,即使程式被裝載到記憶體中的不同地址(即 BASE值不同),而偏移量是不變的,所以程式仍然可以找到正確的入口地址或者常量。
為何要小心?原因是在使用dlopen動態載入共享庫時,如果靜態庫中包含有全域性變數,可能會出現名同地址不同的全域性變數。
解決辦法:總是使用RTLD_GLOBAL載入共享庫,而不是RTLD_
Makefile
- # test shared libraries use static a same static library
- # the global variables defined at static library have the same address
- all: x libshared_lib1.so libshared_lib2.so
- x: x.cpp #libstatic_lib.a #libshared_lib1.so #libshared_lib2.so
- g++ -g -o [email protected] $^ -ldl
- libstatic_lib.a: static_lib.h static_lib.cpp
- g++ -g -fPIC -c static_lib.cpp -I.
- ar cr [email protected] static_lib.o
- libshared_lib1.so: shared_lib1.cpp libstatic_lib.a
- g++ -g -fPIC -shared -o [email protected] $^ -I.
- libshared_lib2.so: shared_lib2.cpp libstatic_lib.a
- g++ -g -fPIC -shared -o [email protected] $^ -I.
- clean:
- rm -f static_lib.o libstatic_lib.a
- rm -f shared_lib1.o libshared_lib1.so
- rm -f shared_lib2.o libshared_lib2.so
- rm -f x
測試程式x.cpp
- #include <dlfcn.h>
- #include <stdio.h>
- #include <stdlib.h>
- extern void call_foo(const char* name, int load_flag);
- int main()
- {
- int flag = RTLD_GLOBAL|RTLD_NOW; // 如果是RTLD_GLOBAL則靜態庫中定義的全域性變數在共享庫中名同地址也同
- //int flag = RTLD_LOCAL|RTLD_NOW;
// 如果是RTLD_LOCAL則靜態庫中定義的全域性變數在共享庫中名同地址不同
- call_foo("./libshared_lib1.so", flag);
- call_foo("./libshared_lib2.so", flag);
- return 0;
- }
- // RTLD_NOW
- // RTLD_LAZY
- // RTLD_GLOBAL
- // RTLD_LOCAL
- void call_foo(const char* name, int load_flag)
- {
- char *error;
- void (*foo)();
- void* handle = dlopen(name, load_flag);
- if (NULL == handle)
- {
- fprintf (stderr, "%s\n", dlerror());
- exit(1);
- }
- dlerror(); /* Clear any existing error */
- *(void **) (&foo) = dlsym(handle, "foo");
- if ((error = dlerror()) != NULL)
- {
- fprintf (stderr, "%s\n", error);
- exit(1);
- }
- (*foo)();
- }
靜態庫標頭檔案static_lib.h
- extern int global_var;
靜態庫實現檔案static_lib.cpp
- #include <stdio.h>
- int global_var = 2013;
第1個共享庫實現檔案shared_lib1.cpp
- #include "static_lib.h"
- #include <stdio.h>
- extern "C" void foo()
- {
- global_var = 1111;
- printf("%p 1-> %d\n", &global_var, global_var);
- }
第2個共享庫實現檔案shared_lib2.cpp
- #include "static_lib.h"
- #include <stdio.h>
- extern "C" void foo()
- {
- printf("%p 2-> %d\n", &global_var, global_var);
- }
測試環境:
x86_64 x86_64 GNU/Linux 2.6.16
附:
1)如果你想覆蓋系統呼叫,可以使用LD_PRELOAD或/etc/ld.so.preload,也可進一步瞭解RTLD_NEXT;
2)靜態庫順序關係:假設X.a依賴Z.a,則順序為X.a Z.a,亦即被依賴的排在後面,否則連結時會報某些符號找不到(詳細請參見:連結靜態庫的順序問題)。
答:結果是即使以RTLD_GLOBAL方式載入,都會出現兩次構造和析構呼叫,如果是RTLD_GLOBAL方式,地址仍然相同,也就是同一個物件執行了兩次構造和析構,後果當然是非常危險。執行測試程式碼x.zip即可得到驗證。
段表(Section Table) 一個描述檔案中各個段的陣列
.code/.text 程式碼段
.data 段儲存的是那些已經初始化了的全域性靜態變數和區域性靜態變數
.rodata/.rodata1 段存放的是隻讀資料,一般是程式裡面的只讀變數(如const修飾的變數)和字串常量
.bss 段存放的是未初始化的全域性變數和區域性靜態變數
.plt/.got 段動態連結的跳轉表和全域性入口表
.symtab 符號表(Symbol Table)
.strtab 字串表(String Table),用於儲存ELF檔案中用到的各種字串
.init/.fini 程式初始化與終結程式碼段
.note 額外的編譯器資訊。比如程式的公司名、釋出版本號等
.line 除錯時的行號表,即原始碼行號與編譯後指令的對應表
.hash 符號雜湊表
.dynamic 動態連結資訊
.debug 除錯資訊
.comment 存放的是編譯器版本資訊,比如字串:”GCC: (GNU) 4.2.0”
自定義段 GCC提供了一個擴充套件機制,使得程式設計師可以指定變數所處的段:
1.__attribute__((section("FOO"))) int global = 42;
2.__attribute__((section("BAR"))) void foo()
{
}
在全域性變數或函式之前加上"__attribute__((section("name")))"屬性就可以把相應的變數或函式放到以"name"作為段名的段中。
- 如果被依賴的不是靜態庫,而是共享庫,則無論何種方式都不存在問題
- 為何即使RTLD_GLOBAL載入,也會執行兩次構造和析構?原因是兩個共享庫存在相同的程式碼段,如果被依賴的是共享庫,則不存在這個問題
- -Wl的使用
- -Wl表示後面的引數傳遞給連結器,其中l是linker的意思。
- 連結時指定共享庫的搜尋路徑(類似於設定LD_LIBRARY_PATH):
- -Wl,-rpath=/usr/local/abc:/data/abc
- 以上也可以分開寫:
- -Wl,-rpath=/usr/local/abc -Wl,-rpath=/data/abc
- 部分庫連結它的靜態庫,部分庫連結它的共享庫:
- -Wl,-static -lb -Wl,-call_shared -la -lz
- 指定連結器:
- -Wl,-dynamic-linker /lib/ld-linux.so.2 -e _so_start
- 指定匯出的符號:
- -Wl,--export-dynamic,--version-script,exports.lds
- exports.lds的格式可以為:
- {
- global:
- foo;
- };
- 指定共享庫的soname:
- -Wl,--export-dynamic,--version-script,exports.lds,-soname=libqhttpd.so
- -rpath 增加共享庫搜尋路徑
- --retain-symbols-file表示不丟棄未定義的符號和需要重定位的符號
- --export-dynamic 建立一個動態連線的可執行程式時, 把所有的符號加到動態符號表中
FROM: http://blog.chinaunix.net/uid-20682147-id-3760647.html