静态库与动态库总结

一、库文件和头文件

所谓库文件,可以将其理解为压缩包文件,该文件内部通常包含不止一个目标文件(也就是二进制文件)。

值得一提的是,库文件中每个目标文件存储的代码,并非完整的程序,而是一个个实用的功能模块。例如,C 语言库文件提供有大量的函数(如 scanf()、printf()、strlen() 等),C++ 库文件不仅提供有使用的函数,还有大量事先设计好的类(如 string 字符串类)。

 库文件的产生,极大的提高了程序员的开发效率,因为很多功能根本不需要从 0 开发,直接调取包含该功能的库文件即可。并且,库文件的调用方法也很简单,以 C 语言中的 printf() 输出函数为例,程序中只需引入 <stdio.h> 头文件,即可调用 printf() 函数。

头文件和库文件的关系:

头文件只存储变量、函数或者类等这些功能模块的声明部分;

库文件才负责存储各模块具体的实现部分;

所有的库文件都提供有相应的头文件作为调用它的接口。也就是说,库文件是无法直接使用的,只能通过头文件间接调用。

头文件和库文件相结合的访问机制,最大的好处在于,有时候我们只想让别人使用自己实现的功能,并不想公开实现功能的源码,就可以将其制作为库文件,这样用户获取到的是二进制文件,而头文件又只包含声明部分,这样就实现了“将源码隐藏起来”的目的,且不会影响用户使用。

事实上,库文件只是一个统称,代指的是一类压缩包,它们都包含有功能实用的目标文件。要知道,虽然库文件用于程序的链接阶段,但编译器提供有 2 种实现链接的方式,分别称为静态链接方式和动态链接方式,其中

采用静态链接方式实现链接操作的库文件,称为静态链接库;

采用动态链接方式实现链接操作的库文件,称为动态链接库;

在 C、C++ 实际开发过程中,除了可以使用系统库文件外,我们还可以根据实际需要,手动创建静态链接库或者动态链接库。

二、静态链接和动态链接

我们把编译后但是还未链接的二进制机器码文件称为目标文件(Object File),那些第三方库是其他人编译打包好的目标文件,这些库里面包含了一些函数,我们可以直接调用而不用自己动手写一遍。在编译构建自己的可执行文件时,使用静态链接的方式,其实就是将所需的静态库与目标文件打包到一起。最终的可执行文件除了有自己的程序外,还包含了这些第三方的静态库,可执行文件比较臃肿。相比而言,动态链接不将所有的第三方库都打包到最终的可执行文件上,而是只记录用到了哪些动态链接库,在运行时才将那些第三方库装载(Load)进来。装载是指将磁盘上的程序和数据加载到内存上。

静态库的本质其实就是 .o 文件的集合,所以它的基础表现与 .o文件没有区别——在链接时(link time),链接器从静态库中搜索所有的可见全局函数/变量符号,并且把这些符号复制到二进制文件(通常是可执行程序)中。

但需要注意的是,并非任何一个源文件都可以被加工成静态链接库,其至少需要满足以下 2 个条件:

1、源文件中只提供可以重复使用的代码,例如函数、设计好的类等,不能包含 main 主函数;

2、源文件在实现具备模块功能的同时,还要提供访问它的接口,也就是包含各个功能模块声明部分的头文件;

静态链接库实现链接操作的方式很简单,即程序文件中哪里用到了库文件中的功能模块,GCC 编译器就会将该模板代码直接复制到程序文件的适当位置,最终生成可执行文件。

使用静态库文件实现程序的链接操作,既有优势也有劣势:

优势是,生成的可执行文件不再需要任何静态库文件的支持就可以独立运行(可移植性强);

劣势是,如果程序文件中多次调用库中的同一功能模块,则该模块代码势必就会被复制多次,生成的可执行文件中会包含多段完全相同的代码,造成代码的冗余。

和使用动态链接库生成的可执行文件相比,静态链接库生成的可执行文件的体积更大。

相比之下,动态链接主要有以下好处:

多个可执行文件可以共享使用系统中的共享库。每个可执行文件都更小,占用的磁盘空间也相对比较小。而静态链接把所依赖的库打包进可执行文件,假如printf()被其他上千个程序使用,就要被打包到上千个可执行文件中,这样会占用了大量磁盘空间。

共享库的之间隔离决定了共享库可以进行小版本的代码升级,重新编译并部署到操作系统上,并不影响它被可执行文件调用。静态链接库的任何函数有了改动,除了静态链接库本身需要重新编译构建,依赖这个函数的所有可执行文件都需要重新编译构建一遍。

当然,共享库也有缺点:

如果将一份目标文件移植到一个新的操作系统上,而新的操作系统缺少相应的共享库,程序将无法运行,必须在操作系统上安装好相应的库才行。也即编译链接时使用的动态库可能是1.0版本,实际运行的操作系统上动态库可能只有2.0版本,此时程序就无法运行。

