导航:首页 > 源码编译 > 编译成动态库位置无关

编译成动态库位置无关

发布时间:2023-08-02 23:20:56

1. 面试 | linux 下的动态链接库问题

在 Linux 开发时,我们经常会看到一些形如 xxx.so 的名称出现,其中 so 是 Shared Object 的缩写,即可以共享的目标文件,也就是我们所称为的动态链接库,和在 Windows 下大家玩 游戏 时遇到的 xxx.dll 错误中的文件是一个类型的。

面试中经常会问到以下问题:

库是写好的现有的,成熟的,可以复用的代码。现实中每个程序都要依赖很多基础的底层库,不可能每个人的代码都从零开始,因此库的存在意义非同寻常。本质上来说库是一种可执行代码的二进制形式,可以被操作系统载入内存执行。

库有两种:

在一个程序的编译过程中,分为以下几个步骤: 预处理 编译 汇编 链接 。本文中讨论的链接库就是针对最后一个步骤“链接”而言的。

动态库和静态库的区别

左图为静态链接库,右图为动态链接库

对于静态链接库而言在链接阶段,会将汇编生成的“目标文件.o”与引用到的库一起链接打包到可执行文件中。因此对应的链接方式称为静态链接:

静态链接可以理解为最后生成了一个“单文件免安装绿色版”的程序,优点在于移植的时候只需要移动这一个文件,缺点在于文件体积非常大,为了解决这样的问题,就有了动态链接库。动态链接库在程序编译时并不会被连接到目标代码中,而是在程序运行时才被载入。

动态库连接到系统空间,如果多个程序连接了同一个库,那么只需要一份,优点在于编译程序的时候不会将对应的库文件全部打包在生成的程序中,而是保留了到对应库的链接,缺点就是移植的时候如果只移动了对应的程序没有安装相关的库的话,就会看到类似以下喜闻乐见的结果了。

在 Linux 下一个动态库有y三个不同名字的文件组成:

当程序在内部列出所需要的链接库时,仅仅使用 soname。当你创建一个链接库时,使用 real name。安装一个新的链接库时,把它复制到一个DLL文件夹里,然后运行程序 ldconfig。ldconfig 检查存在的 real name 文件,并且创建指向它符号链接 soname 文件。可能大家比较常见到的有 libsodium 等。

有了上面关于库的一些基础知识之后,我们可以开始尝试创建一个动态库来供程序使用了。

比如我们有一个求最大值的函数 max(int a,int b,int c) ,放在文件 max.c 中文件内容如下:

可以通过:

将其编译为共享库,-fPIC是编译选项,PIC是 Position Independent Code 的缩写,表示要生成位置无关的代码,这是动态库需要的特性; -shared是链接选项,告诉 gcc 生成动态库而不是可执行文件。为了让用户知道我们的动态库中有哪些接口可用,我们需要编写对应的头文件,比如可以写一个 max.h :

设置一个驱动函数来测试我们编写的动态库:

通过 gcc test.c -L. -lmax来生成 a.out,其中-lmax表示要链接 libmax.so,-L.表示搜索要链接的库文件时包含当前路径。

但是这样直接运行的话,会出现一个错误:

由于 Linux 是通过/etc/ld.so.cache文件搜寻要链接的动态库的,而 /etc/ld.so.cache 是 ldconfig 程序读取 /etc/ld.so.conf 文件生成的,本次使用的动态库 libmax.so 并不在对应的目录下,就会导致程序无法找到对应的动态链接库,这样我们的解决方法有二:

小结

​动态链接库是各个系统中的一个重要的组成部分且在 Linux 开发相关领域中尤为重要,也是一个面试的高频考点,除了动态链接库以外,还有以下相关知识也是高频考点,在面试前一定要准备好:

本文作者:Nova Kwok

2. gcc 生成动态库时-fpic选项是什么意思。

fpic:产生位置无关码

