GCC 静态库 动态库及地址

Linux下,gcc形成可执行程序的过程

gcc [选项] 要编译的文件 [选项] [目标文件]

常见后缀和选项 功能
-E gcc 在预处理结束后停止编译过程
-o 目标文件
.i 已经处理过的C原始程序
-S 选项只进行编译而不进行汇编,生成汇编代码。
-c 将汇编代码转化为.o的二进制目标文件
-E 只激活预处理,这个不生成文件,你需要把它重定向到一个输出文件里面
-S 编译到汇编语言不进行汇编和链接
-c 编译到目标代码
-o 文件输出到 文件
-static 此选项对生成的文件采用静态链接
-g 生成调试信息。GNU 调试器可利用该信息。
-shared 此选项将尽量使用动态库,所以生成文件比较小,但是需要系统由动态库。
-O0 编译器的优化选项之一,表示没有优化
-O1 编译器的优化选项之一,为缺省值
-O2 编译器的优化选项之一
-O3 编译器的优化选项之一,优化级别最高
-w 不生成任何警告信息。
-Wall 生成所有警告信息。
-std=c99 使用C99标准
-I(大i) 指定路径下去寻找头文件
-L (lib)指定去该路径下寻找库文件
-l(小L) 指定库文件名称(去除前缀lib和后缀.a ,并且在-l后紧跟库名称 ,出了原生库,其他都要用-l指定)
ldd xx.out 查看链接方式(动态或者静态)
objdump -S obj.out 查看反汇编

gcc处理

  1. 预处理(进行宏替换)
  • 预处理功能主要包括宏定义,文件包 含,条件编译,去注释等。
  • 预处理指令是以#号开头的代码行。
    gcc -E hello.c o hello.i
  1. 编译(生成汇编)
  • 在这个阶段中,gcc 首先要检査代 码的规范性、是否有语法错误等。以确定代码的实际要做的工作,在检查无误后,gcc 把代码翻译成汇编语言。
    gcc-s hello.io hello.s
  1. 汇编(生成机器可识别代码)
  • 汇编阶段是把编译阶段生成 的“.s“文件转成目标文件。
    gcc-c hello.s -o hello.o
  1. 连接
  • 在成功编译之后,就进入了链接阶段。

  • 可以寻找缺少的函数的具体实现,从库中或者从其他.obj文件。替换原来的CALL的“虚假”地址。

  • 分为静态链接动态链接

  • 动态链接使用动态链接库进行链接,生成的程序在执行的时候需要加载所必需的动态库才能运行。动态链接生成的程序体积较小,但是必需依赖所需的动态库,否则无法执行

  • 静态链接使用静态库进行链接,生成的程序包含程序运行所需要的全部库,可以直接运行,不过静态链接生成的程序体积较大
    gcc hel1o.o -o hello

头文件和库文件的区别

1. 头文件(Header Files)

  • 作用: 头文件通常包含函数的声明、类的定义、宏、全局变量的声明、模板以及其他编译时所需的信息。它们告诉编译器某些函数或类的存在,并提供接口说明。
  • 扩展名: 头文件通常使用 .h.hpp 扩展名。

2. 库文件(Library Files)

  • 作用: 库文件包含函数、类等代码的实际实现。它们是在源代码编译后生成的二进制文件,供链接器在程序链接阶段使用。库文件提供了函数和类的实际执行代码。
  • 扩展名:
    • 静态库: 通常使用 .lib(Windows)或 .a(Linux)扩展名。
    • 动态库: 通常使用 .dll(Windows),.so(Linux),或 .dylib(macOS)

静态库(.a)和动态库(.so)

一部分函数实现会在系统默认的搜索路径 “/usr/lib下的libc.so.6 的库文件中,并在链接的阶段被找到。

这些库文件又分为动态库静态库

静态库

  • 静态库是指编译链接时,把库文件的代码全部加入到可执行文件中,因此生成的文件比较大,但在运行时也就不再需要库文件了

动态库

  • 动态库在编译链接时,并没有把库文件的代码加入到可执行文件中,而是在程序执行时由运行时链接文件加载库,这样可以节省系统资源。后缀名一般为.a

  • 动态库在程序运行的时候,是要能被能被加载到内存的,所以它有x权限。并且如果多个程序都要用到这个动态库,这个动态库也只会被加载一次,故动态库也叫共享库(并且支持写实拷贝,例如多个进程更改动态库中的一些全局变量)。

  • 动态库一般后缀名为.so,如前面所述的 libc.so.6 就是动态库。gcc 在编译时默认使用动态库,如果要使用静态库,要加上-static

    • static是一种建议性选项,如果系统中只提供了静态库,那么gcc只能使用静态链接。

打包库

如果我们想把我们写的方法给别人使用,可以采用以下方法:

  1. 直接提供源文件,让别人编译。
  2. 将我们的源代码打包成为一个库。

打包静态库(以c语言为例)

原理:将.c文件编译为.p,然后将所有的.o文件打包。成一个.a的静态库,需要使用该库的时候,只需要包含进头文件(.h),然后直接将其和.o文件连接即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
lib = libmyprogram.a

${lib}: mymath.o
ar -rc $@ $^

mymath.o: mymath.c
gcc -c $<

.PHONY: clean
clean:
rm -f *.o *.a

.PHONY: output
output:
mkdir -p lib/include
cp *.h lib/include


  • ${lib} 是一个变量,之前在 Makefile 中定义为 lib=libmyprogram。这意味着 ${lib} 实际上等同于 libmyprogram。

  • ar是gnu归档工具(用于生成静态库),rc表示(replace and create)
    使用静态库编译的时候,可能需要加上的若干选项

打包动态库文件

gcc -shared -o libmymethod.so *.o:打包成为动态库。

-shared: 表示生成共享库格式。
-fPIC:产生位置无关码。(position independent code)(常用于将需要打包为动态库的.c文件使用)

库的安装链接和卸载

安装:使用软连接将我们第三方库文件映射到系统自己搜索的库路径下。以便系统可以找到和使用这些库。

链接:库安装完毕之后,只是让你的系统具备了使用该库的能力,而如果想要使用这个库,需要指定需要链接的库。如下:
g++ -o my_program my_program.cpp -ljsoncpp

卸载:将系统库路径下我们的第三方库文件删除。

动态库如何实现的共享?

基础概念:

  • 逻辑地址(相对地址):使用基地址+ 偏移量的方式形成的地址。

进程中动态库的地址

  • 动态库被加载到内存之后,链接器和加载器会把动态库内函数等信息的偏移量,填写到需要使用它的程序中

    • -fPIC:产生位置无关码。也就是直接使用偏移量对库中函数进行编址。
  • 并且,需要使用该动态库的进程,会在自己的页表的共享区内,记录动态库进程的基地址的映射

  • 当运行的进程需要使用动态库内的函数等代码时,动态链接器(loader)会利用动态库加载时生成的重定位表和符号表来解析符号地址,即完成偏移量和基地址的计算。


GCC 静态库 动态库及地址
https://weihehe.top/2024/06/30/gcc-编译链接,究竟干了什么?/
作者
weihehe
发布于
2024年6月30日
许可协议