大多数现代操作系统(Linux、windows 等)都支持动态链接库,也即支持在程序运行时(runtime)链接动态库。动态链接库的文件名在 Linux 中通常以 .so 结尾,在 Windows 中则通常以 .dll 结尾。静态链接库的文件名在 Linux 中通常以 .a 结尾,在 Windows 中则通常以 .lib结尾。

静态链接:

编译器在编译可执行文件时,把需要用到的对应动态链接库(.so或.ilb)中的部分提取出来,链接到可执行文件中去,使可执行文件在运行时不需要依赖于动态链接库。

静态lib将导出声明和实现都放在lib中。编译后所有代码都嵌入到宿主程序。

动态链接:

动态链接的可执行文件需要附带一个的动态链接库,在执行时,需要调用其对应动态链接库中的命令。所以其优点一方面是缩小了执行文件本身的体积,另一方面是加快了编译速度,节省了系统资源。缺点一是哪怕是很简单的程序,只用到了链接库中的一两条命令,也需要附带一个相对庞大的链接库;二是如果其他计算机上没有安装对应的运行库,则用动态编译的可执行文件就不能运行。

三、创建和使用静态库

        libtool和ar都是Linux系统用来创建和管理静态库的工具。通常使用ar创建静态库。

1、libtool

以下是使用 libtool 创建静态库的基本步骤:

  1. 编写源代码: 首先,你需要有一组源代码文件,例如 file1.cfile2.c

  2. 编译源代码: 使用 libtool 编译源代码文件。这通常涉及到将 .c 文件转换为 .lo(libtool object)文件。

     

    libtool --mode=compile gcc -c file1.c -o file1.lo libtool --mode=compile gcc -c file2.c -o file2.lo

    这里,--mode=compile 告诉 libtool 进入编译模式,gcc 是编译器,-c 表示编译不链接,输出 .lo 文件。

  3. 创建静态库: 使用 libtool.lo 文件打包成一个静态库。例如,创建一个名为 libmylib.a 的库:

    libtool --mode=link gcc -o libmylib.a file1.lo file2.lo

    这里,--mode=link 告诉 libtool 进入链接模式,gcc 是链接器,-o 指定输出文件名。

  4. 安装库: 可选地,你可以将库安装到系统的标准库目录中:

    libtool --mode=install cp libmylib.a /usr/local/lib

    这里,--mode=install 用于安装模式,cp 是复制命令,将库复制到 /usr/local/lib 目录。

  5. 更新库索引: 如果你的系统中有多个 .la(libtool库辅助)文件,可以使用以下命令更新库索引:

    libtool --config

    然后按照提示操作。

请注意,libtool 通常与 autoconfautomake 结合使用,以自动处理库的创建和管理。如果你的项目使用了 autoconfautomake,那么你可能会在 Makefile.am 文件中定义库的创建规则,然后使用 make 命令来构建库。

在实际使用中,你可能需要根据你的具体需求和环境来调整命令和参数。如果你需要更详细的帮助,可以查看 libtool 的手册页(通过在终端运行 man libtool 命令)。

2、ar

r 是一个用于创建、修改和提取归档(archive)文件的命令行工具,它在 Unix-like 系统上广泛使用。归档文件通常用于将多个文件组合成一个单一的文件,以便于分发和存储。ar 操作的归档文件通常具有 .a 扩展名,表示它们是静态库文件。

以下是一些常用的 ar 指令及其选项:

  1. -c (create) 创建归档文件: 这个选项用于创建一个新归档文件。如果归档文件已经存在,并且使用 c 选项,ar 会报错。如果想要创建新归档文件并替换已有的同名文件,应该使用 cr 组合选项。

  2. -r (replace or insert) 替换或插入: 使用 r 选项可以将对象文件添加到归档中,如果归档中已存在同名文件,ar 会询问是否替换它。如果使用 r 选项并且归档文件不存在,ar 会报错。

  3. -s (write an index or append) 写入索引或追加: s 选项用于写入或更新归档文件的索引,这个索引用于加速提取和查询操作。当使用 s 选项时,ar 不会询问任何问题,直接执行。

  4. -x (extract or delete) 提取或删除: 使用 -x 选项可以从归档文件中提取所有对象文件。如果配合单个文件名使用,可以只提取那个特定的文件。

  5. -t (list) 列出: t 选项用于列出归档文件中的所有文件,不提取它们。

  1. 创建归档文件

    ar cr libmylib.a file1.o file2.o

    这里 cr 是创建(create)和替换(replace)的缩写,libmylib.a 是输出的归档文件名,file1.ofile2.o 是要添加到归档中的对象文件。

  2. 向归档文件添加文件

    ar r libmylib.a file3.o

    r 是添加(replace)的缩写,用于向已存在的归档文件 libmylib.a 中添加 file3.o

  3. 从归档文件提取文件

    ar x libmylib.a

    x 是提取(extract)的缩写,用于从 libmylib.a 中提取所有文件。

  4. 查看归档文件内容

    ar t libmylib.a

    t 是列出(list)的缩写,用于显示归档文件 libmylib.a 中的所有文件。

  5. 删除归档中的文件

    ar d libmylib.a file1.o

    d 是删除(delete)的缩写,用于从归档文件 libmylib.a 中删除 file1.o

  6. 移动归档中的文件

    ar m libmylib.a file1.o file2.o

    m 是移动(move)的缩写,用于在归档文件 libmylib.a 内部移动 file1.ofile2.o 到指定位置。

  7. 插入文件到归档的指定位置

    ar i libmylib.a file3.o

    i 是插入(insert)的缩写,与 m 选项类似,但通常用于将文件插入到归档的特定位置。

  8. 设置归档文件的选项

    ar S libmylib.a file1.o

    S 是在添加文件时不替换现有文件的缩写。

  9. 使用通配符添加文件

    ar cr libmylib.a *.o

    可以使用通配符(如 *.o)将所有 .o 文件添加到归档中。

