readelf ld-linux.so.2

ld-linux.so.2 是linux下的動態庫加載器/鏈接器,這篇文章主要來講一下 ld-linux.so.2 是如何和Linux 以及相關應用打交道的。


1. 什麼是 ld.linux.so ? 

很多現代應用都是通過動態編譯鏈接的,當一個 需要動態鏈接 的應用被操作系統加載時,系統必須要 定位 然後 加載它所需要的所有動態庫文件。 在Linux環境下,這項工作是由ld-linux.so.2來負責完成的,我們可以通過 ldd 命令來查看一個 應用需要哪些依賴的動態庫:

$ ldd `which ls`
      linux-gate.so.1 =>  (0xb7fff000)
      librt.so.1 => /lib/librt.so.1 (0x00b98000)
      libacl.so.1 => /lib/libacl.so.1 (0x00769000)
      libselinux.so.1 => /lib/libselinux.so.1 (0x00642000)
      libc.so.6 => /lib/libc.so.6 (0x007b2000)
      libpthread.so.0 => /lib/libpthread.so.0 (0x00920000)
      /lib/ld-linux.so.2 (0x00795000)
      libattr.so.1 => /lib/libattr.so.1 (0x00762000)
      libdl.so.2 => /lib/libdl.so.2 (0x0091a000)
      libsepol.so.1 => /lib/libsepol.so.1 (0x0065b000)

當最常見的ls小程序加載時,操作系統會將 控制權 交給 ld-linux.so 而不是 交給程序正常的進入地址。 ld-linux.so.2 會尋找然後加載所有需要的庫文件,然後再將控制權交給應用的起始入口。

上面的ls在啓動時,就需要ld-linux.so加載器將所有的動態庫加載後然後再將控制權移交給ls程序的入口。


ld-linux.so.2 man page給我們更高一層的全局介紹, 它是在 鏈接器(通常是ld)在運行狀態下的部件,用來定位和加載動態庫到應用的運行地址(或者是運行內存)當中去。通常,動態鏈接是 在連接階段當中 隱式指定的。 gcc -W1 options -L/path/included -lxxx 會將 options 傳遞到ld 然後指定相應的動態庫加載。 ELF 文件提供了相應的加載信息, GCC包含了一個特殊的 ELF 頭: INTERP, 這個 INTERP指定了 加載器的路徑,我們可以用readelf 來查看相應的程序


$ readelf -l a.out

Elf file type is EXEC (Executable file)
Entry point 0x8048310
There are 9 program headers, starting at offset 52

Program Headers:
  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
  PHDR           0x000034 0x08048034 0x08048034 0x00120 0x00120 R E 0x4
  INTERP         0x000154 0x08048154 0x08048154 0x00013 0x00013 R   0x1
      [Requesting program interpreter: /lib/ld-linux.so.2]
  LOAD           0x000000 0x08048000 0x08048000 0x004cc 0x004cc R E 0x1000
  LOAD           0x000f0c 0x08049f0c 0x08049f0c 0x0010c 0x00110 RW  0x1000
. . .

ELF 規格要求,假如 PT_INTERP 存在的話,操作系統必須創建這個 interpreter文件的運行映射,而不是這個程序本身, 控制權會交給這個interpreter,用來定位和加載所有的動態庫,




ld-linux.so查找共享庫的順序

Glibc安裝的庫中有一個爲ld-linux.so.X,其中X爲一個數字,在不同的平臺上名字也會不同。可以用ldd查看:

#ldd /bin/cat
linux-gate.so.1 => (0x00bfe000)
libc.so.6 => /lib/libc.so.6 (0x00a4a000)
/lib/ld-linux.so.2 (0x00a28000)

最後一個沒有=>的就是。其中第一個不是實際的庫文件,你是找不到的,它是一個虛擬庫文件用於和kernel交互。

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在可執行的目標文件中被指定,可用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中

編譯時,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


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章