• <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>

            loop_in_codes

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

            淺析靜態(tài)庫(kù)鏈接原理

            靜態(tài)庫(kù)的鏈接基本上同鏈接目標(biāo)文件.obj/.o相同,但也有些不同的地方。本文簡(jiǎn)要描述linux下靜態(tài)庫(kù)在鏈接過(guò)程中的一些細(xì)節(jié)。

            靜態(tài)庫(kù)文件格式

            靜態(tài)庫(kù)遠(yuǎn)遠(yuǎn)不同于動(dòng)態(tài)庫(kù),不涉及到符號(hào)重定位之類(lèi)的問(wèn)題。靜態(tài)庫(kù)本質(zhì)上只是將一堆目標(biāo)文件進(jìn)行打包而已。靜態(tài)庫(kù)沒(méi)有標(biāo)準(zhǔn),不同的linux下都會(huì)有些細(xì)微的差別。大致的格式wiki上描述的較清楚:

            Global header
            -----------------        +-------------------------------
            File header 1       ---> | File name
            File content 1  |        | File modification timestamp 
            -----------------        | Owner ID
            File header 2            | Group ID
            File content 2           | File mode
            -----------------        | File size in bytes
            ...                      | File magic
                                     +-------------------------------
            

            File header很多字段都是以ASCII碼表示,所以可以用文本編輯器打開(kāi)。

            靜態(tài)庫(kù)本質(zhì)上就是使用ar命令打包一堆.o文件。我們甚至可以用ar隨意打包一些文件:

            $ echo 'hello' > a.txt && echo 'world' > b.txt
            $ ar -r test.a a.txt b.txt
            $ cat test.a
            !<arch>
            a.txt/          1410628755  60833 100   100644  6         `
            hello
            b.txt/          1410628755  60833 100   100644  6         `
            world
            

            鏈接過(guò)程

            鏈接器在鏈接靜態(tài)庫(kù)時(shí),同鏈接一般的.o基本相似。鏈接過(guò)程大致可以歸納下圖:

            總結(jié)為:

            • 所有傳入鏈接器的.o都會(huì)被鏈接進(jìn)最終的可執(zhí)行程序;鏈接.o時(shí),會(huì)將.o中的global symbolunresolved symbol放入一個(gè)臨時(shí)表
            • 如果多個(gè).o定義了相同的global symbol,那么就會(huì)得到多重定義的鏈接錯(cuò)誤
            • 如果鏈接結(jié)束了,unresolved symbol表不為空,那么就會(huì)得到符號(hào)未定義的鏈接錯(cuò)誤
            • .a靜態(tài)庫(kù)處理本質(zhì)上就是處理其中的每一個(gè).o,不同的是,如果某個(gè).o中沒(méi)有一個(gè)符號(hào)屬于unresolved symbol表,也就是鏈接器此時(shí)懷疑該.o沒(méi)有必要,那么其就會(huì)被忽略

            可以通過(guò)一些代碼來(lái)展示以上過(guò)程。在開(kāi)發(fā)C++程序時(shí),可以利用文件靜態(tài)變量會(huì)先于main之前執(zhí)行做一些可能利于程序結(jié)構(gòu)的事情。如果某個(gè).o(包含靜態(tài)庫(kù)中打包的.o)被鏈接進(jìn)程序,那么其文件靜態(tài)變量就會(huì)先于main初始化。

            // test.cpp
            #include <stdio.h>
            
            class Test {
            public:
                Test() {
                    printf("Test ctor\n");
                }
            };
            
            static Test s_test;
            
            // lib.cpp
            #include <stdio.h>
            
            class Lib {
            public:
                Lib() {
                    printf("Lib ctor\n");
                }
            };
            
            static Lib s_lib;
            
            // main.cpp
            #include <stdio.h>
            
            int main() {
                printf("main\n");
                return 0;
            }

            以上代碼main.cpp中未引用任何test.cpp``lib.cpp中的符號(hào):

            $ g++ -o test test.o lib.o main.o
            $ ./test
            Lib ctor
            Test ctor
            main
            

            生成的可執(zhí)行程序執(zhí)行如預(yù)期,其鏈接了test.o``lib.o。但是如果把lib.o以靜態(tài)庫(kù)的形式進(jìn)行鏈接,情況就不一樣了:為了做對(duì)比,基于以上的代碼再加一個(gè)文件,及修改main.cpp

            // libfn.cpp
            int sum(int a, int b) {
                return a + b;
            }
            
            // main.cpp
            #include <stdio.h>
            
            int main() {
                printf("main\n");
                extern int sum(int, int);
                printf("sum: %d\n", sum(2, 3));
                return 0;
            }

            libfn.olib.o創(chuàng)建為靜態(tài)庫(kù):

            $ ar -r libfn.a libfn.o lib.o
            $ g++ -o test main.o test.o -lfn -L.
            $ ./test
            Test ctor
            main
            sum: 5
            

            因?yàn)?code>lib.o沒(méi)有被鏈接,導(dǎo)致其文件靜態(tài)變量也未得到初始化。

            調(diào)整鏈接順序,可以進(jìn)一步檢驗(yàn)前面的鏈接過(guò)程:

            # 將libfn.a的鏈接放在main.o前面
            
            $ g++ -o test test.o -lfn main.o  -L.
            main.o: In function `main':
            main.cpp:(.text+0x19): undefined reference to `sum(int, int)'
            collect2: ld returned 1 exit status
            

            這個(gè)問(wèn)題遇到得比較多,也有點(diǎn)讓人覺(jué)得莫名其妙。其原因就在于鏈接器在鏈接libfn.a的時(shí)候,發(fā)現(xiàn)libfn.o依然沒(méi)有被之前鏈接的*.o引用到,也就是沒(méi)有任何符號(hào)在unresolved symbol table,所以libfn.o也被忽略。

            一些實(shí)踐

            在實(shí)際開(kāi)發(fā)中還會(huì)遇到一些靜態(tài)庫(kù)相關(guān)的問(wèn)題。

            鏈接順序問(wèn)題

            前面的例子已經(jīng)展示了這個(gè)問(wèn)題。調(diào)整庫(kù)的鏈接順序可以解決大部分問(wèn)題,但當(dāng)靜態(tài)庫(kù)之間存在環(huán)形依賴(lài)時(shí),則無(wú)法通過(guò)調(diào)整順序來(lái)解決。

            -whole-archive

            -whole-archive選項(xiàng)告訴鏈接器把靜態(tài)庫(kù)中的所有.o都進(jìn)行鏈接,針對(duì)以上例子:

            $ g++ -o test -L. test.o -Wl,--whole-archive -lfn main.o -Wl,--no-whole-archive
            $ ./test
            Lib ctor
            Test ctor
            main
            sum: 5
            

            lib.o也被鏈接了進(jìn)來(lái)。-Wl選項(xiàng)告訴gcc將其作為鏈接器參數(shù)傳入;之所以在命令行結(jié)尾加上--no-whole-archive是為了告訴編譯器不要鏈接gcc默認(rèn)的庫(kù)

            可以看出這個(gè)方法還是有點(diǎn)暴力了。

            –start-group

            格式為:

            --start-group archives --end-group
            

            位于--start-group --end-group中的所有靜態(tài)庫(kù)將被反復(fù)搜索,而不是默認(rèn)的只搜索一次,直到不再有新的unresolved symbol產(chǎn)生為止。也就是說(shuō),出現(xiàn)在這里的.o如果發(fā)現(xiàn)有unresolved symbol,則可能回到之前的靜態(tài)庫(kù)中繼續(xù)搜索。

            $ g++ -o test -L. test.o -Wl,--start-group -lfn main.o -Wl,--end-group
            $ ./test
            Test ctor
            main
            sum: 5
            

            查看ldd關(guān)于該參數(shù)的man page還可以一窺鏈接過(guò)程的細(xì)節(jié):

            The specified archives are searched repeatedly until no new undefined references are created. Normally, an archive is searched only once in the order that it is specified on the command line. If a symbol in that archive is needed to resolve an undefined symbol referred to by an object in an archive that appears later on the command line, the linker would not be able to resolve that reference. By grouping the archives, they all be searched repeatedly until all possible references are resolved.

            嵌套靜態(tài)庫(kù)

            由于ar創(chuàng)建靜態(tài)庫(kù)時(shí)本質(zhì)上只是對(duì)文件進(jìn)行打包,所以甚至可以創(chuàng)建一個(gè)嵌套的靜態(tài)庫(kù),從而測(cè)試鏈接器是否會(huì)遞歸處理靜態(tài)庫(kù)中的.o

            $ ar -r libfn.a libfn.o
            $ ar -r liboutfn.a libfn.a lib.o
            $ g++ -o test -L. test.o main.o -loutfn
            main.o: In function `main':
            main.cpp:(.text+0x19): undefined reference to `sum(int, int)'
            collect2: ld returned 1 exit status
            

            可見(jiàn)鏈接器并不會(huì)遞歸處理靜態(tài)庫(kù)中的文件

            之所以要提到嵌套靜態(tài)庫(kù)這個(gè)問(wèn)題,是因?yàn)槲野l(fā)現(xiàn)很多時(shí)候我們喜歡為一個(gè)靜態(tài)庫(kù)工程鏈接其他靜態(tài)庫(kù)。當(dāng)然,這里的鏈接并非真正的鏈接(僅是打包),這個(gè)過(guò)程當(dāng)然可以聰明到將其他靜態(tài)庫(kù)里的.o提取出來(lái)然后打包到新的靜態(tài)庫(kù)。

            如果我們使用的是類(lèi)似scons這種封裝更高的依賴(lài)項(xiàng)管理工具,那么它是否會(huì)這樣干呢?

            基于之前的例子,我們使用scons來(lái)創(chuàng)建liboutfn.a

            # Sconstruct
            StaticLibrary('liboutfn.a', ['libfn.a', 'lib.o'])
            

            使用文本編輯器打開(kāi)liboutfn.a就可以看到其內(nèi)容,或者使用:

            $ ar -tv liboutfn.a
            rw-r--r-- 60833/100   1474 Sep 14 02:59 2014 libfn.a
            rw-r--r-- 60833/100   2448 Sep 14 02:16 2014 lib.o
            

            可見(jiàn)scons也只是單純地打包。所以,在scons中構(gòu)建一個(gè)靜態(tài)庫(kù)時(shí),再鏈接其他靜態(tài)庫(kù)是沒(méi)有意義的

            參考文檔

            posted on 2014-09-15 22:47 Kevin Lynx 閱讀(4246) 評(píng)論(2)  編輯 收藏 引用 所屬分類(lèi): c/c++

            評(píng)論

            # re: 淺析靜態(tài)庫(kù)鏈接原理 2014-09-16 08:52 ccsdu2009

            受教了  回復(fù)  更多評(píng)論   

            # re: 淺析靜態(tài)庫(kù)鏈接原理 2014-09-16 09:24 zuhd

            最近干貨很多啊  回復(fù)  更多評(píng)論   

            久久中文字幕视频、最近更新| 久久精品aⅴ无码中文字字幕不卡 久久精品aⅴ无码中文字字幕重口 | 久久久精品一区二区三区| 久久久国产99久久国产一| 中文成人无码精品久久久不卡| 久久影院午夜理论片无码| 欧美久久一区二区三区| 99久久无码一区人妻| 99久久人人爽亚洲精品美女| 国产精品激情综合久久| 久久国产美女免费观看精品| 精品久久久无码中文字幕天天| 91性高湖久久久久| 久久久久久久亚洲精品| 久久这里只有精品首页| 久久99精品久久久久久久久久| 久久综合久久综合九色| 精品久久久久久久久久中文字幕| 久久有码中文字幕| 亚洲AV日韩精品久久久久| 91久久精品91久久性色| 久久久国产精华液| 日韩精品无码久久久久久| 国产成人精品久久一区二区三区| 国产精品日韩深夜福利久久| 亚洲国产精品一区二区三区久久| 亚洲国产另类久久久精品黑人| 国产精品久久国产精麻豆99网站 | 色婷婷久久综合中文久久一本| 97久久婷婷五月综合色d啪蜜芽 | 国产成人精品久久一区二区三区av| 99久久亚洲综合精品网站| 久久久久国产一区二区三区| 99久久综合国产精品免费| 成人国内精品久久久久影院| 性高湖久久久久久久久AAAAA| 久久久久久久久无码精品亚洲日韩| 国产香蕉97碰碰久久人人| 久久久亚洲欧洲日产国码二区| 久久93精品国产91久久综合| 韩国免费A级毛片久久|