Linux下Makefile中動態鏈接庫和靜態鏈接庫的生成與調用


Linux下Makefile中動態鏈接庫和靜態鏈接庫的生成與調用

  背景:寫這篇博客的原因是:最近在搞嵌入式,需要交叉編譯opencv庫文件,自己寫Makefile,通過arm-linux-g++編譯、鏈接、生成可執行文件,從而實現了移植的過程。平台是Toradex的Apalis TK1,三千多元,買回來我就后悔了,全是英文資料,還各種Bug,遲遲無法上手。早知如此,還不如直接買Nvidia的Jetson TK1呢。

書歸正傳,今天寫一下Makefile文件中,動態鏈接庫和靜態鏈接庫的生成與調用。

一、概念

  動態鏈接庫:是一種不可執行的二進制程序文件,它允許程序共享執行特殊任務所必需的代碼和其他資源。Windows平台上動態鏈接庫的后綴名是”.dll”,Linux平台上的后綴名是“.so”。Linux上動態庫一般是libxxx.so;相對於靜態函數庫,動態函數庫在編譯的時候並沒有被編譯進目標代碼中,你的程序執行到相關函數時才調用該函數庫里的相應函數,因此動態函數庫所產生的可執行文件比較小。由於函數庫沒有被整合進你的程序,而是程序運行時動態的申請並調用,所以程序的運行環境中必須提供相應的庫。動態函數庫的改變並不影響你的程序,所以動態函數庫的升級比較方便。

  靜態鏈接庫:這類庫的名字一般是libxxx.a;利用靜態函數庫編譯成的文件比較大,因為整個函數庫的所有數據都會被整合進目標代碼中,他的優點就顯而易見了,即編譯后的執行程序不需要外部的函數庫支持,因為所有使用的函數都已經被編譯進去了。當然這也會成為他的缺點,因為如果靜態函數庫改變了,那么你的程序必須重新編譯。

Makefile:利用IDE開發調試的人員可能對Makefile不大理解,其實Makefile就是完成了IDE中的編譯、鏈接、生成等工作,並遵循shell腳本中變量定義與調用的規則。

二、編寫Makefile實現編譯與鏈接

1、准備文件

  我們寫一個簡單的工程吧,此工程包含3個文件,分別是main.cpp,func.cpp和func.h。代碼如下:

  1)main.cpp源文件:包含入口函數 int main()。該源文件中添加了“func.h”頭文件,在入口函數中調用func()函數。func函數中在func.cpp中定義,在func.h中原型聲明。

# main.cpp

#include "func.h"

int main()

{

    func();

    return 0;

}

2)func.h頭文件:對void func()函數進行原型聲明。

 

3)func.cpp源文件:對void func()函數的定義或實現。

# func.cpp

#include "func.h"

void func()

{

    std::cout << "Hello World !" << std::endl;

}

2、編寫Makefile文件

1)定義變量

  首先定義SOURCE,OBJS和TARGET變量,用於指代我們項目中的源文件、目標文件和可執行文件。

2) 設置編譯參數

  CC:配置編譯器為g++,

  LIBS:需要調用的鏈接庫(-l開頭,去掉lib和.so。例:對 libopencv_core.so鏈接庫的調用要寫作:-lopencv_core),

  LDFLAGS:鏈接庫的路徑(-L開頭),

  INCLUDE:頭文件的路徑。

3)鏈接生成

  此步驟生成可執行文件(ELF),鏈接需要用到目標文件,由下一步產生

4)編譯

  此步驟生成目標文件(.o)

5)清理

  此步驟清理可執行文件和所有的目標文件

#######################

# Makefile

#######################

# source object target

SOURCE := main.cpp func.cpp

OBJS   := main.o func.o

TARGET := main

# compile and lib parameter

CC      := g++

LIBS    :=

LDFLAGS := -L.

DEFINES :=

INCLUDE := -I.

CFLAGS  :=

CXXFLAGS:=

# link

#$(TARGET):$(OBJS)

    $(CC) -o $@ $^

# compile

#$(OBJS):$(SOURCE)

    $(CC) -c main.cpp -o main.o

    $(CC) -c func.cpp -o func.o

# clean

clean:

    rm -fr *.o

    rm -fr $(TARGET)

上述Makefile是將編譯和鏈接兩個步驟分開寫的,我們同樣可以直接從源文件生成可執行文件,自動進行編譯鏈接等工作。

