GCC 使用庫文件名進行鏈接

使用 GCC 進行 C/C++ 代碼編譯時,如果代碼中使用到了庫函數,需要使用 -l 選項指定該庫函數所在的庫。如:-lm-lrt-lpthread等。這種方式使用的是庫的縮寫。一個庫的文件名如果是:libxxx.so 或 libxxx.a,則可以使用 -lxxx 進行鏈接。這種規則很常見,但是缺點也很明顯。假設在一臺 Linux 機器上,同時具有 libxxx.so 和 libxxx.a,GCC 會優先鏈接 libxxx.so。雖然,GCC 也提供了 -static 選項可以強制鏈接靜態庫。但是,這時候新的問題出現了,假設有兩個庫 x 和 y,他們都具有靜態庫和動態庫兩個版本。如果我想要鏈接 libx.so 和 liby.a,使用 -static 選項就無法滿足這個要求。我需要更加精細的控制,最好是直接根據文件名直接指定鏈接哪個版本的庫文件,就沒有任何歧義。

GCC 文檔的關於 -l 選項的描述沒有告訴我如何直接使用一個庫文件名。於是翻看 ld 的文檔。在關於 -l 選項的描述中,有這樣一段話:

If namespec is of the form ‘:filename’, ld will search the library path for a file called filename, otherwise it will search the library path for a file called ‘libnamespec.a’.

也就是說可以使用 -l:filename 的形式直接指定庫文件名。這個只是 ld 的選項,GCC 能不能直接使用還需要驗證。設計三個 .cpp 文件,分爲 x.cpp y.cpp 和 main.cpp

// x.cpp
#include <iostream>

void print_x()
{
    std::cout << "x" << std::endl;
}
// y.cpp
#include <iostream>

void print_y()
{
    std::cout << "y" << std::endl;
}
// main.cpp
void print_x();
void print_y();

int main()
{
    print_x();
    print_y();
}

使用如下 Makefile 進行編譯。x.cpp 編譯成 libx.so 和 libx.ay.cpp 編譯成 liby.so 和 liby.amain.cpp 與 libx.so 和 liby.a 編譯鏈接成 main.out

all : main
	
x :
	gcc -o libx.so -shared -fPIC x.cpp
	gcc -o x.o -c x.cpp
	ar crs libx.a x.o

y :
	gcc -o liby.so -shared -fPIC y.cpp
	gcc -o y.o -c y.cpp
	ar crs liby.a y.o

clean :
	rm -f *.out *.o *.so *.a

main : x y
	gcc -o main.out main.cpp -Wl,-rpath=./  -lstdc++ -L. -l:libx.so -l:liby.a

能直接通過編譯,使用 ldd main.out 查看一下動態庫依賴:

        linux-vdso.so.1 (0x00007ffe71ace000)
        libstdc++.so.6 => /lib64/libstdc++.so.6 (0x00007f1bdea54000)
        libx.so => ./libx.so (0x00007f1bdea4f000)
        libc.so.6 => /lib64/libc.so.6 (0x00007f1bde845000)
        libm.so.6 => /lib64/libm.so.6 (0x00007f1bde769000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f1bdec7b000)
        libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x00007f1bde74e000)
PLAINTEXT 複製 全屏

可以看到 libx.so 被動態鏈接,而 liby.a 被靜態鏈接,沒有顯示。執行 main.out,輸出結果也符合預期。

x
y
PLAINTEXT 複製 全屏

由此可見,-l:filename 能直接用於 GCC。這種方法除了控制鏈接的庫是靜態的還是動態的之外,還能用於控制庫的版本號。例如 libx.so 同時存在兩個版本 libx.so.1 和 libx.so.2 ,可以使用 -l:libx.so.1 指定版本號爲 1 的庫。

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