基礎範例

開發時,設置LIBRARY_PATH,以便gcc能夠找到編譯時需要的動態鏈接庫。

發佈時,設置LD_LIBRARY_PATH,以便程序加載運行時能夠自動找到需要的動態鏈接庫。

linux下運行時連結庫的路徑順序

Linux添加環境變量與GCC編譯器添加INCLUDE與LIB環境變量

ld可以叫靜態連接器,ld.so可以叫動態連接器

編譯時用的連結器是ld,而運行時用的連結器是/lib/ld-linux.so.2.

ld-linux.so是專門負責尋找庫文件的庫。 以cat為例,cat首先告訴ld-linux.so它需要libc.so.6這個庫文件,ld-linux.so將按一定順序找到libc.so.6庫再給cat調用。

那ld-linux.so又是怎麼找到的呢? 其實不用找,ld-linux.so的位置是寫死在程序中的,gcc在編譯程序時就寫死在裡面了。Gcc寫到程序中ld-linux.so的位置是可以改變的,通過修改gcc的spec文件。

編譯時,ld-linux.so查找共享庫的順序

1)ld-linux.so.6由gcc的spec文件中所設定 
(2)gcc --print-search-dirs所打印出的路徑,主要是libgcc_s.so等庫。可以通過GCC_EXEC_PREFIX來設定 
(3)LIBRARY_PATH環境變量中所設定的路徑,或編譯的命令行中指定的-L/usr/local/lib 
(4)binutils中的ld所設定的缺省搜索路徑順序,編譯binutils時指定。(可以通過「ld --verbose | grep SEARCH」來查看) 
(5)二進製程序的搜索路徑順序為PATH環境變量中所設定。一般/usr/local/bin高於/usr/bin
(6)編譯時的頭文件的搜索路徑順序,與library的查找順序類似。一般/usr/local/include高於/usr/include

運行時,ld-linux.so查找共享庫的順序:

1)ld-linux.so.6在可執行的目標文件中被指定,可用readelf命令查看 
(2)ld-linux.so.6缺省在/usr/lib和lib中搜索;當glibc安裝到/usr/local下時,它查找/usr/local/lib
(3)LD_LIBRARY_PATH環境變量中所設定的路徑 
(4)/etc/ld.so.conf(或/usr/local/etc/ld.so.conf)中所指定的路徑,由ldconfig生成二進制的ld.so.cache中
  • d2.c
#include <stdio.h>

int p = 3;
void print()
{
    printf("This is the second dll src!\n");
}
  • tdl.c
#include <dlfcn.h>
#include <stdlib.h>
#include <stdio.h>

int main()
{
    print();

    return 0;
}
export  LD_LIBRARY_PATH=LD_LIBRARY_PATH:./

CC = gcc

all: dl.so 
    #$(CC) -o main tdl.c -L./ -ldl 
    $(CC) -o main tdl.c -L./ -ldl -Wl,-rpath=/home/shihyu/dll 
    #$(CC) -o main tdl.c ./libdl.so 
dl.so:
    $(CC) -O -fPIC -shared -o libdl.so d2.c 

.PHONY: clean
clean:                             
    @rm -rf *.o *.so main
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/xxxx/share_lib_path

-L./ -ldl

使用 readelf -d 查詢ELF 的 share lib 路徑

readelf -d main  

Dynamic section at offset 0xe18 contains 25 entries:
  Tag        Type                         Name/Value
 0x0000000000000001 (NEEDED)             Shared library: [libdl.so]
 0x0000000000000001 (NEEDED)             Shared library: [libc.so.6]

執行期 Shared library: [libdl.so]未指定路徑, 需要使用 LD_LIBRARY_PATH 指令

./libdl.so // 編譯跟執行期綁死在目前路徑

gcc編譯指定寫死路徑 Shared library: [./libdl.so] 靈活性不好

readelf -d main  

Dynamic section at offset 0xe18 contains 25 entries:
  Tag        Type                         Name/Value
 0x0000000000000001 (NEEDED)             Shared library: [./libdl.so]
 0x0000000000000001 (NEEDED)             Shared library: [libc.so.6]

-Wl,-rpath=/home/shihyu/dll

or cmake -DCMAKE_INSTALL_PREFIX=/home/shihyu/.mybin/stlink -DCMAKE_EXE_LINKER_FLAGS="-Wl,-rpath,/home/shihyu/.mybin/stlink/lib" ..

  • 動態庫路徑寫入到elf檔案 // (RPATH) Library rpath: [/home/shihyu/dll]
readelf -d main 

Dynamic section at offset 0xe08 contains 26 entries:
  Tag        Type                         Name/Value
 0x0000000000000001 (NEEDED)             Shared library: [libdl.so]
 0x0000000000000001 (NEEDED)             Shared library: [libc.so.6]
 0x000000000000000f (RPATH)              Library rpath: [/home/shihyu/dll]

-Wl,-rpath 使用時機

在C/C++程序裡經常會調用到外部庫函數,最常用的方法莫過於export LD_LIBRARY_PATH,不過使用它存在一些弊端,可能會影響到其它程序的運行。在經歷的大項目中就遇到過,兩個模塊同時使用一外部動態庫,而且版本還有差異,導致其中一模塊出錯,兩模塊是不同時期不同人員分別開發,修正起來費時費力。

對於上述問題,一個比較好的方法是在程序編譯的時候加上參數-Wl,-rpath,指定編譯好的程序在運行時動態庫的目錄。這種方法會將動態庫路徑寫入到elf文件中去