【目录】第一个动态库文件申请第二个动态库文件错误方式:直接重命名正确解决方法:patchelf工具OneMoreThing直接使用现成的第三方库(俗称:wheels)完成自己的业务功能,是很常见的事情。不知道大家有没有遇到过这样的场景:应用需要使用两个动态库中功能不同的函数,但是两个动态库的作者有心灵感应,动态库名一模一样。我应该怎么办?具体来说,我面临的问题是:在编译可执行程序时,可以通过gcc编译参数-lXXX动态链接一个动态库。但是,现在你想链接两个同名的动态库!!该怎么办?第一个动态库文件现在,假设我们正在开发一个机器人应用程序,需要使用第三方动态库中的算法。这个库的源码很简单,如下://第一个动态库源文件RobotMath.c:doublefunc0(doublearg){doubleret=arg+arg;returnret;}doublefunc1(doublearg1,doublearg2){doubleret=arg1+arg2;returnret;}动态库的编译命令为:$gcc-m32-fPIC--shared-olibRobotMath.so-Wl,--soname,libRobotMath.soRobotMath.c以上属性比较常用,请注意-Wl,--soname,libRobotMath.so,用于指定生成的动态库的SONAME,一般用在动态库的版本管理中。为方便起见,此处不添加版本信息。执行gcc命令后,得到一个动态库文件:libRobotMath.so。可以通过patchelf工具查看这个动态库文件的SONAME(在Ubuntu系统下可以直接通过apt-get安装):$patchelf--print-sonamelibRobotMath.solibRobotMath.so//第2行打印的SONAME它就是所谓的SONAME。您也可以测试它并指定其他SONAME,例如:$gcc-m32-fPIC--shared-olibRobotMath.so-Wl,--soname,libRobotMath-1.2.3.soRobotMath.c$patchelf--print-sonamelibRobotMath.solibRobotMath-1.2.3.so//上面的SONAME是第一个动态库,已经解释清楚了。让我们看一下最简单的应用程序。Application//可执行源文件:main.cexterndoublefunc0(doublearg);externdoublefunc1(doublearg1,doublearg2);intmain(intargc,char*agv[]){doublearg=1.1;doubleresult0=func0(arg);printf("result0=%lf\n",result0);doublearg1=1.1,arg2=2.2;doubleresult1=func1(arg1,arg2);printf("result1=%lf\n",result1);return0;}这段代码简直是幼儿园水平,没有太多说明,直接编译(假设动态库已经复制到和main.c同一个文件夹):$gcc-m32-omainmain.c-lRobotMath-L./-Wl,-rpath=./Execute:$./mainresult0=2.200000result1=3.300000完美!第二个动态库文件问题来了:现在应用程序需要实现另一个复杂的算法。本着偷懒的精神,终于在另一个机器人算法相关的库中找到了。这个算法。//第二个动态库源文件RobotMath.c:doublefunc2(doublearg1,doublearg2,doublearg3){doubleret=arg1*arg2*arg3;returnret;}//编译指令$gcc-m32-fPIC--shared-olibRobotMath.so-wl,--soname,libRobotMath.soRobotMath.c但是诡异的是这个算法库输出的动态库的名字其实是libRobotMath.so!与第一个算法库的文件名同名同姓。看来这个名字太吸引人了。我喜欢。如果作者直接选择另一个名字,那么什么也不会发生。如果:名字是libRobotUltra.so,那么你只需要直接复制,然后在编译执行程序的时候直接链接-lRobotUltra即可。错误方式:直接重命名。这样的话,我们可以直接重命名吗?Try:$mvlibRobotMath.solibRobotMath2.so然后将libRobotMath2.so复制到应用程序的目录下,在main.c中,调用这个库中的算法函数func2。externdoublefunc2(doublearg1,doublearg2,doublearg3);intmain(intargc,char*agv[]){//之前的其他代码//...doublearg3=1.1,arg4=2.2,arg5=3.3;doubleresult2=func2(arg3,arg4,arg5);printf("result2=%lf\n",result2);return0;}编译试试:$gcc-m32-omainmain.c-lRobotMath-lRobotMath2-L./-Wl,-rpath=.//tmp/ccDGqFkl.o:Infunction`main':main.c:(.text+0xb4):undefinedreferenceto`func2'collect2:error:ldreturned1exitstatus错误:找不到函数func2。但是libRobotMath2.so显然已经有了这个功能。不信你看:$readelf-slibRobotMath2.so|grepfunc28:0000052a69FUNCGLOBALDEFAULT11func251:0000052a69FUNCGLOBALDEFAULT11func2为什么gcc找不到?好像直接给第二个很粗鲁强行重命名动态库文件不是解决问题的正确方法!正解:patchelf工具大家还记得在第一个库中,我们是不是使用了patchelf这个小工具来查看动态库的SONAME?libRobotMath2.so改名后继续使用查看:$patchelf--print-sonamelibRobotMath2.solibRobotMath.soSONAME还是原来的名字,说明通过mv命令改名只是改变了外观,并没有改变它的心脏。熟悉文件系统的人就会知道,mv命令只是修改了inode节点中库文件的名称,而库文件实际内容所在的块存储空间并没有发生任何变化存储。动态库是ELF格式的文件。操作系统加载动态库时,根据ELF格式的标准,逐层解析文件内容。可以参考很久以前写的一篇文章:Linux系统中编译链接的基石——ELF文件:剥开它的层层,从字节码的粒度去探索。patchelf工具提供了这样一个功能:查看或修改动态库文件的内部信息,包括:SONAME、其他依赖的动态库、rpath路径信息等。$patchelf-hsyntax:patchelf[--set-interpreterFILENAME][--page-sizeSIZE][--print-interpreter][--print-soname]打印'DT_SONAME'entryof.dynamicsection.RaisesanerrorifDT_SONAMEdoesn'texist[--set-sonameSONAME]将“DT_SONAME”条目设置为SONAME。[--set-rpathRPATH][--remove-rpath][--shrink-rpath][--print-rpath][--force-rpath][--add-neededLIBRARY][--remove-neededLIBRARY][--replace-neededLIBRARYNEW_LIBRARY][--print-needed][--no-default-lib][--debug][--version]FILENAME我们可以使用--set-soname这个参数,我们修改它的SONAME:$patchelf--set-sonamelibRobotMath2.solibRobotMath2.so第一个libRobotMath2.so就是设置的SONAME名称;第二个libRobotMath2.so是指定修改哪个动态库文件SONAME;修改后查看修改是否正确:$patchelf--print-sonamelibRobotMath2.solibRobotMath2.soBingo!SONAME修改正确。再次编译可执行程序:$gcc-m32-omainmain.c-lRobotMath-lRobotMath2-L./-Wl,-rpath=./没有报错!执行:$./mainresult0=2.200000result1=3.300000result2=7.986000问题解决了!什么是另一件事?你说这种问题等一千年?你是在表达你对新词的悲伤吗?那说明你走过的路还不够长。记得2015年前后,在开发网关的时候,需要在硬件出来之前在Ubuntu(x86)平台上进行模拟。为了方便跨平台,选择了glib库,但是重新开发了一小部分源码。但是Ubuntu的桌面系统是基于GTK的(底层使用的是glib库),也就是说操作系统在启动的时候已经加载了系统目录下的glib库。然后我们的应用在编译的时候,确实可以链接到自己开发的glib库(本地文件夹下),但是在执行的时候,一直加载不成功,因为动态库的名称冲突。最后只好用patchelf工具改写动态库的名字,包括SONAME,才解决问题。本文转载自微信公众号“IOT物联网小镇”,可通过以下二维码关注。转载本文请联系物联小镇公众号。
