Linux环境编译单个C程序文件

本文涉及的产品
文本翻译,文本翻译 100万字符
图片翻译,图片翻译 100张
语种识别,语种识别 100万字符
简介: Linux环境编译单个C程序文件

我的 Linux 环境是 Ubuntu18,Linux 环境用到的编译链接工具 就是 gcc 命令。

首先 在 Document 目录下 创建一个 c-single 目录 作为本文的项目目录,再创建一个 hello.c 文件

hello.c 代码如下:

#include <stdio.h>
int main()
{
    printf("hello ffmpeg \r\n");
    return 0;
}

截图如下:



执行以下 命令开始编译。

gcc -c -o hello.o hello.c

讲解一下 gcc 命令的用法, -c 是指只编译程序,不进行链接, -o 是指定输出文件名,hello.c 就是输入文件。

gcc 的语法规则是这样的, -o 后面就是输出文件,如果一个 参数前面什么都没有,那他就会被当做输入,hello.c 前面就什么都没有。可以有多个输入文件,编译出一个输出文件。

编译之后会生成 hello.o 目标文件,如下:

从上图可以看出来,hello.o 文件是一个二进制文件,ELF格式。这里推荐用 xelfviewer 来查看,如下:


hello.o 里面已经包含了 hello.c 对应的字节码,所以我们反编译看一下 hello.o 里面的字节码内容,执行以下命令。

objdump -d hello.o

上图我圈出来了一个 call 指令,这里就是 调 printf 函数的指令。但是这个 地址是 00 00 00 ,这肯定是不对的,现在只是一个占位,占了 4个字节,所以这个函数的地址是 4字节大小。

后面 链接器 会通过上面的 .rela.txt (重定位段)来修正 这个 printf 符号的地址,因为这个符号的地址 是在 C 标准库 libc 里面的,链接器需要扫描 libc 库,才能知道这个 符号的具体地址。

虽然本文编译的是单个 C 文件,但是还是会链接其他的外部库的,这个外部库 就是 libc,也叫做运行时库,是 gcc 链接器默认给你加上的,不用指定。

运行时库 这个词 有点不太好理解,你可以简单把它看成是一个外部的第三方库即可,虽然 运行时库跟外部库有一点区别。


现在开始链接程序 ,执行以下命令。

gcc -o hello hello.o

再用 objdump 看一下,会发现,之前的 call 的地址已经被修正 成了 c6 fe ff ff,如下:


我们知道,C/C++ 引入一个外部库的时候,这个外部库可以是动态库,也可以是静态库。

那 hello 引用 的 libc 是以静态还是动态的方式引入的呢?可以通过以下命令查看。

ldd hello

从上图 可以看出来, libc 是以动态库的方式链接给 hello 的。这是因为 gcc 默认是使用动态链接,如果找不到 动态 so 库,才会找 .a 静态库来链接。

那可不可以指定使用静态的方式 把 libc 链接给 hello呢?也是可以的,命令如下:

gcc -o hello hello.o -static -lc

-static 这个选项 会导致所有库都以静态的方式链接,-l 代表这是一个需要连接的库,不需要加 "lib" 。具体请看《LD链接器文档》

这时候,再用 ldd 查看 信息,会发现 已经没有了 libc.so 这个字符。

这里讲一个扩展知识点,Windows 跟 Linux 静态链接 C运行时库的方式不太一样。Windows 是在编译阶段 通过 /MT 或者 /MD 指定的,如下:

cl.exe /D_ISOC99_SOURCE /MT /c /Fo./hello-mt.o hello.c
link.exe /OUT:./hello-mt.exe ./hello-mt.o
cl.exe /D_ISOC99_SOURCE /MD /c /Fo./hello-md.o hello.c
link.exe /OUT:./hello-md.exe ./hello-md.o

而 Linux 的 gcc 编译阶段不需要指定 库的链接方式,是在 链接阶段 才确定是静态库还是动态库。


现在对比一下 两种方式编译的 hello,如下:

从上图可以看出, hello-static 比 hello-shared 大了 800kb,但是 libc.a 静态库明明有 5.3M,这是怎么回事呢?

这是因为 静态库 实际上是一个 归档(archive)文件,静态库里面可能包含了1000多个函数的实现代码,但是 hello 可能只用到 20个函数的代码,那链接器就会从 归档文件,提取这20个函数的代码放到 hello 文件。