ar 工具非常灵活,可以用于多种不同的场景,包括但不限于创建和管理静态库。

3、使用静态库

使用静态链接库:

g++ -static main.o libmyfunction.a -o main

其中,-static 选项强制 GCC 编译器使用静态链接库。

注意,如果 GCC 编译器提示无法找到 libmyfunction.a,还可以使用-L(大写的 L)选项用于向 GCC 编译器指明静态链接库的存储位置(可以借助 pwd 指令查看具体的存储位置);

-l(小写的 L)选项用于指明所需静态链接库的名称,注意这里的名称指的是 xxx 部分,且建议将 -l 和 xxx 直接连用(即 -lxxx),中间不需有空格。

四、创建和使用动态库

1、地址无关PIC

地址无关PIC(Position Independent Code,位置独立代码)

无论何种操作系统上,使用动态链接生成的目标文件中凡是涉及第三方库的函数调用都是地址无关的。假如我们自己编写的程序名为Program 1,Program 1中调用了C标准库的printf(),在生成的目标文件中,不会立即确定printf()的具体地址,而是在运行时去装载这个函数,在装载阶段确定printf()的地址。这里提到的地址指的是进程在内存上的虚拟地址。动态链接库的函数地址在编译时是不确定的,在装载时,装载器根据当前地址空间情况,动态地分配一块虚拟地址空间。

而静态链接库其实是在编译时就确定了库函数地址。比如,我们使用了printf()函数,printf()函数对应有一个目标文件printf.o,静态链接时,会把printf.o链接打包到可执行文件中。在可执行文件中,printf()函数相对于文件头的偏移量是确定的,所以说它的地址在编译链接后就是确定的。

2、创建动态库

总的来说,动态链接库的创建方式有 2 种。

1)直接使用源文件创建动态链接库,采用 gcc 命令实现的基本格式如下:

gcc -fpic -shared 源文件名... -o 动态链接库名

其中,

-shared 选项用于生成动态链接库;

-fpic(还可写成 -fPIC)选项的功能是,令 GCC 编译器生成动态链接库(多个目标文件的压缩包)时,表示各目标文件中函数、类等功能模块的地址使用相对地址,而非绝对地址。这样,无论将来链接库被加载到内存的什么位置,都可以正常使用。

例如,将前面项目中的 greeting.cpp 、name.cpp 这 2 个源文件生成一个动态链接库,执行命令为:

wohu@ubuntu:~/cpp/src$ g++ -fPIC -shared name.cpp  greeting.cpp  -o libmyfunction.so

wohu@ubuntu:~/cpp/src$ ls

function.h  greeting.cpp  libmyfunction.a  libmyfunction.so  main.cpp  name.cpp

注意,动态链接库的命名规则和静态链接库完全相同,只不过在 Linux 发行版系统中,其后缀名用 .so 表示;Windows 系统中,后缀名为 .dll。

2)先使用 gcc -c 指令将指定源文件编译为目标文件,再由目标文件生成动态链接库

注意,为了后续生成动态链接库并能正常使用,将源文件编译为目标文件时,也需要使用 -fpic 选项。

wohu@ubuntu:~/cpp/src$ g++ -c -fPIC name.cpp greeting.cpp 

wohu@ubuntu:~/cpp/src$ ls

function.h  greeting.cpp  greeting.o  libmyfunction.a  main.cpp  name.cpp  name.o

在此基础上,接下来利用上一步生成的目标文件,生成动态链接库:

wohu@ubuntu:~/cpp/src$ g++ -shared greeting.o name.o -o libmyfunction.so

wohu@ubuntu:~/cpp/src$ ls

function.h  greeting.cpp  greeting.o  libmyfunction.a  libmyfunction.so  main.cpp  name.cpp  name.o

3、动态库加载方式

1、windows平台

创建一个动态链接库,会生成x.dll,x.lib

