靜態連結動態連結的連結順序問題和makefile示例
程式碼檔案初始化
a.h
#ifndef A_H
#define A_H
#include <iostream>
class A
{
public:
A(){}
~A(){}
void hello();
};
#endif
a.cpp
#include "a.h"
void A::hello()
{
std::cout << "hello world\n";
}
b.h
#ifndef B_H
#define B_H
#include "a.h"
#include <iostream>
class B
{
public:
void sayB();
private:
A a;
};
#endif
b.cpp
#include "b.h"
void B::sayB()
{
std::cout << "B is running\n";
a.hello();
std::cout << "B is end\n";
}
c.cpp
#include "b.h"
int main()
{
std::cout << "C is running\n";
B b;
b.sayB();
std::cout << "C is end\n";
}
.o檔案編譯可執行檔案
目標檔案(.o檔案)編譯,不講究順序
生成各個檔案的.o檔案
[root@localhost test]# g++ -c *.cpp
[root@localhost test]# g++ -o main a.o b.o c.o
[root@localhost test]# g++ -o main c.o b.o a.o
[root@localhost test]# g++ -o main *.o
[root@localhost test]# ./main
C is running
B is running
hello world
B is end
C is end
直接g++ c.cpp -o main是不行的,會缺依賴,少了B::sayB()的實現
[root@localhost test]# g++ c.cpp -o main
/tmp/cc2F6BQH.o: In function `main':
c.cpp:(.text+0x2c): undefined reference to `B::sayB()'
collect2: error: ld returned 1 exit status
直接g++ c.cpp -o main b.o也是不行的,會缺依賴,少了A::hello()的實現
[root@localhost test]# g++ c.cpp -o main b.o
b.o: In function `B::sayB()':
b.cpp:(.text+0x23): undefined reference to `A::hello()'
collect2: error: ld returned 1 exit status
正確
[root@localhost test]# g++ c.cpp -o main b.o a.o
[root@localhost test]# ./main
C is running
B is running
hello world
B is end
C is end
建立靜態連結檔案並編譯
使用 ar 將目標檔案歸檔
ar crv libabc.a a.o b.o c.o
其中:
- c 如果需要生成新的庫檔案,不要警告
- r 代替庫中現有的檔案或者插入新的檔案
- v 輸出詳細資訊
通過 ar t libabc.a
可以檢視 libabc.a
中包含的目標檔案,還可以通過 ar --help
檢視更多幫助。
注意:我們要生成的庫的檔名必須形如 libxxx.a
,這樣我們在連結這個庫時,就可以用 -lxxx
。反過來講,當我們告訴編譯器 -lxxx
時,編譯器就會在指定的目錄中搜尋 libxxx.a
或是 libxxx.so
。
靜態連結檔案:在連結階段,將原始檔中用到的庫函式與彙編生成的目標檔案.o合併生成可執行檔案。
靜態連結檔案(.a檔案)連結,講究順序,左邊的lib庫依賴右邊的lib庫
不講究順序的錯誤方式:
[root@localhost test]# g++ -o main aaa/liba.a aaa/libb.a aaa/libc.a
aaa/libc.a(c.o): In function `main':
c.cpp:(.text+0x2c): undefined reference to `B::sayB()'
collect2: error: ld returned 1 exit status
[root@localhost test]# g++ -o main aaa/*.a
aaa/libc.a(c.o): In function `main':
c.cpp:(.text+0x2c): undefined reference to `B::sayB()'
collect2: error: ld returned 1 exit status
1、使用3個.a靜態連結檔案三種方式
(1)正確順序的依賴
libc.a依賴libb.a,libb.a依賴liba.a
[root@localhost test]# g++ -o main aaa/libc.a aaa/libb.a aaa/liba.a
(2)多次依賴
在使用一些迴圈依賴關係比較複雜的靜態庫時,也可以在連結序列中,讓一個靜態庫出現多次,來解決一些迴圈依賴。
[root@localhost test]# g++ -o main aaa/liba.a aaa/libb.a aaa/libc.a aaa/libb.a aaa/liba.a
(3)使用Xlinker
Xlinker選項是將引數傳給連結器,連結器在處理”-(”和”-)”之間的靜態庫時,是會重複查詢這些靜態庫的,所以就解決了靜態庫查詢順序問題。不過,這種方式比人工提供連結順序的方式效率會低很多。
[root@localhost test]# g++ -o main -Xlinker "-(" aaa/liba.a aaa/libb.a aaa/libc.a -Xlinker "-)"
2、使用2個.a靜態連結檔案
少了liba
[root@localhost test]# g++ c.cpp -o main aaa/libb.a
aaa/libb.a(b.o): In function `B::sayB()':
b.cpp:(.text+0x23): undefined reference to `A::hello()'
collect2: error: ld returned 1 exit status
連結順序出錯
[root@localhost test]# g++ c.cpp -o main aaa/liba.a aaa/libb.a
aaa/libb.a(b.o): In function `B::sayB()':
b.cpp:(.text+0x23): undefined reference to `A::hello()'
collect2: error: ld returned 1 exit status
正確
[root@localhost test]# g++ c.cpp -o main aaa/libb.a aaa/liba.a
[root@localhost test]# ./main
C is running
B is running
hello world
B is end
C is end
3、將a.cpp和b.cpp製作成.a,然後編譯c.cpp時進行連結
[root@localhost test]# ar crv aaa/libab.a a.o b.o
[root@localhost test]# g++ c.cpp -o main -L./aaa -lab
[root@localhost test]# ./main
C is running
B is running
hello world
B is end
C is end
4、將a.cpp,b.cpp和c.cpp製作成.a,然後直接生成可執行檔案時連結
[root@localhost test]# ar crv aaa/libabc.a a.o b.o c.o
[root@localhost test]# g++ -o main -L./aaa -labc
[root@localhost test]# ./main
C is running
B is running
hello world
B is end
C is end
5、分別製作三個.a,分別連結,發現不行,原因未知
[root@localhost test]# g++ -o main -L./aaa/tmp -la -lb -lc
/usr/bin/ld: ./aaa/tmp/libc.a(c.o): undefined reference to symbol '__cxa_atexit@@GLIBC_2.2.5'
/usr/bin/ld: note: '__cxa_atexit@@GLIBC_2.2.5' is defined in DSO /usr/lib64/libc.so.6 so try adding it to the linker command line
/usr/lib64/libc.so.6: could not read symbols: Invalid operation
collect2: error: ld returned 1 exit status
[root@localhost test]# g++ -o main -L./aaa/tmp -lc -lb -la
/usr/bin/ld: ./aaa/tmp/libc.a(c.o): undefined reference to symbol '__cxa_atexit@@GLIBC_2.2.5'
/usr/bin/ld: note: '__cxa_atexit@@GLIBC_2.2.5' is defined in DSO /usr/lib64/libc.so.6 so try adding it to the linker command line
/usr/lib64/libc.so.6: could not read symbols: Invalid operation
collect2: error: ld returned 1 exit status
建立動態連結檔案並編譯
編譯生成動態庫
g++ -fPIC -shared -o ./soso/libab.so a.cpp b.cpp
實際上上述過程分為編譯和連結兩步, -fPIC是編譯選項,PIC是 Position Independent Code 的縮寫,表示要生成位置無關的程式碼,這是動態庫需要的特性; -shared是連結選項,告訴g++生成動態庫而不是可執行檔案。
上述的一行命令等同於:
g++ -c -fPIC a.cpp b.cpp # 注意必須帶-fPIC,不帶的話生成的.o目標檔案是沒法用於生成.so檔案的
g++ -shared -o libab.so a.o b.o
g++ c.cpp -o main -L./soso -lab
生成main,其中-lab
表示要連結libab.so
。
-L./soso
表示搜尋要連結的庫檔案時包含./soso
路徑。
如果同一目錄下同時存在同名的動態庫和靜態庫,比如 libab.so
和 libab.a
都在當前路徑下,g++會優先連結動態庫。想連結靜態庫需要顯式指定g++ -static c.cpp -o main -L. -lab
動態連結檔案:在程式執行過程中動態呼叫庫檔案,很方便,不佔空間
動態連結檔案(.so檔案)連結,不講究順序
1、使用3個so動態連結檔案
[root@localhost test]# g++ -o main soso/liba.so soso/libb.so soso/libc.so
[root@localhost test]# g++ -o main soso/libc.so soso/libb.so soso/liba.so
[root@localhost test]# g++ -o main soso/*.so
2、使用2個so動態連結檔案
[root@localhost test]# g++ c.cpp -o main soso/libb.so
soso/libb.so: undefined reference to `A::hello()'
collect2: error: ld returned 1 exit status
[root@localhost test]# g++ c.cpp -o main soso/libb.so soso/liba.so
3、將a.cpp和b.cpp製作成so,然後編譯c.cpp時進行連結
[root@localhost test]# g++ -fPIC -shared -o ./soso/libab.so a.cpp b.cpp
[root@localhost test]# g++ c.cpp -o main -L./soso -lab
[root@localhost test]# ./main
./main: error while loading shared libraries: libab.so: cannot open shared object file: No such file or directory
找不到libab.so,因為Linux是通過 /etc/ld.so.cache 檔案搜尋要連結的動態庫的, /etc/ld.so.cache 是 ldconfig 程式讀取 /etc/ld.so.conf 檔案生成的(/etc/ld.so.conf 中並不一定需要包含 /lib 和 /usr/lib,ldconfig程式會自動搜尋這兩個目錄)。
如果我們把 libab.so 所在的路徑新增到 /etc/ld.so.conf 中,再以root許可權執行 ldconfig 程式,更新 /etc/ld.so.cache ,main執行時,就可以找到 libab.so。
也可以直接用命令LD_LIBRARY_PATH=./soso的方式指定動態連結庫路徑。
[root@localhost test]# LD_LIBRARY_PATH=./soso ./main
C is running
B is running
hello world
B is end
C is end
4、將a.cpp,b.cpp和c.cpp製作成so,然後直接生成可執行檔案時連結
[root@localhost test]# g++ -fPIC -shared -o ./soso/libabc.so a.cpp b.cpp c.cpp
[root@localhost test]# g++ -o main -L./soso -labc
[root@localhost test]# LD_LIBRARY_PATH=./soso ./main
C is running
B is running
hello world
B is end
C is end
5、分別製作三個so,分別連結,發現不行,原因未知
[root@localhost test]# g++ -fPIC -shared -o ./soso/liba.so a.cpp
[root@localhost test]# g++ -fPIC -shared -o ./soso/libb.so b.cpp
[root@localhost test]# g++ -fPIC -shared -o ./soso/libc.so c.cpp
[root@localhost test]# g++ -o main -L./soso -la -lb -lc
/usr/bin/ld: /usr/lib/gcc/x86_64-linux/4.8.5/../../../../lib64/crt1.o: undefined reference to symbol '__libc_start_main@@GLIBC_2.2.5'
/usr/bin/ld: note: '__libc_start_main@@GLIBC_2.2.5' is defined in DSO /usr/lib64/libc.so.6 so try adding it to the linker command line
/usr/lib64/libc.so.6: could not read symbols: Invalid operation
collect2: error: ld returned 1 exit status
[root@localhost test]# g++ -o main -L./soso -lc -lb -la
/usr/bin/ld: /usr/lib/gcc/x86_64-linux/4.8.5/../../../../lib64/crt1.o: undefined reference to symbol '__libc_start_main@@GLIBC_2.2.5'
/usr/bin/ld: note: '__libc_start_main@@GLIBC_2.2.5' is defined in DSO /usr/lib64/libc.so.6 so try adding it to the linker command line
/usr/lib64/libc.so.6: could not read symbols: Invalid operation
collect2: error: ld returned 1 exit status
製作makefile
將連結2個和3個動態檔案及靜態檔案的方式做成makefile。
Makefile有三個非常有用的變數:
$@:目標檔案
$^:所有的依賴檔案
$<:第一個依賴檔案。
.PHONY: buildabcso testabcso buildabso testabso buildabca testabca buildaba testaba clean
buildabcso: libabc.so
buildabso: libab.so
buildabca: libabc.a
buildaba: libab.a
libabc.so: a.o b.o c.o
g++ -shared -o $@ $^
libab.so: a.o b.o
g++ -shared -o $@ $^
libabc.a: a.o b.o c.o
ar crv $@ $^
libab.a: a.o b.o
ar crv $@ $^
a.o: a.cpp
g++ -c -fPIC $<
b.o: b.cpp
g++ -c -fPIC $<
c.o: c.cpp
g++ -c -fPIC $<
testabcso: libabc.so
g++ -o main -L. -labc
LD_LIBRARY_PATH=. ./main
testabso: libab.so
g++ c.cpp -o main -L. -lab
LD_LIBRARY_PATH=. ./main
testabca: libabc.a
g++ -o main -L. -labc
./main
testaba: libab.a
g++ c.cpp -o main -L. -lab
./main
clean:
rm -f *.o *.so *.a main
執行make buildabcso
構建生成libabc.so,執行make testabcso
使用該libabc.so動態連結庫生成main可執行檔案並執行,其他build和test同理。
makefile工作原理:
1、make會在當前目錄下找名字叫“Makefile”或“makefile”的檔案。
2、如果make後有對應指令,會去.PHONY中找到對應指令;否則預設的情況下,它會找第一條指令作為目標指令,在這裡就是即buildabcso。make會把匹配到的指令作為最終的目標檔案。
3、make發現buildabcso不存在,於是需要檢查buildabcso該檔案的依賴,在這裡是libabc.so:
①、如果發現libabc.so已經存在,直接執行,由於buildabcso的行為啥都沒定義,只有個依賴字首libabc.so,所以執行buildabcso時其實啥都沒做,即:
[root@localhost test]# make buildabcso
make: Nothing to be done for `buildabcso'.
②、如果發現libabc.so不存在,或是buildabcso所依賴的libabc.so檔案修改時間要比buildabcso這個檔案新,再檢查libabc.so的依賴,也就是a.o b.o c.o,如果a.o和b.o和c.o三個檔案已存在,才能執行g++ -shared -o $@ $^
生成libabc.so,如果三個.o不存在,繼續查對應依賴生成,一直這樣迭代下去。。。
4、make會一層一層去找檔案的依賴關係,直到最終編譯出第一個目標檔案,如果某個命令執行失敗,則make會停止。
相關文章
- 動態連結庫與靜態連結庫
- 【連結 1】與靜態連結庫連結
- linux下靜態連結庫和動態連結庫的區別有哪些Linux
- cmake 連結動態連結庫
- Linux環境下:程式的連結, 裝載和庫[靜態連結]Linux
- 靜態連結之深度解剖
- 使用js動態新增連結隨機連結JS隨機
- 動態連結的相關結構
- C#資料結構-靜態連結串列C#資料結構
- 動態連結庫的生成和使用(二)
- 動態連結庫(DLL)的建立和使用
- 動態連結的PLT與GOTGo
- 資料結構實驗之連結串列一:順序建立連結串列資料結構
- 關於動態連結串列的理解
- 動態連結串列的建立(程式碼)
- [pwn基礎]動態連結原理
- 資料結構練習題(順序表和單連結串列)C++資料結構C++
- 連結串列,樹,順序表操縱
- 硬連結和軟連結
- ssh連結問題
- C++的動態繫結和靜態繫結C++
- Gazebo新增模型並控制模型運動作為動態障礙物(Ubuntu16.04, Gazebo7.16),附錄動態連結庫和靜態連結庫區別模型Ubuntu
- 動態連結的步驟與實現
- 單連結串列建立連結串列出現問題
- IIS無法訪問動態連結庫DLL的原因
- 連結串列專題——面試中常見的連結串列問題面試
- Linux軟連結和硬連結Linux
- linux硬連結和軟連結Linux
- 用動態連結動態洩露system地址並利用
- “軟連結”和“硬連結”的區別
- 硬連結和軟連結的區別
- jQuery動態修改連結的href屬性值jQuery
- 載入動態連結庫——dlopen dlsym dlclose
- C#呼叫C++動態連結庫C#C++
- Linux(8) —— 硬連結和軟連結Linux
- 軟連結和硬連結詳解
- 【資料結構與演算法學習】線性表(順序表、單連結串列、雙向連結串列、迴圈連結串列)資料結構演算法
- CSS 連結偽類選擇器順序原理CSS