鏈接,靜態庫,動態庫

在多個文件編譯之後,每個文件生成了一個.o文件,然後是使用鏈接器,將.o文件鏈接爲一個可執行文件的。鏈接器爲了創建可執行文件,必須完成兩個任務:

1.符號解析:目標文件定義和引用符號。符號解析的目的是將每個符號引用和符號定義聯繫起來。

2.重定位:彙編器和編譯器生成從地址零開始的代碼和數據段。鏈接器通過把每個符號定義與一個存儲器位置(虛存)聯繫起來,然後修改所有對這些符號的引用,使得它們指向這個存儲器的位置,從而重定向這些段。

這兩句話我的理解是1.將不同.o文件中的變量聲明和變量定義聯繫起來,2.因爲每個編譯出來的.o文件都是從地址零開始的,所以當這些.o文件鏈接爲一個文件是,就需要將地址做相應的調整,保證程序執行的正確性。


舉一個例子:

//main.c

void swap();

int buf[2] = {1,2};

int main()

{

swap();

return 0;

}

//swap.c

extern int buf[];

int *bufp0 = &buf[0];

int *bufp1;

void swap()

{

int temp;

bufp1 = &buf[1];

temp = *bufp0;

*bufp0 = *bufp1;

*bufp1 = temp;

}

上述的小程序是交換數組中的兩個數字的位置。

編譯的流程圖爲:


每一個.c文件都會經過編譯生成一個.o文件,所有的.o文件經過連接,生成一個可執行文件。


靜態連接:

我們使用的標準庫,包含了我們經常所用的標準I/O,串操作,整數算術函數等等。下面說下靜態連接的必要性:

如果沒有靜態庫,我們將剛纔的所有庫函數都編譯爲一個.o文件,當要使用是,我們將這個.o文件連接到我們使用庫函數的cpp文件中,例如gcc main.c libc.o. 這樣存在的問題,是系統的每一個可執行文件都含有一份標準函數的完全拷貝,而不是我需要哪些函數就有哪些函數。所以靜態庫應運而生。在鏈接靜態庫時,鏈接器值拷貝被程序引用的目標模塊,這樣就減少了可執行文件在磁盤和存儲器中的大小。

舉個例子:

//addvec.c

void addvec(int *x,int *y, int *z, int n)
{
    int i ; 
    for(i = 0; i < n; i ++) 
        z[i] = x[i] + y[i];
}


//multvec.c

void multvec(int *x, int *y, int *z, int n)
{
    int i;
    for(i = 0; i < n; i ++ )
        z[i] = x[i] * y[i];
}

編譯這兩個.c文件,創建靜態庫

gcc -c addvec.c multvec.c

ar rcs libvector.a addvec.o multvec.o 生成靜態庫libvector.a其中包括兩個.o文件


//main2.c

#include <stdio.h>
#include "vector.h"


int x[2] = {1,2};
int y[2] = {3,4};
int z[2];
int main()
{
    addvec(x,y,z,2);
    printf("z = [%d %d]\n", z[0],z[0]);
    return 0;
}

//vector.h

void addvec(int*, int*,int*,int);
void multvec(int*,int*,int*,int);

先編譯main.o 

gcc -O2 -c main2.c

生成可執行文件

gcc -static -o p2 main2.o ./libvector.a

-static參數告訴編譯器,鏈接器應該構建一個完全連接的可執行目標文件,它可以加載到存儲器並運行,在加載時無須更進一步的連接了。當鏈接器運行時,他判定addvec.o定義的addvec符號是被main.o引用的,所以它拷貝addvec.o到可執行文件。因爲程序不引用任何由multvec.o定義的符號,所以鏈接器就不會拷貝這個模塊到可執行文件。鏈接器還會從libc.a拷貝print.o模塊,以及許多C運行時系統中的模塊,如圖:



最後是動態共享庫

我們有了靜態庫,但是仍然有一些問題:

1靜態庫的更新,如果靜態庫需要更新,那麼所有依賴於該庫的可執行程序都需要重新編譯。

2一些常用的函數,例如標準I/O函數print  在運行時,這些函數會被複制到每個運行進程的代碼段中去,造成了極大的浪費

所以我們引入了動態庫。

程序還是我們在靜態庫中使用的文件

編譯靜態庫:

gcc -shared -fPIC -o libvector.so addvec.c multvec.c

-fPIC選項只是編譯器生成與位置無關的代碼,-shared選項指示鏈接器創建一個共享的目標文件

gcc -o p2 main2.c  ./libector.so

這樣就創建了一個可執行文件p2.

此時,沒有任何libvector.so的代碼和數據段被真正的拷貝到可執行文件p2中,取而代之的是,鏈接器拷貝了一些重定位和符號表的信息。


附:

查看靜態庫中的函數 nm  ****.a

查看動態庫所依賴的其他庫  ldd  ****.so

g++選項

 -L:指定鏈接庫的路徑,-L. 表示要連接的庫在當前目錄中
 -ltest:指定鏈接庫的名稱爲test,編譯器查找動態連接庫時有隱含的命名規則,即在給出的名字前面加上lib,後面加上.so來確定庫的名稱
例如:g++ TestDynamicLibrary.cpp -L../DynamicLibrary -ldynmath

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章