在linux中進行共享庫的連接時最常使用兩個選項:-L選項指定庫的查找目錄,-l選項指定將要使用的共享庫。除了使用這種標準方式進行庫的連接外,還有一種方式:將共享庫文件當成目標文件直接進行連接。就我的理解,這兩種方式的效果應該是同樣的,可是實際開發中卻遇到了不一致的奇怪現象,有待你們幫忙解答,如下列出現象。 linux
例子使用UDT庫進行連接,UDT是用C++語言寫的一個網絡通信庫。使用第一種方式的連接選項和結果以下: c++
# Makefile中的LIBS選項 LIBS = -L../../UDT4/src/ -ludt -lpthread -lstdc++ -lm # make構建結果 ~/work/udt/udt_server_client/server$ make clean;make rm -rf server.o udtserver gcc -Wall -g -O2 -c -o server.o server.c gcc -Wall -g -O2 -o udtserver server.o -L../../UDT4/src/ -ludt -lpthread -lstdc++ -lm server.o: In function `recv_data': ~/work/udt/udt_server_client/server/server.c:138: undefined reference to `udtp_udzero' ~/work/udt/udt_server_client/server/server.c:139: undefined reference to `udtp_udset' ~/udt/udt_server_client/server/server.c:142: undefined reference to `udtp_select' ......... collect2: error: ld returned 1 exit status make: *** [udtserver] Error 1 # 共享庫符號check ~/work/udt/udt_server_client/server$ nm ../../UDT4/src/libudt.a |grep udtp_udzero 00000530 T udtp_udzero ~/work/udt/udt_server_client/server$ nm ../../UDT4/src/libudt.so |grep udtp_udzero 0002db90 t udtp_udzero使用第二種方式進行連接的選項和結果:
# Makefile選項 LIBS = ../../UDT4/src/libudt.a -lpthread -lstdc++ -lm ~/work/udt/udt_server_client/server$ make clean;make rm -rf server.o udtserver gcc -Wall -g -O2 -c -o server.o server.c gcc -Wall -g -O2 -o udtserver server.o ../../UDT4/src/libudt.a -lpthread -lstdc++ -lm # 共享庫符號check ~/work/udt/udt_server_client/server$ nm ../../UDT4/src/libudt.a |grep udtp_udzero 00000530 T udtp_udzero ~/work/udt/udt_server_client/server$ nm ../../UDT4/src/libudt.so |grep udtp_udzero 0002db90 t udtp_udzero第一種方式出現符號找不到的問題,而第二種方式卻OK。除了UDT庫的使用例子外,在開發中使用到FFmpeg庫時也遇到這個問題,昨天同事構建使用ncurses庫時也遇到了一樣的問題。我沒法解釋清楚爲何,但只能選擇第二種方式進行連接和使用庫。
上面的例子是第一種連接方式不行,而第二種OK,而這裏的現象則是第二種方式不行,而第一種方式OK。不過這裏與上面的區別在於:上面顯式使用靜態庫,並且是PC平臺,而這裏使用動態庫,是ARM開發板平臺。 shell
例子中使用到開發板提供商的方案庫,Makefile中的庫連接選項爲: 服務器
LIBS := $(AKUIO_PATH)/lib/libakuio.so \ $(AKUIO_PATH)/lib/libak2dsys.so \ $(AKUIO_PATH)/lib/image.lib \ $(AKUIO_PATH)/lib/libakuio.so.0 \ $(AKUIO_PATH)/lib/libak2dsys.so.0.1.0 \ $(AKUIO_PATH)/lib/libak2dsys.so \ $(AKUIO_PATH)/lib/libakuio.so \ $(AKUIO_PATH)/lib/libakdp.so \ $(AKUIO_PATH)/lib/libakgui.so \ $(AKUIO_PATH)/lib/libakstream.so \ $(AKUIO_PATH)/lib/libakaec2.a \ $(AKUIO_PATH)/lib/libakimage.so \ $(ALSA_PATH)/usr/lib/libasound.so.2.0.0 \ $(AKMEDIA_PATH)/usr/lib/libakaudioAdpcmEnc.so.0.1.0在Ubuntu12.04服務器上交叉編譯能夠編譯和連接成功,在ARM開發板上能夠正常運行,可是要保證這些庫在開發板上的存在路徑和交叉編譯時PC上的庫存在路徑徹底一致,不然會出現找不到共享庫的問題,即便這些庫在開發板的/usr/lib目錄下已經存在,程序依然找不到它們:
[root@anyka /home/sea/system]$ ./ipcr002 ./ipcr002: error while loading shared libraries: /home/sea/work/rock/rock/system/../media/ak_lib/akuiolib/lib/libakdp.so: cannot open shared object file: No such file or directory [root@anyka /home/sea/system]$find /usr/lib -name libakdp.so /usr/lib/libakdp.so可是若是將共享庫的連接方式改爲第一種方式,則不會出現這個問題,Makefile中庫選項:
LIBS := $(AKUIO_PATH)/lib/image.lib \ -L$(AKUIO_PATH)/lib -lakuio -lak2dsys \ -lakdp -lakgui -lakstream -lakaec2 -lakimage \ -L$(ALSA_PATH)/usr/lib -lasound \ -L$(AKMEDIA_PATH)/usr/lib -lakaudioAdpcmEnc此時交叉編譯、連接、運行一切都沒有問題。雖然問題解決了,但我仍是不太理解其中的原理。
這裏的問題除了使用第一種連接方式外,還有另一種解決方案,雖然比較笨拙,但在個人上一個項目中使用嵌入式QT庫時是使用的這種方式:在開發板上創建一個同名的軟鏈接到共享庫所在的位置。以下: 網絡
[root@anyka /home/sea/system]$ mkdir -p /home/sea/work/rock/rock/media/ak_lib/akuiolib/lib [root@anyka /home/sea/system]$ mkdir -p /home/sea/work/rock/rock/system [root@anyka /home/sea/system]$ ln -s /usr/lib/libakdp.so /home/sea/work/rock/rock/media/ak_lib/akuiolib/lib/libakdp.so [root@anyka /home/sea/system]$ ./ipcr002 ./ipcr002: error while loading shared libraries: /home/sea/work/rock/rock/system/../media/ak_lib/akuiolib/lib/libakgui.so: cannot open shared object file: No such file or directory
如今能找到libakdp.so動態庫了,下一個找不到的是libakgui.so庫,只是對每一個庫創建軟鏈接有點繁瑣,當時嵌入式QT只有兩個庫。所以最佳的解決方法仍是使用通用的共享庫連接方式。 ui
連接共享庫時使用通用的選項連接方式,若是行不通再考慮使用直接的目標文件連接方式。 spa