库链接的问题

Posted 菜鸟决心努力A-A

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了库链接的问题相关的知识,希望对你有一定的参考价值。

编译:

编译过程是以每个.cpp文件为独立的编译单位的,生成一个个.obj
编译过程,将引用文件在.cpp文件中展开,并检查是否有正确的声明。如果该函数没有定义,编译器认为在连接过程可以在其他.obj文件中找到。

头文件路径是通过编译器默认设置以及用户通过-I选项添加的,默认的include路径,可以通过指令来查看

echo | g++ -v -x c++ -E -

连接过程,将上面没有定义的函数,在其他.obj中查找定义,如果没有找到,则会报undefine reference的问题。
而查找链接库时所用的路径是,通过变量LIBRARY_PATH设置,但是变量LIBRARY_PATH只是在编译期间使用,作为查找动态链接库时指定查找共享库的路径。

执行

在可执行文件执行时,会通过LD_LIBRARY_PATH查找动态链接库的。
LD_LIBRARY_PATH环境变量用于在程序加载运行期间查找动态链接库时指定除了系统默认路径之外的其他路径。

注意

LD_LIBRARY_PATH中指定多个的路径,查找动态库的时候会按序查找
举个例子,如果我们make了opencv的debug版本,和opencv的release版本,这两个版本在程序中都会用到,所以需要将两个版本的lib的路径,都添加到环境变量LD_LIBRARY_PATH中,假如我们将debug版本的路径放在release版本的前面,那么程序执行运行时,如果需要用到opencv库,那么都会先查找debug的路径,即都执行的是debug的lib,无论用户本意如何。

那么问题来了,这时就涉及为什么要设置两次library path?LIBRARY_LIB设置的路径在运行时不能使用么?
答:开发时,设置LIBRARY_PATH,以便gcc能够找到编译时需要的动态链接库。
发布时(执行时),设置LD_LIBRARY_PATH,以便程序加载运行时能够自动找到需要的动态链接库,如果更改LD_LIBRARY_PATH,不需要重新编译也可以完成更新。

即LIBRARY_PATH只是为了编译时能够确定是否存在连接函数的定义,即obj;而,当编译结束后,可执行文件具执行的时候,是通过LD_LIBRARY_PATH来查找obj的)
所以,是的,LIBRARY_PATH设置的路径不能再运行时使用。

但是我还有个问题
因为是在运行的时候连接,会不会影响速度?因为得查找所有的 动态链接库文件,而编译的时候就可以执行lib目录下的那些库文件

静态链接库,连接时注意事项:
使用静态连接库时,在链接命令中给出所依赖的库时,需要注意库之间的依赖顺序,依赖其他库的库一定要放到被依赖库的前面,这样才能真正避免undefined reference的错误,完成编译链接。
动态链接库不存在这个问题。

动态链接库和静态链接库的区别
静态连接库就是把(lib)文件中用到的函数代码直接链接进目标程序,程序运行的时候不再需要其它的库文件;动态链接就是把调用的函数所在文件模块(DLL)和调用函数在文件中的位置等信息链接进目标程序,程序运行的时候再从DLL中寻找相应函数代码,因此需要相应DLL文件的支持。

静态链接库与动态链接库都是共享代码的方式,如果采用静态链接库,则无论你愿不愿意,lib 中的指令都全部被直接包含在最终生成的 EXE 文件中了。但是若使用 DLL,该 DLL 不必被包含在最终 EXE 文件中,EXE 文件执行时可以“动态”地引用和卸载这个与 EXE 独立的 DLL 文件。静态链接库和动态链接库的另外一个区别在于静态链接库中不能再包含其他的动态链接库或者静态库,而在动态链接库中还可以再包含其他的动态或静态链接库。

我们遇到的问题是:

1.
从别的机器上拷贝过来的工程代码,更新库路径。
但是运行的时候出现段错误。
这是系统安装的opencv库的问题。

2.
我们重新安装了一个debug版本的opencv,并想用这个库。
具体操作如下,make之后(并没有install),将-I,-L路径都连接到信的目录,但是编译的时候出错,错误如下:

技术分享

这是因为,我们的debug库是未安装的,include下头文件不全,所以报这样的错,然后我们将-I路径换为系统的opencv的include,就编译通过了。

3.
但是然后很奇怪的事情发生了,如果直接登录我的账号,执行,会出现段错误,如果李同学从他的账户su到我的账户,执行,就正常。

这是因为我们的~/.bashrc文件不同

他在~/.bashrc中直接将LD_LIBRARY_PATH中的路径赋值为$USER_OPENCV_HOME/lib,而他这个USER_OPENCV_HOME是自己安装的lib,

而我是这样添加的
export LD_LIBRARY_PATH = $LD_LIBRARY_PATH: $USER_OPENCV_LIB/lib
这样,查找动态连接库时,会直接先查找usr/local下的opencv库,而这个库有问题,所以就出现了上述情况。

改为这样就可以了
export LD_LIBRARY_PATH = $USER_OPENCV_LIB/lib: $LD_LIBRARY_PATH

另外,我的.bashrc文件只能通过新开一个会话,才能有更新,用source不行,不知道为什么
另外,调用cv::getBuildInformation()函数,可查看可执行文件当前使用的库的信息,库信息,跟所调用的库有关,即跟LD_LIBRARY_PATH有关。


补充

linux静态链接库与动态链接库的区别及动态库的创建(转)
一、引言

