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处理
- 预处理(进行宏替换)
- 预处理功能主要包括宏定义,文件包 含,条件编译,去注释等。
- 预处理指令是以#号开头的代码行。
gcc -E hello.c o hello.i
- 编译(生成汇编)
- 在这个阶段中,gcc 首先要检査代 码的规范性、是否有语法错误等。以确定代码的实际要做的工作,在检查无误后,gcc 把代码翻译成汇编语言。
gcc-s hello.io hello.s
- 汇编(生成机器可识别代码)
- 汇编阶段是把编译阶段生成 的“.s“文件转成目标文件。
gcc-c hello.s -o hello.o
- 连接
在成功编译之后,就进入了链接阶段。
可以寻找缺少的函数的具体实现,从库中或者从其他.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只能使用静态链接。
打包库
如果我们想把我们写的方法给别人使用,可以采用以下方法:
- 直接提供源文件,让别人编译。
- 将我们的源代码打包成为一个库。
打包静态库(以c语言为例)
原理:将.c
文件编译为.p
,然后将所有的.o
文件打包。成一个.a
的静态库,需要使用该库的时候,只需要包含进头文件(.h
),然后直接将其和.o
文件连接即可。
1 |
|
${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)会利用动态库加载时生成的重定位表和符号表来解析符号地址,即完成偏移量和基地址的计算。