方法:將上述Makefile中的:

# link

#$(TARGET):$(OBJS)

    $(CC) -o $@ $^

# compile

#$(OBJS):$(SOURCE)

    $(CC) -c main.cpp -o main.o

    $(CC) -c func.cpp -o func.o

修改為:

all:

    $(CC) -o $(TARGET) $(SOURCE)

其他內容,不作變化。

6)編譯、執行、清理

make

./main

make clean

NOTE:在執行make的時候會出現Makefile:19: *** missing separator.  Stop的錯誤,提示沒有分割符,在makefile中規定必須以TAB鍵進行縮進.

執行make clean可以清楚當前目錄下編譯的文件.

2、動態鏈接庫的調用

引言:在第一部分中,我們將main.o和func.o兩個目標文件進行鏈接,便生成了main可執行文件。如果甲方並沒有提供func.cppfunc.o,只是提供了libfunc.so這個鏈接庫,我們如何生成可執行文件呢?下文就是講述如何利用動態庫鏈接生成可執行文件。

Makefile如下:

  1)編譯的時候需要通過INCLUDE指明頭文件的路徑

  2)鏈接的時候需要通過LDFLAGS 和 LIBS指明動態庫的路徑和名稱。[A1] 這里需要注意的是,指明動態庫名稱時需要“掐頭去尾”,例:我們需要用到 libfunc.so庫,LIBS必須定義為 -lfunc 。

/*[A1]

LDFLAGS指明動態庫的路徑;

LIBS指明動態庫的名稱,需注意動態庫名稱需要“掐頭去尾”

*/

3)執行的時候,需要把libfunc.so動態庫拷貝到系統環境變量包含的路徑下(比如/lib/usr/lib,這樣程序在運行時才能調用到動態庫。

#######################

# Makefile

#######################

# target

TARGET := main

 # compile and lib parameter

CC      := g++

LDFLAGS := -L/path/to/libfunc.so

LIBS    := -lfunc

DEFINES :=

INCLUDE := -I/path/to/func.h

CFLAGS  :=

CXXFLAGS:=

 # link

$(TARGET):main.o

    $(CC) -o $@ $^ $(LDFLAGS) $(LIBS)

 #compile

main.o:main.cpp

    $(CC) $(INCLUDE) -c $^crm -fr $(TARGET)

 clean:

  rm -fr *.o

    rm -fr $(TARGET)

四、靜態鏈接庫的生成和調用

1、靜態鏈接庫的生成

引言:仍然利用上文中的main.cpp, func.cpp和func.h文件。下面,我們將func.cpp源文件制作成靜態鏈接庫func.a,然后調用該靜態庫對main.cpp進行編譯鏈接。

 Makefile如下:

注意:AR:配置鏈接器為ar

#######################

# Makefile

######################

# compile and lib parameter

CC      := g++

LIBS    :=

LDFLAGS :=

DEFINES :=

INCLUDE := -I.

CFLAGS  :=

CXXFLAGS:=

 # link parameter

AR  := ar

LIB := func.a

#link

$(LIB):func.o

    $(AR) -r $@ $^

#compile

func.o:func.cpp

    $(CC) -c $^ -o $@

 # clean

clean:

   rm -fr *.o

執行make命令之后,就可以在當前目錄生成func.a的靜態鏈接庫了。

注意:靜態鏈接庫必須“以.a結尾”。

2、靜態鏈接庫的調用

  引言:在第一部分中,我們將main.o和func.o兩個目標文件進行鏈接,便生成了main可執行文件;第二部分,我們將main.o和libfunc.so進行鏈接,也可以生成main可執行文件。如果我們既沒有func.o也沒有func.so,該如何生成可執行文件呢?下文就是講述如何利用靜態庫func.a鏈接生成可執行文件。

Makefile如下:

  1)編譯的時候需要通過INCLUDE指明頭文件的路徑

  2)鏈接的時候需要通過LDFLAGS LIBS指明靜態庫的路徑和名稱。這里不需要像動態庫那樣掐頭去尾,直接寫作func.a即可

  3)執行的時候,不需要拷貝func.a至環境變量包含的路徑,直接執行即可。

#######################

# Makefile

#######################

# target

TARGET := main

# compile and lib parameter

CC      := g++

LDFLAGS := -L.

LIBS    := func.a

DEFINES :=

INCLUDE := -I.

CFLAGS  :=

CXXFLAGS:=

