青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

loop_in_codes

低調(diào)做技術(shù)__歡迎移步我的獨立博客 codemaro.com 微博 kevinlynx

linux動態(tài)庫的種種要點

linux下使用動態(tài)庫,基本用起來還是很容易。但如果我們的程序中大量使用動態(tài)庫來實現(xiàn)各種框架/插件,那么就會遇到一些坑,掌握這些坑才有利于程序更穩(wěn)健地運行。

本篇先談?wù)剟討B(tài)庫符號方面的問題。

測試代碼可以在github上找到

符號查找

一個應(yīng)用程序test會鏈接一個動態(tài)庫libdy.so,如果一個符號,例如函數(shù)callfn定義于libdy.so中,test要使用該函數(shù),簡單地聲明即可:

// dy.cpp libdy.so
void callfn() {
    ...
}

// main.cpp test
extern void callfn();

callfn();

在鏈接test的時候,鏈接器會統(tǒng)一進(jìn)行檢查。

同樣,在libdy.so中有相同的規(guī)則,它可以使用一個外部的符號,在它被鏈接/載入進(jìn)一個可執(zhí)行程序時才會進(jìn)行符號存在與否的檢查。這個符號甚至可以定義在test中,形成一種雙向依賴,或定義在其他動態(tài)庫中:

// dy.cpp libdy.so
extern void mfunc();

mfunc();

// main.cpp test
void mfunc() {
    ...
}

在生成libdy.so時mfunc可以找不到,此時mfunc為未定義:

$ nm libdy.so | grep mfun
U _Z5mfuncv

但在libdy.so被鏈接進(jìn)test時則會進(jìn)行檢查,試著把mfunc函數(shù)的定義去掉,就會得到一個鏈接錯誤:

