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

loop_in_codes

低調做技術__歡迎移步我的獨立博客 codemaro.com 微博 kevinlynx

淺析靜態庫鏈接原理

靜態庫的鏈接基本上同鏈接目標文件.obj/.o相同,但也有些不同的地方。本文簡要描述linux下靜態庫在鏈接過程中的一些細節。

靜態庫文件格式

靜態庫遠遠不同于動態庫,不涉及到符號重定位之類的問題。靜態庫本質上只是將一堆目標文件進行打包而已。靜態庫沒有標準,不同的linux下都會有些細微的差別。大致的格式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碼表示,所以可以用文本編輯器打開。

靜態庫本質上就是使用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

鏈接過程

鏈接器在鏈接靜態庫時,同鏈接一般的.o基本相似。鏈接過程大致可以歸納下圖:

總結為:

  • 所有傳入鏈接器的.o都會被鏈接進最終的可執行程序;鏈接.o時,會將.o中的global symbolunresolved symbol放入一個臨時表
  • 如果多個.o定義了相同的global symbol,那么就會得到多重定義的鏈接錯誤
  • 如果鏈接結束了,unresolved symbol表不為空,那么就會得到符號未定義的鏈接錯誤
  • .a靜態庫處理本質上就是處理其中的每一個.o,不同的是,如果某個.o中沒有一個符號屬于unresolved symbol表,也就是鏈接器此時懷疑該.o沒有必要,那么其就會被忽略

可以通過一些代碼來展示以上過程。在開發C++程序時,可以利用文件靜態變量會先于main之前執行做一些可能利于程序結構的事情。如果某個.o(包含靜態庫中打包的.o)被鏈接進程序,那么其文件靜態變量就會先于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中的符號:

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

生成的可執行程序執行如預期,其鏈接了test.o``lib.o。但是如果把lib.o以靜態庫的形式進行鏈接,情況就不一樣了:為了做對比,基于以上的代碼再加一個文件,及修改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創建為靜態庫:

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

因為lib.o沒有被鏈接,導致其文件靜態變量也未得到初始化。

調整鏈接順序,可以進一步檢驗前面的鏈接過程:

# 將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

這個問題遇到得比較多,也有點讓人覺得莫名其妙。其原因就在于鏈接器在鏈接libfn.a的時候,發現libfn.o依然沒有被之前鏈接的*.o引用到,也就是沒有任何符號在unresolved symbol table,所以libfn.o也被忽略。

一些實踐

在實際開發中還會遇到一些靜態庫相關的問題。

鏈接順序問題

前面的例子已經展示了這個問題。調整庫的鏈接順序可以解決大部分問題,但當靜態庫之間存在環形依賴時,則無法通過調整順序來解決。

-whole-archive

-whole-archive選項告訴鏈接器把靜態庫中的所有.o都進行鏈接,針對以上例子:

$ 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也被鏈接了進來。-Wl選項告訴gcc將其作為鏈接器參數傳入;之所以在命令行結尾加上--no-whole-archive是為了告訴編譯器不要鏈接gcc默認的庫

可以看出這個方法還是有點暴力了。

–start-group

格式為:

--start-group archives --end-group

位于--start-group --end-group中的所有靜態庫將被反復搜索,而不是默認的只搜索一次,直到不再有新的unresolved symbol產生為止。也就是說,出現在這里的.o如果發現有unresolved symbol,則可能回到之前的靜態庫中繼續搜索。

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

查看ldd關于該參數的man page還可以一窺鏈接過程的細節:

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.

嵌套靜態庫

由于ar創建靜態庫時本質上只是對文件進行打包,所以甚至可以創建一個嵌套的靜態庫,從而測試鏈接器是否會遞歸處理靜態庫中的.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

可見鏈接器并不會遞歸處理靜態庫中的文件

之所以要提到嵌套靜態庫這個問題,是因為我發現很多時候我們喜歡為一個靜態庫工程鏈接其他靜態庫。當然,這里的鏈接并非真正的鏈接(僅是打包),這個過程當然可以聰明到將其他靜態庫里的.o提取出來然后打包到新的靜態庫。

如果我們使用的是類似scons這種封裝更高的依賴項管理工具,那么它是否會這樣干呢?

基于之前的例子,我們使用scons來創建liboutfn.a

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

使用文本編輯器打開liboutfn.a就可以看到其內容,或者使用:

$ 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

可見scons也只是單純地打包。所以,在scons中構建一個靜態庫時,再鏈接其他靜態庫是沒有意義的

參考文檔

posted on 2014-09-15 22:47 Kevin Lynx 閱讀(4292) 評論(2)  編輯 收藏 引用 所屬分類: c/c++

評論

# re: 淺析靜態庫鏈接原理 2014-09-16 08:52 ccsdu2009

受教了  回復  更多評論   

# re: 淺析靜態庫鏈接原理 2014-09-16 09:24 zuhd

最近干貨很多啊  回復  更多評論   

