GCC編譯,庫的編譯使用及Makefile


我們一般用: ar -rsv obj.o obj2.o libobj.a 命令來將 obj.o obj2.o 生成一個靜態庫 libobj.a
然后在使用: gcc -L/path/ lobj 來連接靜態庫文件 libobj.a

動態庫的生成:gcc -O -fpic -shared -o obj.so obj.c 命令來將生成一個動態庫 obj.so
然后的鏈接使用:gcc -o main main.c ./obj.so 來連接動態庫文件 obj.so

=======================================================================================

..3.. man as:
NAME
AS - the portable GNU assembler.
編譯:as -gstabs -o helloworld.o helloworld.s
鏈接:ld -o helloworld helloworld.o
執行:./helloworld

=======================================================================================

..4.. man objdump:
NAME
objdump - display information from object files.

反匯編: objdump -S -d helloworld
-S 表示 source ,即反匯編的同時顯示源碼;-d 表示 disassemble 即反匯編。
一、靜態庫
靜態庫文件也成為“文檔文件”,它是一些 .o 文件的集合。
在linux中,使用 ar 維護和管理。
使用庫函數時,需include 對應的 .h 頭文件。
庫文件 libName.a 的庫名是 NAME

在 linux 中靜態庫是以 .a 為后綴的文件,共享庫是以 .so 為后綴的文件。
在windows中靜態庫是以 .lib 為后綴的文件,共享庫是以 .dll 為后綴的文件。

二、庫文件的使用

要用 libtest.so 庫里的函數,需要與libtest.so配套的頭文件
包含頭文件有兩種方法,多用第一種方法
(1)源碼里用 #include xxxxxx 包含頭文件
(2)用 gcc 的 -include 包含頭文件

gcc 參數
-l: 指定庫文件。
-L: 指定搜索位置。
-I: 指定頭文件搜索位置。
查看默認搜索位置:$gcc -print-search-dirs
其中,libraries 是庫文件的搜索列表。
e.g. gcc main.c -L. -lstack -Istack -o main

庫文件:libstack.a

gcc 搜索路徑的優先級

頭文件

※搜尋會從-I開始
※然后找gcc的環境變量 C_INCLUDE_PATH,CPLUS_INCLUDE_PATH,OBJC_INCLUDE_PATH
※再找內定目錄

    /usr/include 

    /usr/local/include
        /usr/lib/gcc-lib/i386-linux/2.95.2/include
       /usr/lib/gcc-lib/i386-linux/2.95.2/include/g++-3
       /usr/lib/gcc-lib/i386-linux/2.95.2/i386-linux/include

庫文件
※先找-L
※再找gcc的環境變量LIBRARY_PATH
※再找內定目錄 /lib:/usr/lib: /usr/local/lib:這是當初compile gcc時寫在程序內的

include "FILE.h" 先搜索當前目錄,后搜索系統頭文件目錄。

include <FILE.h> 只搜索系統頭文件目錄。

linux下 默認搜索位置:
頭文件: /usr/include(Unix)

三、靜態庫的創建
(1)將源文件編譯成目標文件:gcc –c Add.c MakeEmpty.c
(2)生成靜態庫:ar –rc liblist.a Add.o MakeEmpty.o

四、ar命令
ar功能:集合許多文件,成為單一的備存文件。在備存文件中,所有成員文件皆保有原來的屬性與權限

  -d  刪除備存文件中的成員文件。   
  -p  顯示備存文件中的成員文件內容。
  -r  將文件插入備存文件中。 
  -t  顯示備存文件中所包含的文件。 
  -x  自備存文件中取出成員文件。    

通過make,可以方便的維護個人靜態函數庫。

在windows下動態鏈接庫是以.dll后綴的文件,而在Linux中,是以.so作后綴的文件。

動態鏈接庫的好處就是節省內存空間。

1、Linux下創建動態鏈接庫

在使用GCC編譯程序時,只需加上-shared選項即可,這樣生成的執行程序即為動態鏈接庫。

例如有文件:hello.c x.h main.c

編譯:gcc hello.c -fPIC -o libhello.so

其中-fPIC選項的作用是:表示編譯為位置獨立的代碼,不用此選項的話編譯后的代碼是位置相關的,

所以動態載入時是通過代碼拷貝的方式來滿足不同的調用,而不能達到真正的代碼段共享的目的.

將main.c與hello.so動態庫gcc main.c -L. -lhello -o main

一、動態鏈接庫

1.創建hello.so動態庫

#include <stdio.h>

void hello(){

        printf("hello world\n");

}

編譯:gcc -fPIC -shared hello.c -o libhello.so

2.hello.h頭文件

void hello();

3.鏈接動態庫

#include <stdio.h>

#include "hello.h"

int main(){

        printf("call hello()");

        hello();

}

復制代碼

編譯:gcc main.c -L. -lhello -o main這里-L的選項是指定編譯器在搜索動態庫時搜索的路徑,告訴編譯器hello庫的位置。"."意思是當前路徑.

3.編譯成夠后執行./main,會提示:

In function main": main.c.text+0x1d): undefined reference to hello"collect2: ld returned 1 exit status這是因為在鏈接hello動態庫時,編譯器沒有找到。

解決方法:

sudo cp libhello.so /usr/lib/這樣,再次執行就成功輸入:

call hello()

二、靜態庫

文件有:main.c、hello.c、hello.h

1.編譯靜態庫hello.o:

gcc hello.c -o hello.o #這里沒有使用-shared

2.把目標文檔歸檔

ar -r libhello.a hello.o #這里的ar相當於tar的作用,將多個目標打包。程序ar配合參數-r創建一個新庫libhello.a,並將命令行中列出的文件打包入其中。這種方法,如果libhello.a已經存在,將會覆蓋現在文件,否則將新創建。

3.鏈接靜態庫

gcc main.c -lhello -L. -static -o main這里的-static選項是告訴編譯器,hello是靜態庫。

或者:

gcc main.c libhello.a -L. -o main這樣就可以不用加-static

4.執行./main

輸出:call hello()

三、借助自帶的ldd實現程序來分析動態庫搜索情況

ldd main

結果:

linux-gate.so.1 => (0x00efd000)

libhello.so => /usr/lib/libhello.so (0x00f6b000)

libc.so.6 => /lib/libc.so.6 (0x001a5000)

/lib/ld-linux.so.2 (0x00eb8000)

如果目標程序沒有鏈接動態庫,則打印“not a dynamic executable”

  1. 當在同一個目錄下靜態庫和共享庫同名時,共享庫優先
    hello.h頭文件

ifndef HELLO_H

define HELLO_H

void print_hello();

endif

hello.c源文件

include "hello.h"

include <stdio.h>

int main(int argc,char *argv[])
{
printf("hello world!");

測試使用源文件main.c

include "hello.h"

int main(int argc,char argv[])
{
printf_hello();
}
1.1 編譯靜態庫和共享庫
[test@hadoop hello]$ ls
hello.c hello.h main.c
[test@hadoop hello]$ gcc -c hello.c
[test@hadoop hello]$ ar crs libhello.a hello.o
[test@hadoop hello]$ gcc -shared -fPIC -o libhello.so hello.o
[test@hadoop hello]$ ls
hello.c hello.h hello.o libhello.a libhello.so main.c
1.2 使用hello庫編譯 main.c
[test@hadoop hello]$ gcc main.c -o main -L. -lhello -I.
[test@hadoop hello]$ ldd main
linux-gate.so.1 => (0x00a11000)
libhello.so => /home/test/programs/c/hello/libhello.so (0x0024b000)
libc.so.6 => /lib/libc.so.6 (0x0024d000)
/lib/ld-linux.so.2 (0x0011f000)
看的出, gcc選擇的是libhello.so, 即共享庫優先
2. 當同一目錄下存在同名靜態庫和共享庫, 那么怎樣選擇靜態庫那?
[test@hadoop hello]$ ls
hello.c hello.h hello.o libhello.a libhello.so main.c
[test@hadoop hello]$ gcc -static main.c -o main -L. -lhello -I. #使用-static 參數阻止鏈接共享庫
[test@hadoop hello]$ ls
hello.c hello.h hello.o libhello.a libhello.so main main.c
[test@hadoop hello]$ ldd main
not a dynamic executable
看的出, gcc此次選擇的是libhello.a, 即靜態庫
3. 編譯程序中同時包含靜態庫和共享庫
hello.h代碼同上
********
hello.c代碼同上*********

calculate.h頭文件

//calculate.h

ifndef CALCULATE_H

define CALCULATE_H

int add(int a,int b);

endif

calculate.c源文件

//calculate.c

include "calculate.h"

int add(int a,int b)
{
return a+b;
}
新的測試文件main.c

include "hello.h"

include "calculate.h"

include <stdio.h>

int main(int argc,char *argv[])
{
print_hello();
int res=add(1,2);
printf("\n1+2=%d\n",res);
}
3.1 將hello的靜態庫文件libhello.a刪除,保留共享庫文件libhello.so
3.2 編譯calculate成為靜態庫libcalculate.a
[test@hadoop hello]$ gcc -c calculate.c
[test@hadoop hello]$ ar crs libcalculate.a calculate.o
[test@hadoop hello]$ ls
calculate.c calculate.h calculate.o hello.c hello.h libcalculate.a libhello.so main.c

3.3 使用libcalculate.a和libhello.so編譯main
[test@hadoop hello]$ gcc main.c -o main -L. -lhello -lcalculate -I.
[test@hadoop hello]$ ls
calculate.c calculate.h calculate.o hello.c hello.h libcalculate.a libhello.so main main.c
[test@hadoop hello]$ ldd main
linux-gate.so.1 => (0x00769000)
libhello.so => /home/test/programs/c/hello/libhello.so (0x00c6d000)
libc.so.6 => /lib/libc.so.6 (0x0013e000)
/lib/ld-linux.so.2 (0x0011f000)
看得出, 我們成功了:)

前言

我們通常把一些公用函數制作成函數庫,供其他程序使用。函數庫分為靜態庫和動態庫兩種。本文講解如何制作屬於自己的靜態庫。

什么是靜態庫?

通常來說,靜態庫以.a作為后綴,且以lib開頭。類似於libxxx.a。靜態庫在程序編譯時會被連接到目標代碼中,程序運行時將不再需要該靜態庫。

ar命令詳解

Linux ar命令用於創建或者操作靜態庫。

ar命令的參數如下:

參數 意義
-r 將objfile文件插入靜態庫尾或者替換靜態庫中同名文件
-x 從靜態庫文件中抽取文件objfile
-t 打印靜態庫的成員文件列表
-d 從靜態庫中刪除文件objfile
-s 重置靜態庫文件索引
-v 顯示詳細信息
-c 創建靜態庫文件
制作靜態庫

test.c

include <stdio.h>

include "test.h"

void test(){

printf("This is a static library\n");

}

1
2
3
4
5
6
7
8
9
10
test.h

define TEST_H

ifndef TEST_H

void test();

endif

1
2
3
4
5
6
編譯成可重定位文件,即生成.o文件:

在這里插入圖片描述

為了制作成靜態庫,我們需要使用ar命令。

ar -rcs libtest.a test.o #庫名一般以.a為擴展名,以lib開頭
ar -t libtest.a #查看內容
test.o
1
2
3
在這里插入圖片描述輸出信息可以看到,靜態庫以.a作為后綴,且以lib開頭,這時候就制作好了自己的靜態庫了。

制作好了靜態庫,下面來使用它。

靜態庫的使用

編寫一個main.c文件進行測試:

在這里插入圖片描述
main.c

include <stdio.h>

include "test.h"

int main(int argc, char const *argv[])
{
test();

return 0;

}

1
2
3
4
5
6
7
8
9
10
在這里插入圖片描述
出現信息為test未定義引用,原因是test已經編譯成靜態庫。

解決辦法為:
在這里插入圖片描述

靜態庫的代碼在編譯時鏈接到應用程序中,因此編譯時庫文件必須存在,並且需要通過"-L"參數傳遞路徑給編譯器。

鏈接的庫名為libtest.a,在鏈接的時候,去掉開頭的lib和后綴.a,前面再加l,就變成了-ltest,其他庫也是類似。

例如,你如果看到程序鏈接使用-lm,說明它使用了名為libm.a的庫。可以參考這一篇在編譯時時為什么要鏈接 -lm

總結

編譯靜態庫時先使用-rcs選項,再利用ar工具產生,然后把一些文件可重定位文件打包在一起。

https://www.cnblogs.com/stewarttzy/p/3932507.html?utm_source=tuicool
make -f binding.Makefile
make -f Makefile
make -f base64_sse42.target.mk

將持續更新

一,gcc和g++編譯命令基礎

gcc/g++在執行編譯工作的時候,總共需要4步