解释一下,位置无关码就是可以在进程的任意内存位置执行的目标码,动态链接库必须使用。

3. 编译时找不到动态库

报错:

分析:
  应该是动态库链接的库位置不对,默认在目录usr/lib/ 下查找动态库。

方式一 用ln -s建立创建软连接,确保/usr/lib下存在库。
  ln -s /绝对路径/源 /绝对路径/目的
方式二键誉州 编译时使用-rpath 或者-rpath-link,例虚毕如如下gcc编译时设置
mips64el-redhat-linux-gcc -o test *.c -Wall -L./lib/mips64le/ -lpthread -lm -Wl,-rpath=/usr/稿蔽local/gcc-4.8.3-d197-n64-loongson/usr/mips64el-redhat-linux/lib64/

挂载命令的顺序

4. gcc编译错误!

提示说需要-fPIC编译,然后在链接动态库的地方加上-fPIC的参数编译结果还是报错,需要把共享库所用到的所有静态库都采用-fPIC编译一边才可以成功的在64位环境下编译出动态库。
这里的-fPIC指的是地址无关代码

你看看这里的这篇文章吧:http://www.cnblogs.com/lightsalt/archive/2011/10/19/2217628.html

5. makefile 生成动态库和静态库的区别

生成动态库的时候要注意,编译生成目标文件的时候加上-fPIC参数,生成位置无关的可重定位代码,然后链接的时候加上-shared生成动态共享库。比如一个hello.c,生成静态库:

gcc-ohello.o-chello.c
arrcslibhello.ahello.o

生成动态库的命令:

gcc-fPIChello.o-chello.c
gcc-shared-olibhelllo.sohello.o

还有一个区别是:静态库参与链接过程,而动态库不链接到可执行文件中,可执行程序在运行的时候,对应的动态库也要加载到内存中,否则可执行程序运行不了。

更多详细细节,可以网络搜索视频教程:Makefile工程实践

6. 动态库编译详解

当前类介绍:upper.c ( upper) 依赖于 bottom.c(play)

说明:当执行可执行程序的时候,需要去/lib. /user/lib下和LD_LIBRARY_PATH下寻找so.并不会在当前目录下寻找.

所以执行./main.out会报错.如下:

解决方案:指定.so运行搜寻路径

1.-Wl,-rpath ./mypath 加入参数,并且将libplay.so 到./mypath目录下.

2.设置LD_LIBRARY_PATH,指定目录.

说明:指定了-Wl,-rpath, 设置LD_LIBRARY_PATH也是可以生效的.并不是说只会去-Wl,-rpath下寻找.

首先生成一个bottom.so,然后用upper.so去依赖bottom.so, 然后main.c 再去依赖upper.so.

说明:这里编译的时候直接出错,是因为没有指定搜寻路径,所以无法通过编译.

解决编译问题方案.

1.我们依然采用LD_LIBRARY_PATH的方式可以解决编译和运行的问题.

2.生成libplay的时候,直接指定-Wl,-rpath 给libbottom.可以解决编译不通过的问题.

3.依赖所有库

依赖所有库只能解决编译问题,无法处理运行的路径.

另一种思路:我们在执行main.out的时候 执行-Wl,-rpath.并不在生成libplay的时候指定,看下是否正常.

由此可见,-Wl,-rpath 只能针对直接依赖的libplay.so指定了路径,但是libbottom还是无法查找到 .但是LD_LIBRARY是可以的.

rpath只能对直接依赖的so设置搜寻目录,并且可以设置所有依赖的编译路径.

总结: 解决编译问题,在生成libplay的时候指定-Wl,-rpath运行路径,或者设置LD_LIBRARAY_PATH,都可以解决这个问题.

当我们现在拥有的so包含一个直接依赖的so和很多间接依赖的so,但是没有设置rpath.所以是不能直接依赖主so进行编译和运行的.

为了通过编译:

1.在只链接主so的情况下可以去设置rpath或者LD_LIBRARY_PATH.