通常情况下,对函数库的链接是放在编译时期(compile time)完成的。所有相关的对象文件(object file)与牵涉到的函数库(library)被链接合成一个可执行文件(executable file)。程序在运行时,与函数库再无瓜葛,因为所有需要的函数已拷贝到自己门下。所以这些函数库被成为静态库(static libaray),通常文件名为“libxxx.a”的形式。

其实,我们也可以把对一些库函数的链接载入推迟到程序运行的时期(runtime)。这就是如雷贯耳的动态链接库(dynamic link library)技术。

二、动态链接库的特点与优势

首先让我们来看一下,把库函数推迟到程序运行时期载入的好处:

  1. 可以实现进程之间的资源共享。
    什么概念呢?就是说,某个程序的在运行中要调用某个动态链接库函数的时候,操作系统首先会查看所有正在运行的程序,看在内存里是否已有此库函数的拷贝了。如果有,则让其共享那一个拷贝;只有没有才链接载入。这样的模式虽然会带来一些“动态链接”额外的开销,却大大的节省了系统的内存资源。C的标准库就是动态链接库,也就是说系统中所有运行的程序共享着同一个C标准库的代码段。

  2. 将一些程序升级变得简单。用户只需要升级动态链接库,而无需重新编译链接其他原有的代码就可以完成整个程序的升级。Windows 就是一个很好的例子。

  3. 甚至可以真正坐到链接载入完全由程序员在程序代码中控制。
    程序员在编写程序的时候,可以明确的指明什么时候或者什么情况下,链接载入哪个动态链接库函数。你可以有一个相当大的软件,但每次运行的时候,由于不同的操作需求,只有一小部分程序被载入内存。所有的函数本着“有需求才调入”的原则,于是大大节省了系统资源。比如现在的软件通常都能打开若干种不同类型的文件,这些读写操作通常都用动态链接库来实现。在一次运行当中,一般只有一种类型的文件将会被打开。所以直到程序知道文件的类型以后再载入相应的读写函数,而不是一开始就将所有的读写函数都载入,然后才发觉在整个程序中根本没有用到它们。

三、动态链接库的创建

由于动态链接库函数的共享特性,它们不会被拷贝到可执行文件中。在编译的时候,编译器只会做一些函数名之类的检查。在程序运行的时候,被调用的动态链接库 函数被安置在内存的某个地方,所有调用它的程序将指向这个代码段。因此,这些代码必须实用相对地址,而不是绝对地址。在编译的时候,我们需要告诉编译器, 这些对象文件是用来做动态链接库的,所以要用地址不无关代码(Position Independent Code (PIC))。

对gcc编译器,只需添加上 -fPIC 标签,如:

gcc -fPIC -c file1.c
gcc -fPIC -c file2.c
gcc -shared libxxx.so file1.o file2.o

注意到最后一行,-shared 标签告诉编译器这是要建立动态链接库。这与静态链接库的建立很不一样,后者用的是 ar 命令。也注意到,动态链接库的名字形式为 “libxxx.so” 后缀名为 “.so”

2、动态库的链接

在1、中,我们已经成功生成了一个自己的动态链接库libtest.so,下面我们通过一个程序来调用这个库里的函数。程序的源文件为:test.c。

test.c:

#include “so_test.h”
int main()
{
test_a();
test_b();
test_c();
return 0;

}

l 将test.c与动态库libtest.so链接生成执行文件test:

$ gcc test.c -L. -ltest -o test

l 测试是否动态连接,如果列出libtest.so,那么应该是连接正常了

$ ldd test

l 执行test,可以看到它是如何调用动态库中的函数的。

3、编译参数解析
最主要的是GCC命令行的一个选项:
-shared 该选项指定生成动态连接库(让连接器生成T类型的导出符号表,有时候也生成弱连接W类型的导出符号),不用该标志外部程序无法连接。相当于一个可执行文件

l -fPIC:表示编译为位置独立的代码,不用此选项的话编译后的代码是位置相关的所以动态载入时是通过代码拷贝的方式来满足不同进程的需要,而不能达到真正代码段共享的目的。

l -L.:表示要连接的库在当前目录中

l -ltest:编译器查找动态连接库时有隐含的命名规则,即在给出的名字前面加上lib,后面加上.so来确定库的名称

l LD_LIBRARY_PATH:这个环境变量指示动态连接器可以装载动态库的路径。

l 当然如果有root权限的话,可以修改/etc/ld.so.conf文件,然后调用 /sbin/ldconfig来达到同样的目的,不过如果没有root权限,那么只能采用输出LD_LIBRARY_PATH的方法了。


undefined reference问题总结(转)
http://ticktick.blog.51cto.com/823160/431329
1. 链接时缺失了相关目标文件(.o)
测试代码如下

//main.c
int main()
{
test();
}
//test.c
#include <stdio.h>
void test()
{
printf("test!\n");
}

然后编译

gcc -c test.c  
gcc -c main.c

得到两个 .o 文件,一个是 main.o,一个是 test.o ,然后我们链接 .o 得到可执行程序:

gcc -o main main.o

这时,你会发现,报错了:
技术分享
这就是最典型的undefined reference错误,因为在链接时发现找不到某个函数的实现文件,本例中test.o文件中包含了test()函数的实现,所以如果按下面这种方式链接就没事了。

gcc -o main main.o test.o

2….








































以上是关于库链接的问题的主要内容,如果未能解决你的问题,请参考以下文章

在编译/链接期间无法禁用 Armadillo Wrapper

linux上动态链接期间符号的替代实现

Linux上静态库和动态库的编译和使用

gcc编译选项

与静态库链接不等同于与其对象链接

第三个库在编译期间报告错误(使用 cocoaPods)