# link

$(TARGET):main.o

    $(CC) -o $@ $^ $(LIBS)

#compile

main.o:main.cpp

    $(CC) -c $^ -o $@

# clean

clean:

    rm -fr *.o

    rm -fr $(TARGET)

 

 

linux ar 命令的使用說明和例子

用途說明  

  創建靜態庫.a文件。用C/C++開發程序時經常用到,但我很少單獨在命令行中使用ar命令,一般寫在makefile中,有時也會在shell腳 本中用到。關於Linux下的庫文件、靜態庫、動態庫以及怎樣創建和使用等相關知識,參見本文后面的相關資料【3】《關於Linux靜態庫和動態庫的分析》。  

  常用參數  格式:ar rcs  libxxx.a xx1.o xx2.o  

  參數r:在庫中插入模塊(替換)。當插入的模塊名已經在庫中存在,則替換同名的模塊。如果若干模塊中有一個模塊在庫中不存在,ar顯示一個錯誤消息,並不替換其他同名模塊。 默認的情況下,新的成員增加在庫的結尾處,可以使用其他任選項來改變增加的位置。

  參數c:創建一個庫。不管庫是否存在,都將創建。  

  參數s:創建目標文件索引,這在創建較大的庫時能加快時間。(補充:如果不需要創建索引,可改成大寫S參數;如果。a文件缺少索引,可以使用ranlib命令添加)  

  格式:ar t libxxx.a  顯示庫文件中有哪些目標文件,只顯示名稱。  

  格式:ar tv libxxx.a  顯示庫文件中有哪些目標文件,顯示文件名、時間、大小等詳細信息。  

  格式:nm -s libxxx.a  顯示庫文件中的索引表。  

  格式:ranlib libxxx.a  為庫文件創建索引表。  

使用示例  

  示例一 在shell腳本中使用Bash代碼OS=`uname -r`  ar rcs libhycu.a.$OS *.o  

  示例二 在makefile中使用 Makefile代碼 $(BIN1): $(BIN1_OBJS) ar rcs $@ $^  

  示例三 創建並使用靜態庫  

  第一步:編輯源文件,test.h test.c main.c。其中main.c文件中包含main函數,作為程序入口;test.c中包含main函數中需要用到的函數。vi test.h test.c main.c[A2]  /*Vi 多個源文件*/

  第二步:將test.c編譯成目標文件。  gcc -c test.c 如果test.c無誤,就會得到test.o這個目標文件。     如果是gcc test.c 則默認生成a.out文件

  第三步:由.o文件創建靜態庫。  ar rcs libtest.a test.o

  第四步:在程序中使用靜態庫。  gcc -o main main.c -L. -ltest[A1]   因為是靜態編譯,生成的執行文件可以獨立於.a文件運行。 /*GCC 命令行詳解 -L 指定庫的路徑 -l 指定需連接的庫名*/

  第五步:執行。  ./main 

 

示例四創建並使用動態庫  

  第一步:編輯源文件,test.h test.c main.c。其中main.c文件中包含main函數,作為程序入口;test.c中包含main函數中需要用到的函數。  vi test.h test.c main.c  

  第二步:將test.c編譯成目標文件。  gcc -c test.c  前面兩步與創建靜態庫一致。

  第三步:由.o文件創建動態庫文件。  gcc -shared -fPIC -o libtest.so test.o 

  第四步:在程序中使用動態庫。  gcc -o main main.c -L. -ltest  當靜態庫和動態庫同名時,gcc命令將優先使用動態庫。  

  第五步:執行。  LD_LIBRARY_PATH=. ./main 


    如果想創建一個動態鏈接庫,可以使用 GCC 的-shared選項。輸入文件可以是源文件、匯編文件或者目標文件。

    -shared該選項指定生成動態連接庫(讓連接器生成T類型的導出符號表,有時候也生成弱連接W類型的導出符號),不用該標志外部程序無法連接。相當於一個可執行文件。

    另外還得結合-fPIC選項。-fPIC 選項作用於編譯階段,告訴編譯器產生與位置無關代碼(Position-Independent Code);這樣一來,產生的代碼中就沒有絕對地址了,全部使用相對地址,所以代碼可以被加載器加載到內存的任意位置,都可以正確的執行。這正是共享庫所要求的,共享庫被加載時,在內存的位置不是固定的。

 轉自:https://blog.csdn.net/u011964923/article/details/73297443

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM