GCC上的动态库与静态库

如何创建和链接使用

最后更新:

文件名、后缀名

  • 静态链接库文件的后缀名:Linux通常用 .a ,Windows通常用 .lib
  • 动态链接库文件的后缀名:Linux通常用 .so ,Windows通常用 .dll
库类型 Linux-GCC Windows-MinGW Windows-MSVC
静态库 libxxx.a libxxx.a xxx.lib
动态库 libxxx.so libxxx.dll xxx.dll
动态库附加文件 libxxx.dll.a xxx.lib
  • 动态库附加文件libxxx.dll.a有时会有其他形式:libxxx.alibxxxdll.a,后者改变了库名

创建动态库

格式

  • gcc -fPIC -shared 源文件名(列表)... -o 动态链接库名
  • gcc -fPIC -shared 目标文件名(列表)... -o 动态链接库名

单个源文件/目标直接生成动态库

  • 方法1: gcc -fPIC -shared xxx.c -o libxxx.so
  • 方法2: gcc -fPIC -shared xxx.o -o libxxx.so

多个源文件/目标生成动态库

  • 方法1: gcc -fPIC -shared xxx1.c xxx2.c xxx3.c -o libxxx.so
    或: gcc -fPIC -shared *.c -o libxxx.so
  • 方法2: gcc -fPIC -shared xxx1.o xxx2.o xxx3.o -o libxxx.so

备注

  • libxxx.so可在Windows上改为libxxx.dll

Windows上小熊猫C++创建的C语言动态库TestShared-C

  • gcc.exe -c dllmain.c -o dllmain.o -O2 -pipe -DBUILDING_DLL=1

  • windres.exe -i TestShared-C_private.rc --input-format=rc -o Test-O coff

  • gcc.exe -mdll dllmain.o TestShared-C_private.res -o libTestShared-C.dll -s -static -Wl,--output-def,libTestShared-C.def,--out-implib,libTestShared-C.a

  • 注:-DBUILDING_DLL=1是为了让dll.h中的宏BUILDING_DLL生效,下同

Windows上小熊猫C++创建的C++语言动态库TestShared-CPP

  • g++.exe -c dllmain.cpp -o dllmain.o -O2 -std=c++2a -pipe -DBUILDING_DLL=1

  • windres.exe -i TestShared-CPP_private.rc --input-format=rc -o TestShared-CPP_private.res -O coff

  • g++.exe -mdll dllmain.o TestShared-CPP_private.res -o libTestShared-CPP.dll -s -static -Wl,--output-def,libTestShared-CPP.def,--out-implib,libTestShared-CPP.a

总结

  • 单步:
    g++.exe -fPIC -mdll xxx1.c xxx2.c xxx3.c -o libxxx.dll -O2 -std=c++20 -pipe -s -static -Wl,--output-def,libxxx.def,--out-implib,libxxx.dll.a -DXXXXXX=1
    解析:
    • g++.exe -fPIC -mdll xxx1.c xxx2.c xxx3.c -o libxxx.dll
    • -O2 -std=c++20 -pipe -s -static
    • -Wl,--output-def,libxxx.def,--out-implib,libxxx.dll.a
    • -DXXXXXX=1
  • 分步:
    • g++.exe -c xxx1.c xxx2.c xxx3.c -O2 -std=c++20 -pipe -DXXXXXX=1
      g++.exe -c xxx1.c -O2 -o xxx1.o -std=c++20 -pipe -DXXXXXX=1
    • g++.exe -fPIC -mdll xxx1.o xxx2.o xxx3.o -o libxxx.dll -s -static -Wl,--output-def,libxxx.def,--out-implib,libxxx.dll.a

gcc编译选项说明

  • -c:只编译,不链接,生成目标文件.o
  • -shared:用于生成动态链接库(shared library)
  • -mdll :为DLL生成代码(Generate code for a DLL)
  • -fPIC:生成位置无关代码,方便动态链接,常用于编译动态链接库(shared library)
  • -Wl,<选项>:将逗号分隔的 <选项> 传递给链接器。
  • --output-def <file>:产生与导出符号相关的def文件(Generate a .DEF file for the built DLL)
  • --out-implib FILE:产生用于提供__img_符号的导入库(Generate import library)
  • --kill-at:从导出的符号中删除@nn(Remove @nn from exported symb)
  • -Wl,--output-def,libTestShared-CPP.def,--out-implib,libTestShared-CPP.a中不要有空格