2.或者链接所有so.

为了通过运行:

为了正常运行可以设置LD_LIBRARY_PATH.

--disable-new-dtags,---dt-needed-entries

结论概述:

1.我们在生成间接依赖的库的时候,为了保证其他库可以直接依赖,需要加入-Wl,-rpath.保证编译通过.

2.LD_LIBRARY_PATH可以解决一切编译运行问题.

7. 调用动态库的时候有几个问题会经常碰到

通常这样做就可以解决库无法链接的问题了。 makefile里面怎么正确的编译和连接生成.so库文件,然后又是在其他程序的makefile里面如何编译和连接才能调用这个逗搏库文件的函数?答:你需要告诉动态链接器、加载器ld.so在哪里才能找到这个共享库,可以设置环境变量把库的路径添加到库目录/lib和/usr/lib,LD_LIBRARY_PATH=$(pwd),这种方法采用命令行方法不太方便,一种替代方法 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^注释^^^^^^^^^^^^^^^^^^^^^^^^^^^^ LD_LIBRARY_PATH可以在/etc/profile还是 ~/.profile还是 ./bash_profile里设置,或者.bashrc里, 改完后运行source /etc/profile或 . /etc/profile 更好的办法是添入/etc/ld.so.conf, 然后执行 /sbin/ldconfig ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^注释^^^^^^^^^^^^^^^^^^^^^^^^^^^^是把库路径添加到/etc/ld.so.conf,然后以root身份运行ldconfig 也可以在连接的时候指定文件路径和名称 -I -L. GCC=gccCFLAGS=-Wall -ggdb -fPIC#CFLAGS=all: libfunc test libfunc:func.o func1.o $(GCC) -shared -Wl,-soname,libfunc.so.1 -o libfunc.so.1.1 $ 编译目标文件时使用gcc的姿野-fPIC选项,产生与位置无关的代码并能被加载到任何地址: gcc ?fPIC ?g ?c liberr.c ?o liberr.o 使用gcc的-shared和-soname选项; 使用gcc的-Wl选项把参数传递给连接器ld; 使用gcc的-l选项显示的连接C库,以保证可以得到所需的启动(startup)代码,从而避免程序在使用不同的,可能不兼容版本的C库的系统上不能启动执行。 gcc ?g ?shared ?Wl,-soname,liberr.so ?o liberr.so.1.0.0 liberr.o ?lc 建立相应的符号连接: ln ?s liberr.so.1.0.0 liberr.so.1; ln ?s liberr.so.1.0.0 liberr.so; 在MAKEFILE中:$@表示规则中的目标文件集。在模式规则中,如果有多个目标,那么,"$@"就是匹配于目标中模式定义的集合。$%仅迹指喊当目标是函数库文件中,表示规则中的目标成员名。例如,如果一个目标是"foo.a(bar.o)",那么,"$%"就是"bar.o","$@"就是 "foo.a"。如果目标不是函数库文件(Unix下是[.a],Windows下是[.lib]),那么,其值为空。

阅读全文

与编译成动态库位置无关相关的资料

热点内容
如何用mamp本地web服务器 浏览:869
如何加密自己js代码 浏览:623
排列组合a与c的算法 浏览:534
如何在文件夹中找到同名内容 浏览:786
有什么app文字转韩文配音 浏览:372
循环宏1命令 浏览:35
斐波那契数列矩阵算法 浏览:674
公式保护后加密不了 浏览:82
java跳转到jsp 浏览:819
327平方根算法 浏览:216
win7美化命令行终端 浏览:797
免加密狗图片 浏览:485
一只透明的鸟是什么app 浏览:817
空气压缩机油批发商 浏览:69
linuxifexist 浏览:4
加密tf卡拷入文件 浏览:399
山西php工资 浏览:673
福州看病预约用什么小程序app 浏览:238
php保留两位小数不四舍五入 浏览:292
黑马程序员路径大全 浏览:1000