动态链接库有两种加载方式:

     1.一种是静态加载,就是在编译的时候就载入动态链接库。此种方法可调用类方法.

           可执行程序静态加载动态链接库需要三个文件 x.dll, x.lib, x.h

           可执行程序的头文件加入:

            #include “x.h”

    #pragma comment(lib,“x.lib”)

           编译时还要附加库目录,防止程序编译时无法找到x.dll。

     2 .动态加载

   只需要x.dll文件。

   在程序执行需要该动态链接库的地方加载x.dll。

   然后获取需要的x.dll库里面的函数或数据.

   该方法不能调用类方法.

  可执行程序调用了动态链接库,其运行不能缺少动态链接库.

2、Linux平台

在Linux平台上,动态库的加载方式主要分为静态加载和动态加载两种:

静态加载(Static Linking)

静态加载指的是在编译时将动态库链接到程序中。这种方式下,程序在运行时搜索和加载动态库。静态加载通常使用以下步骤:

  • 编译源代码为对象文件,使用 -fpic 或 -fPIC 选项以生成与位置无关的代码(Position Independent Code,PIC)。
  • 使用 -shared 选项将对象文件打包成动态库(.so 文件)。
  • 在编译程序时,使用 -l 选项链接到动态库,例如 gcc program.c -o program -lmylib

动态加载(Dynamic Linking)

动态加载指的是在程序运行到加载处时才加载动态库。这种方式使得程序在编译时不需要依赖库文件,而是在运行时根据需要加载。动态加载通常使用以下步骤:

  • 在程序中使用 dlopen 函数在运行时加载动态库。
  • 使用 dlsym 函数获取库中函数的地址。
  • 使用完毕后,使用 dlclose 函数关闭动态库。
  • 如果发生错误,使用 dlerror 函数获取错误信息。

动态加载允许程序在运行时根据需要加载和卸载库,而静态加载则在链接时就将库链接到程序中。

4、使用动态库

通过前面章节的学习我们知道,动态链接库的使用场景就是和项目中其它源文件或目标文件一起参与链接。以前面例子为例,前面我们将 greeting.cpp、name.cpp 打包到了 libmyfunction.so 动态链接库中,此时该项目中仅剩 main.cpp 源程序文件,因此执行 demo 项目也就演变成了将 main.cpp 和 libmyfunction.so 进行链接,进而生成可执行文件。

注意,function.h 头文件并不直接参与编译,因为在程序的预处理阶段,已经对项目中需要用到的头文件做了处理。

执行如下指令,即可借助动态链接库成功生成可执行文件:

wohu@ubuntu:~/cpp/src$ g++ main.cpp  libmyfunction.so -o main

wohu@ubuntu:~/cpp/src$ ls

function.h  greeting.cpp  greeting.o  libmyfunction.a  libmyfunction.so  main  main.cpp  name.cpp  name.o

注意,生成的可执行文件 main 通常无法直接执行,例如:

wohu@ubuntu:~/cpp/src$ ./main

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

可以看到,执行过程中无法找到 libmyfunction.so 动态链接库。通过执行 ldd main 指令,可以查看当前文件在执行时需要用到的所有动态链接库,以及各个库文件的存储位置:

wohu@ubuntu:~/cpp/src$ ldd main

        linux-vdso.so.1 =>  (0x00007fffb17ea000)

        libmyfunction.so => not found

        libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007f548673a000)

        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f5486370000)

        libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f5486067000)

        /lib64/ld-linux-x86-64.so.2 (0x00007f5486abc000)

        libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f5485e51000)

可以看到,main 文件的执行需要 7 个动态链接库的支持,其中就包括 libmyfunction.so,但该文件无法找到,因此 main 执行会失败。

运行由动态链接库生成的可执行文件时,必须确保程序在运行时可以找到这个动态链接库。常用的解决方案有如下几种:

将链接库文件移动到标准库目录下(例如 /usr/lib、/usr/lib64、/lib、/lib64);

在终端输入

export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:xxx

其中 xxx 为动态链接库文件的绝对存储路径(此方式仅在当前终端有效,关闭终端后无效);

修改 ~/.bashrc 或 ~/.bash_profile 文件,即在文件最后一行添加

export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:xxx

其中 xxx 为动态链接库文件的绝对存储路径,保存之后,执行 source bashrc 指令(此方式仅对当前登陆用户有效)。

在本示例中采用第二种方案,即在终端输入

export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/wohu/cpp/src

即可使 main 成功执行。

5、动态库版本信息

dynamic library version information =  <M>.<m>.<p>

其中,每个助记符可能使用一个或多个数字来表示

M:主版本号

m:次版本号

p:补丁(很小的代码改动)版本号

动态库的SONAME

library soname = lib + <library name> + .so + <library major version digit(s)>

举例来说:

库文件libz.so.1.2.3.4的SONAME则是libz.so.1

实际上只有主版本号的数字在库SONAME中起作用,这意味着即使库的次版本号是不同的,也可能使用同一个SONAME值来表示。

