3-共享库的创建与应用

要用到的文件

文件树如下

  .
  |-bin
  |-lib
  |-src
    |-add.c
    |-common.h
    |-main.c
    |-sub.c
// 文件名:common.h
int add(int a, int b);
int sub(int a, int b);
// 文件名:add.c
int add(int a, int b) {
  return a + b;
}
// 文件名:sub.c
int sub(int a, int b) {
  return a - b;
}
// 文件名:main.c
#include <stdio.h>
#include "common.h"
int main() {
  printf("3 + 1 = %d\n", add(3, 1));
  printf("3 - 1 = %d\n", sub(3, 1));
}

生成动态库的命令

先进入到 src 目录。

生成动态库主要有两条命令

$ gcc -c -fpic add.c sub.c
$ gcc -shared add.o sub.o -o ../lib/libbase.so

如果你嫌上面两条命令麻烦,也可以这样

$ gcc -fpic -shared add.c sub.c -o ../lib/libbase.so

上面的命令执行完成后,会在 lib 目录下生成 libbase.so 文件。至于选项 -fpic 稍后解释。选项 -shared 有点类似于上一篇静态库中的 ar -r 命令,这里只是换成了 gcc -shared。

使用动态库

  • 方法一
$ gcc -c main.c
$ gcc main.o ../lib/libbase.so -o ../bin/app

完成后,在 bin 目录下会生成 app 可执行文件。

$ ldd bin/app

上述命令执行完成后,可以看到类似这样的信息

$ ldd bin/app
linux-gate.so.1 =>  (0xb77dc000)
../lib/libbase.so (0xb77d5000)
libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb7603000)
/lib/ld-linux.so.2 (0x80009000)
  • 方法二
$ gcc -c main.c
$ gcc main.o -L../lib -lbase -o ../bin/app

完成后,同样在 bin 下生成 app,但是需要注意的是,这个 app 和之前的那个 app 是有本质上的区别。通过 ldd 可以看出。

$ ldd bin/app
linux-gate.so.1 =>  (0xb7799000)
libbase.so => not found
libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb75c3000)
/lib/ld-linux.so.2 (0x8002c000)

注意,上面的 libbase.so 是未发现。如果你执行 app,也会失败:

./app: error while loading shared libraries: libbase.so: cannot open shared object file: No such file or directory

因为在链接最终的文件的时候,方法一是指定了一个相对路径的,最终生成的可执行文件中也包含了路径信息,加载器加载so文件的时候,知道根据这个相对路径去查找。当然指定绝对路径也是没问题的,但是千万别这么干。

而方法二只是告诉链接器动态库的名字,当加载动态库的时候只根据名字并不知道要上哪去找。

有同学会问,-L选项不是已经告诉了路径了吗?这里-L参数只是告诉链接器这个位置下面有so文件,但是最终生成的可执行文件并不包含路径信息。

解决的方法很简单,添加一个环境变量 LD_LIBRARY_PATH = $YOURPRJ/bin就好。$YOURPRJ/bin 是你 libbase.so 所在的路径。

export LD_LIBRARY_PATH = $YOURPRJ/bin

这样再次 ldd 你的可执行程序就正常了。

-fpic 选项

请允许我从 man 手册中抄一段话:

Generate position-independent code (PIC) suitable for use in a shared library, if supported for the target machine. Such code accesses all constant addresses through a global offset table (GOT). The dynamic loader resolves the GOT entries when the program starts (the dynamic loader is not part of GCC; it is part of the operating system). If the GOT size for the linked executable exceeds a machine-specific maximum size, you get an error message from the linker indicating that -fpic does not work; in that case, recompile with -fPIC instead. (These maximums are 8k on the SPARC and 32k on the m68k and RS/6000. The x86 has no such limit.)

这段话大意是说,如果目标机器支持的话,-fpic选项会在共享库中生成与位置无关代码。这种代码将使用全局变量偏移表(GOT)来访问所有的常数地址。当程序(被操作系统)启动的时候,动态加载器将会解析GOT中的条目。如果GOT的大小超过一定限制,将会链接失败,这个时候你可以使用 -fPIC 来替代。

到这里就够了,如果你想了解更多,需要学习一下有关 linux 可执行文件的格式,这种文件是 ELF 格式的,而在 Windows下,可执行文件通常是 PE 格式的。你可以把 ELF 文件理解成某个结构体如 struct ELF {...}实例化后的一个对象。想了解更多的话,你需要查阅相关的资料。

  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值