跟我學c 中級篇 Linux下的動態庫之一

2021-10-09 14:34:50 字數 4587 閱讀 8531

在前面將動態庫的知識初步學習了一下,本篇將著重講一下linux下的動態庫的編寫和使用。在gcck和 g++中,都提供了豐富的編譯選項,用來給程式設計師編譯動態加提供較多的選擇。可是,對於新手來說,如此多的編譯選項反而會讓其感覺到迷茫。所以還是那句話,先從簡單的入手,不要上去就啃大部頭,省得忙個昏天黑地反而打擊了自己的積極性。

linux下的動態庫以.so結尾,命名方式必須是以lib開頭,中間填充自己想取得動態加的名字。比如你想生成乙個叫order的排序庫,那麼最終編譯時應該是lib+order+.so(liborder.so),這個樣子。這一點不如window做的好,它可以起任何在規範下的名字,不必要如此。

另外在庫路徑下經常可以看到以後綴*.so.2之類的方式的動態庫,其實那只是一種軟鏈結,當使用ls -al命令檢視時,可以得到下面的結果:

ld-linux-x86-64.so.2 -> /lib/x86_64-linux-gnu/ld-2.27.so
這就是說這個軟鏈結,是鏈結到後面的那個真實具體的so庫上的。通常這種用法是用來進行版本管理的,比如庫的名字為了保持一致,但為了區分不同的版本,可以軟體鏈結形成不同的符號檔案。也就是說其命名的規則可以如下:

lib + name +.so+x.y.z 後面的xyz可以用來表示主次及發行版本號等,或者自己另有約定。

另外乙個新手需要注意的是,一定要保證鏈結各方的版本一致性,這個既包括自己編寫的迭代版本,又要保證平台版本,比如,x64平台的庫和x32平台的庫就不能同時使用,否則可能編譯鏈結時就會報鏈結錯誤。

2、 -fpic編譯選項

前面已經說過,這個意思是編譯與位置無關的**。在32位上可忽略,但是在x64平台上還是要使用的。

這裡有個小細節需要注意一下,有細心的可以在一些資料中看到-fpic全小寫的這個編譯選項,這二者有什麼不同呢?相同點如前文所述都是生成與位置無關的**,重點在不同點,如果全域性偏移表(got)大小超過計算機的最大值,則-fpic不起作用,這時候兒就得用-fpic重新編譯。其中got的大小依據不同的os則有不同,比如x86上沒有限制(這意味著二者在x86是等同的),而在aarch64上為28k,其它一些具體的也有不同。換句話說,如果你在x86平台上程式設計,可以忽略這個不同。

但是為了安全起見,建議都使用-fpic來編譯自己的動態庫,防止出現跨平台使用時的一些不容易查詢的問題。

3、-w 警告資訊引數 -wl選項option傳遞給鏈結器

這個有比較多的用法,包括-wall,看名字就知道,把-w開頭的警告大部分都給合到一起了,但有一部分仍然排除。具體的可看一下gcc的資料。

-wl其實就是把相關的鏈結引數選項傳遞給鏈結器,如果option中包含逗號就分割一下。

其實,編譯過程中仍然會有不少的編譯選項和鏈結選項,整個gcc的文件有幾十頁,有興趣的可以看看,但不要太過沉迷進去。其實好多一般是用不到的。

1、動態庫的生成

動態庫的生成過程中有幾個棘手的問題,第乙個是c++的改名機制,在前面的文章裡提到過c++編譯器在編譯原始檔過程中會復用某種規則修改程式中的名字,這就需要鏈結器對其進行適應,一般有兩種方法,一種是建立名字修改的規則,也就是形成一種標準技術,但這有點為難。不過好在主流的編譯器廠商不多,而實際廣泛應用的也不過三兩種,這還可以忍受。另外一種就是像c編譯器一樣,復用一些關鍵字來告訴編譯器不要改名。

第二個在前面也提到過,就是靜態變數的初始化順序,也包括全域性變數,這就需要編寫時儘量減少靜態和全域性變數的使用。使用時,也盡量要集中它們到單獨的檔案中,不要滿天飛的定義全域性和靜態變數。對於c++可以讓類提供乙個單獨的init函式來處理這些定義順序,人為的控制順序。或者也可以採用某種特定的方式,通過程式來訪問,比如採用飽漢模式提前定義好全域性或者靜態物件,其它訪問都通過此物件來完成。

最後乙個是模板的問題,模板的引入本身是為了方便,但模板的靜態編譯又使其需要的條件更加嚴格一些,而模板例項化或者說特化的模式又會產生不同的**。基礎型別如int等還比較好處理。但是當遇到一些具體的物件型別時,就比較麻煩了,特別是在動態庫中,解決的方法有兩類,一類是預先使用弱符號來建立模板特化的**,編譯器生成所有的特化**和相對就把弱符號,在鏈結時如果沒有用到就拋棄。另外一類是,鏈結器在結束鏈結前都不考慮模板特化的**。只有當其它的鏈結任務都完成後,再檢查**,確實具體的模板特化的**,並復用編譯器生成,並插入到可執行檔案。