1.預處理,生成.i的文件[預處理器cpp]
2.將預處理后的文件不轉換成匯編語言,生成文件.s[編譯器egcs]
3.有匯編變為目標代碼(機器代碼)生成.o的文件[匯編器as]
4.連接目標代碼,生成可執行程序[鏈接器ld]

[參數詳解]
-x language filename
   設定文件所使用的語言,使后綴名無效,對以后的多個有效.也就是根
   據約定C語言的后綴名稱是.c的,而C++的后綴名是.C或者.cpp,如果
   你很個性,決定你的C代碼文件的后綴名是.pig 哈哈,那你就要用這
   個參數,這個參數對他后面的文件名都起作用,除非到了下一個參數
   的使用。
   可以使用的參數嗎有下面的這些
     c', objective-c', c-header', c++', cpp-output',      assembler', and `assembler-with-cpp'.
   看到英文,應該可以理解的。
   例子用法:
   gcc -x c hello.pig
  
-x none filename
  關掉上一個選項,也就是讓gcc根據文件名后綴,自動識別文件類型
  例子用法:
  gcc -x c hello.pig -x none hello2.c
  
-c
  只激活預處理,編譯,和匯編,也就是他只把程序做成obj文件
  例子用法:
  gcc -c hello.c
  他將生成.o的obj文件

-S
  只激活預處理和編譯,就是指把文件編譯成為匯編代碼。
  例子用法
  gcc -S hello.c
  他將生成.s的匯編代碼,你可以用文本編輯器察看

-E
  只激活預處理,這個不生成文件,你需要把它重定向到一個輸出文件里
  面.
  例子用法:
  gcc -E hello.c > pianoapan.txt
  gcc -E hello.c | more
  慢慢看吧,一個hello word 也要與處理成800行的代碼

-o
  指定目標名稱,缺省的時候,gcc 編譯出來的文件是a.out,很難聽,如果
  你和我有同感,改掉它,哈哈
  例子用法
  gcc -o hello.exe hello.c (哦,windows用習慣了)
  gcc -o hello.asm -S hello.c

-pipe
  使用管道代替編譯中臨時文件,在使用非gnu匯編工具的時候,可能有些問
  題
  gcc -pipe -o hello.exe hello.c

-ansi
  關閉gnu c中與ansi c不兼容的特性,激活ansi c的專有特性(包括禁止一
  些asm inline typeof關鍵字,以及UNIX,vax等預處理宏,

-fno-asm
  此選項實現ansi選項的功能的一部分,它禁止將asm,inline和typeof用作
  關鍵字。
    
-fno-strict-prototype
  只對g++起作用,使用這個選項,g++將對不帶參數的函數,都認為是沒有顯式
  的對參數的個數和類型說明,而不是沒有參數.
  而gcc無論是否使用這個參數,都將對沒有帶參數的函數,認為城沒有顯式說
  明的類型
  
-fthis-is-varialble
  就是向傳統c++看齊,可以使用this當一般變量使用.
  
-fcond-mismatch
  允許條件表達式的第二和第三參數類型不匹配,表達式的值將為void類型
  
-funsigned-char
-fno-signed-char
-fsigned-char
-fno-unsigned-char
  這四個參數是對char類型進行設置,決定將char類型設置成unsigned char(前
  兩個參數)或者 signed char(后兩個參數)
  
-include file
  包含某個代碼,簡單來說,就是便以某個文件,需要另一個文件的時候,就可以
  用它設定,功能就相當於在代碼中使用#include
  例子用法:
  gcc hello.c -include /root/pianopan.h
  
-imacros file
  將file文件的宏,擴展到gcc/g++的輸入文件,宏定義本身並不出現在輸入文件
  中
  
-Dmacro
  相當於C語言中的#define macro
  
-Dmacro=defn
  相當於C語言中的#define macro=defn
  
-Umacro
  相當於C語言中的#undef macro

-undef
  取消對任何非標准宏的定義
  
-Idir
  在你是用#include"file"的時候,gcc/g++會先在當前目錄查找你所制定的頭
  文件,如果沒有找到,他回到缺省的頭文件目錄找,如果使用-I制定了目錄,他
  回先在你所制定的目錄查找,然后再按常規的順序去找.
  對於#include,gcc/g++會到-I制定的目錄查找,查找不到,然后將到系
  統的缺省的頭文件目錄查找
  
-I-
  就是取消前一個參數的功能,所以一般在-Idir之后使用
  
-idirafter dir
  在-I的目錄里面查找失敗,講到這個目錄里面查找.
  
-iprefix prefix
-iwithprefix dir
  一般一起使用,當-I的目錄查找失敗,會到prefix+dir下查找
  
-nostdinc
  使編譯器不再系統缺省的頭文件目錄里面找頭文件,一般和-I聯合使用,明確
  限定頭文件的位置
  
-nostdin C++
  規定不在g++指定的標准路經中搜索,但仍在其他路徑中搜索,.此選項在創建
  libg++庫使用
  
-C
  在預處理的時候,不刪除注釋信息,一般和-E使用,有時候分析程序,用這個很
  方便的
  
-M
  生成文件關聯的信息。包含目標文件所依賴的所有源代碼
  你可以用gcc -M hello.c來測試一下,很簡單。
  
-MM
  和上面的那個一樣,但是它將忽略由#include造成的依賴關系。
  
-MD
  和-M相同,但是輸出將導入到.d的文件里面
  
-MMD
  和-MM相同,但是輸出將導入到.d的文件里面
  
-Wa,option
  此選項傳遞option給匯編程序;如果option中間有逗號,就將option分成多個選
  項,然后傳遞給會匯編程序
  
-Wl.option
  此選項傳遞option給連接程序;如果option中間有逗號,就將option分成多個選
  項,然后傳遞給會連接程序.
  

-llibrary
  制定編譯的時候使用的庫
  例子用法
  gcc -lcurses hello.c
  使用ncurses庫編譯程序
  
-Ldir
  制定編譯的時候,搜索庫的路徑。比如你自己的庫,可以用它制定目錄,不然
  編譯器將只在標准庫的目錄找。這個dir就是目錄的名稱。
  
-O0
-O1
-O2
-O3
  編譯器的優化選項的4個級別,-O0表示沒有優化,-O1為缺省值,-O3優化級別最
  高  
  
-g
  只是編譯器,在編譯的時候,產生條是信息。
  
-gstabs
  此選項以stabs格式聲稱調試信息,但是不包括gdb調試信息.
  
-gstabs+
  此選項以stabs格式聲稱調試信息,並且包含僅供gdb使用的額外調試信息.
  
-ggdb
  此選項將盡可能的生成gdb的可以使用的調試信息.
-static
  此選項將禁止使用動態庫,所以,編譯出來的東西,一般都很大,也不需要什么
動態連接庫,就可以運行.
-share
  此選項將盡量使用動態庫,所以生成文件比較小,但是需要系統由動態庫.
-traditional
  試圖讓編譯器支持傳統的C語言特性

二,靜態庫,共享庫的編譯與使用

1.靜態庫創建及使用

示例:

復制代碼
  base.h->a.cpp , baes.h->b.cpp

  g++ -c a.cpp
  g++ -c b.cpp
  ar -r libsay.a a.o b.o  # 編譯靜態庫:將所有目標文件打包入庫中

  g++ main.cpp libsay.a -o run
復制代碼

2.共享庫(動態庫)創建及使用

示例:

1   g++ -shared -fpic avg.cpp -o avg.so (編譯動態庫)
2
3   g++ main.cpp avg.so -o run

三,makefile的編寫

1.makefile的規則

target (目標文件或label) : prerequisites (生成target所需要的東西)
command (執行的命令)
解釋:

  最終要生成的目標(run.bin) :中間的目標文件(a1.o a2.o a3.o)

      編譯命令

2.makefile中常用函數表

(一)、字符串處理函數
1.$(subst FROM,TO,TEXT)
函數名稱:字符串替換函數—subst。
函數功能:把字串“TEXT”中的“FROM”字符替換為“TO”。
返回值:替換后的新字符串。

2.$(patsubst PATTERN,REPLACEMENT,TEXT)
函數名稱:模式替換函數—patsubst。
函數功能:搜索“TEXT”中以空格分開的單詞,將否符合模式“TATTERN”替換為“REPLACEMENT”。參數“PATTERN”中可以使用模
式通配符“%”來代表一個單詞中的若干字符。如果參數“REPLACEMENT”中也包含一個“%”,那么“REPLACEMENT”中的“%”將是
“TATTERN”中的那個“%”所代表的字符串。在“TATTERN”和“REPLACEMENT”中,只有第一個“%”被作為模式字符來處理,后續的作為字符本上來處理。在兩個參數中當使用第一個“%”本是字符本身時,可使用反斜杠“\”對它進行轉義處理。
返回值:替換后的新字符串。
函數說明:參數“TEXT”單詞之間的多個空格在處理時被合並為一個空格,但前導和結尾空格忽略。

3.$(strip STRINT)
函數名稱:去空格函數—strip。
函數功能:去掉字串(若干單詞,使用若干空字符分割)“STRINT”開頭和結尾的空字符,並將其中多個連續空字符合並為一個空字符。
返回值:無前導和結尾空字符、使用單一空格分割的多單詞字符串。
函數說明:空字符包括空格、[Tab]等不可顯示字符。

4.$(findstring FIND,IN)
函數名稱:查找字符串函數—findstring。
函數功能:搜索字串“IN”,查找“FIND”字串。
返回值:如果在“IN”之中存在“FIND”,則返回“FIND”,否則返回空。
函數說明:字串“IN”之中可以包含空格、[Tab]。搜索需要是嚴格的文本匹配。

5.$(filter PATTERN…,TEXT)
函數名稱:過濾函數—filter。
函數功能:過濾掉字串“TEXT”中所有不符合模式“PATTERN”的單詞,保留所有符合此模式的單詞。可以使用多個模式。模式中一般需要包含模式字符“%”。存在多個模式時,模式表達式之間使用空格分割。
返回值:空格分割的“TEXT”字串中所有符合模式“PATTERN”的字串。
函數說明:“filter”函數可以用來去除一個變量中的某些字符串,我們下邊的例子中就是用到了此函數。

6.$(filter-out PATTERN...,TEXT)
函數名稱:反過濾函數—filter-out。
函數功能:和“filter”函數實現的功能相反。過濾掉字串“TEXT”中所有符合模式“PATTERN”的單詞,保留所有不符合此模式的單詞。可以有多個模式。存在多個模式時,模式表達式之間使用空格分割。。
返回值:空格分割的“TEXT”字串中所有不符合模式“PATTERN”的字串。
函數說明:“filter-out”函數也可以用來去除一個變量中的某些字符串,(實現和“filter”函數相反)。

7.$(sort LIST)
函數名稱:排序函數—sort。
函數功能:給字串“LIST”中的單詞以首字母為准進行排序(升序),並取掉重復的單詞。
返回值:空格分割的沒有重復單詞的字串。
函數說明:兩個功能,排序和去字串中的重復單詞。可以單獨使用其中一個功能。

8.$(word N,TEXT)
函數名稱:取單詞函數—word。
函數功能:取字串“TEXT”中第“N”個單詞(“N”的值從1開始)。
返回值:返回字串“TEXT”中第“N”個單詞。
函數說明:如果“N”值大於字串“TEXT”中單詞的數目,返回空字符串。如果“N”為0,出錯!

9.$(wordlist S,E,TEXT)
函數名稱:取字串函數—wordlist。
函數功能:從字串“TEXT”中取出從“S”開始到“E”的單詞串。“S”和“E”表示單詞在字串中位置的數字。
返回值:字串“TEXT”中從第“S”到“E”(包括“E”)的單詞字串。
函數說明:“S”和“E”都是從1開始的數字。
當“S”比“TEXT”中的字數大時,返回空。如果“E”大於“TEXT”字數,返回從“S”開始,到“TEXT”結束的單詞串。如果“S”大於“E”,返回空。

10.$(words TEXT)
函數名稱:統計單詞數目函數—words。
函數功能:字算字串“TEXT”中單詞的數目。
返回值:“TEXT”字串中的單詞數。

11.$(firstword NAMES…)
函數名稱:取首單詞函數—firstword。
函數功能:取字串“NAMES…”中的第一個單詞。
返回值:字串“NAMES…”的第一個單詞。
函數說明:“NAMES”被認為是使用空格分割的多個單詞(名字)的序列。函數忽略“NAMES…”中除第一個單詞以外的所有的單詞。

(二)、文件名處理函數
1.$(dir NAMES…)
函數名稱:取目錄函數—dir。
函數功能:從文件名序列“NAMES…”中取出各個文件名目錄部分。文件名的目錄部分就是包含在文件名中的最后一個斜線(“/”)(包括斜線)之前的部分。
返回值:空格分割的文件名序列“NAMES…”中每一個文件的目錄部分。
函數說明:如果文件名中沒有斜線,認為此文件為當前目錄(“./”)下的文件。

2.$(notdir NAMES…)
函數名稱:取文件名函數——notdir。
函數功能:從文件名序列“NAMES…”中取出非目錄部分。目錄部分是指最后一個斜線(“/”)(包括斜線)之前的部分。刪除所有文件名中的目錄部分,只保留非目錄部分。
返回值:文件名序列“NAMES…”中每一個文件的非目錄部分。
函數說明:如果“NAMES…”中存在不包含斜線的文件名,則不改變這個文件名。以反斜線結尾的文件名,是用空串代替,因此當“NAMES…”中存在多個這樣的文件名時,返回結果中分割各個文件名的空格數目將不確定!這是此函數的一個缺陷。

3.$(suffix NAMES…)
函數名稱:取后綴函數—suffix。
函數功能:從文件名序列“NAMES…”中取出各個文件名的后綴。后綴是文件名中最后一個以點“.”開始的(包含點號)部分,如果文件名中不包含一個點號,則為空。
返回值:以空格分割的文件名序列“NAMES…”中每一個文件的后綴序列。
函數說明:“NAMES…”是多個文件名時,返回值是多個以空格分割的單詞序列。如果文件名沒有后綴部分,則返回空。

4.$(basename NAMES…)
函數名稱:取前綴函數—basename。
函數功能:從文件名序列“NAMES…”中取出各個文件名的前綴部分(點號之后的部分)。前綴部分指的是文件名中最后一個點號之前的部分。
返回值:空格分割的文件名序列“NAMES…”中各個文件的前綴序列。如果文件沒有前綴,則返回空字串。
函數說明:如果“NAMES…”中包含沒有后綴的文件名,此文件名不改變。如果一個文件名中存在多個點號,則返回值為此文件名的最后一個點號之前的文件名部分。

5.$(addsuffix SUFFIX,NAMES…)
函數名稱:加后綴函數—addsuffix。
函數功能:為“NAMES…”中的每一個文件名添加后綴“SUFFIX”。參數“NAMES…”為空格分割的文件名序列,將“SUFFIX”追加到此序列的每一個文件名的末尾。
返回值:以單空格分割的添加了后綴“SUFFIX”的文件名序列。

6.$(addprefix PREFIX,NAMES…)
函數名稱:加前綴函數—addprefix。
函數功能:為“NAMES…”中的每一個文件名添加前綴“PREFIX”。參數“NAMES…”是空格分割的文件名序列,將“SUFFIX”添加到此序列的每一個文件名之前。
返回值:以單空格分割的添加了前綴“PREFIX”的文件名序列。

7.$(join LIST1,LIST2)
函數名稱:單詞連接函數——join。
函數功能:將字串“LIST1”和字串“LIST2”各單詞進行對應連接。就是將“LIST2”中的第一個單詞追加“LIST1”第一個單詞字后合並為一個單詞;將“LIST2”中的第二個單詞追加到“LIST1”的第一個單詞之后並合並為一個單詞,……依次列推。
返回值:單空格分割的合並后的字(文件名)序列。
函數說明:如果“LIST1”和“LIST2”中的字數目不一致時,兩者中多余部分將被作為返回序列的一部分。

8.$(wildcard PATTERN)
函數名稱:獲取匹配模式文件名函數—wildcard
函數功能:列出當前目錄下所有符合模式“PATTERN”格式的文件名。
返回值:空格分割的、存在當前目錄下的所有符合模式“PATTERN”的文件名。
函數說明:“PATTERN”使用shell可識別的通配符,包括“?”(單字符)、“*”(多字符)等。

(三)、其它函數
1.$(foreach VAR,LIST,TEXT)
函數功能:函數“foreach”不同於其它函數。它是一個循環函數。類似於Linux的shell中的循環(for語句)。這個函數的工作過程是這樣
的:如果必要(存在變量或者函數的引用),首先展開變量“VAR”和“LIST”;而表達式“TEXT”中的變量引用不被展開。執行時把“LIST”中使
用空格分割的單詞依次取出賦值給變量“VAR”,然后執行“TEXT”表達式。重復直到“LIST”的最后一個單詞(為空時結束)。“TEXT”中的變量
或者函數引用在執行時才被展開,因此如果在“TEXT”中存在對“VAR”的引用,那么“VAR”的值在每一次展開式將會到的不同的值。
返回值:空格分割的多次表達式“TEXT”的計算的結果。

2.$(if CONDITION,THEN-PART[,ELSE-PART])
函數功能:函數“if”提供了一個在函數上下文中實現條件判斷的功能。就像make所支持的條件語句—ifeq。第一個參數“CONDITION”,在函
數執行時忽略其前導和結尾空字符並展開。“CONDITION”的展開結果非空,則條件為真,就將第二個參數“THEN_PATR”作為函數的計算表達
式,函數的返回值就是第二表達式的計算結果;“CONDITION”的展開結果為空,將第三個參數
“ELSE-PART”作為函數的表達式,返回結果為第三個表達式的計算結果。
返回值:根據條件決定函數的返回值是第一個或者第二個參數表達式的計算結果。當不存在第三個參數“ELSE-PART”,並且“CONDITION”展開為空,函數返回空。
函數說明:函數的條件表達式“CONDITION”決定了,函數的返回值只能是“THEN-PART”或者“ELSE-PART”兩個之一的計算結果。

3.\((call VARIABLE,PARAM,PARAM,...) 函數功能:“call”函數是唯一一個可以創建定制參數化的函數的引用函數。我們可以將一個變量定義為一個復雜的表達式,用“call”函數根據不同的參數對它進行展開來獲得不同的結果。 在執行時,將它的參數“PARAM”依次賦值給臨時變量“\)(1)”、“\((2)”(這些臨時變量定義在“VARIABLE”的值中,參考下邊的例 子)…… call函數對參數的數目沒有限制,也可以沒有參數值,沒有參數值的“call”沒有任何實際存在的意義。執行時變量“VARIABLE”被展開為在函數 上下文有效的臨時變量,變量定義中的“\)(1)”作為第一個參數,並將函數參數值中的第一個參數賦值給它;變量中的“\((2)”一樣被賦值為函數的第二個 參數值;依此類推(變量\)(0)代表變量“VARIABLE”本身)。之后對變量“VARIABLE” 表達式的計算值。
返回值:參數值“PARAM”依次替換“\((1)”、“\)(2)”…… 之后變量“VARIABLE”定義的表達式的計算值。
函數說明:1.
函數中“VARIBLE”是一個變量名,而不是對變量的引用。因此,通常“call”函數中的“VARIABLE”中不包含“$”(當然,除了此變量名是
一個計算的變量名)。2.
當變量“VARIBLE”是一個make內嵌的函數名時(如“if”、“foreach”、“strip”等),對“PARAM”參數的使用需要注意,因
為不合適或者不正確的參數將會導致函數的返回值難以預料。3. 函數中多個“PARAM”之間使用逗號分割。4.
變量“VARIABLE”在定義時不能定義為直接展開式!只能定義為遞歸展開式。

4.value函數
\((value VARIABLE) 函數功能:不對變量“VARIBLE”進行任何展開操作,直接返回變量“VARIBALE”代表的值。這里“VARIABLE”是一個變量名,一般不包含“\)”(當然,除了計算的變量名),
返回值:變量“VARIBALE”所定義文本值(不展開其中的變量或者函數應用)。

5.eval函數
函數功能:函數“eval”是一個比較特殊的函數。使用它我們可以在我們的Makefile中構造一個可變的規則結構關系(依賴關系鏈),其中可以使用其
它變量和函數。函數“eval”對它的參數進行展開,展開的結果作為Makefile的一部分,make可以對展開內容進行語法解析。展開的結果可以包含
一個新變量、目標、隱含規則或者是明確規則等。也就是說此函數的功能主要是:根據其參數的關系、結構,對它們進行替換展開。
返回值:函數“eval”的返回值時空,也可以說沒有返回值。
函數說明:“eval”函數執行時會對它的參數進行兩次展開。第一次展開過程發是由函數本身完成的,第二次是函數展開后的結果被作為Makefile內容
時由make解析時展開的。明確這一點對於使用“eval”函數非常重要。在理解了函數“eval”二次展開的過程后。實際使用時,當函數的展開結果中存
在引用(格式為:\((x))時,那么在函數的參數中應該使用“\)\(”來代替“\)”。因為這一點,所以通常它的參數中會使用函數“value”來取一個變量
的文本值。

6.origin函數
\((origin VARIABLE) 函數功能:函數“origin”查詢參數“VARIABLE”(通常是一個變量名)的出處。 函數說明:“VARIABLE”是一個變量名而不是一個變量的引用。因此通常它不包含“\)”(當然,計算的變量名例外)。
返回值:返回“VARIABLE”的定義方式。用字符串表示。
. undefined
變量“VARIABLE”沒有被定義。
. default
變量“VARIABLE”是一個默認定義(內嵌變量)。如“CC”、“MAKE”、“RM”等變量。如果在Makefile中重新定義這些變量,函數返回值將相應發生變化。
. environment
變量“VARIABLE”是一個系統環境變量,並且make沒有使用命令行選項“-e”(Makefile中不存在同名的變量定義,此變量沒有被替代)。
. environment override
變量“VARIABLE”是一個系統環境變量,並且make使用了命令行選項“-e”。Makefile中存在一個同名的變量定義,使用“make -e”時環境變量值替代了文件中的變量定義。
. file
變量“VARIABLE”在某一個makefile文件中定義。
. command line
變量“VARIABLE”在命令行中定義。
. override
變量“VARIABLE”在makefile文件中定義並使用“override”指示符聲明。
. automatic
變量“VARIABLE”是自動化變量。

7.shell函數
不同於除“wildcard”函數之外的其它函數。make可以使用它來和外部通信。
函數功能:函數“shell”所實現的功能和shell中的引用(``)相同。實現了命令的擴展。意味着需要一個shell
命令作為它的參數,而返回的結果是此命令在shell中的執行結果。make僅僅對它的回返結果進行處理;make將函數的返回結果中的所有換行符
(“\n”)或者一對“\n\r”替換為單空格;並去掉末尾的回車符號(“\n”)或者“\n\r”。函數展開式時,它所調用的命令(它的參數)得到執
行。除了對它的引用出現在規則的命令行中和遞歸的變量定義引用以外,其它決大多數情況下,make在讀取Makefile時函數shell就被擴展。
返回值:函數“shell”的參數在shell中的執行結果。
函數說明:函數本身的返回值是其參數的執行結果,沒有進行任何處理。對結果的處理是由make進行的。當對函數的引用出現在規則的命令行中,命令行在執行
時函數引用才被展開。展開過程函數參數的執行時在另外一個shell進程中完成的,因此對於出現在規則命令行的多級“shell”函數引用需要謹慎處理,
否則會影響效率(每一級的“shell”函數的參數都會有各自的shell進程)。

8.error 函數
$(error TEXT…)
函數功能:產生致命錯誤,並提示“TEXT…”信息給用戶,之后退出make的執行。需要說明的是:“error”函數是在函數展開式(函數被調用時)才
提示信息並結束make進程。因此如果函數出現在命令中或者一個遞歸的變量定義中時,在讀取Makefile時不會出現錯誤。而只有包含
“error”函數引用的命令被執行,或者定義中引用此函數的遞歸變量被展開時,才會提示致命信息“TEXT…”同時make退出執行。
返回值:空字符
函數說明:“error”函數一般不出現在直接展開式的變量定義中,否則在make讀取Makefile時將會提示致命錯誤。

  1. warning 函數
    $(warning TEXT…)
    函數功能:函數“warning”類似於函數“error”,區別在於它不會導致致命錯誤(make不退出),而只是提示“TEXT…”,make的執行過程繼續。
    返回值:空字符
    函數說明:用法和“error”類似,展開過程相同。

3.makefile模板

復制代碼
GXX=g++
GCC=gcc
CXXFLAG=-fopenmp -march=core2 -O3 -fomit-frame-pointer -pipe
CFLAG=\((CXXFLAG) DIR_INC=./include DIR_SRC=./src DIR_OBJ=./obj DIR_BIN=./bin DIR_LIB=/home/your_dir/env/lib_dir1/ LDFLAG=-Wl,--rpath=\)(DIR_LIB)/lib,--rpath=/home/your_dir/env/lib,--rpath=$(PWD)/lib,-O3 -fopenmp

指定include的路徑

INCLUDE=-I$(DIR_INC) -I./lib/include -I \((DIR_LIB)/include -I\)(DIR_LIB)/include/h_file_dir

指定lib的路徑

LIBS=-L $(DIR_LIB)/lib -lxxx1 -lxxx2 -lxxx3 -L/home/your_dir/env/lib -L $(PWD)/lib -lxxx4

TARGET=\((DIR_BIN)/exe_name DEF= -DLINUX #-DPATH #-DDEBUG SRC=\)(wildcard \({DIR_SRC}/*.cpp) OBJS=\)(SRC:\((DIR_SRC)/%.cpp=\)(DIR_OBJ)/%.o)

\((TARGET):\)(OBJS)
$(GXX) \((GXXFLAG)\)(LDFLAG) -o $@ $(OBJS) $(LIBS)

\((TARGET):\)(OBJS)

ar cru $(TARGET) $(OBJS)

\((DIR_OBJ)/%.o:\)(DIR_SRC)/%.cpp
$(GXX) -o $@ -c $< $(CXXFLAG) $(INCLUDE) $(DEF)

clean:
rm -fr $(OBJS) $(TARGET);
復制代碼

說明:(1)利用自動化變量“$@”,這個變量表示着目前規則中所有的目標的集合。

    (2)\(<是第一個"prerequisite", 不知道怎么翻譯,就是target:后面的列表里的第一個. (Often the prerequisites include header files as well, which you do not want to mention in the recipe. The automatic variable `\)<' is just the first prerequisite:)
  示例:

 VPATH = src:../headers
 foo.o : foo.c defs.h hack.h
         cc -c $(CFLAGS) $< -o $@

  這里$<就是foo.c

