高級C/C++編譯技術之讀書筆記(四)之定位庫文件

                                                                                    

  最近有幸閱讀了《高級C/C++編譯技術》深受啓發,該書深刻淺出地講解了構建過程(編譯、連接)中的各類細節,從多個角度展現了程序與庫文件或代碼的集成方法,提出了面向代碼複用和系統集成的軟件架構設計方法,以及系統開發過程當中疑難問題的解決方案。
  如下將回頭記錄下其中的關鍵要點,以便後面查閱。

本節思惟導圖

1. linux庫命名規則

(1)靜態庫:lib+<library name>. + alinux

(2)動態庫:lib+<<library name> + .so + <library version information>ios

(3)動態庫的版本信息算法

  dynamic libaray version information = <M>.<m>.<p>緩存

  M:主版本號架構

  m:次版本號函數

  p:補丁(很小的代碼改動)版本號工具

(4)動態庫的soname測試

  soname = lib+<libaray name>+.so+Mspa

  動態庫的soname一般由鏈接器嵌入二進制文件的專有ELF字段中,一般用特定的鏈接器選項,將表示庫soname的字符串傳遞給連接器操作系統

g++ -shared <list of object files> -Wl, -soname, libfoo.so.1 -o libfoo.so.1.0.0

注意:(1)-Wl選項告訴編譯器將後面的參數傳遞給連接器

   (2)凡是間接調用連接器時,咱們須要在連接器參數前追加「Wl,」

2. 構建過程當中庫文件定位規則

  在Linux中用-L和-l選項來指定構建過程當中庫文件路徑

(1)將完整的庫文件路徑分紅兩個部分:目錄路徑和庫文件名

(2)將目錄路徑添加到-L鏈接器選項後面,並傳遞給連接器

(3)將庫文件名添加到-l參數後面,並傳遞給鏈接器

g++ main.o -L../sharedlib -lwokingdemo -o demo

在使用g++命令行一次性完整編譯連接兩個過程時,應在在連接器前添加-Wl,

g++ -Wall -fPIC main.cpp -Wl, -L../sharedlib -Wl, -lworkingdeno -o demo

3. 運行時動態庫文件定位規則

  對於經驗不足的linux軟件工程師常常會遇到找到不動態庫的狀況而不知所措,主要是對如下內容不夠了解。

  動態庫運行時搜索算法由一組規則約束,按照優先級從高到低列出

(1)預加載庫

  毫無疑問,預加載庫應該擁有最高的搜索優先級,裝載器會首先加載這些庫,而後纔開始搜索其它庫,有兩種方法能夠指定預加載庫

a:經過設置LD_PRELOAD環境變量

export LD_PRELOAD=/home/fate/shareddir/libtest.so:$LD_PRELOAD

b:經過修改、etc/ld.so.preload文件

指定預加載庫並不符合標準的設計規範。相反,該方案僅用於特殊狀況,好比設計壓力測試、診斷已經對原始代碼的緊急補丁等

(2)rpath

  rpath和runpath均可供咱們使用,可是runpath在運行時搜索優先級列表中賦予了更高的優先級,只有在runpath缺失的狀況下,rpath纔是linux裝載器剩餘的搜索路徑信息中具備最高優先級的。

  但若是ELF二進制文件的runpath(DT_RUNPATH)字段是非空的,那麼rpath會被忽略

g++ -Wl, -R/home/fate/shared/ -ltestlibrary

  或者,也能夠用LD_RUN_PATH環境變量來設置rpath

export LD_RUN_PATH=/home/fate/shared:$LD_RUN_PATH

(3)LD_LIBARAY_PATH

  從庫搜索概念發展初期開始,開發人員就但願可使用一種臨時應急的有效機制來驗證它們的設計,經過特定的環境變量(LD_LIBRARY_PATH)就能解決咱們遇到的問題,當沒有rpath時,該路徑就是路徑搜索信息中優先級最高的

export LD_LIBRARY_PATH=/home/fate/shared/:$LD_LIBRARY_PATH
注意:該機制只應用於實驗目的,軟件產品的產品版本不該該依賴於這種機制

 (4)runpath

  設置runpath的方法和設置rpath的方法很是類似,爲了傳遞-R或-rpath連接器選項,須要使用額外的--enable-new-dtags連接選項

g++ -Wl,-R/home/fate/shared/ -Wl, --enable-new-dtags -ltestlibrary

(5)ldconfig緩存

  一種標準的代碼部署過程是基於運行linux的ldconfig工具,ldconfig會將指定的目錄路徑插入動態庫搜索列表中,該列表維護在文件/etc/ld.so.conf中。一樣地,系統會掃描新加入的目錄路徑,其結果是將發現的庫文件名添加到庫文件名列表中,該表維護在/etc/ld.so.cache文件中

4. 默認庫文件路徑

  /lib和/usr/lib是linux操做系統保存動態庫的兩個默認路徑

5. 優先級方案小節

  總的來講,優先級方案能夠概括爲如下兩種版本:

(1)若是指定了RUNPATH(即LD_RUNPATH字段非空)

  a. LD_LIBRARY_PATH

  b. runpath(LD_RUNPATH)

  c. ld.so.cache

  d. 默認路徑

(2)若是沒有指定runpath

  a. 被加載庫的RPATh,而後是二進制文件的RPATH,直到可執行文件或者動態庫將這些庫所有加載完畢爲止

  b. LD_LIBRAYR_PATH

  c. ld.so.cache

  d. 默認路徑

6.示例:動態庫的建立和函數調用

linux_so.h

#pragma once

#ifdef __cplusplus
extern "C"
{
#endif
void fun();
#ifdef __cplusplus
}
#endif

linux_so.c

#include "linux_so.h"

#include <iostream>
using namespace std;

void fun()
{
    cout << "print fun" << endl;
}

main.c

#include "stdio.h"

#include "linux_so.h"

int main()
{
    fun();
    return 0;
}

編譯、連接

g++ -Wall -g -o0 -fPIC -shared linux_so.c -o liblinux_so.so

 g++ -Wall -g -o0 -fPIC main.c -Wl,-L./ -Wl,-llinux_so -o out

  export LD_LIBRARY_PATH=/home/fate/sharedir/dlltest/:$LD_LIBRAYR_PATH

 輸出

相關文章
相關標籤/搜索