创建静态库

格式

简单地说,静态库是一个目标文件的简单集合。因此,首先有目标文件。再把生成的目标文件编译为静态库。

  • 第一步:先生成.o目标文件
    gcc -c 源文件列表
    gcc -c *.c
    gcc -c myfunc.c myproc.c将得到myfunc.omyproc.o
  • 第二步:由ar(archive,归档的意思)把多个目标文件集合起来编译为静态库。
    ar rcs 静态链接库名称 目标文件1 目标文件2 ...
    ar rcs libname.a *.ogcc *.o -static -o libname.a
    ar rcs libmyjob.a myfunc.o myproc.o

单个源文件/目标直接生成静态库

  • 正确方法: ar rcs libxxx.a xxx.o
  • 错误方法: ar rcs libxxx.a xxx.c(静态库可以生成;当运行连接了该静态库的可执行程序会报错:could not read symbols:Archive has no index;run ranlib to add one)

多个源文件/目标生成静态库

  • 正确方法: ar rcs libxxx.a xxx1.o xxx2.o xxx3.o
  • 错误方法: ar rcs libxxx.a xxx1.c xxx2.c xxx3.c(静态库可以生成;当运行连接了该静态库的可执行程序会报错:could not read symbols:Archive has no index;run ranlib to add one)

Windows上小熊猫C++创建的C语言静态库TestStatic-C

  • gcc.exe -c hello.c -o hello.o -O2 -pipe
  • windres.exe -i TestStatic-C_private.rc --input-format=rc -o TestStatic-C_private.res -O coff
  • ar r libTestStatic-C.a hello.o TestStatic-C_private.res 消息ar: creating libTestStatic-C.a
  • ranlib libTestStatic-C.a

Windows上小熊猫C++创建的C语言静态库TestStatic-CPP

  • g++.exe -c hello.cpp -o hello.o -O2 -std=c++2a -pipe
  • windres.exe -i TestStatic-CPP_private.rc --input-format=rc -o TestStatic-CPP_private.res -O coff
  • ar r libTestStatic-CPP.a hello.o TestStatic-CPP_private.res 消息ar: creating libTestStatic-CPP.a
  • ranlib libTestStatic-CPP.a

总结

  • g++.exe -c xxx1.c xxx2.c xxx3.c -O2 -std=c++20 -pipe
    g++.exe -c xxx1.c -O2 -o xxx1.o -std=c++20 -pipe
  • ar rcs libxxx.a xxx1.o xxx2.o xxx3.o

ar及其参数说明

  • ar 是 Linux 的一个备份压缩命令,它可以将多个文件打包成一个备份文件(也叫归档文件),也可以从备份文件中提取成员文件。ar 命令最常见的用法是将目标文件打包为静态链接库。
  • 参数r:在库中插入模块(替换)。当插入的模块名已经在库中存在,则替换同名的模块。如果若干模块中有一个模块在库中不存在,ar显示一个错误消息,并不替换其他同名模块。默认的情况下,新的成员增加在库的结尾处,可以使用其他任选项来改变增加的位置。
  • 参数c:创建一个库。不管库是否存在,都将创建。
  • 参数s:创建目标文件索引,这在创建较大的库时能加快时间。(补充:如果不需要创建索引,可改成大写S参数;如果.a文件缺少索引,可以使用ranlib命令添加)
  • 有些场合ar rcs又用ar -rcar -rar r替代使用

链接(使用)动态库和静态库