四,其他問題

1.rpath鏈接選項

SDK庫的名稱為liba.so, 其依賴於libb.so和libc.so,那么在編譯應用程序的時候使用以下命令:

+++++++++++++++++++++++++++++++++++++++++++++

gcc -o test test.c -I. -L. -la -lb -lc

+++++++++++++++++++++++++++++++++++++++++++++

將SDK庫即liba.so交給其他的開發人員,其他的開發人員可不想編譯的時候,顯示的鏈接liba.so所依賴的庫。他們更願意編譯的時候,只顯示地鏈接liba.so。

rpath鏈接選項主要有兩個功能:

(1)程序運行時,優先到rpath指定的目錄去尋找依賴庫

(2)程序鏈接時,在指定的目錄中,隱式的鏈接那些動態庫所需要的鏈接庫。

往往我們都熟知第一個功能,忽略第二個功能。而第二個功能正是現在所需要的。

我們將liba.so,libb.so 和libc.so拷貝的同一個目錄中,然后利用rpath鏈接應用程序,這樣編譯便不需要顯示的去鏈接liba.so所依賴的庫了。

+++++++++++++++++++++++++++++++++++++++++++++

gcc -o test test.c -I. -L. -la -Wl,-rpath=.

+++++++++++++++++++++++++++++++++++++++++++++

