本文主要介紹了一下在linux下開發c/c++時候,不可避免的會開發或者生成.o .a .so這種中間庫狀態的檔案(可能是自己寫了乙個lib讓別人呼叫,或者提供.c/.cpp檔案嵌入別人的makefile工程)。如何檢視這些庫檔案的一些基本資訊。有時候大家編譯程式時候(確切的說是鏈結器鏈結的時候)很多錯誤例如"undefine reference",之類的常見錯誤,原因就是因為沒有找到.o .a .so的庫檔案,導致鏈結失敗。
1、linux庫檔案
2、庫檔案的使用方式
3、利用tar/nm檢視庫檔案的資訊
1、庫檔案的定義之類的就不在此累贅了,有興趣google之。說白了就是我們寫好一些對應的.h和.c(.cpp)檔案,然後通過編譯器的編譯,生成中間**供他人使用,他人只需要將你的中間**include進自己的程式即可。注意,編譯器編譯成最終可執行的檔案需要好幾步,基本可以分為:文字解析->語法解析->此法分析->預處理分析->編譯->連線。生成中間庫是沒有鏈結階段的,在linux gcc下通過-c引數指定只編譯不鏈結,所以如果寫了乙個.c檔案用到了比如pthread_create之類的外部呼叫,在gcc -c編譯的時候不用-lpthread因為這個時候是不需要鏈結的。
2、(對庫檔案熟悉話直接跳過)在各個系統平台上,庫檔案的格式和形式各不相同,windows下就是如同***.dll或者***.lib,*inux下就是***.so或者***.a。兩種分別對應的是靜態庫和動態庫,靜態庫會連同編譯器編譯鏈結進入程式成為程式的一部分,好處是作為程式的一部分不用每次執行時都去load(弊端是可能很多程序都用到這個庫但是每個程序中都有乙份,動態庫的話記憶體中只有乙份,通過重定向來載入),而且不會導致因庫的缺失而執行失敗,壞處是會導致可執行檔案偏大。動態庫是程式執行時動態載入到程序裡去的,而且可以多程序共,並且方便軟體更新,直接替換老的庫即可。
一、寫了乙個.h宣告乙個foo()函式,然後在.c或者.cpp中實現foo()函式
二、gcc(g++) -c -o foo.o foo.c (注意此處不需要.h標頭檔案,標頭檔案只是對庫的對外介面描述)
三、生成靜態庫: ar -r libfoo.a foo.o (靜態庫.a其實就是.o檔案的壓縮包,注意這裡不支援把.a打入.a)
生成動態庫: gcc foo.c -fpic -shared -o libfoo.so(-fpic意思是生成位置無關**,因為動態庫是執行時載入的,需要對**進行重定向,不清楚可以google一下)
四、寫個含有main函式的檔案,並呼叫foo()函式 : gcc(g++) -o test test.c -lfoo,這裡-lfoo意思是去找以lib開頭的某.so或.a檔案,預設優先找.so動態庫。
需要注意一下的是,如果是cpp引用了c的庫或.c那麼標頭檔案裡要用externc "c"關鍵字來指定按c的方式讀取(根本上是因為c和c++的函式簽名不一致,因為c++支援過載,所以按c++的方式是找不到同名的c函式的)。在使用動態庫的情況下,程式回去一些預定義的地方找.so檔案。比如/usr/lib/下,如果需要自己指定,請修改/etc/ld.so.conf檔案。並用ldconfig來重新整理cache。
3、如果我們需要檢視自己寫的庫的資訊時可以用nm來檢視,如檢視庫中有哪些函式,有哪些全域性變數,有哪些依賴別的庫的東西等等,下面我們寫乙個例子來說明一下:
[cpp]view plain
copy
print?
#include
int g1;
int g2 = 0;
static
int g3;
static
int g4=0;
const
int g5=0;
static
const
int g6 = 0;
int main(int argc, char *argv)
void foo1(){}
static
void foo2(){}
[cpp]view plain
copy
print?
void overload(int i){}
[cpp]view plain
copy
print?
void overload(float i){}
linux的nm命令可以乙個檔案中的符號列表,列出以上**gcc -c編譯出的a.o(a.a a.so)可以通過nm命令來檢視其中的符號資訊:
[cpp]view plain
copy
print?
0000000000000000 t
0000000000000000 d
0000000000000000 b
0000000000000000 r
0000000000000000 r
0000000000000000 n
0000000000000000 n
0000000000000000 b g1
0000000000000004 b g2
0000000000000008 b g3
000000000000000c b g4
000000000000001c r g5
0000000000000020 r g6
u __gxx_personality_v0
0000000000000000 t main
0000000000000000 a nm.cpp
u printf
000000000000003e t _z4foo1v
0000000000000044 t _z4foo2v
0000000000000054 t _z8overloadf
000000000000004a t _z8overloadi
0000000000000010 b _zz4maine2st
符號型別:介紹幾個最常用的,其他的如果遇到了直接google:
b --- 全域性非初始化資料段(bbs段)的符號,其值表示該符號在bss
段中的偏移,如g1
b --- 全域性static的符號,如g3
r --- const型唯讀的變數(readonly)
n --- debug用的符號
t --- 位於**區的符號,比如本檔案裡的函式main foo
t --- 位於**區的符號,一般是static函式
u --- 位於本檔案外的呼叫函式或變數符號,比如系統的printf()函式
這裡要注意的是,本人使用g++編譯的,所以是按c++的支援過載的函式風格編譯的,可以看到所有函式均帶了字首和字尾,字首代表屬於類的名字,字尾代表引數列表的型別縮寫,因為過載必須是區分引數型別,這裡也可以看出,為什麼返回值不同的函式不是過載,因為符號表裡沒有返回值的記錄。
Visual Studio除錯之符號檔案
前面在不能設定斷點的檢查步驟和visual studio除錯之斷點高階篇提到了除錯符號檔案及其作用,這篇文章我將要介紹除錯符號檔案的一些用法,如果你已經很熟悉除錯檔案的話,盡可以跳過本文。1.設定斷點,因為偵錯程式需要符號檔案提供的對映關係,將源 行的行號轉換成對應的機器 的位址。2.檢視程式堆疊,...
函式庫檔案中的符號表 nm命令
nm命令可以列出乙個函式庫檔案中的符號表。它對於靜態的函式庫和共享的函式庫都起作用。對於乙個給定的函式庫,nm命令可以列出函式庫中定義的所有符號,包括每個符號的值和型別。還可以給出在原程式中這個函式 符號 是在多少行定義的,不過這必須要求編譯該函式庫的時候加 l 選項。關於符號的型別,這裡我們再多討...
檢視 o, obj檔案符號列表,強大的nm命令
nm命令可以檢視,linux以及windows下的.o obj檔案中的符號列表,其中.o檔案可以由gcc,g 編譯得到,obj由vc編譯得到。太爽了。以後遇到undefined reference錯誤的時候就可以這個命令搞定了。舉一例子,在下面的原始碼中呼叫了乙個myprint函式 include ...