linux 多源代码文件编译
分类: LINUX
前面一节介绍了Linux下头文件的一些基础知识,有内核头文件和用户空间头文件,但之后的讲解,都是基于用户空间的程序开发说起,而重点讲一些gcc相关的编译知识,以及后续会对 库文件,ELF文件,Makefile, AutoMake等作进一步的讲解。
有一点需要明确一下,在系列文章中,不会对代码或程序作过多的讲解,而是主要讲方法和整理思路,对于程序算法或结构之类的,相信大家可以从书上或网上的长篇大论中去学习研究,甚至直接去研究kernel或glibc中的程序算法足矣。
1. 不同的C标准
1). C语言的标准也是五花八门,那么,为什么要有各种不同的标准来约束我们编写C程序呢?
试想,如果把一个程序从平台A移植到平台B,但两个平台所支持的是两套不同的C函数接口,这样的程序从A移植到B,其难度是很大的,需要根据B平台自己的函数接口重新改写原程序。所以,需要有那么一套标准,来约束大家,基于这样的一个标准写出来的程序,可以轻易的移植到支持这个标准的所有平台。
C语言标准大概的演变过程如下:
2). Linux中最常见的两套标准是ANSI C和 POSIX C,那么,这两套标准有什么不同呢?
在Linux中,glibc是编写C程序的主要库,而glibc对于C语言标准的支持,除了上面的ANSI C系列标准,还有另外一套标准POSIX C,POSIX C在所有类Unix系统中几乎都能被支持,glibc中系统调用相关的接口都是符号POSIX C标准的,如:#include <sys/ioctl.h>等。
所以,如果想要编写能够在Windows和Linux之间方便移植的程序,尽量使用符合ANSI C标准的C语言;如果想要编写能够方便在Unix族系统之间移植的程序,尽量使用符合POSIX C标准的C语言。
2. GCC编译工具
GCC是Linux系统中一个功能强大的编译器,不仅可以对C,C 等程序进行编译,还支持交叉编译,所谓交叉编译是指针对不同硬件平台的编译,如ARM, MIPS等。
GCC常用的参数有:
-c |
生成目标文件,但不做链接 |
-O{n} |
优化代码,n 为 0, 1, 2, 3 几个等级 |
-Wall |
显示所有可能的警告信息 |
-w |
不显示任何警告信息 |
-g |
生成gdb必要的调试信息 |
-I{dir} |
添加头文件搜索路径 (字母 i 的大写) |
-include filename |
包含名为filename的头文件 |
-L{dir} |
添加 -L库搜索路径 (字母 l 的大写) |
-l{name} |
链接库文件, 比如 -lm 表示链接 libm.so |
-lpthread |
链接线程库 |
-fPIC |
生成位置无关代码,通常是共享库 |
-share |
使用动态库 |
3. GCC编译过程
以下面一个简单的源代码为例:
- /* demo.c */
- #include <stdio.h>
- int main(void)
- {
- printf("==echo main ==\n");
- return 0;
- }
1). 完整的编译过程:源文件 => 预处理 => 翻译 => 汇编 => 链接 => 可执行文件
# gcc -E demo.c -o demo.i |
# gcc -S demo.i -o demo.s |
# gcc -c demo.s -o demo.o |
# gcc demo.o -o demo |
gcc -E 参数对源程序demo.c作预处理,生成一个 demo.i 文件
gcc -S 参数对demo.i 文件进行汇编翻译,生成一个demo.s文件
gcc -c 参数对demo.s 文件进行目标转换,生成一个demo.o文件
最后使用gcc把demo.o这个目标文件链接成可执行程序即可
值得注意的是,gcc生成目标文件其实是调用 as命令来处理的,而链接的过程又是使用ld这个链接器来生成可执行程序,gcc对ld进行了一定的封装,后面会详细介绍ld的用法。
对这么一段简单的源代码,如果每次编译都要使用如此复杂的编译过程,显然是很浪费时间的,gcc可以对上面的完整过程进行简化。
2). 简化为两步:源文件 => 目标文件 => 可执行程序
# gcc -c demo.c -o demo.o |
# gcc demo.o -o demo |
上面步骤直接使用 -c 参数来生成目标文件 demo.o,然后再使用gcc将目标文件链接成可执行程序。
3). 简化为一步:源文件 => 可执行程序
# gcc demo.o -o demo |
虽说是简化,但其实gcc内部还是会完成上面的整个编译过程。
4. 多个源文件的编译
上面是针对一个源文件的编译过程,如果是多个源文件,gcc又如何进行编译呢? 以下面的三个源文件为例:
- /* demo.c */
- #include <stdio.h>
- #include "test.h"
- int main(void)
- {
- printf("==echo main ==\n");
- func();
- return 0;
- }
- /* test.c */
- #include <stdio.h>
- #include "test.h"
- void func()
- {
- printf("==echo func ==\n");
- }
- /* test.h */
- #ifdef TEST_H_
- #define TEST_H_
- void func();
- #endif
上面的源代码非常简单,就是在main()函数里调用了由test.c实现的func()函数,针对这种多个源文件的编译,其关键在于把每一个相关的源文件先生成目标.o文件,然后再把所有的.o目标文件链接成可执行程序即可:
# gcc -c test.c -o test.o |
# gcc -c demo.c -o demo.o |
# gcc test.o demo.o -o demo |
当然,也可以忽略上面生成目标文件的过程,直接一步到位的生成可执行程序:
# gcc demo.c test.c -o demo |
5. ld链接器
上面使用gcc来将目标文件链接成可执行程序的过程,实际上是调用ld这个链接器来实现的,使用strace跟踪发现,在gcc链接的过程中,首先调用了一个collect2的程序来链接成生可执行文件,而进一步跟踪发现,collect2实际是ld链接器的一个封装。
ld 是一个GNU链接工具,用来把各种目标文件或库文件链接在一起,形成可执行文件,gcc之所以对ld进行封装,是因为如果我们单纯的使用ld命令来链接生成可执行程序,其过程或写法相对比较复杂。
假如我们已经使用gcc将上面的test.c和demo.c生成了目标文件test.o和demo.o,那么,如果纯粹的使用ld来链接这两个目标文件的过程如下:
# ld -dynamic-linker /lib/ld-linux.so.2 \ > /usr/lib/crt1.o \ > /usr/lib/crti.o \ > /usr/lib/crtn.o \ > demo.o test.o \ > -lc \ > -o demo |
ld命令输入文件顺序一般是:
# ld [dynamic-linker] [crt1.o] [crti.o] [crtn.o] [user_obj] [user_lib] [sys_lib] [ -o bin]
首先,需要使用ld-linux.so.2这个linux的动态加载器(dynamic loader),来定位和加载程序所需的动态库,大多数的linux应用程序都是用这个加载器来加载的动态共享库的。
然后,需要指定程序启动的入口以及一些初始化工作,大家都知道一个C程序的入口点是main()函数,其实是不准确的,真正的入口点是_start,ld会把目标程序和crt1.o链接在一起,先调用crt1.o里面的_start,再通过_start调用main函数进入真正的程序主体。
接下来,就是指定所需要处理的目标文件,以及相关的库文件,最终生成可执行程序。
所以,相对ld命令,使用gcc的一个明显的好处就是无需手动的去指定链接过程中所需的各种文件,gcc已经完全帮你处理了这个复杂的过程。
后面一节将进一步介绍目标文件,库文件等方面的知识。
linux 多源代码文件编译相关推荐
- linux使用make命令编译错误,有关linux的make文件编译问题
我现在编译一个linux下的软件包,一周前编译还可以,但是现在却无法编译了,提示很多错误,但是这些错误觉得又不错误,对make如何管理又不是很了解,请高手指点一下. Makefile文件内容如下(比较 ...
- linux创建c文件编译错误,gcc已安装,编译其他软件时提示c编译器无法创建可执行文件...
操作系统是:redhat 5以上版本,具体忘了 系统内核是:Linux localhost.localdomain 2.6.32-358.el6.x86_64 #1 SMP Tue Jan 29 11 ...
- linux c 多文件编译,Linuxc - 多c文件程序编译执行
多文件使用,一起编译 定义max.h int max(int a,int b); 定义max.c #include "max.h" int max(int a,int b) { i ...
- Linux下获取文件编译的时间
#include <stdio.h>#define MONTH_PER_YEAR 12 // 一年12月 #define YEAR_MONTH_DAY 20 // 年月日缓存大小 #def ...
- Linux系列学习(二) - Vim编辑器的介绍及使用、文件编译的过程、Makefile工具、Gdb调试器
目录 引言: 基本命令补充: cat命令: man命令: head命令: tail命令: find命令: grep命令: grep命令与管道"|" 的结合使用: ta ...
- 使用 Visual Studio 对源代码文件进行哈希处理以确保文件完整性
对所有编译的软件语言来说,将人类可读代码转换成计算机可读代码都是一项软件保障挑战: 用户如何有信心相信在其计算机上运行的软件程序是根据开发者创建的同一源代码文件生成的呢? 这不一定,即使源代码文件经过 ...
- linux下find查找带有指定权限的文件(windows下编译的源代码文件)
find -type f -perm -o=x 查找用户在windows下编译的源代码文件 转载于:https://blog.51cto.com/axlrose/1357610
- linux中命令对c文件进行编译,Linux下C语言编译基础及makefile的编写
这篇文章介绍在LINUX下进行C语言编程所需要的基础知识.在这篇文章当中,我们将会学到以下内容: 源程序编译 Makefile的编写 程序库的链接 程序的调试 头文件和系统求助 1.源程序的编译 在L ...
- C Primer Plus 第9章 函数 9.4 多源代码文件程序的编译
2019独角兽企业重金招聘Python工程师标准>>> 9.4.1 UNIX 首先假定UNIX系统下安装了标准的UNIX C 编译器cc.文件file1.c和file2.c中包含有 ...
最新文章
- ASP.NET Web Forms – 服务器控件简介
- 解决 Python shell 中 Delete/Backspace 键乱码问题
- 地铁时光机第一阶段冲刺六
- Xamarin.Forms XAML控件的公共属性
- 计算机网络基础(缩短版)
- 【kafka】kafka UnknownProducerIdException raised broker locate producer metadata producerId
- 高速计算机的应用领域概括,[其它课程]计算机基础教案第一篇第一、二章.doc
- 64位Websphere MQ7在64位RHEL5下的安装及使用
- 帧中继的基本配置(Basic FrameRealy)
- JavaScript读取JSON文件
- Jmeter 修改背景色和字体
- Android 逆向笔记 —— 说说 Dalvik 及其指令集
- 为什么使用use strict可以节约你的时间
- 瑞萨RAe2studio快速上手视频笔记 一、瑞萨RAe2studio介绍
- 解决Android Studio 安装APK时device support,but apk only supports armeabi-v7 问题
- SpringSecurity原理:探究SpringSecurity运作流程
- Python挑战游戏( PythonChallenge)闯关之路Level- 1
- 学习牛津书的免费网站
- 面试详解之Java8为什么用红黑树来实现HashMap
- 六种常见WIFI部署场景