动态库的SONAME通产由链接器嵌入二进制库文件的专有ELF字段中。通常使用特定的链接器选项,将表示库SONAME的字符串传递给链接器。

五、注意事项

1、如果你的静态库可能会被动态库使用,那么静态库编译的时候需要-fPIC选项。

静态库其实是一系列.o文件的打包,将.o文件制作成静态库的过程是不会发生链接的。

所以链接静态库就约等于将静态库解包后的.o文件一个一个全部链接。

-fPIC选项的生效阶段在编译阶段,使用-fPIC选项编译的.o文件会生成位置无关的代码

如果一个动态库需要链接一个静态库,那么就相当于动态库去链接一系列的.o文件,而编译动态库所需的.o文件时一般是推荐使用-fPIC编译选项的。

2、链接多个静态库时顺序问题

如果lib1.a中func1调用lib2.a的实现,则链接顺序为

gcc -o main main.o lib1.a lib2.a 

否则会提示undefined reference to `func1'

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

   

3、在c++代码中链接c语言的库

    如果你的库文件由c代码生成的,则在c++代码中链接库中的函数时,也会碰到undefined reference的问题。

    原因就是main.cppc++代码,调用了c语言库的函数,因此链接的时候找不到,解决方法:即在main.cpp中,把与c语言库test.a相关的头文件包含添加一个extern "C"的声明即可。

      

        g++ -o main main.cpp test.a 

             再编译会发现,问题已经成功解决。

4、同时链接静态库和动态库

g++ XXX.cpp -Fpic -o XXX.o -ldl -Wl,-Bstatic -laaa -Wl,-Bdynamic

同时链接动态库 libdl.a 和静态库 libaaa.a

其中,-Wl,-Bstatic 表示后面的库需要静态链接,-Wl,-Bdynamic 表示后面的库需要动态链接。注意系统的运行库使用动态连接的方式,所以当动态库在静态库前面连接时,必须在命令行最后使用动态连接的命令才能正常连接。最后的-Wl,-Bdynamic表示将缺省库链接模式恢复成动态链接。

-Wl是告诉编译器把后面的参数传递给链接器(ld)。因为在编译 C++ 程序的时候,编译器(g++)调用了一个叫链接器(ld)的程序,它的作用是把各种对象文件、库文件链接在一起,生成可执行文件。

-Wl就是为了把编译器的参数传递给链接器,相当于直接传递给链接器一样,其后的参数都是传递给链接器的选项。例如,-Wl,-rpath-link,-Wl,-Bdynamic等选项,都是用来控制链接器行为的选项。

5、使用链接过动态库的静态库

如果有静态库libXXX.a,它用了其他的动态库的函数,比如libAA.so的AA()函数,libBB.so的BB()函数,libCC.so的CC()函数,那么,该libXXX.a对这些动态库的调用仍是动态调用,而不是把动态库的相关函数copy到自己身上。任何其他程序,想用libXXX.a,链接时都需要链接libXXX.a时加上所依赖的动态库。

6、静态库和动态库链接时的搜索路径

现代连接器在处理动态库时将链接时路径(Link-time path)和运行时路径(Run-time path)分开,用户可以通过-L指定链接时库的路径,通过-R(或-rpath)指定程序运行时库的路径,大大提高了库应用的灵活性。

1) 静态库链接时搜索路径

1. ld会去找gcc命令中的参数-L

2. 再找gcc的环境变量LIBRARY_PATH

3. 再找内定目录 /lilb、/usr/lib、/usr/local/lib 这是当初compile gcc时写在程序内的

2)程序执行时动态连接库的搜索路径

  • RPATH: 写在elf文件中
  • LD_LIBRARY_PATH: 环境变量
  • RUNPATH: 写在elf文件中
  • ldconfig的缓存/etc/ld.so.cache
  • /usr/lib/lib

1、 编译目标代码时指定的动态库搜索路径

    gcc 使用-R或-rpath选项来在编译时就指定库的查找路径,并且该库的路径信息保存在可执行文件中,运行时它会直接到该路径查找库。

// 使用以下两种方式都可以,-Wl选项告诉编译器将后面的参数传递给连接器

#gcc -o main main.c -L. ladd -Wl,-R.

#gcc -o main main.c -L. ladd -Wl,rpath=.

#gcc -o main main.c -L. ladd -Wl,rpath .

2. 环境变量LD_LIBRARY_PATH指定的动态库搜索路径

    LD_LIBRARY_PATH设定是全局的,过多的使用可能会影响到其他应用程序的运行,所以多用在调试。 

// 当前调试时可以设置LD_LIBRARY_PATH环境变量

#LD_LIBRARY_PATH=.

#export LD_LIBRARY_PATH=.

// 或

#setenv LD_LIBRARY_PATH=.

3. ldconfig命令缓存的so配置/etc/ld.so.cache,ldconfig主要是在默认搜寻目录(/lib和/usr/lib)以及动态库配置文件/etc/ld.so.conf内所列的目录下,搜索出可共享的动态链接库,进而创建出动态装入程序(ld.so)所需的连接和缓存文件。缓存文件默认为 /etc/ld.so.cache。

3) 库的链接时路径和运行时路径

7、如何判断静态库或者目标文件是否-fPIC编译

readelf --relocs XXX.a | egrep '(GOT|PLT|JU?MP_SLOT)'

上句大多数时候(和平台有关)可以正确判断是否是以fPIC选项编译的,如果输出为空,基本可以表明不是以fPIC选项编译的,若有输出,基本上表明是以fPIC选项编译的。另外,由于静态库是多个目标文件的打包,所以最好把静态库解包之后再对每个目标文件进行判断,这样比较准确。

如果要用在动态库中,o文件和a文件都应该以fPIC选项编译。 fPIC是编译选项也是链接选项,如果编译的时候加了fPIC,链接的时候也必须加上。

8、开源库如何配置

linux ./configure 的参数详解_linux configure.sh-CSDN博客

安装开源常用流程如下:

./configuremakemake install

configure 脚本负责在你使用的系统上准备好软件的构建环境。确保接下来的构建和安装过程所需要的依赖准备好,并且搞清楚使用这些依赖需要的东西。

当 configure 配置完毕后,使用 make 命令执行构建。这个过程会执行在 Makefile 文件中定义的一系列任务将软件源代码编译成可执行文件。源码包一般没有一个最终的 Makefile 文件,一般是一个模版文件 Makefile.in 文件,然后 configure 根据系统的参数生成一个定制化的 Makefile 文件。make install 命令就是将可执行文件、第三方依赖包和文档复制到正确的路径。

configure和make 脚本文件非常复杂,一般是通过autotools 的工具集打包的。这个工具集包含 autoconf 、automake 等工具。

config脚本:先创建一个描述文件 configure.ac 来描述 configure 需要做的事情。configure.ac 使用 m4sh 写,m4sh 是 m4 宏命令和 shell 脚本的组合。使用 autoconf 将 configure.ac 生成 configure 脚本。

make脚本:先写一个 Makefile.am 脚本,然后通过 automake 工具生成 Makefile.in 脚本。

通过脚本的help选项可以知道脚本的配置方式,

./configure –help

       例如需要修改CFLAGS选项支持-fPIC只需要

       ./configure CFLAGS=-fPIC

prefix选项是配置安装的路径,如果不配置该选项,安装后可执行文件默认放在/usr/local/bin,库文件默认放在/usr/local/lib,配置文件默认放在/usr/local/etc,其它的资源文件放在/usr/local/share,比较凌乱。

./configure --prefix=/usr/local/test

可以把所有资源文件放在/usr/local/test的路径中,不会杂乱。

--with-includes=DIRECTORIES  
DIRECTORIES   是一系列冒号分隔的目录,这些目录将被加入编译器的头文件   搜索列表中.如果你有一些可选的包(比如   GNU   Readline)安装在   非标准位置,你就必须使用这个选项,以及可能还有相应的   --with-libraries   选项.    
   
例子:--with-includes=/opt/gnu/include:/usr/sup/include    
   
--with-libraries=DIRECTORIES  
DIRECTORIES  
是一系列冒号分隔的目录,这些目录是用于查找库文件的.   如果你有一些包安装在非标准位置,你可能就需要使用这个选项   (以及对应的--with-includes选项).   

9、链接动态库与使用动态库版本判断

Linux环境中动态库文件(.so文件)的realname,soname和linkname_linux 查看so库的realname-CSDN博客

linkname:

在链接时使用,一般格式为lib$(name).so,通常是$(realname)文件或者$(soname)文件的软链接。

当使用-l$(name)选项时,链接器会从相应目录链接lib$(name).so的文件,如果该文件不存在,则会链接lib$(name).a文件,如果该文件也不存在,会发生链接错误。

如果链接的是lib$(name).a文件,在运行程序时也就没$(realname)文件和$(soname)文件什么事了。当时使用-l$(name)链接库文件时,链接器会读取lib$(name).so文件中的soname值,并将其记录在生成的程序中。

realname:

实际等同于库文件的filename,是在库文件生成时就被指定的,如:gcc -shared -o $(realname) dependence flags

realname的一般格式为 lib$(name).so.$(major).$(minor).$(revision),$(name)是动态库的名字,$(major).$(minor).$(revision)分别表示主版本号,子版本号和修正版本号

soname:

在库文件生成时被指定,如:gcc -shared -o $(realname) dependence flags -Wl,-soname,$(soname)

其一般格式为lib$(name).so.$(major).$(minor),soname会被写入库文件中

可以使用readelf -d $(realname)查看库文件的soname

不同realname的库文件可以有相同的soname,有利于库文件的升级和兼容,例如当版本从1.0.0升级到1.0.1,库文件的接口没有变化(或者接口增加,但原有接口不变)的情况下,可以指定相同的soname,这样使用1.0.0生成的程序仍然可以运行。

soname在链接和加载库文件时使用,当时使用-l$(name)链接库文件时,链接器会读取lib$(name).so文件中的soname值,并将其记录在生成的程序中,当运行程序时,会从相应的目录加载名为$(soname)的文件,所以,在运行程序之前,$(soname)的库文件必须已生成。

可以采用软连接的方式生成该文件,如:ln -s $(realname) $(soname)

也可以使用ldconfig命令自动生成,如 ldconfig -n $(dir) 会生成$(dir)目录下所有库文件对应的$(soname)文件。

10、获取GCC(或make)时,实际编译器和链接器正在执行的命令

方法一:通用方法

使用dry run,如下
    $ make -n
这将显示make 命令正在试图做的事情。
通过 $ make -h 命令,查看帮助可知,make -n实际并不运行任何命令,只是把make试图做的事情显示出来。

方法二:特定方法,适用于使用autotools产生的库makefile文件

使用autotools(你必须发布./configure)产生的库makefile文件常常有一个verbose选项,所以基本上,使用
    make VERBOSE=1 make V=1
你将获取到全部命令。但是这取决于makefile产生的方式。

make命令的-d选项可能会有帮助的,但是会有很长的输出,而且也不是实际运行的命令,而是大量的调试信息。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/757807.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Linux 网络:网卡 promiscuous 模式疑云

文章目录 1. 前言2. 问题场景3. 问题定位和分析4. 参考资料 1. 前言 限于作者能力水平&#xff0c;本文可能存在谬误&#xff0c;因此而给读者带来的损失&#xff0c;作者不做任何承诺。 2. 问题场景 调试 Marvell 88E6320 时&#xff0c;发现 eth0 出人意料的进入了 promis…

【吊打面试官系列-MyBatis面试题】MyBatis 与 Hibernate 有哪些不同?

大家好&#xff0c;我是锋哥。今天分享关于 【MyBatis 与 Hibernate 有哪些不同&#xff1f;】面试题&#xff0c;希望对大家有帮助&#xff1b; MyBatis 与 Hibernate 有哪些不同&#xff1f; 1、Mybatis 和 hibernate 不同&#xff0c;它不完全是一个 ORM 框架&#xff0c;因…

grpc学习golang版( 四、多服务示例 )

系列文章目录 第一章 grpc基本概念与安装 第二章 grpc入门示例 第三章 proto文件数据类型 第四章 多服务示例 第五章 多proto文件示例 第六章 服务器流式传输 第七章 客户端流式传输 第八章 双向流示例 文章目录 一、前言二、定义proto文件三、编写server服务端四、编写Client客…

盘点全球Top10大云计算平台最热门技能证书

小李哥花了一年半时间终于考下全球10大云的77张认证&#xff0c;今天盘点下各个云的热门证书&#xff0c;希望能帮到非CS专业转IT和刚刚入行云计算的小伙伴。 排名取自23年Yahoo云计算市场份额排名报告&#xff0c;我会从云平台、证书价格、证书热门程度做推荐。 1️⃣亚马逊云…

MathType7.6永久破解激活码注册码 包含安装包下载

MathType是一款强大的数学公式编辑器&#xff0c;它能够帮助用户轻松编辑各种复杂的数学公式和符号。无论是学生、教师还是科研人员&#xff0c;MathType都能提供专业、精确的数学公式编辑服务。 在学习和工作中&#xff0c;我们常常会遇到需要编写数学公式的情况。然而&#x…

Python 算法交易实验74 QTV200第二步(改): 数据清洗并写入Mongo

说明 之前第二步是打算进入Clickhouse的&#xff0c;实测下来有一些bug 可以看到有一些分钟数据重复了。简单分析原因&#xff1a; 1 起异步任务时&#xff0c;还是会有两个任务重复的问题&#xff0c;这个在同步情况下是不会出现的2 数据库没有upsert模式。clickhouse是最近…

除了重塑千行百业,生成式AI还能改善运动健康

飞速发展的生成式AI与大模型技术&#xff0c;不但正在重塑千行百业&#xff0c;而且还能有效改善人们的运动健康。 生成式AI技术应用的挑战 随着生活品质的不断提升&#xff0c;人们对于健康问题也越来越重视。作为一家以“AI重塑健康与美”为使命的AI数字健康解决方案提供商&a…

langchain学习总结

大模型开发遇到的问题及langchain框架学习 背景&#xff1a; 1、微场景间跳转问题&#xff0c;无法实现微场景随意穿插 2、大模型幻读&#xff08;推荐不存在的产品、自己发挥&#xff09; 3、知识库检索&#xff0c;语义匹配效果较差&#xff0c;匹配出的结果和客户表述的…

解决指南:如何应对错误代码 0x80070643

在使用Windows操作系统过程中&#xff0c;用户可能会遭遇各种错误代码&#xff0c;其中错误 0x80070643是比较常见的一种。这个错误通常在安装更新或某些软件时发生&#xff0c;尤其是在微软的Windows Defender或其他Microsoft安全产品以及.NET Framework更新过程中更为常见。本…

动画重定向——当给一个人物模型用别人物的动画时,会遇到人物与动画不匹配问题,怎么解决呢?

每日一句&#xff1a;实践出真知&#xff0c;试错方确信 目录 最开始我想的原因&#xff01; 分析一下动画相关参数 Animator组件参数详解&#xff1a; 人物模型的导入设置参数&#xff1a; Skinned Mesh Renderer组件详解: Skinned Mesh Renderer工作原理 设置Skinned …

【吴恩达深度学习笔记系列】Logistic Regression 【理论】

Binary Classification: Logistic Regression: y ^ σ ( w T x b ) \hat{y}\sigma{(w^T xb)} y^​σ(wTxb) using sigmoid function σ 1 1 e − z \sigma \frac{1}{1e^{-z}} σ1e−z1​. 【torch.sigmoid(x)】 Sigmoid ( x ) 1 1 e − x \text{Sigmoid}(x)\frac{1}{…

nginx优势以及应用场景,编译安装和nginx

一. Nginx是什么&#xff1f; 1. Nginx概述 高性能、轻量级Web服务软件系统资源消耗低对HTTP并发连接的处理能力高单台物理服务器可支持30,000&#xff5e;50,000个并发请求Nginx&#xff08;发音同 “engine x”&#xff09;是一个高性能的反向代理和Web服务器软件&#xff0c…

【05】从0到1构建AI生成思维导图应用 -- 前端交互实现

【05】从0到1构建AI生成思维导图应用 – 前端交互实现 大家好&#xff01;最近自己做了一个完全免费的AI生成思维导图的网站&#xff0c;支持下载&#xff0c;编辑和对接微信公众号&#xff0c;可以在这里体验&#xff1a;https://lt2mind.zeabur.app/ 上一章&#xff1a;http…

【C++】初识C++(一)

一.什么是C C语言是结构化和模块化的语言&#xff0c;适合处理较小规模的程序。对于复杂的问题&#xff0c;规模较大的程序&#xff0c;需要高度 的抽象和建模时&#xff0c;C语言则不合适。为了解决软件危机&#xff0c; 20世纪80年代&#xff0c; 计算机界提出了OOP(object o…

Mathematica训练课(46)-- 一些常用的画图函数

在前面的课程中&#xff0c;我们已经梳理了Plot的画图用法&#xff0c;今天就详细梳理一下其他的画图函数用法&#xff1b; 1. 画一条直线 2. Circle(圆) 3. Disk&#xff08;圆盘&#xff09; 4. 画出一个矩形 5. 箭头

itext生成pdf文件demo示例

需求 在PDF文件中植入一些信息&#xff08;pdf模版&#xff09; 制作模版 可以看到下面红色箭头标注位置&#xff0c;这都是我们需要动态写入数据的表单域&#xff0c;可以使用wps等工具来制作 点击编辑表单&#xff0c;可以给对应空间添加表单域&#xff0c;表单域名称是ke…

ic基础|功耗篇04:门级低功耗技术

大家好&#xff0c;我是数字小熊饼干&#xff0c;一个练习时长两年半的IC打工人。我在两年前通过自学跨行社招加入了IC行业。现在我打算将这两年的工作经验和当初面试时最常问的一些问题进行总结&#xff0c;并通过汇总成文章的形式进行输出&#xff0c;相信无论你是在职的还是…

UE5材质之HLSL:深度

UE4/5的Custom节点&#xff1a;在VScode使用HLSL&#xff08;新手入门用&#xff09;_vscode写hlsl-CSDN博客 效果&#xff1a; 材质节点&#xff1a; 自定义节点代码&#xff1a; float3 rayStepViewDir*-1; float4 inputTexTexture2DSample(TexObject,TexObjectSampler,uv)…

AGPT•intelligence:带你领略全新量化交易的风采

随着金融科技的快速发展&#xff0c;量化交易已经成为了投资领域的热门话题。越来越多的投资者开始关注和使用量化交易软件来进行投资决策。在市场上有许多量化交易软件可供选择。 Delaek&#xff0c;是一位资深的金融科技专家&#xff0c;在 2020年成立一家专注于数字资产量化…

【第三方JSON库】org.json.simple用法初探—Java编程【Eclipse平台】【不使用项目管理工具】【不添加依赖解析】

本文将重点介绍&#xff0c;在不使用项目管理工具&#xff0c;不添加依赖解析情况下&#xff0c;【第三方库】JSON.simple库在Java编程的应用。 JSON.simple是一种由纯java开发的开源JSON库&#xff0c;包含在JSON.simple.jar中。它提供了一种简单的方式来处理JSON数据和以JSO…