Linux命令之ar - 創建靜態庫.a文件和動態庫.so
 轉自:http://blog.csdn.net/eastonwoo/article/details/8241693

用途說明
  創建靜態庫。a文件。用C/C++開發程序時經常用到,但我很少單獨在命令行中使用ar命令,一般寫在makefile中,有時也會在shell腳 本中用到。關於Linux下的庫文件、靜態庫、動態庫以及怎樣創建和使用等相關知識,參見本文后面的相關資料【3】《關於Linux靜態庫和動態庫的分析》。
  常用參數
  格式:ar rcs libxxx.a xx1.o xx2.o
  參數r:在庫中插入模塊(替換)。當插入的模塊名已經在庫中存在,則替換同名的模塊。如果若干模塊中有一個模塊在庫中不存在,ar顯示一個錯誤消息,並不替換其他同名模塊。默認的情況下,新的成員增加在庫的結尾處,可以使用其他任選項來改變增加的位置。【1】
  參數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
  第二步:將test.c編譯成目標文件。
  gcc -c test.c
  如果test.c無誤,就會得到test.o這個目標文件。
  第三步:由。o文件創建靜態庫。
  ar rcs libtest.a test.o
  第四步:在程序中使用靜態庫。
  gcc -o main main.c -L. -ltest
  因為是靜態編譯,生成的執行文件可以獨立於。a文件運行。
  第五步:執行。
  ./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

     動態庫除了在默認的的路徑/lib 和 /usr/lib 下還可以通過"在配置文件/etc/ld.so.conf中指定動態庫搜索路徑",“環境變量的方式”和“在編譯目標代碼時指定該程序的動態庫搜索路徑(通過gcc 的參數"-Wl,-rpath,"指定)”三種,他們的優先級也不一樣;

     下面介紹第二種:

例如:

export LD_LIBRARY_PATH=.

但本文為了舉例方便,使用另一種設置環境變量的方法,既在命令前加環境變量設置,該環境變量只對該命令有效,當該命令執行完成后,該環境變量就無效了。如下述命令:

LD_LIBRARY_PATH=. ./main

  示例五 查看靜態庫中的文件
  [root@node56 lib]# ar -t libhycu.a
  base64.c.o
  binbuf.c.o
  cache.c.o
  chunk.c.o
  codec_a.c.o
  …
  xort.c.o
  [root@node56 lib]#
  [root@node56 lib]# ar -tv libhycu.a
  rw-r--r-- 0/0 7220 Jul 29 19:18 2011 base64.c.o
  rw-r--r-- 0/0 2752 Jul 29 19:18 2011 binbuf.c.o
  rw-r--r-- 0/0 19768 Jul 29 19:18 2011 cache.c.o
  …
  rw-r--r-- 0/0 4580 Jul 29 19:18 2011 xort.c.o
  [root@node56 lib]#
  [root@node56 lib]# nm -s libhycu.a | less
  Archive index:
  Base64Enc in base64.c.o
  GetBase64Value in base64.c.o
  Base64Dec in base64.c.o
  encode64 in base64.c.o
  decode64 in base64.c.o
  check64 in base64.c.o
  test64 in base64.c.o
  …
  chunk_alloc in chunk.c.o
  [root@node56 lib]#

本篇文章來源於 黑基網-中國最大的網絡安全站點 原文鏈接:http://www.hackbase.com/tech/2011-08-09/64867.html

Linux下gcc編譯生成動態鏈接庫.so文件並調用它
動態庫
.so在linux下用c和c++編程時經常會碰到,最近在網站找了幾篇文章介紹動態庫的編譯和鏈接,總算搞懂了這個之前一直不太了解得東東,這里做個筆記,也為其它正為動態庫鏈接庫而苦惱的兄弟們提供一點幫助。
1、動態庫的編譯

下面通過一個例子來介紹如何生成一個動態庫。這里有一個頭文件:so_test.h,三個.c文件:test_a.c、test_b.c、test_c.c,我們將這幾個文件編譯成一個動態庫:libtest.so。

//so_test.h:

include "stdio.h"

void test_a();
void test_b();
void test_c();

//test_a.c:

include "so_test.h"

void test_a()
{
printf("this is in test_a...\n");
}

//test_b.c:

include "so_test.h"

void test_b()
{
printf("this is in test_b...\n");
}

//test_c.c:

include "so_test.h"

void test_c()
{
printf("this is in test_c...\n");
}
將這幾個文件編譯成一個動態庫:libtest.so
$ gcc test_a.c test_b.c test_c.c -fPIC -shared -o libtest.so

2、動態庫的鏈接
在1、中,我們已經成功生成了一個自己的動態鏈接庫libtest.so,下面我們通過一個程序來調用這個庫里的函數。程序的源文件為:test.c。

test.c:

include "so_test.h"

int main()
{
test_a();
test_b();
test_c();
return 0;
}
將test.c與動態庫libtest.so鏈接生成執行文件test:
$ gcc test.c -L. -ltest -o test
測試是否動態連接,如果列出libtest.so,那么應該是連接正常了
$ ldd test
執行test,可以看到它是如何調用動態庫中的函數的。
3、編譯參數解析
最主要的是GCC命令行的一個選項:
-shared該選項指定生成動態連接庫(讓連接器生成T類型的導出符號表,有時候也生成弱連接W類型的導出符號),不用該標志外部程序無法連接。相當於一個可執行文件

-fPIC:表示編譯為位置獨立的代碼,不用此選項的話編譯后的代碼是位置相關的所以動態載入時是通過代碼拷貝的方式來滿足不同進程的需要,而不能達到真正代碼段共享的目的。

-L.:表示要連接的庫在當前目錄中

-ltest:編譯器查找動態連接庫時有隱含的命名規則,即在給出的名字前面加上lib,后面加上.so來確定庫的名稱

LD_LIBRARY_PATH:這個環境變量指示動態連接器可以裝載動態庫的路徑。

當然如果有root權限的話,可以修改/etc/ld.so.conf文件,然后調用 /sbin/ldconfig來達到同樣的目的,不過如果沒有root權限,那么只能采用輸出LD_LIBRARY_PATH的方法了。

4、注意

調用動態庫的時候有幾個問題會經常碰到,有時,明明已經將庫的頭文件所在目錄 通過 “-I” include進來了,庫所在文件通過 “-L”參數引導,並指定了“-l”的庫名,但通過ldd命令察看時,就是死活找不到你指定鏈接的so文件,這時你要作的就是通過修改 LD_LIBRARY_PATH或者/etc/ld.so.conf文件來指定動態庫的目錄。通常這樣做就可以解決庫無法鏈接的問題了。

在linux下可以用export命令來設置這個值,在linux終端下輸入:
export LD_LIBRARY_PATH=/opt/au1200_rm/build_tools/bin: \(LD_LIBRARY_PATH:    然后再輸入:export    即會顯示是否設置正確    export方式在重啟后失效,所以也可以用 vim /etc/bashrc ,修改其中的LD_LIBRARY_PATH變量。    例如:LD_LIBRARY_PATH=\)LD_LIBRARY_PATH:/opt/au1200_rm/build_tools/bin。

gcc 生成 .a靜態庫和 .so動態庫
Posted on 2012-04-13 22:30 網名還沒想好 閱讀(5911) 評論(1) 編輯 收藏
我們通常把一些公用函數制作成函數庫,供其它程序使用。函數庫分為靜態庫和動態庫兩
種。靜態庫在程序編譯時會被連接到目標代碼中,程序運行時將不再需要該靜態庫。動態
庫在程序編譯時並不會被連接到目標代碼中,而是在程序運行是才被載入,因此在程序運
行時還需要動態庫存在。本文主要通過舉例來說明在Linux中如何創建靜態庫和動態庫,以
及使用它們。

在創建函數庫前,我們先來准備舉例用的源程序,並將函數庫的源程序編譯成.o文件。

第1步:編輯得到舉例的程序--hello.h、hello.c和main.c;

hello.c(見程序2)是函數庫的源程序,其中包含公用函數hello,該函數將在屏幕上輸出"
Hello XXX!"。hello.h(見程序1)為該函數庫的頭文件。main.c(見程序3)為測試庫文件的
主程序,在主程序中調用了公用函數hello。

程序1: hello.h

ifndef HELLO_H

define HELLO_H

void hello(const char *name);

endif //HELLO_H

程序2: hello.c

include <stdio.h>

void hello(const char *name)
{
printf("Hello %s!\n", name);
}

程序3: main.c

include "hello.h"

int main()
{
hello("everyone");
return 0;
}

第2步:將hello.c編譯成.o文件;

無論靜態庫,還是動態庫,都是由.o文件創建的。因此,我們必須將源程序hello.c通過g
cc先編譯成.o文件。

在系統提示符下鍵入以下命令得到hello.o文件。

gcc -c hello.c

我們運行ls命令看看是否生存了hello.o文件。

ls

hello.c hello.h hello.o main.c

在ls命令結果中,我們看到了hello.o文件,本步操作完成。

下面我們先來看看如何創建靜態庫,以及使用它。

第3步:由.o文件創建靜態庫;

靜態庫文件名的命名規范是以lib為前綴,緊接着跟靜態庫名,擴展名為.a。例如:我們將
創建的靜態庫名為myhello,則靜態庫文件名就是libmyhello.a。在創建和使用靜態庫時,
需要注意這點。創建靜態庫用ar命令。

在系統提示符下鍵入以下命令將創建靜態庫文件libmyhello.a。

ar -crv libmyhello.a hello.o

我們同樣運行ls命令查看結果:

ls

hello.c hello.h hello.o libmyhello.a main.c

ls命令結果中有libmyhello.a。

第4步:在程序中使用靜態庫;

靜態庫制作完了,如何使用它內部的函數呢?只需要在使用到這些公用函數的源程序中包
含這些公用函數的原型聲明,然后在用gcc命令生成目標文件時指明靜態庫名,gcc將會從
靜態庫中將公用函數連接到目標文件中。注意,gcc會在靜態庫名前加上前綴lib,然后追
加擴展名.a得到的靜態庫文件名來查找靜態庫文件。

在程序3:main.c中,我們包含了靜態庫的頭文件hello.h,然后在主程序main中直接調用公
用函數hello。下面先生成目標程序hello,然后運行hello程序看看結果如何。