青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <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>
            亚洲精品永久免费| 99热在这里有精品免费| 欧美在线影院| 亚洲美女av电影| 蜜臀99久久精品久久久久久软件| 欧美视频在线看| 亚洲激情国产精品| 免费一级欧美片在线观看| 欧美自拍偷拍午夜视频| 国产麻豆精品视频| 午夜久久福利| 亚洲男人的天堂在线| 国产精品毛片在线看| 亚洲午夜小视频| 亚洲午夜精品久久久久久浪潮| 欧美日韩免费一区| 一本色道久久加勒比88综合| 亚洲国产一区二区三区高清| 久久综合福利| 亚洲看片一区| 一本色道久久加勒比88综合| 国产精品国产三级国产专播精品人 | 亚洲欧洲日韩女同| 欧美电影在线免费观看网站| 欧美影院视频| 久久精品视频在线播放| 亚洲国产精品一区二区尤物区| 欧美高清影院| 欧美精品一区二区在线播放| 亚洲永久免费| 午夜在线一区| 在线播放日韩| 亚洲精品在线免费观看视频| 国产欧美精品一区二区三区介绍 | 亚洲国产午夜| 欧美午夜宅男影院在线观看| 欧美一级视频精品观看| 久久久夜夜夜| 亚洲精品男同| 亚洲一区二区视频| 国语自产偷拍精品视频偷| 欧美成人三级在线| 欧美激情成人在线| 欧美一级成年大片在线观看| 久久一区二区三区四区| 中文精品视频一区二区在线观看| 亚洲自拍高清| 亚洲高清久久网| 亚洲在线成人精品| 一区二区亚洲欧洲国产日韩| 亚洲精品国产品国语在线app| 狠狠爱综合网| 欧美日本韩国在线| 欧美电影打屁股sp| 欲色影视综合吧| 麻豆久久婷婷| 狠狠久久亚洲欧美| 久久高清福利视频| 亚洲国产精品久久久久| 亚洲精品日韩欧美| 欧美区在线播放| 99精品视频免费| 欧美一区二区三区精品| 国产一区二区三区久久久久久久久 | 亚欧成人在线| 久久久久久香蕉网| 亚洲国产精品v| 国产精品高潮在线| 欧美影院成人| 亚洲精品免费在线播放| 亚洲欧美日韩国产中文在线| 久久琪琪电影院| 亚洲精品美女久久7777777| 国产欧美精品在线| 欧美精品国产精品日韩精品| 亚洲香蕉伊综合在人在线视看| 欧美激情免费观看| 久久综合狠狠综合久久综合88| 99国内精品久久| 亚洲第一免费播放区| 国产精品一区二区三区久久久| 免费日韩成人| 久久精品免费观看| 亚洲欧美日韩精品久久奇米色影视 | 久久免费黄色| 亚洲一区二区在线| 亚洲人成小说网站色在线 | 久久久精品国产99久久精品芒果| 亚洲免费观看在线视频| 亚洲国产电影| 亚洲欧洲三级电影| 日韩亚洲在线| 一本色道久久综合亚洲91| 亚洲国产成人久久综合一区| 免费精品视频| 亚洲精品欧美日韩专区| av成人激情| 久久成人国产精品| 一本色道久久加勒比88综合| 亚洲欧美日韩精品久久亚洲区| 欧美在线视频播放| 欧美国产先锋| 亚洲一品av免费观看| 欧美亚洲在线视频| 蜜臀a∨国产成人精品| 欧美国产亚洲精品久久久8v| 国产精品ⅴa在线观看h| 国产偷自视频区视频一区二区| 亚洲电影免费观看高清| 亚洲欧洲偷拍精品| 欧美亚洲在线观看| 欧美电影免费| 亚洲欧洲av一区二区三区久久| 久久精品国产99国产精品| 欧美精品大片| 好看不卡的中文字幕| 99精品99| 亚洲国产毛片完整版 | 欧美日韩国内自拍| 欧美成人免费在线| 香蕉国产精品偷在线观看不卡 | 午夜精品久久久久影视| 欧美xxx成人| 亚洲国产欧美日韩精品| 久久综合亚州| 亚洲欧美日韩国产一区| 国产精品萝li| 亚洲一区欧美激情| 一区二区三区色| 国产精品久久久久久久久久久久久 | 欧美日韩二区三区| 91久久综合| 亚洲福利在线视频| 欧美成人第一页| 亚洲视频免费观看| 亚洲欧美日韩一区| 激情综合色丁香一区二区| 美女主播精品视频一二三四| 美女黄毛**国产精品啪啪| 亚洲激情视频在线播放| 亚洲日本中文字幕区| 欧美天天影院| 欧美高清视频在线播放| 国产精品jizz在线观看美国| 久久精品一二三| 欧美精品亚洲一区二区在线播放| 一区二区国产精品| 午夜国产欧美理论在线播放 | 日韩视频不卡| 亚洲午夜性刺激影院| 亚洲国产精品第一区二区三区| 亚洲激情网站免费观看| 国产精品你懂的| 欧美成人四级电影| 国产视频久久| 在线视频精品一区| 亚洲国产一区在线| 亚洲欧美电影院| 夜夜嗨av一区二区三区四区| 久久精品av麻豆的观看方式| 欧美不卡视频一区发布| 先锋影院在线亚洲| 国产精品草莓在线免费观看| 老司机久久99久久精品播放免费| 国产精品久久久久久久7电影| 亚洲第一页在线| 亚洲精品久久久久久一区二区 | 亚洲三级影片| 亚洲精品国产拍免费91在线| 亚洲一区二区在线播放| 欧美一区1区三区3区公司| 欧美日韩另类综合| 欧美成人资源| 亚洲国产二区| 欧美插天视频在线播放| 免费不卡亚洲欧美| 91久久国产精品91久久性色| 久久久久久一区二区三区| 久久午夜电影网| 激情欧美一区二区三区| 欧美国产一区视频在线观看| 亚洲国产天堂久久综合| 亚洲精品国产精品国产自| 久久三级视频| 亚洲国产精品传媒在线观看 | 欧美黄色视屏| 日韩视频中文| 欧美影院久久久| 亚洲精品美女在线| 久久精品国产亚洲高清剧情介绍| 校园春色国产精品| 悠悠资源网亚洲青| 国产区精品视频| 国产亚洲一区精品| 国产伦理一区| 国产精品一区二区女厕厕| 久久久青草婷婷精品综合日韩 | 性久久久久久久久| 在线视频亚洲| 一区二区三区日韩精品视频|