TypeCodes

解决make编译链接动态库错误:libxx.so: undefined reference to xx

最近在写一个Makefile,调试时遇到了libsrcpbl.so: undefined reference to gcProgramName的问题。在这个Makefile脚本里面,终极目标是通过链接一个自定义的动态库libsrcpbl.so生成一个ELF目标文件。

由于链接生成libsrcpbl.so动态库的.o文件比较多,无法定位具体的错误程序文件和位置,所以折腾了较长时间。

1 场景再现

为了能快速定位出错的位置,自己写了一个很简单的测试程序test.c,该程序调用了libsrcpbl.so动态库中的一个自定义的公共头文件pbl_global.h

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
/** 
 * @FileName    tst.c
 * @Describe    a simple test program to find out the reason of dynamic library undefined reference.
 * @Author      vfhky 2015.08.12 https://typecodes.com/cseries/libraryundefinedref.html
 */
#include <stdio.h>
#include "pbl_global.h"     //为减少“干扰”,还可以去掉这个头文件引用

int main( int argc, char ** argv )
{
    printf( "exit\n" );
    return 0;
}

接着使用make命令编译链接生成目标ELF文件test,但是在ld链接自定义的动态库libsrcpbl.so的时候报错。为了减少干扰,还可以把pbl_global.h头文件引用去掉。也就是tst.c中是一个及其普通的程序,与libsrcpbl.so中的数据完全没关系,但是还是报出同样的错误:

[root@typecodes test]# make
gcc -std=c99 -D_GNU_SOURCE  -g -Wall  -I /root/gcc_test/tcp_msg/include/ -I /root/gcc_test/tcp_msg/include/CommAPI/ -I /root/gcc_test/tcp_msg/include/pbl/ -o /root/gcc_test/tcp_msg/src/test/tst.o -c tst.c
g++ -std=c99 -D_GNU_SOURCE   /root/gcc_test/tcp_msg/src/test/tst.o -L /root/gcc_test/tcp_msg/lib  -lsrcpbl -Xlinker "-("  -Xlinker "-)" -o /root/gcc_test/tcp_msg/bin/test
/root/gcc_test/tcp_msg/lib/libsrcpbl.so: undefined reference to `gcProgramName'
collect2: error: ld returned 1 exit status
make: *** [/root/gcc_test/tcp_msg/bin/test] Error 1

ld链接时报错:undefined reference

2 查找 libsrcpbl.so: undefined reference to gcProgramName 的原因

通过make显示的内容,基本判断Makefile文件是正确的。用nm libsrcpbl.so命令查看一下链接的动态库,结果如下:

##### 使用nm查看动态库
[root@typecodes test]# nm -A /root/gcc_test/tcp_msg/lib/libsrcpbl.so|grep gcProgramName
/root/gcc_test/tcp_msg/lib/libsrcpbl.so:                 U gcProgramName
[root@typecodes test]#

使用nm查看动态库中的符号

根据上图中的信息,结合前文《Unix系统中nm命令展示目标文件符号的方法》nm命令的man手册关于符号U的说明,推测可能原因:libsrcpbl.so动态库文件中存在gcProgramName符号(即变量),需要从test.o对象文件中链接进来,但是test.c程序中没有定义gcProgramName变量,所以报错了。

于是查看原工程中的头文件pbl_global.h,果然发现了gcProgramName是一个外部全局变量,其声明如下:

extern char gcProgramName[20 + 1];

接着发现生成这个libsrcpbl.so动态库的一个源程序中,通过引用头文件pbl_global.h,然后直接使用了这个外部全局变量gcProgramName,因此需要在tst.c对这个变量进行定义。

2 解决方法

找到原因后,解决就很简单了:只要在test.c程序中定义这个外部全局变量gcProgramName即可。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
#include <stdio.h>

//定义外部全局变量,解决libsrcpbl.so: undefined reference to gcProgramName的问题
char gcProgramName[20 + 1] = {0x00};
int main( int argc, char ** argv )
{
    //在这里就可以随便赋值使用了
    memcpy( gcProgramName, "TestPrg", strlen( "TestPrg" ) );
    printf( "exit\n" );
    return 0;
}

最后重新make一下,链接生成ELF文件成功!

使用nm查看动态库中的符号

4 总结

在make编译链接某个动态库lxx.so时,如果报libxx.so: undefined reference to xx的话,很可能是由下面两种原因引起的。Unix/Linux系统中使用nmobjdumpreadelf命令可以辅助调试目标文件。

1、链接的动态库中存在只做了声明的自定义的函数/全局变量,但是在其它对象文件中没有实现定义;
2、链接生成的目标文件中引用了某动态库的自定义函数/全局变量,但是没有通过`-lXX`选项链接进来。
打赏支持

Comments »