2、動態庫呼叫動態庫

動態庫可以呼叫動態庫,甚至可以形成鏈式結構,但這時候兒就需要考慮載入的順序了。實際應用動態庫的場景不外乎以下幾種:程式單獨呼叫乙個動態庫;程式單獨呼叫n個動態庫,但庫之間沒有聯絡;程式呼叫多個動態庫,動態庫之間有依賴,即有互相呼叫的現象。

3、例程

前文載入了乙個例程,基本可以直接移植過來,但這裡提供了一套額外的動態呼叫的方式:

#include int compare(int a,int b)

else

return 0;

}

動態呼叫的方式:

#include #include #include #include typedef int( * maxfunc)(int,int);

int main()

{ void * ph = null;

ph = dlopen("libcompare.so",rtld_lazy);

if (null == ph)

{std::cout<

g++ -rdynamic -o dc dycall.cpp -ldl

說明一下:

rdynamic用來通知鏈結器將所有符號新增到動態符號表中,以方便dlopen的實現向後的跟蹤,-ldl表示一定將dlib庫鏈結於此程式。

dlopen用來開啟相關的庫物件,通過對名字的判定來載入具體的動態庫。它有兩個引數,rtld_now表示在此函式呼叫完成所有必要後再定位而rtld_lazy表示在需要時再進行定位,另外還有rtld_local 和rtld_global 模式,用來對載入符號的限定

dlsym 用來將dlopen取得的物件來得到相關函式在物件檔案中的符號的位址

dlerror 返回上一次出現錯誤的字串錯誤

dlclose 關閉目標檔案

在上面的**中,會報func error,也就是說無法找到compare這個函式,為什麼呢,如果是c檔案用gcc編譯就不存在這個問題,如果用g++編譯就會有這個問題,怎麼辦呢?用nm命令看一下這個庫檔案:

$ nm libcompare.so

0000000000201030 b __bss_start

0000000000201030 b completed.7409

u __cxa_atexit@@glibc_2.2.5

w __cxa_finalize@@glibc_2.2.5

0000000000000640 t deregister_tm_clones

00000000000006b0 t __do_global_dtors_aux

0000000000200dd0 t __do_global_dtors_aux_fini_array_entry

0000000000201028 d __dso_handle

0000000000200dd8 d _dynamic

0000000000201030 d _edata

0000000000201038 b _end

0000000000000770 t _fini

00000000000006f0 t frame_dummy

0000000000200dc0 t __frame_dummy_init_array_entry

0000000000000868 r __frame_end__

0000000000201000 d _global_offset_table_

000000000000075a t _global__sub_i_compare.cpp

w __gmon_start__

000000000000077c r __gnu_eh_frame_hdr

00000000000005e0 t _init

w _itm_deregistertmclonetable

w _itm_registertmclonetable

0000000000000670 t register_tm_clones

0000000000201030 d __tmc_end__

0000000000000711 t _z41__static_initialization_and_destruction_0ii

00000000000006f5 t _z7compareii

u _znst8ios_base4initc1ev@@glibcxx_3.4

u _znst8ios_base4initd1ev@@glibcxx_3.4

0000000000000779 r _zstl19piecewise_construct

0000000000201031 b _zstl8__ioinit

後面會根據動態庫的應用如互相呼叫、除錯(檢視工具等)、呼叫靜態庫等逐步展開各種情形的學習,並對其一些內部機理進行分析,從小處一點一滴的學起,不要想著一步登頂,用侯捷的話來說「勿在浮沙築高台」。

跟我學c 中級篇 pimpl

private implementation,私有化實現。在c 中,由於語言本身的限制,沒有純粹的介面定義。這就導致了在介面的使用上很多c 的人員都是隨心而動。有用抽象類的純虛函式的,有直接用c型別的介面的。有乾脆提供介面類的 不一而足吧。根據實際情況,實事求是的選擇才是乙個好的標準。在c 中,大量...

跟我學c 中級篇 動態庫

在linux下建立動態庫的編譯選項有乙個 fpic,它的意思是與位置無關 position independet code 那麼它有什麼意義呢?在沒有出現這個選項前,是不是就不能建立動態庫呢。答案肯定是否定的。其實這就是上面提到的發展過程中的,如果沒有這個選項,只能完全載入副本,而如果使用了這個選項...

跟我學C 中級篇 STL的學習

c 的標準庫主要包含兩大類,首先是包含c的標準庫的,當然,為了適應c 對一些c庫進行了少許的修改和增加。最重要的當然是物件導向的c 庫 而c 庫又可以分成兩大類,即物件導向的c 庫和標準模板庫,也就是題目中的stl。另外在此基礎上,還要提醒同學們的是,除了上面的庫,在各個平台的開發廠商中,還會針對實...