3-共享库的创建与应用

    xiaoxiao2022-08-06  85

    文章目录

    要用到的文件生成动态库的命令使用动态库-fpic 选项

    要用到的文件

    文件树如下

    . |-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 {...}实例化后的一个对象。想了解更多的话,你需要查阅相关的资料。

    转载请注明原文地址: https://ju.6miu.com/read-1132128.html
    最新回复(0)