2.3 如何判斷一個程式有沒有連結動態庫?
答案是用file實用程式。
file程式是用來判斷檔案型別的,在file命令下,所有檔案都會原形畢露的。
順便說一個技巧。有時在 windows下用瀏覽器下載tar.gz或tar.bz2檔案,字尾名會變成奇怪的tar.tar,到Linux有些新手就不知怎麼解壓了。但 Linux下的檔案型別並不受檔案字尾名的影響,所以我們可以先用命令file xxx.tar.tar看一下檔案型別,然後用tar加適當的引數解壓。
另外,還可以藉助程式ldd實用程式來判斷。
ldd是用來列印目標程式(由命令列引數指定)所連結的所有動態庫的資訊的,如果目標程式沒有連結動態庫,則列印“not a dynamic executable”,ldd的用法請參考manpage。
3 建立自己的庫
3.1 建立動態庫
建立檔案hello.c,內容如下:
#include
void hello(void)
{
printf("Hello World\n");
}
用命令gcc -shared hello.c -o libhello.so編譯為動態庫。可以看到,當前目錄下多了一個檔案libhello.so。
[leo@leo test]$ file libhello.so
libhello.so: ELF 32-bit LSB shared object, Intel 80386, version 1 (SYSV), not stripped
看到了吧,檔案型別是shared object了。
再編輯一個測試檔案test.c,內容如下:
int
main()
{
hello();
return 0;
}
這下可以編譯了:)
[leo@leo test]$ gcc test.c
/tmp/ccm7w6Mn.o: In function `main':
test.c:(.text+0x1d): undefined reference to `hello'
collect2: ld returned 1 exit status
連結時gcc找不到hello函式,編譯失敗:(。原因是hello在我們自己建立的庫中,如果gcc能找到那才教見鬼呢!ok,再接再厲。
[leo@leo test]$ gcc test.c -lhello
/usr/lib/gcc/i686-pc-Linux-gnu/4.0.0/../../../../i686-pc-Linux-gnu/bin/ld: cannot find -lhello
collect2: ld returned 1 exit status
[leo@leo test]$ gcc test.c -lhello -L.
[leo@leo test]$
第一次編譯直接編譯,gcc預設會連結標準c庫,但符號名hello解析不出來,故連線階段通不過了。
現在用gcc test.c -lhello -L.已經編譯成功了,預設輸出為a.out。現在來試著執行一下:
[leo@leo test]$ ./a.out
./a.out: error while loading shared libraries: libhello.so: cannot open shared object file: No such file or directory
咦,怎麼回事?原來雖然連結時連結器(dynamic linker)找到了動態庫libhello.so,但動態載入器(dynamic loader, 一般是/lib/ld-Linux.so.2)卻沒找到。再來看看ldd的輸出:
[leo@leo test]$ ldd a.out
Linux-gate.so.1 => (0xffffe000)
libhello.so => not found
libc.so.6 => /lib/libc.so.6 (0x40034000)
/lib/ld-Linux.so.2 (0x40000000)
果然如此,看到沒有,libhello.so => not found。
Linux為我們提供了兩種解決方法:
1.可以把當前路徑加入 /etc/ld.so.conf中然後執行ldconfig,或者以當前路徑為引數執行ldconfig(要有root許可權才行)。
2.把當前路徑加入環境變數LD_LIBRARY_PATH中
當然,如果你覺得不會引起混亂的話,可以直接把該庫拷入/lib,/usr/lib/等位置(無可避免,這樣做也要有許可權),這樣連結器和載入器就都可以準確的找到該庫了。
我們採用第二種方法:
[leo@leo test]$ export LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH
[leo@leo test]$ ldd a.out
Linux-gate.so.1 => (0xffffe000)
libhello.so => ./libhello.so (0x4001f000)
libc.so.6 => /lib/libc.so.6 (0x40036000)
/lib/ld-Linux.so.2 (0x40000000)
哈哈,這下ld-Linux.so.2就可以找到libhello.so這個庫了。
現在可以直接執行了:
[leo@leo test]$ ./a.out
Hello World
3.2 建立靜態庫
仍使用剛才的hello.c和test.c。
第一步,生成目標檔案。
[leo@leo test]$ gcc -c hello.c
[leo@leo test]$ ls hello.o -l
-rw-r--r-- 1 leo users 840 5月 6 12:48 hello.o
第二步,把目標檔案歸檔。
[leo@leo test]$ ar r libhello.a hello.o
ar: creating libhello.a
OK,libhello.a就是我們所建立的靜態庫了,簡單吧:)
[leo@leo test]$ file libhello.a
libhello.a: current ar archive
下面一行命令就是教你如何在程式中連結靜態庫的:
[leo@leo test]$ gcc test.c -lhello -L. -static -o hello.static
我們來用file命令比較一下用動態庫和靜態庫連結的程式的區別:
[leo@leo test]$ gcc test.c -lhello -L. -o hello.dynamic
正如前面所說,連結器預設會連結動態庫(這裡是libhello.so),所以只要把上個命令中的 -static引數去掉就可以了。
用file實用程式驗證一下是否按我們的要求生成了可執行檔案:
[leo@leo test]$ file hello.static hello.dynamic
hello.static: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), for GNU/Linux 2.6.6, statically linked, not stripped
hello.dynamic: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), for GNU/Linux 2.6.6, dynamically linked (uses shared libs), not stripped
不妨順便練習一下ldd的用法:
[leo@leo test]$ ldd hello.static hello.dynamic
hello.static:
not a dynamic executable
hello.dynamic:
Linux-gate.so.1 => (0xffffe000)
libhello.so => ./libhello.so (0x4001f000)
libc.so.6 => /lib/libc.so.6 (0x40034000)
/lib/ld-Linux.so.2 (0x40000000)
OK,看來沒有問題,那就比較一下大小先:
[leo@leo test]$ ls -l hello.[ds]*
-rwxr-xr-x 1 leo users 5911 5月 6 12:54 hello.dynamic
-rwxr-xr-x 1 leo users 628182 5月 6 12:54 hello.static
看到區別了吧,連結靜態庫的目標程式和連結動態庫的程式比起來簡直就是一個龐然大物!
這麼小的程式,很難看出執行時間的差別,不過為了完整起見,還是看一下time的輸出吧:
[leo@leo test]$ time ./hello.static
Hello World
real 0m0.001s
user 0m0.000s
sys 0m0.001s
[leo@leo test]$ time ./hello.dynamic
Hello World
real 0m0.001s
user 0m0.000s
sys 0m0.001s
如果程式比較大的話,應該效果會很明顯的。
原文地址:http://www.linuxeden.com/html/develop/20100702/103643.html