共享库的链接与交叉编译程序的运行问题

共享库的链接问题:奇怪的现象

在linux中进行共享库的链接时最常使用两个选项:-L选项指定库的查找目录,-l选项指定将要使用的共享库。除了使用这种标准方式进行库的链接外,还有一种方式:将共享库文件当成目标文件直接进行链接。就个人理解,这两种方式的效果应该是一样的,但是实际开发中却遇到了不一致的奇怪现象,有待大家帮忙解答,以下列出现象。

例子使用UDT库进行链接,UDT是用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开发板平台。

例子中使用到开发板提供商的方案库,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只有两个库。因此最佳的解决方法还是使用通用的共享库链接方式。

建议

链接共享库时使用通用的选项链接方式,如果行不通再考虑使用直接的目标文件链接方式。

转载于:https://my.oschina.net/shelllife/blog/164472

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值