方法1:-I 头文件目录 -L 库文件目录 -l库名xxx(无前缀lib和后缀名)

  • g++ main.o -o main -I {INCLUDE_PATH} -L {LIB_PATH} -lname
  • g++ main.cpp -o main -I {INCLUDE_PATH} -L {LIB_PATH} -lname
  • 举例链接动态库:
    g++ main.cpp -o main.exe -O2 -std=c++20 -pipe -s -I D:\C++Lib\XXX\include -L D:\C++Lib\XXX\lib -lxxx
  • 举例链接静态库:
    g++ main.cpp -o main.exe -O2 -std=c++20 -pipe -s -static -I D:\C++Lib\XXX\include -L D:\C++Lib\XXX\lib -lxxx

方法2:-I 头文件目录 -L 库文件目录 -l:完整文件名+后缀

  • g++ main.o -o main -I {INCLUDE_PATH} -L {LIB_PATH} -l:完整文件名+后缀
  • g++ main.cpp -o main -I {INCLUDE_PATH} -L {LIB_PATH} -l:完整文件名+后缀
  • 举例链接动态库:
    g++ main.cpp -o main.exe -O2 -std=c++20 -pipe -s -I D:\C++Lib\XXX\include -L D:\C++Lib\XXX\lib -l:libxxx.dll
  • 举例链接静态库:
    g++ main.cpp -o main.exe -O2 -std=c++20 -pipe -s -static -I D:\C++Lib\XXX\include -L D:\C++Lib\XXX\lib -l:libxxx.a

方法3:-I 头文件目录 文件路径+完整文件名+后缀

  • g++ main.o -o main -I {INCLUDE_PATH} {LIB_PATH_AND_LIBNAME}
  • g++ main.cpp -o main -I {INCLUDE_PATH} {LIB_PATH_AND_LIBNAME}
  • 举例链接动态库:
    g++ main.cpp -o main.exe -O2 -std=c++20 -pipe -s -I D:\C++Lib\XXX\include D:\C++Lib\XXX\lib\libxxx.dll
  • 举例链接静态库:
    g++ main.cpp -o main.exe -O2 -std=c++20 -pipe -s -static -I D:\C++Lib\XXX\include D:\C++Lib\XXX\lib\libxxx.a

说明:

  • -I 头文件目录 可用设置环境变量set C_INCLUDE_PATH=头文件目录替代
  • -L 库文件目录 可用设置环境变量set LIBRARY_PATH=库文件目录替代
  • 文件路径+完整文件名+后缀不能省略路径,但可以用相对路径,LIBRARY_PATH对方法3无效

其他

名称修饰

  • 使用extern "C"来关闭名称修饰
  • 使用编译器选项关闭名称修饰:一些编译器提供了选项来关闭名称修饰,例如在GCC编译器中,可以使用-fno-rtti选项关闭名称修饰。

函数导出

  • MSVC def文件__declspec(dllexport)
  • MinGW64 __declspec(dllexport)
  • cmake+MinGW64 默认会导出所有函数

环境变量

  • C_INCLUDE_PATH头文件路径的环境变量:
  • CPLUS_INCLUDE_PATH头文件路径的环境变量:
  • LIBRARY_PATH库文件路径的环境变量
  • LD_LIBRARY_PATH是一个Linux环境变量,用于指定动态链接库的搜索路径。当一个程序运行时,如果需要使用动态链接库,系统会在LD_LIBRARY_PATH中指定的路径中搜索相应的库文件,以便程序能够正确运行。

查看用命令

  • nm libxxx.a 查看库中包含的函数信息
  • ldd main查看可执行程序依赖的动态库

fpic和fPIC比较

  • 相同点:都是为了在动态库中生成位置无关的代码,通过全局偏移表(GOT)访问所有常量地址。程序启动时动态加载程序解析GOT条目。
  • 不同点:如果链接的可执行文件的GOT大小超过计算机架构特定的最大值,则会在编译链接时报错误消息,提示 -fpic 不起作用;因此在这种情况下,需要使用 -fPIC 重新编译。GOT大小因芯片架构的不同而大小不一样,SPARC上为8k,在AArch64上为28k(笔者遇到的就是它,GOT超出了限制,所以报错),在m68k和RS / 6000上为32k。而x86上没有此限制。
  • 温馨提示:为了保障程序在跨平台编译时整体可用,通常情况下建议都使用 fPIC
     转载说明:请附上本文链接及上述版权声明。