動態連結 VS 靜態聯結
在 Linux 中
執行檔我們可以編程成靜態聯結以及動態連結
以下我們舉一個簡短的程序作為例子:
#include
int main()
{
printf(
test
);
}
若我們執行 :
[root@hlchou /root]# gcc test
c
o test
所產生出來的執行檔 test
預設為使用動態函式庫
所以我們可以用以下的指令 :
[root@hlchou /root]# ldd test
libc
so
=> /lib/libc
so
(
x
)
/lib/ld
linux
so
=> /lib/ld
linux
so
(
x
)
來得知目前該執行檔共用了哪些動態函式庫
以我們所舉的 test 執行檔來說
共用了兩個動態函式庫
分別為 libc
so
與 ld
linux
so
我們還可以透過下面的 file 指令
來得知該執行檔的相關屬性
如下
[root@hlchou /root]# file test
test: ELF
bit LSB executable
Intel
version
dynamically
linked (use s shared libs)
not stripped
not stripped 表示這個執行檔還沒有透過 strip 指令來把執行時用不到的符號
以及相關除錯的資訊刪除
舉個例子來說
目前這個test 執行檔大小約為
bytes
[root@hlchou /root]# ls
l test
rwxr
xr
x
root root
Oct
:
test
經過strip後
則變為
bytes
[root@hlchou /root]# strip test
[root@hlchou /root]# ls
l test
rwxr
xr
x
root root
Oct
:
test
不過讀者必須注意到一點
經過 strip 過的執行檔
就無法透過其它的除錯軟件從裡面取得函式在編程時所附的相關資訊
這些資訊對我們在除錯軟件時
可以提供不少的幫助
各位在應用上請自行注意
相對於編程出來使用動態函式庫的執行檔 test
我們也可以做出靜態聯結的執行檔 test
[root@hlchou /root]# gcc
static test
c
o test
透過指令 ldd
我們可以確定執行檔 test 並沒有使用到動態函式庫
[root@hlchou /root]# ldd test
not a dynamic executable
再透過指令 file
可以注意到 test 目前為 statically linked
且亦尚未經過 strip
[root@hlchou /root]# file test
test: ELF
bit LSB executable
Intel
version
statically
linked
not stripped
相信大夥都會好奇
使用靜態聯結
且又沒有經過 strip 刪去不必要的符號的執行檔的大小會是多少
透過 ls
l來看
我們發現大小變成
bytes 比起靜態聯結的執行檔大了相當多
[root@hlchou /root]# ls
l test
rwxr
xr
x
root root
Oct
:
test
若再經過 strip
則檔案大小變為
bytes
[root@hlchou /root]# strip test
[root@hlchou /root]# ls
l test
rwxr
xr
x
root root
Oct
:
test
與使用動態函式庫的執行檔 test 比較起來
大了約
倍 (
/
)
因此
整體來說
在使用的環境中使用動態函式庫並且經過 strip 處理的話
可以讓整體的空間較為精簡
許多執行檔都會用到同一組的函式庫
像 libc 中的函式是每個執行檔都會使用到的
若是使用動態函式庫
則可以盡量減少同樣的函式庫內容重復存在系統中
進而達到節省空間的目的
筆者一年前曾寫過一個可以用來刪去動態函式庫中不必要函式的工具
針對這個只用到了 printf 的程序來產生新的 libc
so 的話
我們可以得到一個精簡過的 libc
so 大小約為
bytes
[root@hlchoua lib]# ls
l libc
so*
rwxr
xr
x
root root
Nov
:
libc
so
lrwxrwxrwx
root root
Nov
:
libc
so
> libc
so
與靜態聯結的執行檔大小
bytes 比較起來
若是在這個環境中使用了動態函式庫的話成本約為
+
=
bytes
不過這是只有一個執行檔的情況下
使用動態函式庫的環境會小輸給使用靜態聯結的環境
在一個基本的 Linux 環境中
如果大量的使用動態函式庫的話
像是有
個以上的執行檔的話
那用動態函式庫的成本就大大的降低了
像如果兩個執行檔都只用到了 printf
那靜態聯結的成本為
*
=
bytes
而使用動態函式庫的成本為
*
+
=
bytes
兩者相差約一倍
很明顯的
我們可以看到動態函式庫在 Linux 環境中所發揮的妙用
它大幅的降低了整體環境的持有成本
提高了環境空間的利用率
ld
linux
so
在 RedHat
中
我們可以在 /lib 或是 /usr/lib 目錄底下找到許多系統上所安裝的動態函式庫
在文章的這個部分
筆者將把整個函式庫大略的架構作一個說明
其實 Linux 跟 Windows 一樣
提供了一組很基本的動態函式庫
在 Windows 上面我們知道 kernel
dll 提供了其它動態函式庫基本的函式呼叫
而在 Linux 上面則透過 ld
linux
so
提供了其它動態函式庫基本的函式
在筆者電腦的 RedHat
上
ld
linux
so
是透過 link 到 ld
so(這部分需視各人所使用的 glibc 版本不同而定)
rwxr
xr
x
root root
Jan
:
ld
so
lrwxrwxrwx
root root
Jan
:
ld
linux
so
> ld
so
ld
linux
so 是屬於 Glibc (GNU C Library) 套件的一部分
只要是使用 Glibc 動態函式庫的環境
就可以見到 ld
linux
so 的蹤影
接下來
我們透過指令 ldd 來驗證出各個函式庫間的階層關系
首先如下圖我們執行了
ldd ls
ldd pwd
與
ldd vi
可以看出各個執行檔呼叫了哪些動態函式庫
像執行檔 ls 呼叫了 /lib/libc
so
(
x
)與 /lib/ld
linux
so
(
x
)
而括號內的數字為該函式庫載入記憶體的位置
在本文的稍後
會介紹到函式庫載入時的細節
到時讀者會有更深入的了解
其實我們不難發現
在 Linux 上使用動態函式庫的執行檔
幾乎都會去呼叫 libc
so
與 ld
linux
so
這兩個動態函式庫
筆者過去修改 Glibc 的套件時
也了解到在 Linux 中函式庫的關系
ld
linux
so
算是最底層的動態函式庫
它本身為靜態聯結
主要的工作是提供基本的函式給其他的函式庫
而我們最常會呼叫的 libc
so
則是以 ld
linux
so
為基礎的一個架構完成的動態函式庫
它幾乎負責了所有我們常用的標准 C 函式庫
像是我們在 Linux 下寫的 Socket 程序
其中的connect()
bind()
send()
之類的函式
都是由 libc
so
所提供的
也因此
libc
so
的大小也是相當可觀的
在 RedHat
中經過 strip 後
大小約為
bytes
[root@hlchoua /root]# ldd /bin/ls
libc
so
=> /lib/libc
so
(
x
)
/lib/ld
linux
so
=> /lib/ld
linux
so
(
x
)
[root@hlchoua /root]# ldd /bin/pwd
libc
so
=> /lib/libc
so
(
x
)
/lib/ld
linux
so
=> /lib/ld
linux
so
(
x
)
[root@hlchoua /root]# ldd /bin/vi
libtermcap
so
=> /lib/libtermcap
so
(
x
)
libc
so
=> /lib/libc
so
(
x
b
)
/lib/ld
linux
so
=> /lib/ld
linux
so
(
x
)
如下
我們透過 ldd 驗證 vi 所用到的動態函式庫 /lib/libtermcap
so
它本身是呼叫了 libc
so
的函式所組成的
[root@hlchoua /root]# ldd /lib/libtermcap
so
libc
so
=> /lib/libc
so
(
x
)
/lib/ld
linux
so
=> /lib/ld
linux
so
(
x
)
接下來
我們依序測試了 /lib/libc
so
與 /lib/ld
linux
so
[root@hlchoua /root]# ldd /lib/libc
so
/lib/ld
linux
so
=> /lib/ld
linux
so
(
x
)
[root@hlchoua /root]# ldd /lib/ld
linux
so
statically linked
我們可以整理以上的結論
畫成如下的一個架構圖
在這個圖中
我們可以清楚的明白 ld
linux
so
負責了最基礎的函式
而 libc
so
再根據這些基本的函式架構了完整的 C 函式庫
供其它的動態函式庫或是應用程序來呼叫
透過筆者所寫的一個 ELF 工具程序(注二)
我們也可以清楚的看到 libc
so
呼叫了 ld
linux
so
哪些函式
[root@hlchoua /root]# /I
elf /lib/libc
so
|more
========================================================
open_target_file:/lib/libc
so
==>ld
linux
so
__register_frame_table
cfsetispeed
xdr_int
_t
utmpname
_dl_global_scope_alloc
__strcasestr
hdestroy_r
rename
__iswctype_l
__sigaddset
xdr_callmsg
pthread_setcancelstate
xdr_union
__wcstoul_internal
setttyent
strrchr
__sysv_signal
┅(more)
From:http://tw.wingwit.com/Article/program/Oracle/201311/16795.html