法一 # gcc -o hello main.c -L. –lmyhello,自定義的庫時,main.c還可放在-L.和 –lmyhello之間,但是不能放在它倆之后,否則會提示myhello沒定義,但是是系統的庫時,如g++ -o main(-L/usr/lib) -lpthread main.cpp就不出錯。

法二 #gcc main.c libmyhello.a -o hello

法三:先生成main.o:gcc -c main.c ,再生成可執行文件:gcc -o hello main.o libmyhello.a,動態庫連接時也可以這樣做。

./hello

Hello everyone!

我們刪除靜態庫文件試試公用函數hello是否真的連接到目標文件 hello中了。

rm libmyhello.a

rm: remove regular file `libmyhello.a'? y

./hello

Hello everyone!

程序照常運行,靜態庫中的公用函數已經連接到目標文件中了。

我們繼續看看如何在Linux中創建動態庫。我們還是從.o文件開始。

第5步:由.o文件創建動態庫文件;

動態庫文件名命名規范和靜態庫文件名命名規范類似,也是在動態庫名增加前綴lib,但其
文件擴展名為.so。例如:我們將創建的動態庫名為myhello,則動態庫文件名就是libmyh
ello.so。用gcc來創建動態庫。

在系統提示符下鍵入以下命令得到動態庫文件libmyhello.so。

gcc -shared -fPCI -o libmyhello.so hello.o (-o不可少)

我們照樣使用ls命令看看動態庫文件是否生成。

ls

hello.c hello.h hello.o libmyhello.so main.c

第6步:在程序中使用動態庫;

在程序中使用動態庫和使用靜態庫完全一樣,也是在使用到這些公用函數的源程序中包含
這些公用函數的原型聲明,然后在用gcc命令生成目標文件時指明動態庫名進行編譯。我們
先運行gcc命令生成目標文件,再運行它看看結果。

gcc -o hello main.c -L. -lmyhello

(或 #gcc main.c libmyhello.so -o hello 不會出錯(沒有libmyhello.so的話,會出錯),但是接下來./hello 會提示出錯,因為雖然連接時用的是當前目錄的動態庫,但是運行時,是到/usr/lib中找庫文件的,將文件libmyhello.so復制到目錄 /usr/lib中就OK了)

./hello

./hello: error while loading shared libraries: libmyhello.so: cannot open shar
ed object file: No such file or directory

哦!出錯了。快看看錯誤提示,原來是找不到動態庫文件libmyhello.so。程序在運行時,
會在/usr/lib和/lib等目錄中查找需要的動態庫文件。若找到,則載入動態庫,否則將提
示類似上述錯誤而終止程序運行。我們將文件libmyhello.so復制到目錄/usr/lib中,再試
試。

mv libmyhello.so /usr/lib

./hello

Hello everyone!

成功了。這也進一步說明了動態庫在程序運行時是需要的。

我們回過頭看看,發現使用靜態庫和使用動態庫編譯成目標程序使用的gcc命令完全一樣,
那當靜態庫和動態庫同名時,gcc命令會使用哪個庫文件呢?抱着對問題必究到底的心情,
來試試看。

先刪除除.c和.h外的所有文件,恢復成我們剛剛編輯完舉例程序狀態。

rm -f hello hello.o /usr/lib/libmyhello.so

ls

hello.c hello.h main.c

在來創建靜態庫文件libmyhello.a和動態庫文件libmyhello.so。

gcc -c hello.c

ar -cr libmyhello.a hello.o (或-cvr )

gcc -shared -fPCI -o libmyhello.so hello.o

ls

hello.c hello.h hello.o libmyhello.a libmyhello.so main.c

通過上述最后一條ls命令,可以發現靜態庫文件libmyhello.a和動態庫文件libmyhello.s
o都已經生成,並都在當前目錄中。然后,我們運行gcc命令來使用函數庫myhello生成目標
文件hello,並運行程序 hello。

gcc -o hello main.c -L. –lmyhello (動態庫和靜態庫同時存在時,優先使用動態庫, 當然,直接#gcc main.c libmyhello.a -o hello的話,就是指定為靜態庫了)

./hello

./hello: error while loading shared libraries: libmyhello.so: cannot open shar
ed object file: No such file or directory

從程序hello運行的結果中很容易知道,當靜態庫和動態庫同名時,gcc命令將優先使用動態庫,默認去連/usr/lib和/lib等目錄中的動態庫,將文件libmyhello.so復制到目錄/usr/lib中即可。

Note:
編譯參數解析
最主要的是GCC命令行的一個選項:
-shared 該選項指定生成動態連接庫(讓連接器生成T類型的導出符號表,有時候也生成弱連接W類型的導出符號),不用該標志外部程序無法連接。相當於一個可執行文件
-fPIC 表示編譯為位置獨立的代碼,不用此選項的話編譯后的代碼是位置相關的所以動態載入時是通過代碼拷貝的方式來滿足不同進程的需要,而不能達到真正代碼段共享的目的。

-L. 表示要連接的庫在當前目錄中;(多個庫:在編譯命令行中,將使用的靜態庫文件放在源文件后面就可以了。比如:gcc -L/usr/lib myprop.c libtest.a libX11.a libpthread.a -o myprop
其中-L/usr/lib指定庫文件的查找路徑。編譯器默認在當前目錄下先查找指定的庫文件,如前面的“法二 #gcc main.c libmyhello.a -o hello”)

-lmyhello 編譯器查找動態連接庫時有隱含的命名規則,即在給出的名字前面加上lib,后面加上.so或.a來確定庫的名稱libmyhello.so或libmyhello.a。
LD_LIBRARY_PATH 這個環境變量指示動態連接器可以裝載動態庫的路徑。
當然如果有root權限的話,可以修改/etc/ld.so.conf文件,然后調用 /sbin/ldconfig來達到同樣的目的,不過如果沒有root權限,那么只能采用輸出LD_LIBRARY_PATH的方法了。

調用動態庫的時候有幾個問題會經常碰到,有時,明明已經將庫的頭文件所在目錄 通過 “-I” include進來了,庫所在文件通過 “-L”參數引導,並指定了“-l”的庫名,但通過ldd命令察看時,就是死活找不到你指定鏈接的so文件,這時你要作的就是通過修改 LD_LIBRARY_PATH或者/etc/ld.so.conf文件來指定動態庫的目錄。通常這樣做就可以解決庫無法鏈接的問題了。

另:

從上述可知,如何找到生成的動態庫有3種方式:

(1)把庫拷貝到/usr/lib和/lib目錄下。

(2)在LD_LIBRARY_PATH環境變量中加上庫所在路徑。

例如動態庫libhello.so在/home/example/lib目錄下:

\(export LD_LIBRARY_PATH=\)LD_LIBRARY_PATH:/home/example/lib

(3) 修改/etc/ld.so.conf文件,把庫所在的路徑加到文件末尾,並執行ldconfig刷新。這樣,加入的目錄下的所有庫文件都可見。

附:像下面這樣指定路徑去連接系統的靜態庫,會報錯說要連接的庫找不到:

g++ -o main main.cpp -L/usr/lib libpthread.a

必須這樣g++ -o main main.cpp -L/usr/lib -lpthread才正確 。

自定義的庫考到/usr/lib 下時,

g++ -o main main.cpp -L/usr/lib libpthread.a libthread.a libclass.a 會出錯,但是這樣 g++ -o main main.cpp -L/usr/lib -lpthread -lthread -lclass 就正確了。

轉自:http://hi.baidu.com/������/blog/item/e58ed2f142913ea7a50f525e.html

=================================

以下內容,轉載自

http://www.newsmth.net/bbsanc.php?path=%2Fgroups%2Fcomp.faq%2FCProgramming%2Fyuanchuang%2FM.1129250654.M0

=================================

在GCC下實現自己的靜態庫

靜態庫的有關知識,請自行了解,這里只是講一點簡單的實現過程。

1、$mkdir static_lib_demo
2、用vim新產生文件main.c welcome.c
//---------------main.c------------------

include <stdio.h>

int main()
{
printf("Hello,world!\n");
disp();
return 0;
}
//-------------welcome.c------------------

include <stdio.h>

void disp()
{
printf("welcome to NEWSMTH!\n");
}

3、編譯產生obj文件
\(gcc -c main.c welcome.c 4、產生.a庫文件 \)ar -r libwelcome.a welcome.o
5、用生成的庫文件進行鏈接
$gcc main.o -o main -L/home/zgj/static_lib_demo -lwelcome
其中:/home/zgj/static_lib_demo是libwelcome.a所在的目錄
6、./main
Hello,world!
welcome to NEWSMTH!
完成演示

其它:
1、察看.a里有哪些obj文件
\(ar -t libwelcome.a welcome.o [zgj@localhost static_lib_demo]\) ar -vt libwelcome.a
rw-rw-r-- 507/507 792 Oct 14 08:01 2005 welcome.o
2、.a庫文件包含多個obj文件
\(ar -r libwelcome.a welcome.o welcome2.o 如果libwelcome.a存在,則libwelcome.a被更新。 如果已存在的libwelcome.a內已經包含welcome.o,則libwelcome.a內welcome.o被更新。 如果已存在的libwelcome.a內沒有包含welcome.o,則添加welcome.o到libwelcome.a內,libwelcome.a內原其他obj文件不變。 從libwelcome.a內刪除welcome.o \) ar -d libwelcome.a welcome.o
3、如果libwelcome.a在當前目錄,還可以直接用libwelcome.a進行鏈接
$gcc -o main main.o libwelcome.a
4、其它參數詳細內容請看gcc(1)、ar(1)

本文轉自:https://blog.csdn.net/youqika/article/details/54617525

1. 坑多的辦法

-static

如果需要鏈接成不依賴任何so文件的程序,用ldd查看顯示為"not a dynamic executable",但是這個選項時不推薦的。

即使像這樣鏈接(c++):“-static-libgcc -static-libstdc++ -static”

  一是會出現警告,比如我使用了系統調用getaddrinfo,“Using 'getaddrinfo' in statically linked applications requires at runtime the shared libraries from the glibc version used for l
inking”

  二是會掛(c代碼沒試過),類似如下的調用棧,在main函數之前,知道原因的同學請留言:-)

0 0x0000000000000000 in ?? ()

1 0x0000000000437c61 in std::locale::_S_initialize() ()

2 0x0000000000437ca3 in std::locale::locale() ()

3 0x0000000000435ce4 in std::ios_base::Init::Init() ()

4 0x0000000000401925 in __static_initialization_and_destruction_0 (__initialize_p=1, __priority=65535) at /usr/include/c++/5/iostream:74

5 0x000000000040194f in _GLOBAL__sub_I__Z4addrv () at src/cotasia.cpp:49

6 0x00000000004cf377 in __libc_csu_init ()

7 0x00000000004ce97e in generic_start_main ()

8 0x00000000004cebca in __libc_start_main ()

9 0x0000000000400d89 in _start ()

  1. 普遍的做法

就是直接連接.a文件全路徑,這沒啥好說的,就把它當.o文件一樣鏈接。

  1. 優雅的做法

既然是庫,-l和-L才是正派的做法,比如同一目錄下有libxxx.a文件和libxxx.so文件,gcc默認會鏈接so,改變這一默認行為的方法就是:將"-lxxx"改為"-l:libxxx.a"

另外如果程序鏈接的是靜態庫(libxxx.a),那么用ldd查不到,ldd只能查看鏈接的動態庫(libxxx.so)。

gcc 生成 .a靜態庫和 .so動態庫
Posted on 2012-04-13 22:30 網名還沒想好 閱讀(5912) 評論(1) 編輯 收藏
我們通常把一些公用函數制作成函數庫,供其它程序使用。函數庫分為靜態庫和動態庫兩
種。靜態庫在程序編譯時會被連接到目標代碼中,程序運行時將不再需要該靜態庫。動態
庫在程序編譯時並不會被連接到目標代碼中,而是在程序運行是才被載入,因此在程序運
行時還需要動態庫存在。本文主要通過舉例來說明在Linux中如何創建靜態庫和動態庫,以
及使用它們。

在創建函數庫前,我們先來准備舉例用的源程序,並將函數庫的源程序編譯成.o文件。

第1步:編輯得到舉例的程序--hello.h、hello.c和main.c;

hello.c(見程序2)是函數庫的源程序,其中包含公用函數hello,該函數將在屏幕上輸出"
Hello XXX!"。hello.h(見程序1)為該函數庫的頭文件。main.c(見程序3)為測試庫文件的
主程序,在主程序中調用了公用函數hello。

程序1: hello.h

ifndef HELLO_H

define HELLO_H

void hello(const char *name);

endif //HELLO_H

程序2: hello.c

include <stdio.h>

void hello(const char *name)
{
printf("Hello %s!\n", name);
}

程序3: main.c

include "hello.h"

int main()
{
hello("everyone");
return 0;
}

第2步:將hello.c編譯成.o文件;

無論靜態庫,還是動態庫,都是由.o文件創建的。因此,我們必須將源程序hello.c通過g
cc先編譯成.o文件。

在系統提示符下鍵入以下命令得到hello.o文件。

gcc -c hello.c

我們運行ls命令看看是否生存了hello.o文件。

ls

hello.c hello.h hello.o main.c

在ls命令結果中,我們看到了hello.o文件,本步操作完成。

下面我們先來看看如何創建靜態庫,以及使用它。

第3步:由.o文件創建靜態庫;

靜態庫文件名的命名規范是以lib為前綴,緊接着跟靜態庫名,擴展名為.a。例如:我們將
創建的靜態庫名為myhello,則靜態庫文件名就是libmyhello.a。在創建和使用靜態庫時,
需要注意這點。創建靜態庫用ar命令。

在系統提示符下鍵入以下命令將創建靜態庫文件libmyhello.a。

ar -crv libmyhello.a hello.o

我們同樣運行ls命令查看結果:

ls

hello.c hello.h hello.o libmyhello.a main.c

ls命令結果中有libmyhello.a。

第4步:在程序中使用靜態庫;

靜態庫制作完了,如何使用它內部的函數呢?只需要在使用到這些公用函數的源程序中包
含這些公用函數的原型聲明,然后在用gcc命令生成目標文件時指明靜態庫名,gcc將會從
靜態庫中將公用函數連接到目標文件中。注意,gcc會在靜態庫名前加上前綴lib,然后追
加擴展名.a得到的靜態庫文件名來查找靜態庫文件。

在程序3:main.c中,我們包含了靜態庫的頭文件hello.h,然后在主程序main中直接調用公
用函數hello。下面先生成目標程序hello,然后運行hello程序看看結果如何。

法一 # gcc -o hello main.c -L. –lmyhello,自定義的庫時,main.c還可放在-L.和 –lmyhello之間,但是不能放在它倆之后,否則會提示myhello沒定義,但是是系統的庫時,如g++ -o main(-L/usr/lib) -lpthread main.cpp就不出錯。

法二 #gcc main.c libmyhello.a -o hello

法三:先生成main.o:gcc -c main.c ,再生成可執行文件:gcc -o hello main.o libmyhello.a,動態庫連接時也可以這樣做。

./hello

Hello everyone!

我們刪除靜態庫文件試試公用函數hello是否真的連接到目標文件 hello中了。

rm libmyhello.a

rm: remove regular file `libmyhello.a'? y