静态链接并不是会把静态库的内容全部复制,只有用到才会复制。链接器能通过一些符号跟数据结构知道 hello 用了 libc.a 里面的哪些函数。按需提取。


通常 静态链接 C 运行时库 是为了解决兼容性问题。因为 Linux 系统各个版本的 C运行时库版本 可能不一样。

解决不同版本的问题,还有另一个技巧,不使用静态链接,直接把 libc.so 复制到运行程序当前目录,跟程序一起发布。

然后再在 /etc/ld.so.conf.d/ ,添加相关配置,再执行 sudo ldconfig 即可。因为 ld.so.conf.d 里面的路径是优先于 /lib/usr/lib 目录的。

在 Windows 环境,不需要 ldconfig,动态库会默认从当前目录优选查找。


参考资料:

1,《gcc/g++静态链接和动态链接解决glibc版本不兼容的问题》

目录
相关文章
|
18天前
|
Linux 开发工具 Perl
在Linux中,有一个文件,如何删除包含“www“字样的字符?
在Linux中,如果你想删除一个文件中包含特定字样(如“www”)的所有字符或行,你可以使用多种文本处理工具来实现。以下是一些常见的方法:
39 5
|
18天前
|
安全 Linux 数据安全/隐私保护
在 Linux 系统中,查找文件所有者是系统管理和安全审计的重要技能。
在 Linux 系统中,查找文件所有者是系统管理和安全审计的重要技能。本文介绍了使用 `ls -l` 和 `stat` 命令查找文件所有者的基本方法,以及通过文件路径、通配符和结合其他命令的高级技巧。还提供了实际案例分析和注意事项,帮助读者更好地掌握这一操作。
36 6
|
18天前
|
Linux
在 Linux 系统中,`find` 命令是一个强大的文件查找工具
在 Linux 系统中,`find` 命令是一个强大的文件查找工具。本文详细介绍了 `find` 命令的基本语法、常用选项和具体应用示例,帮助用户快速掌握如何根据文件名、类型、大小、修改时间等条件查找文件,并展示了如何结合逻辑运算符、正则表达式和排除特定目录等高级用法。
53 6
|
19天前
|
关系型数据库 MySQL Linux
Linux环境下MySQL数据库自动定时备份实践
数据库备份是确保数据安全的重要措施。在Linux环境下,实现MySQL数据库的自动定时备份可以通过多种方式完成。本文将介绍如何使用`cron`定时任务和`mysqldump`工具来实现MySQL数据库的每日自动备份。
42 3
|
19天前
|
监控 关系型数据库 MySQL
Linux环境下MySQL数据库自动定时备份策略
在Linux环境下,MySQL数据库的自动定时备份是确保数据安全和可靠性的重要措施。通过设置定时任务,我们可以每天自动执行数据库备份,从而减少人为错误和提高数据恢复的效率。本文将详细介绍如何在Linux下实现MySQL数据库的自动定时备份。
30 3
|
7月前
|
Linux
百度搜索:蓝易云【Linux中如何对文件进行压缩和解压缩?】
这些是在Linux中进行文件压缩和解压缩的常见方法。根据您的需求和具体情况,可能会使用其他压缩工具和选项。您可以通过查阅相应命令的帮助文档来获取更多详细信息。
89 1
|
7月前
|
NoSQL Java Linux
Linux常用命令(文件目录操作、拷贝移动、打包压缩、文本编辑、查找)
Linux常用命令(文件目录操作、拷贝移动、打包压缩、文本编辑、查找)
|
7月前
|
算法 Java Linux
Linux下文件增删改查定位压缩操作与权限所属用户
Linux下文件增删改查定位压缩操作与权限所属用户
74 0
26Linux - 文件管理(文件压缩解压:bzip2)
26Linux - 文件管理(文件压缩解压:bzip2)
62 0
|
7月前
|
Java Shell Linux
Linux【脚本 01】简单Shell脚本实现定时备份文件、压缩、删除超时文件操作(showDoc文件备份脚本举例)
Linux【脚本 01】简单Shell脚本实现定时备份文件、压缩、删除超时文件操作(showDoc文件备份脚本举例)
359 0