4 解析未定义的符号(上)

1.二分搜索原则对查找未知位置的语法错误非常有用。

2.库,库缺少问题
缺少库的常见提醒:undefined reference to “function”
库分为两种:静态库、动态库
当编译调用静态库中函数代码时,那些函数成为最终可执行文件的一部分;
当是动态库时,直到实际执行时,这些函数才会真正附到调动代码上。

在Unix系统上,静态库文件名后面加后缀 .a, 即archive;动态库后缀一般为.so, 即shared object; 另外,库名称一般以lib开头。
$ ar rc file.c -l88 -L path //创建静态库

$ gcc  -fPIC  -c b.c //生成目标文件b.o
$ gcc  -shared  -o lib88 b.o //将目标文件b.o编译生成动态库

1 linux ar 命令
2 gcc编译参数-fPIC的一些问题
-L,指示GCC告诉LD在查找函数时顺便在除当前目录外的其他目录中查找一下(以及默认目录)

通过ldd命令可以检查程序需要哪些库,如果有,操作系统可以在何处找到它们。
4 解析未定义的符号(上)_第1张图片
首先程序在默认路径中查找,没有找到lib88.so,后者不在默认路径中;
解决这种问题的方式可以是向搜索路径中添加路径/pathname;

# setenv LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/pathname
# export LD_LIBRARY_PATH

开源软件中库的用法
  安装开源软件时,常常会出现一个问题,即源代码的配置文件找不到某些库,试图通过LD_LIBRARY_PATH解决问题时可能失败,这个问题的根源在于配置文件调用名为pkgconfig的程序。这个程序从某些原数据文件中接收关于库的信息。这样的文件名后缀为 .pc,前缀是库的名称。例如,libgcj.pc中包含库文件libgcj.so*的位置。
  pkgconfig搜索 .pc文件的默认目录取决于pkgconfig本身的位置。例如,如果程序位置/usr/lib中,则搜索/usr/lib。如果所需的库是/lib/local/lib,仅在这个目录中搜索还不够。为了解决这个问题,设置环境变量PKG_CONFIG_PATH.

3.链接
  像gcc这样的命令实际上并不是一个单纯的编译命令,它管理着程序构建的过个阶段,包括像预处理(-E)、编译(-S)、汇编(-c)和链接(ld),它通常被称为编译器驱动程序。
  GCC常用命令 
   可以将编译和链接分为两步,且在大型项目中,以模块化方式构建可执行文件是一个好的思想。如果不这样,修改某个源文件将要求重新编译所有源代码,而使用模块化的方法可以只重新编译哪些受代码修改影响的部分。
   (1)解析未定义符号
   *丢失连接参数
   比如连接过程中,丢失了包含函数fun定义的连接参数fun.o,而出现undefined reference to “fun”;
  * 搜索丢失的符号
  在Unix系统中可通过find & grep来定位文件,比如:
  4 解析未定义的符号(上)_第2张图片
  实用程序nm 提供了一个符号清单,解析对象中符号是否被定义,一般与grep 结合使用,可通过-o选项将文件名放在行首;
  4 解析未定义的符号(上)_第3张图片
  4 解析未定义的符号(上)_第4张图片
  其中第二列的T:函数的符号包含在对象的文件的文本部分中;
  U:表示需要解析的符号;
  
  也可通过man, man sqrtf, 显示此函数在math库中,可在编译时添加链接参数-l**m**, 可知其在库中的名字为lib**m**.so, 除去前后缀,只需中间的名字m相同。
  
  链接顺序问题
  如果直接将对象文件作为链接器的参数,那么它的所有符号都将被链接到最后的可执行文件中,这样会产生一个较大的目标文件。为避免此问题,可使用库或归档文件来代替对象文件。链接器将从库中挑选那些包含当前未定义符号的对象文件,大多数链接器对提供给它的对象和库只进行一次处理,按一下算法:
  (1)首先载入对象文件,对象文件包含初始化代码,初始化代码也调用C程序的main函数,也就是说,连接器至少以未定义的符号main开始,编译器驱动程序自动将包含初始化例程的对象文件提供给连接器。
  (2)然后,连接器按参数的顺序继续走查指定的对象文件和库。连接器从每个库中将那些至少包含一个未解析符号的对象文件挑出来。从中挑出所有符号,无论是否对连接器中标记为未定义的符号进行解析。当新对象文件载入连接器时,可能产生新的未定义符号。
  (3)如果在链接行的结尾,所有符号均已解析,则链接过程成功。

$ ar -r -o libfactorial.a factorial.o

$ gcc -o calc_factorial libfactorial.a main.o
undefined reference to ‘factorial’
解释:当链接器到达libfactorial.a库时,其列表中只有未定义的符号main; 在libfactorial.a中,没有为main符号提供定义的文件,且库中定义的符号未被挑选出来,因为链接器中没有它的未解析符号;最后,连接器载入对象文件main.o,这里main符号被定义,但main()例程中还要调用factorial(),因此它在未定义的符号列表中创建一个新的未解析项,连接器到达参数列表末尾,仍有未解析项,故程序终止。

你可能感兴趣的:(GDB)