./hello

Hello everyone!

程序照常運行,靜態庫中的公用函數已經連接到目標文件中了。

我們繼續看看如何在Linux中創建動態庫。我們還是從.o文件開始。

第5步:由.o文件創建動態庫文件;

動態庫文件名命名規范和靜態庫文件名命名規范類似,也是在動態庫名增加前綴lib,但其
文件擴展名為.so。例如:我們將創建的動態庫名為myhello,則動態庫文件名就是libmyh
ello.so。用gcc來創建動態庫。

在系統提示符下鍵入以下命令得到動態庫文件libmyhello.so。

gcc -shared -fPCI -o libmyhello.so hello.o (-o不可少)

我們照樣使用ls命令看看動態庫文件是否生成。

ls

hello.c hello.h hello.o libmyhello.so main.c

第6步:在程序中使用動態庫;

在程序中使用動態庫和使用靜態庫完全一樣,也是在使用到這些公用函數的源程序中包含
這些公用函數的原型聲明,然后在用gcc命令生成目標文件時指明動態庫名進行編譯。我們
先運行gcc命令生成目標文件,再運行它看看結果。

gcc -o hello main.c -L. -lmyhello

(或 #gcc main.c libmyhello.so -o hello 不會出錯(沒有libmyhello.so的話,會出錯),但是接下來./hello 會提示出錯,因為雖然連接時用的是當前目錄的動態庫,但是運行時,是到/usr/lib中找庫文件的,將文件libmyhello.so復制到目錄 /usr/lib中就OK了)

./hello

./hello: error while loading shared libraries: libmyhello.so: cannot open shar
ed object file: No such file or directory

哦!出錯了。快看看錯誤提示,原來是找不到動態庫文件libmyhello.so。程序在運行時,
會在/usr/lib和/lib等目錄中查找需要的動態庫文件。若找到,則載入動態庫,否則將提
示類似上述錯誤而終止程序運行。我們將文件libmyhello.so復制到目錄/usr/lib中,再試
試。

mv libmyhello.so /usr/lib

./hello

Hello everyone!

成功了。這也進一步說明了動態庫在程序運行時是需要的。

我們回過頭看看,發現使用靜態庫和使用動態庫編譯成目標程序使用的gcc命令完全一樣,
那當靜態庫和動態庫同名時,gcc命令會使用哪個庫文件呢?抱着對問題必究到底的心情,
來試試看。

先刪除除.c和.h外的所有文件,恢復成我們剛剛編輯完舉例程序狀態。

rm -f hello hello.o /usr/lib/libmyhello.so

ls

hello.c hello.h main.c

在來創建靜態庫文件libmyhello.a和動態庫文件libmyhello.so。

gcc -c hello.c

ar -cr libmyhello.a hello.o (或-cvr )

gcc -shared -fPCI -o libmyhello.so hello.o

ls

hello.c hello.h hello.o libmyhello.a libmyhello.so main.c

通過上述最后一條ls命令,可以發現靜態庫文件libmyhello.a和動態庫文件libmyhello.s
o都已經生成,並都在當前目錄中。然后,我們運行gcc命令來使用函數庫myhello生成目標
文件hello,並運行程序 hello。

gcc -o hello main.c -L. –lmyhello (動態庫和靜態庫同時存在時,優先使用動態庫, 當然,直接#gcc main.c libmyhello.a -o hello的話,就是指定為靜態庫了)

./hello

./hello: error while loading shared libraries: libmyhello.so: cannot open shar
ed object file: No such file or directory

從程序hello運行的結果中很容易知道,當靜態庫和動態庫同名時,gcc命令將優先使用動態庫,默認去連/usr/lib和/lib等目錄中的動態庫,將文件libmyhello.so復制到目錄/usr/lib中即可。

Note:
編譯參數解析
最主要的是GCC命令行的一個選項:
-shared 該選項指定生成動態連接庫(讓連接器生成T類型的導出符號表,有時候也生成弱連接W類型的導出符號),不用該標志外部程序無法連接。相當於一個可執行文件
-fPIC 表示編譯為位置獨立的代碼,不用此選項的話編譯后的代碼是位置相關的所以動態載入時是通過代碼拷貝的方式來滿足不同進程的需要,而不能達到真正代碼段共享的目的。

-L. 表示要連接的庫在當前目錄中;(多個庫:在編譯命令行中,將使用的靜態庫文件放在源文件后面就可以了。比如:gcc -L/usr/lib myprop.c libtest.a libX11.a libpthread.a -o myprop
其中-L/usr/lib指定庫文件的查找路徑。編譯器默認在當前目錄下先查找指定的庫文件,如前面的“法二 #gcc main.c libmyhello.a -o hello”)

-lmyhello 編譯器查找動態連接庫時有隱含的命名規則,即在給出的名字前面加上lib,后面加上.so或.a來確定庫的名稱libmyhello.so或libmyhello.a。
LD_LIBRARY_PATH 這個環境變量指示動態連接器可以裝載動態庫的路徑。
當然如果有root權限的話,可以修改/etc/ld.so.conf文件,然后調用 /sbin/ldconfig來達到同樣的目的,不過如果沒有root權限,那么只能采用輸出LD_LIBRARY_PATH的方法了。

調用動態庫的時候有幾個問題會經常碰到,有時,明明已經將庫的頭文件所在目錄 通過 “-I” include進來了,庫所在文件通過 “-L”參數引導,並指定了“-l”的庫名,但通過ldd命令察看時,就是死活找不到你指定鏈接的so文件,這時你要作的就是通過修改 LD_LIBRARY_PATH或者/etc/ld.so.conf文件來指定動態庫的目錄。通常這樣做就可以解決庫無法鏈接的問題了。

另:

從上述可知,如何找到生成的動態庫有3種方式:

(1)把庫拷貝到/usr/lib和/lib目錄下。

(2)在LD_LIBRARY_PATH環境變量中加上庫所在路徑。

例如動態庫libhello.so在/home/example/lib目錄下:

\(export LD_LIBRARY_PATH=\)LD_LIBRARY_PATH:/home/example/lib

(3) 修改/etc/ld.so.conf文件,把庫所在的路徑加到文件末尾,並執行ldconfig刷新。這樣,加入的目錄下的所有庫文件都可見。

附:像下面這樣指定路徑去連接系統的靜態庫,會報錯說要連接的庫找不到:

g++ -o main main.cpp -L/usr/lib libpthread.a

必須這樣g++ -o main main.cpp -L/usr/lib -lpthread才正確 。

自定義的庫考到/usr/lib 下時,

g++ -o main main.cpp -L/usr/lib libpthread.a libthread.a libclass.a 會出錯,但是這樣 g++ -o main main.cpp -L/usr/lib -lpthread -lthread -lclass 就正確了。

轉自:http://hi.baidu.com/������/blog/item/e58ed2f142913ea7a50f525e.html

=================================

以下內容,轉載自

http://www.newsmth.net/bbsanc.php?path=%2Fgroups%2Fcomp.faq%2FCProgramming%2Fyuanchuang%2FM.1129250654.M0

=================================

在GCC下實現自己的靜態庫

靜態庫的有關知識,請自行了解,這里只是講一點簡單的實現過程。

1、$mkdir static_lib_demo
2、用vim新產生文件main.c welcome.c
//---------------main.c------------------

include <stdio.h>

int main()
{
printf("Hello,world!\n");
disp();
return 0;
}
//-------------welcome.c------------------

include <stdio.h>

void disp()
{
printf("welcome to NEWSMTH!\n");
}

3、編譯產生obj文件
\(gcc -c main.c welcome.c 4、產生.a庫文件 \)ar -r libwelcome.a welcome.o
5、用生成的庫文件進行鏈接
$gcc main.o -o main -L/home/zgj/static_lib_demo -lwelcome
其中:/home/zgj/static_lib_demo是libwelcome.a所在的目錄
6、./main
Hello,world!
welcome to NEWSMTH!
完成演示

其它:
1、察看.a里有哪些obj文件
\(ar -t libwelcome.a welcome.o [zgj@localhost static_lib_demo]\) ar -vt libwelcome.a
rw-rw-r-- 507/507 792 Oct 14 08:01 2005 welcome.o
2、.a庫文件包含多個obj文件
\(ar -r libwelcome.a welcome.o welcome2.o 如果libwelcome.a存在,則libwelcome.a被更新。 如果已存在的libwelcome.a內已經包含welcome.o,則libwelcome.a內welcome.o被更新。 如果已存在的libwelcome.a內沒有包含welcome.o,則添加welcome.o到libwelcome.a內,libwelcome.a內原其他obj文件不變。 從libwelcome.a內刪除welcome.o \) ar -d libwelcome.a welcome.o
3、如果libwelcome.a在當前目錄,還可以直接用libwelcome.a進行鏈接
$gcc -o main main.o libwelcome.a
4、其它參數詳細內容請看gcc(1)、ar(1)

linux下生成.so文件和.a文件
test.h

復制代碼
1 #ifndef TEST_H
2 #define TEST_H
3
4 void TestA();
5 void TestB();
6
7 #endif
復制代碼

test_a.cpp

復制代碼
1 #include <stdio.h>
2 #include "test.h"
3
4 void TestA()
5 {
6 printf("TestA func\n");
7 }
復制代碼

test_b.cpp

復制代碼
1 #include <stdio.h>
2 #include "test.h"
3
4 void TestB()
5 {
6 printf("TestB func\n");
7 }
復制代碼

生成so文件的命令

g++ test_a.cpp test_b.cpp -fPIC -shared -o libtest.so
生成.a文件的命令

1 gcc -c test_a.cpp
2 gcc -c test_b.cpp
3 ar -r libtest.a test_a.o test_b.o

test.cpp

復制代碼
1 #include "test.h"
2
3 int main()
4 {
5 TestA();
6 TestB();
7
8 return 0;
9 }
復制代碼

采用動態庫編譯命令

g++ test.cpp -o test -L. -ltest

執行

export LD_LIBRARY_PATH=./
./test
執行結果如下。

采用靜態庫編譯命令

g++ -static -o test -L. -ltest test.cpp
執行效果

靜態庫的嵌套調用,有時候我想做一個自己的靜態庫,它里面要調用其他靜態庫里面的函數,經過試驗

這個好像用ar -r不行,所以就在鏈接的時候需要兩個庫文件都包含,同時要有這一個頭文件才行。。。

Linux下gcc編譯生成動態連接庫*.so文件並調用它 node

動態庫*.so在linux下用c和c++編程時常常會碰到,最近在網站找了幾篇文章介紹動態庫的編譯和連接,總算搞懂了這個以前一直不太了解得東東,這里作個筆記,也為其它正為動態庫連接庫而苦惱的兄弟們提供一點幫助。
1、動態庫的編譯

下面經過一個例子來介紹如何生成一個動態庫。這里有一個頭文件:so_test.h,三個.c文件:test_a.c、test_b.c、test_c.c,咱們將這幾個文件編譯成一個動態庫:libtest.so。

//so_test.h:

include "stdio.h"

void test_a();
void test_b();
void test_c();

//test_a.c:

include "so_test.h"

void test_a()
{
printf("this is in test_a...\n");
}

//test_b.c:

include "so_test.h"

void test_b()
{
printf("this is in test_b...\n");
}

//test_c.c:

include "so_test.h"

void test_c()
{
printf("this is in test_c...\n");
}
將這幾個文件編譯成一個動態庫:libtest.so
$ gcc test_a.c test_b.c test_c.c -fPIC -shared -o libtest.so

2、動態庫的連接
在1、中,咱們已經成功生成了一個本身的動態連接庫libtest.so,下面咱們經過一個程序來調用這個庫里的函數。程序的源文件為:test.c。

test.c:

include "so_test.h"

int main()
{
test_a();
test_b();
test_c();
return 0;
}
將test.c與動態庫libtest.so連接生成執行文件test:
$ gcc test.c -L. -ltest -o test
測試是否動態鏈接,若是列出libtest.so,那么應該是鏈接正常了
$ ldd test
執行test,能夠看到它是如何調用動態庫中的函數的。
3、編譯參數解析
最主要的是GCC命令行的一個選項:
-shared該選項指定生成動態鏈接庫(讓鏈接器生成T類型的導出符號表,有時候也生成弱鏈接W類型的導出符號),不用該標志外部程序沒法鏈接。至關於一個可執行文件

-fPIC:表示編譯為位置獨立的代碼,不用此選項的話編譯后的代碼是位置相關的因此動態載入時是經過代碼拷貝的方式來知足不一樣進程的須要,而不能達到真正代碼段共享的目的。

-L.:表示要鏈接的庫在當前目錄中

-ltest:編譯器查找動態鏈接庫時有隱含的命名規則,即在給出的名字前面加上lib,后面加上.so來肯定庫的名稱

LD_LIBRARY_PATH:這個環境變量指示動態鏈接器能夠裝載動態庫的路徑。

固然若是有root權限的話,能夠修改/etc/ld.so.conf文件,而后調用 /sbin/ldconfig來達到一樣的目的,不過若是沒有root權限,那么只能采用輸出LD_LIBRARY_PATH的方法了。

4、注意

調用動態庫的時候有幾個問題會常常碰到,有時,明明已經將庫的頭文件所在目錄 經過 "-I" include進來了,庫所在文件經過 "-L"參數引導,並指定了"-l"的庫名,但經過ldd命令察看時,就是死活找不到你指定連接的so文件,這時你要做的就是經過修改 LD_LIBRARY_PATH或者/etc/ld.so.conf文件來指定動態庫的目錄。一般這樣作就能夠解決庫沒法連接的問題了。

在linux下能夠用export命令來設置這個值,在linux終端下輸入:
export LD_LIBRARY_PATH=/opt/au1200_rm/build_tools/bin: \(LD_LIBRARY_PATH:    而后再輸入:export    即會顯示是否設置正確    export方式在重啟后失效,因此也能夠用 vim /etc/bashrc ,修改其中的LD_LIBRARY_PATH變量。    例如:LD_LIBRARY_PATH=\)LD_LIBRARY_PATH:/opt/au1200_rm/build_tools/bin。 mysql

Linux下靜態庫,動態庫,以及arm平台下庫的基本概念 linux

1、基本概念 c++

1.1、什么是庫 sql

在 windows 平台和 Linux 平台下都大量存在着庫。 編程

本質上來講庫是 一種可執行代碼的二進制形式,能夠被操做系統載入內存執行。 vim

因為 windows 和 linux 的平台不一樣(主要是編譯器、匯編器和鏈接器 的不一樣),所以兩者庫的二進制是不兼容的。 windows

本文僅限於介紹 linux 下的庫。 bash

1.2、 庫的種類 函數

linux 下的庫有兩種:靜態庫和共享庫(動態庫)。

兩者的不一樣點在於代碼被載入的時刻不一樣。

靜態庫的代碼在編譯過程當中已經被載入可執行程序,所以體積較大。

靜態用.a為后綴, 例如: libhello.a

共享庫(動態庫)的代碼是在可執行程序運行時才載入內存的,在編譯過程當中僅簡單的引用,所以代碼體積較小。

動態一般用.so為后綴, 例如:libhello.so

共享庫(動態庫)的好處是,不一樣的應用程序若是調用相同的庫,那么在內存里只須要有一份該共享庫的實例。

為了在同一系統中使用不一樣版本的庫,能夠在庫文件名后加上版本號為后綴,例如: libhello.so.1.0,因為程序鏈接默認以.so為文件后綴名。因此為了使用這些庫,一般使用創建符號鏈接的方式。

ln -s libhello.so.1.0 libhello.so.1 ln -s libhello.so.1 libhello.so

1.三、靜態庫,動態庫文件在linux下是如何生成的:

如下面的代碼為例,生成上面用到的hello庫:

/* hello.c */

include "hello.h"

void sayhello()

{

printf("hello,world ");

}

首先用gcc編繹該文件,在編繹時可使用任何合法的編繹參數,例如-g加入調試代碼等:

$gcc -c hello.c -o hello.o

一、生成靜態庫 生成靜態庫使用ar工具,其實ar是archive的意思

$ar cqs libhello.a hello.o

二、生成動態庫 用gcc來完成,因為可能存在多個版本,所以一般指定版本號:

$gcc -shared -o libhello.so.1.0 hello.o

1.四、庫文件是如何命名的,有沒有什么規范:

在 linux 下,庫文件通常放在/usr/lib和/lib下,

靜態庫的名字通常為libxxxx.a,其中 xxxx 是該lib的名稱;

動態庫的名字通常為libxxxx.so.major.minor,xxxx 是該lib的名稱,major是主版本號,minor是副版本號

1.五、可執行程序在執行的時候如何定位共享庫(動態庫)文件 :

當系統加載可執行代碼(即庫文件)的時候,可以知道其所依賴的庫的名字,可是還須要知道絕對路徑,此時就須要系統動態載入器 (dynamic linker/loader)

對於 elf 格式的可執行程序,是由 ld-linux.so* 來完成的,它前后搜索 elf 文件的 DT_RPATH 段—環境變量LD_LIBRARY_PATH—/etc/ld.so.cache 文件列表— /lib/,/usr/lib 目錄找到庫文件后將其載入內存

如: export LD_LIBRARY_PATH='pwd'

將當前文件目錄添加為共享目錄

1.六、使用ldd工具,查看可執行程序依賴那些動態庫或着動態庫依賴於那些動態庫:

ldd 命令能夠查看一個可執行程序依賴的共享庫,

例如 # ldd /bin/lnlibc.so.6

=> /lib/libc.so.6 (0×40021000)/lib/ld-linux.so.2

=> /lib/ld- linux.so.2 (0×40000000)

能夠看到 ln 命令依賴於 libc 庫和 ld-linux 庫

使用如下的命令查看arm平台的依賴關系

注意:arm-linux-readelf -d busybox | grep Shared grep后面能夠不要,注意大小Shared第一個字母大寫

1.七、使用nm工具,查看靜態庫和動態庫中有那些函數名(T類表示函數是當前庫中定義的,U類表示函數是被調用的,在其它庫中定義的,W類是當前庫中定義,被其它庫中的函數覆蓋)。:

有時候可能須要查看一個庫中到底有哪些函數,nm工具能夠打印出庫中的涉及到的全部符號,這里的庫既能夠是靜態的也能夠是動態的。

nm列出的符號有不少, 常見的有三種::

一種是在庫中被調用,但並無在庫中定義(代表須要其余庫支持),用U表示;

一種是在庫中定義的函數,用T表示,這是最多見的;

另一種是所 謂的"弱態"符號,它們雖然在庫中被定義,可是可能被其余庫中的同名符號覆蓋,用W表示。

例如,假設開發者但願知道上文提到的hello庫中是否引用了 printf():

$nm libhello.so | grep printf

發現printf是U類符號,說明printf被引用,可是並無在庫中定義。

由此能夠推斷,要正常使用hello庫,必須有其它庫支持,使用ldd工具查看hello依賴於哪些庫:

$ldd hello libc.so.6=>/lib/libc.so.6(0x400la000) /lib/ld-linux.so.2=>/lib/ld-linux.so.2 (0x40000000)

從上面的結果能夠繼續查看printf最終在哪里被定義,有興趣能夠Go on

1.八、使用ar工具,能夠生成靜態庫,同時能夠查看靜態庫中包含那些.o文件,即有那些源文件構成。

可使用 ar -t libname.a 來查看一個靜態庫由那些.o文件構成。

可使用 ar q libname.a xxx1.o xxx2.o xxx3.o ... xxxn.o 生成靜態庫

Linux下進行程序設計時,關於庫的使用:

1、gcc/g++命令中關於庫的參數:

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

-fPIC:表示編譯為位置獨立(地址無關)的代碼,不用此選項的話,編譯后的代碼是位置相關的,因此動態載入時,是經過代碼拷貝的方式來知足不一樣進程的須要,而不能達到真正代碼段共享的目的。

-L:指定連接庫的路徑,-L. 表示要鏈接的庫在當前目錄中

-ltest:指定連接庫的名稱為test,編譯器查找動態鏈接庫時有隱含的命名規則,即在給出的名字前面加上lib,后面加上.so來肯定庫的名稱

LD_LIBRARY_PATH:這個環境變量指示動態鏈接器能夠裝載動態庫的路徑。

固然若是有root權限的話,能夠修改/etc/ld.so.conf文件,而后調用 /sbin/ldconfig來達到一樣的目的,

不過若是沒有root權限,那么只能采用修改LD_LIBRARY_PATH環境變量的方法了。

調用動態庫的時候,有幾個問題會常常碰到:

一、有時,明明已經將庫的頭文件所在目錄 經過 "-I" include進來了,庫所在文件經過 "-L"參數引導,並指定了"-l"的庫名,但經過ldd命令察看時,就是死活找不到你指定連接的so文件,這時你要做的就是經過修改 LD_LIBRARY_PATH或者/etc/ld.so.conf文件來指定動態庫的目錄。一般這樣作就能夠解決庫沒法連接的問題了。

2、靜態庫連接時搜索路徑的順序:

  1. ld會去找gcc/g++命令中的參數-L;

  2. 再找gcc的環境變量LIBRARY_PATH,它指定程序靜態連接庫文件搜索路徑;

export LIBRARY_PATH=$LIBRARY_PATH:data/home/billchen/lib

  1. 再找默認庫目錄 /lib /usr/lib /usr/local/lib,這是當初compile gcc時寫在程序內的。

3、動態連接時、執行時搜索路徑順序:

  1. 編譯目標代碼時指定的動態庫搜索路徑;

  2. 環境變量LD_LIBRARY_PATH指定動態庫搜索路徑,它指定程序動態連接庫文件搜索路徑;

export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:data/home/billchen/lib

  1. 配置文件/etc/ld.so.conf中指定的動態庫搜索路徑;

  2. 默認的動態庫搜索路徑/lib;

  3. 默認的動態庫搜索路徑/usr/lib。

4、靜態庫和動態連接庫同時存在的問題:

當一個庫同時存在靜態庫和動態庫時,好比libmysqlclient.a和libmysqlclient.so同時存在時:

在Linux下,動態庫和靜態庫同事存在時,gcc/g++的連接程序,默認連接的動態庫。

可使用下面的方法,給鏈接器傳遞參數,看是否連接動態庫仍是靜態庫。

-WI,-Bstatic -llibname //指定讓gcc/g++連接靜態庫

使用:

gcc/g++ test.c -o test -WI,-Bstatic -llibname

-WI,-Bdynamic -llibname //指定讓gcc/g++連接動態庫

使用:

gcc/g++ test.c -o test -WI,-Bdynamic -llibname

若是要徹底靜態加在,使用-static參數,即將全部的庫以靜態的方式鏈入可執行程序,這樣生成的可執行程序,再也不依賴任何庫,同事出現的問題是,這樣編譯出來的程序很是大,占用空間。

5、有關環境變量:

LIBRARY_PATH環境變量:指定程序靜態連接庫文件搜索路徑

LD_LIBRARY_PATH環境變量:指定程序動態連接庫文件搜索路徑

6、動態庫升級問題:

在動態連接庫升級時,

不能使用cp newlib.so oldlib.so,這樣有可能會使程序core掉;

而應該使用:

rm oldlib.so 而后 cp newlib.so oldlib.so

或者

mv oldlib.so oldlib.so_bak 而后 cp newlib.so oldlib.so

為何不能用cp newlib.so oldlib.so ?

在替換so文件時,若是在不停程序的狀況下,直接用 cp new.so old.so 的方式替換程序使用的動態庫文件會致使正在運行中的程序崩潰。

解決方法:

解決的辦法是采用"rm+cp" 或"mv+cp" 來替代直接"cp" 的操做方法。

linux系統的動態庫有兩種使用方法:運行時動態連接庫,動態加載庫並在程序控制之下使用。

一、為何在不停程序的狀況下,直接用 cp 命令替換程序使用的 so 文件,會使程序崩潰?

不少同窗在工做中遇到過這樣一個問題,在替換 so 文件時,若是在不停程序的狀況下,直接用cp new.so old.so的方式替換程序使用的動態庫文件會致使正在運行中的程序崩潰,退出。

這與 cp 命令的實現有關,cp 並不改變目標文件的 inode,cp 的目標文件會繼承被覆蓋文件的屬性而非源文件。實際上它是這樣實現的:

strace cp libnew.so libold.so 2>&1 |grep open.lib..so

open("libnew.so", O_RDONLY|O_LARGEFILE) = 3

open("libold.so", O_WRONLY|O_TRUNC|O_LARGEFILE) = 4

在 cp 使用"O_WRONLY|O_TRUNC" 打開目標文件時,原 so 文件的鏡像被意外的破壞了。這樣動態連接器 ld.so 不能訪問到 so 文件中的函數入口。從而致使 Segmentation fault,程序崩潰。ld.so 加載 so 文件及"再定位"的機制比較復雜。

二、怎樣在不中止程序的狀況下替換so文件,而且保證程序不會崩潰?

答案是采用"rm+cp" 或"mv+cp" 來替代直接"cp" 的操做方法。

在用新的so文件 libnew.so 替換舊的so文件 libold.so 時,若是采用以下方法:

rm libold.so //若是內核正在使用libold.so,那么inode節點不會馬上別刪除掉。

cp libnew.so libold.so

采用這種方法,目標文件 libold.so 的 inode 其實已經改變了,原來的 libold.so 文件雖然不能用 "ls"查看到,但其 inode 並無被真正刪除,直到內核釋放對它的引用。

(即: rm libold.so,此時,若是ld.so正在加在libold.so,內核就在引用libold.so的inode節點,rm libold.so的inode並無被真正刪除,當ld.so對libold.so的引用結束,inode才會真正刪除。這樣程序就不會崩潰,由於它還在使用舊的libold.so,當下次再使用libold.so時,已經被替換,就會使用新的libold.so)

同理,mv只是改變了文件名,其 inode 不變,新文件使用了新的 inode。這樣動態連接器 ld.so 仍然使用原來文件的 inode 訪問舊的 so 文件。於是程序依然能正常運行。

(即: mv libold.so ***后,若是程序使用動態庫,仍是使用舊的inode節點,當下次再使用libold.so時,就會使用新的libold.so)

到這里,為何直接使用"cp new_exec_file old_exec_file"這樣的命令時,系統會禁止這樣的操做,而且給出這樣的提示"cp: cannot create regular file `old': Text file busy"。這時,咱們采用的辦法仍然是用"rm+cp"或者"mv+cp"來替代直接"cp",這跟以上提到的so文件的替換有一樣的道理。

可是,為何系統會阻止 cp 覆蓋可執行程序,而不阻止覆蓋 so 文件呢?

這是由於 Linux 有個 Demand Paging 機制,所謂"Demand Paging",簡單的說,就是系統為了節約物理內存開銷,並不會程序運行時就將全部頁(page)都加載到內存中,而只有在系統有訪問需求時才將其加載。"Demand Paging"要求正在運行中的程序鏡像(注意,並不是文件自己)不被意外修改,所以內核在啟動程序后會鎖定這個程序鏡像的 inode。

對於 so 文件,它是靠 ld.so 加載的,而ld.so畢竟也是用戶態程序,沒有權利去鎖定inode,也不該與內核的文件系統底層實現耦合。

gcc指定頭文件路徑及動態連接庫路徑

本文詳細介紹了linux 下gcc頭文件指定方法,以及搜索路徑順序的問題。另外,還總結了,gcc動態連接的方法以及路徑指定,一樣也討論了搜索路徑的順序問題。本文包含了不少的例子,具備很強的操做性,但願讀者本身去走一遍。
一.#include <>與#include ""

include <>直接到系統指定的某些目錄中去找某些頭文件。

include ""先到源文件所在文件夾去找,而后再到系統指定的某些目錄中去找某些頭文件。

二.gcc指定頭文件的三種狀況:

1.會在默認狀況下指定到/usr/include文件夾(更深層次的是一個相對路徑,gcc可執行程序的路徑是/usr/bin/gcc,那么它在實際工做時指定頭文件頭徑是一種相對路徑方法,換算成絕對路徑就是加上/usr/include,如#include 就是包含/usr/include/stdio.h)

2.GCC還使用了-I指定路徑的方式,即
gcc -I 頭文件所在文件夾(絕對路徑或相對路徑都可) 源文件
舉一個例子:
設當前路徑為/root/test,其結構以下:
include_test.c
include/include_test.h
有兩種方法訪問到include_test.h。

  1. include_test.c中#include "include/include_test.h"而后gcc include_test.c便可

  2. include_test.c中#include 或者#include 而后gcc –I include include_test.c也可

  3. 參數:-nostdinc使編譯器再也不系統缺省的頭文件目錄里面找頭文件,通常和-I聯合使用,明確限定頭文件的位置。

在編譯驅動模塊時,因為非凡的需求必須強制GCC不搜索系統默認路徑,也就是不搜索/usr/include要用參數-nostdinc,還要本身用-I參數來指定內核頭文件路徑,這個時候必須在Makefile中指定。

頭文件搜索順序:
1.由參數-I指定的路徑(指定路徑有多個路徑時,按指定路徑的順序搜索)

2.而后找gcc的環境變量 C_INCLUDE_PATH, CPLUS_INCLUDE_PATH, OBJC_INCLUDE_PATH

3.再找內定目錄
/usr/include
/usr/local/include
/usr/lib/gcc-lib/i386-linux/2.95.2/include
/usr/lib/gcc-lib/i386-linux/2.95.2/../../../../include/g++-3
/usr/lib/gcc-lib/i386-linux/2.95.2/../../../../i386-linux/include

庫文件,可是若是裝gcc的時候,是有給定的prefix的話,那么就是
/usr/include
prefix/include
prefix/xxx-xxx-xxx-gnulibc/include
prefix/lib/gcc-lib/xxxx-xxx-xxx-gnulibc/2.8.1/include

三.Linux指定動態庫路徑

眾所周知,Linux動態庫的默認搜索路徑是/lib和/usr/lib。動態庫被建立后,通常都復制到這兩個目錄中。當程序執行時須要某動態庫, 而且該動態庫還未加載到內存中,則系統會自動到這兩個默認搜索路徑中去查找相應的動態庫文件,而后加載該文件到內存中,這樣程序就可使用該動態庫中的函 數,以及該動態庫的其它資源了。在Linux 中,動態庫的搜索路徑除了默認的搜索路徑外,還能夠經過如下三種方法來指定。

1.在配置文件/etc/ld.so.conf中指定動態庫搜索路徑。
能夠經過編輯配置文件/etc/ld.so.conf來指定動態庫的搜索路徑,該文件中每行為一個動態庫搜索路徑。每次編輯完該文件后,都必須運行命令ldconfig使修改后的配置生效。

舉一個例子:
全部源文件:
源文件1: lib_test.c

include

void prt()
{
printf("You found me!!!/n");
}
源文件2: main.c
void prt();
int main()
{
prt();
return 0;
}
操做過程:
咱們經過如下命令用源程序lib_test.c來建立動態庫 lib_test.so。

gcc –o lib_test.o -c lib_test.c

gcc -shared -fPIC -o lib_test.so lib_test.o

或者直接一條指令:

gcc –shared –fPIC –o lib_test.so lib_test.c

注意:
-fPIC參數聲明連接庫的代碼段是能夠共享的,
-shared參數聲明編譯為共享庫。請注意此次咱們編譯的共享庫的名字叫作
lib_test.so,這也是Linux共享庫的一個命名的慣例了:后綴使用so,而名稱使用libxxxx格式。

接着經過如下命令編譯main.c,生成目標程序main.out。

gcc -o main.out -L. –l_test main.c

請注意為何是-l_test?

而后把庫文件移動到目錄/root/lib中。

mkdir /root/lib

mv lib_test.so /root/lib/ lib_test.so

最后編輯配置文件/etc/ld.so.conf,在該文件中追加一行/root/lib。

運行程序main.out:

./main.out

./main.out: error while loading shared libraries: lib_test.so: cannot open shared object file: No such file or directory

出錯了,系統未找到動態庫lib_test.so。找找緣由,原來在編輯完配置文件/etc/ld.so.conf后,沒有運行命令ldconfig,因此剛才的修改還未生效。咱們運行ldconfig后再試試。

ldconfig

./main.out

You found me!!!

程序main.out運行成功,而且打印出正確結果。

2.經過環境變量LD_LIBRARY_PATH指定動態庫搜索路徑。
經過設定環境變量LD_LIBRARY_PATH也能夠指定動態庫搜索路徑。當經過該環境變量指定多個動態庫搜索路徑時,路徑之間用冒號":"分隔。下面經過例2來講明本方法。

舉一個例子:
此次咱們把上面獲得的文件lib_test.so移動到另外一個地方去,如/root下面,而后設置環境變量LD_LIBRARY_PATH找到lib_test.so。設置環境變量方法以下:

export LD_LIBRARY_PATH=/root

而后運行:

./main.out

You found me!!!

注意:設置環境變量LD_LIBRARY_PATH=/root是不行的,非得export才行。

3.在編譯目標代碼時指定該程序的動態庫搜索路徑。
還能夠在編譯目標代碼時指定程序的動態庫搜索路徑。-Wl,表示后面的參數將傳給link程序ld(由於gcc可能會自動調用ld)。這里經過gcc 的參數"-Wl,-rpath,"指定
舉一個例子:
此次咱們還把上面獲得的文件lib_test.so移動到另外一個地方去,如/root/test/lib下面,
由於咱們須要在編譯目標代碼時指定可執行文件的動態庫搜索路徑,因此須要用gcc命令從新編譯源程序main.c(見程序2)來生成可執行文件main.out。

gcc -o main.out -L. –l_test -Wl,-rpath,/root/test/lib main.c

運行結果:

./main.out

You found me!!!

程序./main.out運行成功,輸出的結果正是main.c中的函數prt的運行結果。所以程序main.out搜索到的動態庫是/root/test/lib/lib_test.so。

關於-Wl,rpath的使用方法我再舉一個例子,應該不難從中看出指定多個路徑的方法:
gcc -Wl,-rpath,/home/arc/test,-rpath,/lib/,-rpath,/usr/lib/,-rpath,/usr/local/lib test.c

以上介紹了三種指定動態庫搜索路徑的方法,加上默認的動態庫搜索路徑/lib和/usr/lib,共五種動態庫的搜索路徑,那么它們搜索的前后順序是什么呢?讀者能夠用下面的方法來試驗一下:
(1) 用前面介紹的方法生成5個lib_test.so放在5個不一樣的文件夾下面,要求每個lib_test.so都惟一對應一個搜索路徑,並注意main.out程序輸出的不一樣。
(2) 運行main.out,便可看出他是那個搜索路徑下的,而后刪除這個路徑下的lib_test.so,而后再運行。依此類推操做,便可推出搜索順序。

能夠得出動態庫的搜索路徑搜索的前后順序是:

1.編譯目標代碼時指定的動態庫搜索路徑;

2.環境變量LD_LIBRARY_PATH指定的動態庫搜索路徑;

3.配置文件/etc/ld.so.conf中指定的動態庫搜索路徑;

4.默認的動態庫搜索路徑/lib;

5.默認的動態庫搜索路徑/usr/lib。

在上述1、2、3指定動態庫搜索路徑時,均可指定多個動態庫搜索路徑,其搜索的前后順序是按指定路徑的前后順序搜索的。有興趣的讀者本身驗證。

PS:此文網上搜得,原始出處已經沒法訪問,故沒法給出原文連接。


免責聲明!

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



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