其他链接:编译器ar工具参数使用
很重要的操作
gcc -E main.c //查看预处理结果比如头文件是哪个
gcc -E -dM main.c > 1.txt //展开所有的宏
gcc -Wp,-MD,abc.dep -c -o main.o main.c // 生成依赖文件 abc.dep,后面 Makefile 会用
echo 'main(){}'| gcc -E -v - // 它会列出头文件目录、库目录(LIBRARY_PATH)
编译分为:预处理–>编译–>汇编–>链接,
最常用的-c
选项是前三步。
一、gcc 编译静态库
1.1 什么是linux中的静态库
一般,以lib
为前缀,以.a
为后缀,比如libname.a
就是一个名为name的静态库。
1.2 如何在linux中生成静态库
先用gcc
的-c
参数编译得到.o
格式的目标文件,然后用ar
工具将目标文件打包,最终得到.a
静态库
1.3 举例
gcc -c add.c sub.c mult.c -I ./include
-I指定头文件路径
ar -rcs lib<xxx>.a 一系列.o
ar工具的参数 自己起的名 手写所有.o或匹配模式*.o
1.4 静态库咋发布
要给人家lib<xxx>.a
和 *.h
头文件一共俩文件。
1.5 如何使用静态库
先将lib<xxx>.a
和 *.h
以及测试文件test.c放一起。
gcc test.c -o app -L ./ -l xxx
可执行文件 指定静态库路径 指定静态库名字,xxx为掐头去尾的静态库名称
空格可以省略 空格可以省略
注意
-L
参数指定静态库路径中间的空格可以省略。-l
参数指定静态库中间的空格可以省略。- 当所需库文件在当前目录下时,
-l xxx
指定静态库名称的方式可以改为直接写lib<xxx>.a
,那该库就不需要-L
选项啦,只有写成-l
方式时才会去-L
指定的路径查找库。如下也行:
二、gcc 编译动态库
2.1 什么是linux中的动态库
一般,以lib
为前缀,以.so
为后缀,比如libname.so
就是一个名为name的动态库。
2.2 如何在linux中生成动态库
先用gcc
的-c
参数和-fPIC
参数编译得到.o
格式的位置无关文件,然后用-shared
参数编译,最终得到.so
动态库
-fPIC
参数的作用?
- -fPIC与-fpic都是在编译时加入的选项,用于生成位置无关的代码(Position-Independent-Code)。这两个选项都是可以使代码在加载到内存时使用相对地址,所有对固定地址的访问都通过全局偏移表(GOT)来实现。-fPIC和-fpic最大的区别在于是否对GOT的大小有限制。-fPIC对GOT表大小无限制,所以如果在不确定的情况下,使用-fPIC是更好的选择。
- -fPIE与-fpie是等价的。这个选项与-fPIC/-fpic大致相同,不同点在于:-fPIC用于生成动态库,-fPIE用与生成可执行文件。再说得直白一点:-fPIE用来生成位置无关的可执行代码。
详情看:https://www.cnblogs.com/20170722-kong/articles/12291904.html
2.3 举例
gcc 源文件*.c -c -fPIC -I ./include
-I指定头文件路径
gcc -shared *.o -o libxxx.so
*.o为匹配模式 xxx名自己起的
2.4 动态库咋发布
头文件和库文件都给人家。
2.5 如何使用动态库(就一种方法)
gcc test.c -o app -L ./ -l xxx
可执行文件 指定动态库路径 指定动态库名字,xxx为掐头去尾的动态库名称
空格可以省略 空格可以省略
2.6 动态库找不到怎么办
这就要引出动态链接器的概念了,当用户需要加载动态库时,动态连接器便要从一下位置找了:
- ELF可执行文件中动态段DT_RPATH指定;
gcc
加入链接参数“-Wl,-rpath
”指定动态库搜索路径;(优先级最高) - 环境变量
LD_LIBRARY_PATH
指定路径; /etc/ld.so.cache
中缓存的动态库路径。可以通过修改配置文件/etc/ld.so.conf
增删路径(修改后需要运行ldconfig命令);- 系统目录的
/lib/
; - 系统目录的
/usr/lib/
;
动态链接器与LD_PRELOAD的关系,到底插入在哪了影响了链接过程?挖个坑。
解决方法:
- (确保正确使用动态库的方法学会了)
- 调查LD_LIBRARY_PATH中有没有,没有将动态库绝对路径加进去,只对当前终端有效
- 用vim修改
/etc/ld.so.conf
文件,在末尾添加绝对路径,然后sudo ldconfig
将/etc/ld.so.conf
文件更新到/etc/ld.so.cache
中。 - 将动态库的软连接放到
/lib
或/usr/lib
中:sudo ln -s /xxx/libxxx.so /usr/lib/libxxx.so
。
2.7 ldd 调查动态库依赖
ldd 是 Linux 中,用于打印共享库依赖性的工具。当你运行 ldd 命令时,它会显示指定共享库或可执行文件所依赖的其他共享库。
我随便写了一个简单的打印函数编译成动态库:
root/ # ldd libtest.so
linux-vdso.so.1 (0x0000ffff9a21b000)
libc.so.6 => /usr/lib/libc.so.6 (0x0000ffff9a000000)
/usr/lib/ld-linux-aarch64.so.1 (0x0000ffff9a1de000)
linux-vdso.so.1 (0x0000ffff9a21b000)
linux-vdso.so.1 是 “Linux Virtual Dynamic Shared Object” 的缩写。VDSO 是内核提供的一个虚拟共享库,用于加速某些系统调用的执行。它不是实际的文件,而是内核在运行时映射到进程地址空间的一部分。
(0x0000ffff9a21b000) 是该共享库在进程地址空间中的加载地址。
libc.so.6 => /usr/lib/libc.so.6 (0x0000ffff9a000000)
libc.so.6 是 C 标准库的实现,几乎所有 Linux 程序都会依赖它。
=> /usr/lib/libc.so.6 表示 libc.so.6 的实际文件路径是 /usr/lib/libc.so.6。
(0x0000ffff9a000000) 是该共享库在进程地址空间中的加载地址。
/usr/lib/ld-linux-aarch64.so.1 (0x0000ffff9a1de000)
/usr/lib/ld-linux-aarch64.so.1 是动态链接器(Dynamic Linker)的路径。动态链接器是负责加载和链接共享库的程序。
(0x0000ffff9a1de000) 是该动态链接器在进程地址空间中的加载地址。
总结一下,libtest.so 依赖以下共享库:
linux-vdso.so.1:内核提供的虚拟共享库,用于加速系统调用。
libc.so.6:C 标准库的实现。
/usr/lib/ld-linux-aarch64.so.1:动态链接器,用于加载和链接共享库。
这些信息显示了 libtest.so 在运行时需要加载的共享库及其在进程地址空间中的加载地址。
三、查看 gcc
详细的执行过程
增加 -v
选项或者-###
选项
四、 动态库与静态库中内容查看
nm 可以查看静态库与动态库内容
ar -t libmylib.a # 列出所有文件
ar -tv libmylib.a # 详细模式(显示权限、时间等)
nm -s libmylib.a # 列出组成.o文件的详细函数信息
五、合并多个静态库
# 方法1:先提取所有.o文件,再重新打包
ar -x lib1.a
ar -x lib2.a
ar -rcs libcombined.a *.o
# 方法2:使用--whole-archive(GCC特有)
gcc -Wl,--whole-archive -o output lib1.a lib2.a -Wl,--no-whole-archive
补充:ar 命令核心选项(必选)
参数 | 含义 |
---|---|
r | 替换(Replace):将文件添加到归档文件中,若已存在则替换。 |
c | 创建(Create):若归档文件不存在,则创建它(不产生警告)。 |
s | 生成索引(Generate Symbol Table):强制生成归档文件的符号表。 即使未修改,也会更新 .a 文件的时间戳。 |
q | 追加(Quick Append):快速将文件追加到归档文件末尾(不排序)。 |
t | 列表(List):显示归档文件中包含的所有成员文件。 |
x | 提取(Extract):从归档文件中提取所有成员文件到当前目录。 |
d | 删除(Delete):从归档文件中删除指定的成员文件。 |
六、chroot 编译(是否能交叉编译待确认)
chroot
命令用于将指定目录作为“虚拟根目录”,使系统在该目录下运行程序,就像在真实的根目录(/
)中一样。在其中执行编译操作,完成后退出,不会污染主机编译环境。
假设你下载的文件系统目录为 /path/to/crosstoolfs
,以下是完整流程:
1. 准备文件系统目录
确保目录包含交叉编译所需的工具链、库文件和头文件(如 bin/
、lib/
、include/
等)。
# 示例目录结构(交叉编译工具链通常位于 `bin/` 下)
ls /path/to/crosstoolfs
# 输出可能包含:bin/ lib/ include/ usr/ etc/
2. 进入 chroot 环境
使用 chroot
命令切换到目标目录,并指定交叉编译所需的 shell(如 /bin/bash
):
sudo chroot /path/to/crosstoolfs /bin/bash
sudo
:通常需要管理员权限执行chroot
。/bin/bash
:指定在 chroot 环境中运行的 shell,可替换为/bin/sh
等。
执行后,终端提示符会变为 root@localhost:/#
,表示当前根目录已切换为 /path/to/crosstoolfs
。
3. 执行交叉编译
在 chroot 环境中,直接使用文件系统内的工具链编译程序。
假设工具链为 arm-linux-gnueabihf-gcc
(位于 bin/
目录):
arm-linux-gnueabihf-gcc your_source.c -o your_output -Iinclude -Llib
- 头文件路径:通过
-Iinclude
指定文件系统中的头文件目录。 - 库文件路径:通过
-Llib
指定文件系统中的库文件目录。
4. 退出 chroot 环境
编译完成后,输入以下命令退出:
exit # 返回到宿主系统
三、常见问题与解决方案
1. 权限问题
- 报错:
chroot: failed to run command ‘/bin/bash’: Permission denied
- 原因:目标目录权限不足或非 root 用户执行。
- 解决:确保使用
sudo
或切换为 root 用户,并检查目录权限(chmod -R 755 /path/to/crosstoolfs
)。
2. 缺少必要文件系统组件
- 报错:
chroot: failed to run command ‘/bin/bash’: No such file or directory
- 原因:目标目录中缺少
bin/bash
或相关依赖库。 - 解决:
- 确保文件系统完整(如从正规交叉编译工具链包中获取)。
- 手动复制所需文件到目标目录(如
cp /usr/bin/bash /path/to/crosstoolfs/bin/
)。
- 原因:目标目录中缺少
3. 交叉编译工具链路径问题
- 报错:
arm-linux-gnueabihf-gcc: command not found
- 原因:工具链未在
PATH
中或路径错误。 - 解决:
export PATH=/bin:$PATH # 假设工具链在 chroot 环境的 /bin/ 目录下
- 原因:工具链未在
五、总结命令流程
# 1. 宿主系统:切换到文件系统目录(可选)
cd /path/to/crosstoolfs
# 2. 宿主系统:进入 chroot 环境(需 root 权限)
sudo chroot . /bin/bash # "." 表示当前目录为根目录
# 3. chroot 环境中:执行交叉编译
arm-linux-gnueabihf-gcc your_code.c -o output -static # 示例编译命令
# 4. chroot 环境中:退出
exit