./libdy.so: undefined reference to `mfunc()'

同樣,如果我們動態(tài)載入libdy.so,此時當(dāng)然可以鏈接通過,但是在載入時同樣得到找不到符號的錯誤:

#ifdef DY_LOAD
    void *dp = dlopen("./libdy.so", RTLD_LAZY);
    typedef void (*callfn)();
    callfn f = (callfn) dlsym(dp, "callfn");
    f();
    dlclose(dp);
#else
    callfn();
#endif

得到錯誤:

./test: symbol lookup error: ./libdy.so: undefined symbol: _Z5mfuncv

結(jié)論:基于以上,我們知道,如果一個動態(tài)庫依賴了一些外部符號,這些外部符號可以位于其他動態(tài)庫甚至應(yīng)用程序中。我們可以再鏈接這個動態(tài)庫的時候就把依賴的其他庫也鏈接上,或者推遲到鏈接應(yīng)用程序時再鏈接。而動態(tài)加載的庫,則要保證在加載該庫時,進(jìn)程中加載的其他動態(tài)庫里已經(jīng)存在該符號。

例如,通過LD_PRELOAD環(huán)境變量可以讓一個進(jìn)程先加載指定的動態(tài)庫,上面那個動態(tài)加載啟動失敗的例子,可以通過預(yù)先加載包含mfunc符號的動態(tài)庫解決:

$ LD_PRELOAD=libmfun.so ./test
...

但是如果這個符號存在于可執(zhí)行程序中則不行:

$ nm test | grep mfunc
0000000000400a00 T _Z5mfuncv
$ nm test | grep mfunc
0000000000400a00 T _Z5mfuncv
$ ./test
...
./test: symbol lookup error: ./libdy.so: undefined symbol: _Z5mfuncv

符號覆蓋

前面主要講的是符號缺少的情況,如果同一個符號存在多分,則更能引發(fā)問題。這里談到的符號都是全局符號,一個進(jìn)程中某個全局符號始終是全局唯一的。為了保證這一點,在鏈接或動態(tài)載入動態(tài)庫時,就會出現(xiàn)忽略重復(fù)符號的情況。

這里就不提同一個鏈接單位(如可執(zhí)行程序、動態(tài)庫)里符號重復(fù)的問題了

函數(shù)

當(dāng)動態(tài)庫和libdy.so可執(zhí)行程序test中包含同名的函數(shù)時會怎樣?根據(jù)是否動態(tài)加載情況還有所不同。

當(dāng)直接鏈接動態(tài)庫時,libdy.so和test都會鏈接包含func函數(shù)的fun.o,為了區(qū)分,我把func按照條件編譯得到不同的版本:

// fun.cpp
#ifdef V2
extern "C" void func() {
    printf("func v2\n");
}
#else
extern "C" void func() {
    printf("func v1\n");
}
#endif

// Makefile
test: libdy obj.o mainfn
    g++ -g -Wall -c fun.cpp -o fun.o # 編譯為fun.o
    g++ -g -Wall -c main.cpp #-DDY_LOAD
    g++ -g -Wall -o test main.o obj.o fun.o -ldl mfun.o -ldy -L.

libdy: obj
    g++ -Wall -fPIC -c fun.cpp -DV2 -o fun-dy.o  # 定義V2宏,編譯為fun-dy.o
    g++ -Wall -fPIC -shared -o libdy.so dy.cpp -g obj.o fun-dy.o

這樣,test中的func就會輸出func v1;libdy.so中的func就會輸出func v2。test和libdy.o確實都有func符號:

$ nm libdy.so | grep func
0000000000000a60 T func

$nm test | grep func
0000000000400a80 T func

在test和libdy.so中都會調(diào)用func函數(shù):

// main.cpp test
int main(int argc, char **argv) {
    func();
    ...
    callfn(); // 調(diào)用libdy.so中的函數(shù)
    ...
}

// dy.cpp libdy.so
extern "C" void callfn() {
    ... 
    printf("callfn\n");
    func();
    ...
}

運行后發(fā)現(xiàn),都調(diào)用的是同一個func

$ ./test
...
func v1
...
callfn
func v1

結(jié)論,直接鏈接動態(tài)庫時,整個程序運行的時候符號會發(fā)生覆蓋,只有一個符號被使用。在實踐中,如果程序和鏈接的動態(tài)庫都依賴了一個靜態(tài)庫,而后他們鏈接的這個靜態(tài)庫版本不同,則很有可能因為符號發(fā)生了覆蓋而導(dǎo)致問題。(靜態(tài)庫同普通的.o性質(zhì)一樣,參考淺析靜態(tài)庫鏈接原理)

更復(fù)雜的情況中,多個動態(tài)庫和程序都有相同的符號,情況也是一樣,會發(fā)生符號覆蓋。如果程序里沒有這個符號,而多個動態(tài)庫里有相同的符號,也會覆蓋。

但是對于動態(tài)載入的情況則不同,同樣的libdy.so我們在test中不鏈接,而是動態(tài)載入:

int main(int argc, char **argv) {
    func();
#ifdef DY_LOAD
    void *dp = dlopen("./libdy.so", RTLD_LAZY);
    typedef void (*callfn)();
    callfn f = (callfn) dlsym(dp, "callfn");
    f();
    func();
    dlclose(dp);
#else
    callfn();
#endif
    return 0;
}

運行得到:

$ ./test
func v1
...
callfn
func v2
func v1

都正確地調(diào)用到各自鏈接的func

結(jié)論,實踐中,動態(tài)載入的動態(tài)庫一般會作為插件使用,那么其同程序鏈接不同版本的靜態(tài)庫(相同符號不同實現(xiàn)),是沒有問題的。

變量

變量本質(zhì)上也是符號(symbol),但其處理規(guī)則和函數(shù)還有點不一樣(是不是有點想吐槽了)。

// object.h
class Object {
public:
    Object() {
#ifdef DF
        s = malloc(32);
        printf("s addr %p\n", s);
#endif
        printf("ctor %p\n", this);
    }

    ~Object() {
        printf("dtor %p\n", this);
#ifdef DF
        printf("s addr %p\n", s);
        free(s);
#endif
    }

    void *s;
};

extern Object g_obj;

我們的程序test和動態(tài)庫libdy.so都會鏈接object.o。首先測試test鏈接libdy.so,test和libdy.so中都會有g_obj這個符號:

// B g_obj 表示g_obj位于BSS段,未初始化段

$ nm test | grep g_obj
0000000000400a14 t _GLOBAL__I_g_obj
00000000006012c8 B g_obj
$ nm libdy.so | grep g_obj
000000000000097c t _GLOBAL__I_g_obj
0000000000200f30 B g_obj

運行:

$ ./test
ctor 0x6012c8
ctor 0x6012c8
...
dtor 0x6012c8
dtor 0x6012c8

g_obj被構(gòu)造了兩次,但地址一樣。全局變量只有一個實例,似乎在情理之中。

動態(tài)載入libdy.so,變量地址還是相同的:

$ ./test
ctor 0x6012a8
...
ctor 0x6012a8
...
dtor 0x6012a8
dtor 0x6012a8

結(jié)論,不同于函數(shù),全局變量符號重復(fù)時,不論動態(tài)庫是動態(tài)載入還是直接鏈接,變量始終只有一個。

但詭異的情況是,對象被構(gòu)造和析構(gòu)了兩次。構(gòu)造兩次倒無所謂,浪費點空間,但是析構(gòu)兩次就有問題。因為析構(gòu)時都操作的是同一個對象,那么如果這個對象內(nèi)部有分配的內(nèi)存,那就會對這塊內(nèi)存造成double free,因為指針相同。打開DF宏實驗下:

$ ./test
s addr 0x20de010
ctor 0x6012b8
s addr 0x20de040
ctor 0x6012b8
...
dtor 0x6012b8
s addr 0x20de040
dtor 0x6012b8
s addr 0x20de040

因為析構(gòu)的兩次都是同一個對象,所以其成員s指向的內(nèi)存被釋放了兩次,從而產(chǎn)生了double free,讓程序coredump了。

總結(jié),全局變量符號重復(fù)時,始終會只使用一個,并且會被初始化/釋放兩次,是一種較危險的情況,應(yīng)當(dāng)避免在使用動態(tài)庫的過程中使用全局變量。

posted on 2014-11-04 00:55 Kevin Lynx 閱讀(7990) 評論(1)  編輯 收藏 引用 所屬分類: c/c++

評論

# re: linux動態(tài)庫的種種要點 2015-06-11 09:09 sdhzdmzzl

我昨天也碰到了這個問題。可以參加http://www.ibm.com/developerworks/cn/linux/l-cn-sdlstatic/  回復(fù)  更多評論   

青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            欧美在线3区| 91久久精品一区二区三区| 黄色日韩精品| 韩日精品在线| 在线看日韩av| 亚洲日本视频| 一区二区久久| 午夜精品国产更新| 久久精品国产精品亚洲| 美女网站久久| 99在线精品视频| 久久都是精品| 欧美日韩dvd在线观看| 国产精品视频一区二区三区 | 欧美日韩伦理在线免费| 欧美天天视频| 一区二区三区四区蜜桃| 亚洲午夜精品久久| 久久成人一区二区| 欧美乱妇高清无乱码| 国产欧美日韩在线播放| 91久久久久久| 久久av资源网| 日韩视频在线一区二区三区| 欧美一区永久视频免费观看| 欧美日韩国产色视频| 国内精品国产成人| 亚洲欧美日韩精品久久亚洲区 | 久久成人一区| 欧美日本视频在线| 狠狠88综合久久久久综合网| 一本色道88久久加勒比精品| 久久人人爽人人爽| 亚洲无线观看| 欧美日本一区二区三区| 国内一区二区三区| 亚洲综合导航| 99精品国产99久久久久久福利| 欧美在线视频免费播放| 国产精品毛片在线看| 99视频在线精品国自产拍免费观看| 久久精品国产综合| 亚洲综合成人婷婷小说| 欧美视频导航| 一区二区欧美视频| 亚洲激情自拍| 老司机67194精品线观看| 国产免费亚洲高清| 午夜精品久久久久久久白皮肤| 亚洲精品乱码久久久久久久久| 久久欧美中文字幕| **网站欧美大片在线观看| 久久人人爽人人爽爽久久| 午夜国产一区| 国产亚洲欧美色| 久久青草欧美一区二区三区| 欧美一区二区三区四区在线观看| 国产欧美日韩一区二区三区在线| 午夜国产精品影院在线观看| 一区二区三区四区国产| 欧美日韩在线观看视频| 亚洲综合色噜噜狠狠| 亚洲性av在线| 国产日韩精品久久| 久久在线免费视频| 久久视频免费观看| 亚洲精品欧美日韩专区| 亚洲美女视频在线观看| 欧美性大战久久久久| 亚洲欧美综合网| 午夜综合激情| 亚洲国产高清aⅴ视频| 亚洲国产成人不卡| 亚洲美洲欧洲综合国产一区| 欧美日韩a区| 欧美一区二区三区精品| 欧美一级淫片aaaaaaa视频| 国内久久精品| 亚洲国产精品悠悠久久琪琪| 欧美91大片| 一本色道久久综合精品竹菊| 亚洲桃色在线一区| 国产一区再线| 亚洲国产精品小视频| 欧美无砖砖区免费| 久久裸体视频| 欧美日本韩国一区| 久久男女视频| 免费一级欧美片在线播放| 亚洲视频图片小说| 久久国产精品第一页| 亚洲精品在线一区二区| 亚洲女爱视频在线| 亚洲精品美女| 欧美一区视频| 日韩亚洲综合在线| 欧美在线观看网站| 亚洲一区二区三区777| 久久久久久97三级| 午夜精品福利一区二区蜜股av| 久久嫩草精品久久久精品| 亚洲午夜在线| 麻豆久久婷婷| 欧美伊人影院| 欧美片在线观看| 久久久最新网址| 国产精品网站在线观看| 亚洲电影欧美电影有声小说| 国产区欧美区日韩区| 亚洲精品一区二区三区福利| 狠狠入ady亚洲精品| 亚洲天天影视| 日韩午夜在线视频| 玖玖综合伊人| 久久综合九色综合欧美就去吻| 国产精品电影在线观看| 亚洲欧洲美洲综合色网| 伊人久久大香线| 欧美在线观看www| 午夜精品在线| 欧美视频在线观看视频极品| 欧美国产在线电影| 欲香欲色天天天综合和网| 香蕉国产精品偷在线观看不卡| 亚洲一区二区三区久久| 欧美日韩一级片在线观看| 亚洲黄色性网站| 亚洲人成网站999久久久综合| 久久久精品2019中文字幕神马| 欧美在线免费| 国产欧美日韩在线视频| 亚洲免费一级电影| 校园春色综合网| 国产麻豆精品视频| 性久久久久久久| 久久经典综合| 黄色一区二区在线| 久久免费少妇高潮久久精品99| 久久久久久久久久久一区| 国产一区二区三区久久| 欧美精品日韩| 欧美激情网友自拍| 精品999网站| 久久久精品午夜少妇| 欧美.com| 亚洲精选大片| 国产精品久久久久9999高清 | 欧美专区18| 久久综合999| 亚洲国产精品美女| 欧美激情片在线观看| 亚洲国产精品一区二区三区| 日韩天堂在线视频| 国产精品扒开腿做爽爽爽视频| 一区二区三区免费看| 久久精品国产久精国产爱| 韩国一区电影| 欧美国产日韩一二三区| 亚洲一区二区三区高清| 久久久亚洲国产天美传媒修理工 | 欧美一区二区免费视频| 国产亚洲欧美日韩在线一区 | avtt综合网| 久久精品视频在线免费观看| 亚洲国产精品一区在线观看不卡| 欧美精品福利| 性欧美精品高清| 亚洲高清视频中文字幕| 亚洲永久免费| 亚洲电影成人| 欧美午夜大胆人体| 久久精选视频| 亚洲无人区一区| 亚洲第一区色| 久久国产精品一区二区三区| 亚洲精品国产无天堂网2021| 国产精品日韩在线| 欧美二区在线| 久久国产精品久久w女人spa| 99视频+国产日韩欧美| 另类酷文…触手系列精品集v1小说| 99精品热视频只有精品10| 国内久久婷婷综合| 国产精品视频内| 欧美日韩的一区二区| 久久婷婷丁香| 欧美亚洲三区| 中日韩在线视频| 亚洲国产老妈| 免费在线亚洲欧美| 久久精品国产99国产精品| 亚洲一区二区伦理| 99re视频这里只有精品| 在线观看视频一区二区欧美日韩| 国产精品海角社区在线观看| 欧美精品日本| 欧美经典一区二区三区| 裸体一区二区| 六月婷婷久久| 另类欧美日韩国产在线|