未定义符号的链接问题通用解决方法

在linux下做c/c++开发时,经常遇到一个问题,就是如果代码引用了大量的第三方库,链接的时候常忘记或者根本就不知道具体要链接哪个库,导致
链接的时候报未定义的引用,特别是一些库你不太熟悉时,甚至都不知道该链接谁。

/usr/bin/ld: CMakeFiles/gstdemo.dir/main.c.o: in function `main':
/home/a/test/gstdemo/main.c:178: undefined reference to `gst_audio_info_set_format'
/usr/bin/ld: /home/a/test/gstdemo/main.c:179: undefined reference to `gst_audio_info_to_caps'

如何解决这个问题?以往我只能靠网络上搜索,但是全得凭运气,万一搜索不到呢?其实在对应的动态库文件里面的符号表就会包含里面定义的函数或者调
用的函数。所以用grep进行搜索就找得到相关的库文件。例如对于上面未定义的引用,

grep -snr "gst_audio_info_set_format" /usr/lib/x86_64-linux-gnu/

Binary file /usr/lib/x86_64-linux-gnu/libgstaudio-1.0.so.0.1603.0 matches
Binary file /usr/lib/x86_64-linux-gnu/libgstbadaudio-1.0.so.0.1603.0 matches
Binary file /usr/lib/x86_64-linux-gnu/girepository-1.0/GstAudio-1.0.typelib matches
Binary file /usr/lib/x86_64-linux-gnu/gstreamer-1.0/libgstdecklink.so matches
Binary file /usr/lib/x86_64-linux-gnu/gstreamer-1.0/libgstvorbis.so matches
Binary file /usr/lib/x86_64-linux-gnu/gstreamer-1.0/libgstwavpack.so matches
Binary file /usr/lib/x86_64-linux-gnu/gstreamer-1.0/libgstalaw.so matches
Binary file /usr/lib/x86_64-linux-gnu/gstreamer-1.0/libgstmpg123.so matches
Binary file /usr/lib/x86_64-linux-gnu/gstreamer-1.0/libgstspeex.so matches
Binary file /usr/lib/x86_64-linux-gnu/gstreamer-1.0/libgstinterleave.so matches
Binary file /usr/lib/x86_64-linux-gnu/gstreamer-1.0/libgstrawparse.so matches
Binary file /usr/lib/x86_64-linux-gnu/gstreamer-1.0/libgstmulaw.so matches
Binary file /usr/lib/x86_64-linux-gnu/gstreamer-1.0/libgstopus.so matches
Binary file /usr/lib/x86_64-linux-gnu/gstreamer-1.0/libgstadpcmdec.so matches
Binary file /usr/lib/x86_64-linux-gnu/gstreamer-1.0/libgstdv.so matches
Binary file /usr/lib/x86_64-linux-gnu/gstreamer-1.0/libgstdtsdec.so matches
Binary file /usr/lib/x86_64-linux-gnu/gstreamer-1.0/libgstlibav.so matches
Binary file /usr/lib/x86_64-linux-gnu/gstreamer-1.0/libgstmxf.so matches
Binary file /usr/lib/x86_64-linux-gnu/gstreamer-1.0/libgstsiren.so matches
Binary file /usr/lib/x86_64-linux-gnu/gstreamer-1.0/libgstsbc.so matches
Binary file /usr/lib/x86_64-linux-gnu/gstreamer-1.0/libgstgsm.so matches
Binary file /usr/lib/x86_64-linux-gnu/gstreamer-1.0/libgstfaad.so matches
Binary file /usr/lib/x86_64-linux-gnu/gstreamer-1.0/libgstflac.so matches

这么多库中,一般来说只会有一个地方是真正定义函数的,其他都是调用它,这时候可以通过readelf工具查看谁定义了它,readelf的-s选项可以查看符
号表信息,

readelf -s gstreamer-1.0/libgstopus.so

Symbol table '.dynsym' contains 155 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND 
     1: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND gst_tag_list_new_empty
     2: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND g_free
     3: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND gst_audio_decoder_set_lat
     ....
     ....
     ....

readelf -s会输出多列内容,其中Ndx这一列如果是UND表示此elf文件(这里是动态库)没定义这个符号,是引用的一个外部符号(这里是函数),
也就是说当前这个库会调用一个其他库定义的函数,当Ndx为一个具体数字时,表示此符号是这个elf文件内部定义的,也就是说我们要链接的就是这个动态库。

grep -snr "gst_audio_info_set_format" /usr/lib/x86_64-linux-gnu/ | awk '{print $3}' | xargs readelf -s | grep "gst_audio_info_set_format"
   
   502: 000000000001b1a0   518 FUNC    GLOBAL DEFAULT   14 gst_audio_info_set_format
   137: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND gst_audio_info_set_format
readelf: Error: Not an ELF file - it has the wrong magic bytes at the start
    66: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND gst_audio_info_set_format
   152: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND gst_audio_info_set_format
   114: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND gst_audio_info_set_format
    38: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND gst_audio_info_set_format
    66: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND gst_audio_info_set_format
   135: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND gst_audio_info_set_format
   142: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND gst_audio_info_set_format
   104: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND gst_audio_info_set_format
    41: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND gst_audio_info_set_format
    69: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND gst_audio_info_set_format
    37: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND gst_audio_info_set_format
   166: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND gst_audio_info_set_format
    68: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND gst_audio_info_set_format
   213: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND gst_audio_info_set_format
   256: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND gst_audio_info_set_format
    26: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND gst_audio_info_set_format
    57: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND gst_audio_info_set_format
    46: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND gst_audio_info_set_format
    55: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND gst_audio_info_set_format
   229: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND gst_audio_info_set_format

可以看出来第一个grep出来的so文件定义了gst_audio_info_set_format这个函数。so文件为/usr/lib/x86_64-linux-gnu/libgstaudio-1.0.so.0.1603.0
直接通过-l指定连接路径就行了,但是又可能头文件位置不知道,又得-I来指定,实在麻烦。其实在linux下可以通过pkg-config命令来获取这些信息。

pkg-config命令工作原理是搜索指定路径里面的.pc文件,而pc文件里面就描述了库的详细信息,包括头文件,库的路径,它的依赖库。例如此处,输出需要的链接信息

pkg-config --libs gstreamer-audio-1.0
-lgstaudio-1.0 -lgstbase-1.0 -lgstreamer-1.0 -lgobject-2.0 -lglib-2.0

输出需要的CFLAGS信息,

pkg-config --cflags gstreamer-audio-1.0
-I/usr/include/gstreamer-1.0 -I/usr/include/glib-2.0 -I/usr/lib/x86_64-linux-gnu/glib-2.0/include -pthread -I/usr/include/orc-0.4

通常会指定这2个选项

gcc main.c -o main `pkg-config --libs --cflags gstreamer-audio-1.0`

此处有个问题,怎么通过so文件找到对于的pc文件?我暂时至能通过看名字,两者的关联不知道怎么查看。

此外,查看当前系统pc文件的搜索目录的方式:

pkg-config --variable pc_path pkg-config
posted @ 2023-03-07 13:40  thammer  阅读(523)  评论(0编辑  收藏  举报