Linux 动态库的编译和使用
1. 动态链接库简介
动态库又叫动态链接库,是程序运行的时候加载的库,当动态链接库正确安装后,所有的程序都可以使用动态库来运行程序。动态库是目标文件的集合,目标文件在动态库中的组织方式是按特殊的方式组织形成的。在动态库中函数和变量的地址是相对地址而不是绝对地址,其真实地址在调用动态库的程序加载时形成的。
动态库的名字有别名(soname), 真名(realname)和链接名(linkername)。别名是由一个lib前缀,然后是库的名字,最后以“.so”结尾来构成。真名是动态链接库的真实名字,一般总是在别名的基础上添加一个版本号信息。除此之外还有一个链接名,他是在程序链接的时候使用的名字。
动态库安装的时候,总是复制库文件到某一个目录,然后使用一个软链接生成一个别名,在库文件更新的时候,仅仅更新软链接即可。
2. 生成动态链接库
生成动态链接库的命令比较简单:
2.1 使用-shared 告诉编译器生成一个动态链接库
2.2 使用选项-fPIC或者-fpic,使得生成的代码与位置无关
gcc -shared -Wl, -soname, libstr.so -o libstr.so.1 string.c
其中,“-shared” 表示要生成的为动态链接库文件;
“-soname, libstr.so” 表示生成的动态链接库的别名为“libstr.so”;
“-o libstr.so” 表示生成名字为“libstr.so.1”的实际动态链接库文件;
2.3 动态链接库的安装
生成动态链接库后,一个很重要的操作是安装,一般情况下,我们将库文件放到系统默认的搜索路径下,常用的有/lib, /usr/lib, /usr/local/lib 。将 动态链接库放到这三个中任意个目录都可以。
3. 动态链接库的配置文件
一般情况下,动态链接库不能随意使用。如果要在运行的程序中使用动态链接库,需要制定系统的动态链接库搜索路径,只有让系统能找到运行时需要的动态链接库才能使用它。 系统中的配置文件/etc/ld.so.conf便是动态链接库的搜索路径配置文件。在这个文件内存放着可以被Linux共享的动态链接库所在目录的名字(系统默认的/lib, /usr/lib除外)。 多个目录之间可以使用空格,换行符进行隔开。
在ubantu虚拟机下查看“/ld.so.conf”文件:
book@www.100ask.org:~$ cat /etc/ld.so.conf
include /etc/ld.so.conf.d/*.conf
根据内容然后查看“/etc/ld.so.conf.d/”目录下的文件:
book@www.100ask.org:/etc/ld.so.conf.d$ cat *.conf
/usr/lib/x86_64-linux-gnu/libfakeroot
# libc default configuration
/usr/local/lib
/usr/lib/vmware-tools/lib32/libvmGuestLib.so
/usr/lib/vmware-tools/lib64/libvmGuestLib.so
/usr/lib/vmware-tools/lib32/libvmGuestLibJava.so
/usr/lib/vmware-tools/lib64/libvmGuestLibJava.so
/usr/lib/vmware-tools/lib32/libDeployPkg.so
/usr/lib/vmware-tools/lib64/libDeployPkg.so
# Multiarch support
/lib/x86_64-linux-gnu
/usr/lib/x86_64-linux-gnu
/usr/lib/x86_64-linux-gnu/mesa-egl
/usr/lib/x86_64-linux-gnu/mesa
# Legacy biarch compatibility support
/lib32
/usr/lib32
book@www.100ask.org:/etc/ld.so.conf.d$
所以这个百问网的虚拟机动态库的搜索路径包含了上述列出的目录。
4. 动态链接库管理命令
为了让新增加的动态链接库能够被系统所共享,我们需要设置运行动态链接库的管理命令ldconfig。 ldconfig命令的作用是在系统的默认搜索路径(/lib, /usr/lib, /usr/local/lib)以及动态链接库配置文件所列出的目录里搜索动态链接库,然后创建动态链接装入程序需要的链接和缓存文件。 搜索完毕后将结果写入到缓存文件“/etc/ld.so.cache”中, 文件中保存的是已经排好序的动态链接库名字列表,一般情况下里面的动态链接库很多,我们可以使用ldconfig -p命令来查看列表对应的动态库信息:
book@www.100ask.org:/etc$ ldconfig -p
1137 libs found in cache `/etc/ld.so.cache'
libzvbi.so.0 (libc6,x86-64) => /usr/lib/x86_64-linux-gnu/libzvbi.so.0
libzvbi-chains.so.0 (libc6,x86-64) => /usr/lib/x86_64-linux-gnu/libzvbi-chains.so.0
libzmq.so.5 (libc6,x86-64) => /usr/lib/x86_64-linux-gnu/libzmq.so.5
libzeitgeist-2.0.so.0 (libc6,x86-64) => /usr/lib/x86_64-linux-gnu/libzeitgeist-2.0.so.0
libzeitgeist-1.0.so.1 (libc6,x86-64) => /usr/lib/x86_64-linux-gnu/libzeitgeist-1.0.so.1
libz.so.1 (libc6,x86-64) => /lib/x86_64-linux-gnu/libz.so.1
libz.so.1 (libc6) => /usr/lib32/libz.so.1
... ...
使用ldconfig命令默认情况下不输出扫描的结果信息,它的作用是更新系统默认搜索路径和配置文件中制定的搜索路径,然后将扫描结果缓存到“/etc/ld.so.cache”中,供运行程序快速访问调用。
我们也可以通过ldconfig命令来直接指定搜索路径:ldconfig 目录名
但这个是指临时制定,重新执行ldconfig则不会再包括制定的目录,除非在配置文件中添加上该目录。
5. 使用动态链接库
在编译程序的时候,使用动态链接库和静态链接库是一致的, 使用“-l库名”的形式,编译器在生成可执行文件的时候会链接该链接库文件。例如:
gcc -o test main.c -L ./ -lstr
-L : 指定链接动态库的路径
-lstr : 制定链接的动态库名称
这里需要注意的是: 编译的链接动态库和运行的动态链接库并不一致。 运行时的动态链接库需要放到系统搜索路径下。
6. 动态加载库的使用
动态加载库和动态链接库不同的是, 一般的动态链接库需要在程序启动的时候就要寻找动态链接库,找到库函数。而动态加载库可以使用程序的方法控制什么时候 加载。
动态加载库主要函数有: dlopen(), dlclose(), dlsym()和dlerror()。
6.1 打开动态库dlopen()函数
函数dlopen()按照用户指定的方式打开动态链接库。
void *dlopen(const char *filename, int flags);
# filename: 为动态链接库的文件名,当然可以包括路径部分
# flags: 打开方式,一般选择RTLD_LASY
# 函数返回值为库指针
例如我们可以使用下面的栗子打开指定目录下的动态库libbhd_client.so:
void *handle = dlopen("/tos/so/libbhd_client.so", RTLD_LASY);
6.2 获取函数指针dlsys()函数
我们使用动态链接库的最主要目的便是使用其中的函数接口(一个原因是模块间互相独立开发,另一个在于非开源保密)。 函数dlsys()可以获取指定函数名的函数指针,之后我们可以使用函数指针进行相关操作。
void *dlsym(void *handle, char *symbol)
# handle : 为使用函数dlopen()获取到的动态链接库指针
# symbol : 函数的名称
# 返回值为函数指针
6.3 使用动态加载库的栗子1:
#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>
#include <gnu/lib-names.h> /* Defines LIBM_SO (which will be a
string such as "libm.so.6") */
int main(void)
{
void *handle;
double (*cosine)(double);
char *error;
handle = dlopen(LIBM_SO, RTLD_LAZY);
if (!handle) {
fprintf(stderr, "%s\n", dlerror());
exit(EXIT_FAILURE);
}
dlerror(); /* Clear any existing error */
cosine = (double (*)(double)) dlsym(handle, "cos");
/* According to the ISO C standard, casting between function pointers and 'void *', as done above, produces undefined results. POSIX.1-2003 and POSIX.1-2008 accepted this state of affairs and proposed the following workaround:
*(void **) (&cosine) = dlsym(handle, "cos");
This (clumsy) cast conforms with the ISO C standard and will avoid any compiler warnings. The 2013 Technical Corrigendum to POSIX.1-2008 (a.k.a.POSIX.1-2013) improved matters by requiring that conforming implementations support casting 'void *' to a function pointer. Nevertheless, some compilers (e.g., gcc with the '-pedantic' option) may complain about the cast used in this program. */
error = dlerror();
if (error != NULL) {
fprintf(stderr, "%s\n", error);
exit(EXIT_FAILURE);
}
printf("%f\n", (*cosine)(2.0));
dlclose(handle);
exit(EXIT_SUCCESS);
}
6.4 使用动态加载库的栗子2:
由于我们可以通过程序指定动态加载库的时间,通过动态加载库可以实现模块的动态扩展。
思路如下:
- 在某个特定目录放不同模块编译生成的动态库;
- 程序中遍历该目录下所有的符合条件的动态库,然后打开动态库获取相关函数(例如module_init()),一般为模块的注册或者初始化函数,完成相应模块的加载或初始化操作;
- 这种情况下有个特点:每一个模块的初始化函数名都是固定的(如module_init()),这样便可以完成模块的动态加载。
具体代码如下:
#define BFD_CLIENT_REG_FUNC_NAME "bfd_client_register"
static int bfd_client_reg_init(const char *path)
{
struct dirent *dp;
DIR *dfd = NULL;
struct stat stbuf;
memset(&stbuf, 0, sizeof(struct stat));
if(stat(path, &stbuf))
{
bfd_debug("can not access:%s.\n", path);
goto err_out;
}
if ((stbuf.st_mode & S_IFMT) != S_IFDIR)
{
bfd_debug("st_mode error:%d.\n", stbuf.st_mode);
goto err_out;
}
if((dfd = opendir(path)) == NULL)
{
bfd_debug("open error:%s\n", path);
goto err_out;
}
while((dp = readdir(dfd)) != NULL)
{
if(0 == strcmp(dp->d_name, ".") || 0 == strcmp(dp->d_name, ".."))
{
continue;
}
if(dp->d_name == strstr(dp->d_name, "libtos_ipp_"))
{
char client_path[256];
strcpy(client_path, path);
if('/' != path[strlen(path) -1])
{
strcat(client_path, "/");
}
strcat(client_path, dp->d_name);
bfd_client_reg_load(client_path);
}
}
closedir(dfd);
return 0;
err_out:
if(NULL != dfd)
{
closedir(dfd);
}
return -1;
}
static int bfd_client_reg_load(const char *dl_name)
{
int id = -1;
bfd_client_init_func func;
void *handle;
char *error;
handle = dlopen(dl_name, RTLD_LAZY);
if(NULL == handle)
{
goto err_out;
}
func = dlsym(handle, BFD_CLIENT_REG_FUNC_NAME); /*bfd_client_register: 使用模块注册函数完成相应模块的注册*/
if((error = dlerror()) != NULL)
{
goto err_out;
}
id = func(); /*执行注册函数*/
if(id < 0 || id > 5)
{
bfd_debug("id err:%d.\n", id);
goto err_out;
}
return 0;
err_out:
bfd_debug("err_out.\n");
if(handle)
{
dlclose(handle);
}
return -1;
}
Linux 动态库的编译和使用的更多相关文章
- Linux动态库的编译与使用 转载
http://hi.baidu.com/linuxlife/blog/item/0d3e302ae2384d3a5343c1b1.html Linux下的动态库以.so为后缀,我也是初次在Linux下 ...
- Linux动态库的编译与使用
转载: http://hi.baidu.com/linuxlife/blog/item/0d3e302ae2384d3a5343c1b1.html Linux下的动态库以.so为后缀,我也是初次在Li ...
- Linux动态库的编译与使用 转载【转】
转自:http://www.cnblogs.com/leaven/archive/2010/06/11/1756294.html http://hi.baidu.com/linuxlife/blog/ ...
- linux动态库编译和使用
linux动态库编译和使用详细剖析 引言 重点讲述linux上使用gcc编译动态库的一些操作.并且对其深入的案例分析.最后介绍一下动态库插件技术, 让代码向后兼容.关于linux上使用gcc基础编译, ...
- Linux上静态库和动态库的编译和使用
linux上静态库和动态库的编译和使用(附外部符号错误浅谈) 这就是静态库和动态库的显著区别,静态库是编译期间由链接器通过include目录找到并链接到到可执行文件中,而动态库则是运行期间动态调用,只 ...
- linux动态库编译和使用详细剖析 - 后续
引言 - 也许是修行 很久以前写过关于动态库科普文章, 废话反正是说了好多. 核心就是在 linux 上面玩了一下 dlopen : ) linux动态库编译和使用详细剖析 - https://www ...
- linux动态库默认搜索路径设置的三种方法
众所周知, Linux 动态库的默认搜索路径是 /lib 和 /usr/lib .动态库被创建后,一般都复制到这两个目录中.当程序执行时需要某动态库, 并且该动态库还未加载到内存中,则系统会自动到这两 ...
- linux动态库加载RPATH, RUNPATH
摘自http://gotowqj.iteye.com/blog/1926771 linux动态库加载RPATH, RUNPATH 链接动态库 如何程序在连接时使用了共享库,就必须在运行的时候能够找到共 ...
- Android NDK开发及调用标准linux动态库.so文件
源:Android NDK开发及调用标准linux动态库.so文件 预备知识及环境搭建 1.NDK(native development Kit)原生开发工具包,用来快速开发C.C++动态库,并能自动 ...
随机推荐
- 某学院m3u8视频解密获取分析实战分享
[免责声明]本文来源于作者个人学习整理,仅供学习交流使用,不构成商业目的.所有资源均系本人个人学习或网络收集,仅提供一个展示.介绍.观摩学习的博文,不对其内容的准确性.可靠性.正当性.安全性.合法性等 ...
- Bugku-域名解析
做题之前了解一下域名解析,域名是为了方便记忆而专门建立的一套地址转换系统,要访问一台互联网上的服务器,最终还必须通过IP地址来实现,域名解析就是将域名重新转换为IP地址的过程.一个域名对应一个IP地址 ...
- idea构建servlet程序
1 新建maven项目 勾选maven_web模板 2 idea加载后应是如图所示 3 在main目录下新增两个文件夹,一个java 设置为源码根目录,另一个是resources 设置为源目录 4 在 ...
- Python -类型提示 Type Hints
为什么会有类型提示 Python是一种动态类型语言,这意味着我们在编写代码的时候更为自由,运行时不需要指定变量类型 但是与此同时 IDE 无法像静态类型语言那样分析代码,及时给我们相应的提示,比如字符 ...
- Create Virtual Network with VirtualBox on Mint 14
VirtualBox version: VirtualBox-4.2.18-88780-Linux_x86.run Host OS: Linux Mint 14 Xfce Setup Network ...
- 【原创】利用动态二进制加密实现新型一句话木马之PHP篇
概述 本系列文章重写了java..net.php三个版本的一句话木马,可以解析并执行客户端传递过来的加密二进制流,并实现了相应的客户端工具.从而一劳永逸的绕过WAF或者其他网络防火墙的检测. 本来是想 ...
- 快速上手CH340N电路设计(CH340N USB转串口模块 USB Type-C接口 CH340系列芯片讲解)
一.上模块 二.功能分析 l 芯片:CH340N l 输入接口:USB.TYPE-C l 输出接口:TTL(5V\3.3V\GND\TX\RX) l 指示灯:电源.TX.RX ...
- STM32—4线SPI驱动SSD1306 OLED
文章目录 一.OLED简介 二.驱动SSD1306所需知识 1.引脚介绍 2.通信时序 3.显存GRAM 4.字库 5.SSD1306基本命令 三.代码讲解 1.相关引脚配置 2.模拟SPI通信 3. ...
- Linux、Windows 下手动生成 sha256 等类型的校验文件
目录 1 - 校验文件的作用 2 - Linux 下生成校验文件 3 - Windows 下生成校验文件 参考资料 版权声明 1 - 校验文件的作用 从网服务器下载文件,尤其是比较大的文件时,很容易由 ...
- 手机摄像头作为视频画面捕捉设备与OBS连接(有线)
手机有线连接OBS(以无他伴侣作为测试案例) 1.硬件设备 安卓手机1台.安卓手机对应的数据线.电脑2台(windows系统,1台做OBS与手机连接.1台做直播后台:配置等如果能达到要求也可以只使用一 ...