ldd 以及 ld-linux.so.2


最近跟編譯工具干上了,可能是問題積累集中爆發的結果。

今天對 ld-linux.so.x 有很大興趣,想對它多些了解,遂百度之。發現了指令 ldd。

 

關於 ldd

其實 ldd 是一個腳本,並不是一個二進制文件。

它的原理很簡單:當環境變量 LD_TRACE_LOADED_OBJECTS 不為空時,只顯示依賴關系,不執行程序。我們可以手動試試。

ldd 開啟了一個獨立的運行空間,設置了環境變量,然后執行程序,把執行的結果返回。

其他的變量(和值)分別對應一些選項:
  -d, --data-relocs -》 LD_WARN=yes
  -r, --function-relocs -》LD_WARN和LD_BIND_NOW=yes
  -u, --unused -》 LD_DEBUG=“unused”
  -v, --verbose -》 LD_VERBOSE=yes
LD_TRACE_LOADED_OBJECTS為必要環境變量,其他視具體情況。

 

編譯 dummy.c

繼續使用 dummy.c 對工具鏈進行實驗。

$ echo 'main(){}' > dummy.c
$ gcc dummy.c -o demo
$ ldd demo 
    linux-gate.so.1 =>  (0x00a6c000)
    libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0x00ad5000)
    /lib/ld-linux.so.2 (0x00a71000)

所以,最簡單沒有任何任務的 dummy.c ,需要這 3 個動態庫才能執行:

1. linux-gate.so.1 這個庫無法在文件下系統中找到。它只是一個虛擬的動態鏈接庫(VDSO, Virtual Dynamically Shared Object),是一個完整的 elf shared object,是內核鏡像中的某個特定頁;在使用 exec() 執行新的鏡像時,linux-gate.so 被頁面映射到程序的進程空間中(process's memmory)。想對它進一步了解,可以閱讀參考 1 。

2. libc.so.6 標准 C 庫。就是我們在制作工具鏈時編譯出來的那一個。

3. ld-linux.so.2 動態連接的實際執行者。

在手上的 armbox 中進行上面實驗,並沒有 linux-gate.so.1:

$ ldd demo 
    libc.so.6 => /lib/arm-linux-gnueabihf/libc.so.6 (0x76e13000)
    /lib/ld-linux-armhf.so.3 (0x76f27000)

今天的主角是 ld-linux.so.x ,這里就不對 linux-gate.so.1 進行深究了。

 

ELF 中的 PT_INTERP

如果只是說 ld-linux.so.x 是動態鏈接庫的解釋器,那么,未免有些單調了。我先從追蹤 gcc 編譯出來程序開始。

$ strace ./demo
execve("./demo", ["./demo"], [/* 41 vars */]) = 0
brk(0)                                  = 0x9712000
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
mmap2(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7795000
...

這里可以看到,執行文件是交給 execve() 來解釋執行的。那么,這里的問題就變成了 execve() 怎么解釋這 demo 的問題。沒有特殊設置,那么,這里的 demo 肯定是 ELF 格式的文件。。。所以,這里就最后變成了,execve() 怎么解釋及執行 ELF 文件的問題。

不想去一點點摳 ELF 的具體結構,所以 man execv() 看了一下。有如下描述:

 If the executable is a dynamically linked ELF executable, the interpreter named the PT_INTERP segment is  used  to load  the  needed  shared  libraries. This interpreter is typically /lib/ld-linux.so.1 for binaries linked with the Linux libc 5, or /lib/ld-linux.so.2 for binaries linked with the glibc 2 

 execve() 是 <unistd.h> 中的函數。

使用 readelf 查看我們編譯出的二進制文件:

Elf file type is EXEC (Executable file)
Entry point 0x8048300
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]
...

這里看到的內容和我們上面了解到的是一致的。

所以,我們在這里得出結論:1) 使用 ld-linux.so.* 作為解釋器,是寫在二進制文件中的,比如上面編譯好的 demo 中。另外的,2) 其它庫的查找和加載,則是 ld-linux.so.* 完成的。

 

ld-linux.so.* 如何加載庫?

目前已知的方法是,使用環境變量 LD_LIBRARY_PATH=/lib/ 來定位庫的位置。待探索。

 

參考:

1. http://www.trilithium.com/johan/2005/08/linux-gate/

2. man execve

3. man elf


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM