gcc工具使用(编译静态库,动态库)

其他链接:编译器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为掐头去尾的静态库名称
			  			 空格可以省略     空格可以省略

注意

  1. -L参数指定静态库路径中间的空格可以省略。
  2. -l参数指定静态库中间的空格可以省略。
  3. 当所需库文件在当前目录下时,-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 动态库找不到怎么办

这就要引出动态链接器的概念了,当用户需要加载动态库时,动态连接器便要从一下位置找了:

  1. ELF可执行文件中动态段DT_RPATH指定;gcc加入链接参数“-Wl,-rpath”指定动态库搜索路径;(优先级最高)
  2. 环境变量LD_LIBRARY_PATH指定路径;
  3. /etc/ld.so.cache中缓存的动态库路径。可以通过修改配置文件/etc/ld.so.conf 增删路径(修改后需要运行ldconfig命令);
  4. 系统目录的 /lib/;
  5. 系统目录的 /usr/lib/;

动态链接器与LD_PRELOAD的关系,到底插入在哪了影响了链接过程?挖个坑。

解决方法:

  1. (确保正确使用动态库的方法学会了)
  2. 调查LD_LIBRARY_PATH中有没有,没有将动态库绝对路径加进去,只对当前终端有效
  3. 用vim修改/etc/ld.so.conf文件,在末尾添加绝对路径,然后sudo ldconfig/etc/ld.so.conf文件更新到/etc/ld.so.cache中。
  4. 将动态库的软连接放到/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 或相关依赖库。
    • 解决
      1. 确保文件系统完整(如从正规交叉编译工具链包中获取)。
      2. 手动复制所需文件到目标目录(如 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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值