在Unix系统上,从源文件、目标文件、可执行文件的编译过程
是由“编译器驱动”(compiler driver)完成的:
unix> gcc -o hello hello.c
在这里,gcc的编译器驱动程序读取源文件hello.c,
- #include <stdio.h>
- int main()
- {
- printf("hello, world/n");
- return 0;
- }
并把它翻译成一个可执行目标文件hello,这个过程是分为四个阶段完成的。如下图所示,执行这四个阶段的程序(预处理器、编译器、汇编器和链接器)一起构成了编译系统。
预处理阶段:预处理器(cpp)根据以字符#开头的命令(directives),修改原始的C程序。如hello.c中第一行的#include <stdio.h>指令,它告诉预处理器读取系统头文件stdio.h的内容,并把它直接插入到程序文本中去。结果就得到另一个c程序,通常是以.i作为文件扩展名的。
编译阶段:编译器(ccl)将文本文件hello.i翻译成文本文件hello.s,它包括一个汇编语言程序。汇编语言程序中的每一条语句都一种标准的文本格式,确切地描述了一条低级机器语言指令。汇编语言为不同的高级语言的不同编译器提供了通用的输出语言。
汇编阶段:汇编器(as)将hello.s翻译成机器语言指令,再将这些指令打包成一种叫“可重定位”(relocatable)目标程序的格式,并将结果保存在目标文件hello.o中。hello.o文件是一个二进制文件,它的字节编码是机器语言指令而不是字符。如果用文本编辑器打开,看到的将是一群乱码。
链接阶段:hello程序调用了printf函数(标准C库中的一个函数,每个C编译器都提供),printf函数存在于一个名为printf.o的单独的预编译目标文件中,而这个文件必须以某种方式并入到hello.o程序中。链接器(ld) 就负责处理这种并入,结果得到一个可执行目标文件/hello文件(或称为可执行文件)。可执行文件加载到存储器后,由系统负责执行。
原文地址:http://blog.csdn.net/lychee007/article/details/4123130
———————————————————————————————————————————————————————————————————————————————
gcc编译过程
现代编译器常见的编译过程:
源文件-->预处理-->编译/优化-->汇编-->链接-->可执行文件
对于gcc而言:
第一步 预处理
命令: gcc -o test.i -E test.c
或者 cpp -o test.i test.c (这里cpp不是值c plus plus,而是the C Preprocessor)
结果: 生成预处理后的文件test.i(可以打开后与预处理前进行比对,当然长度会吓你一跳)
作用: 读取c源程序,对伪指令和特殊符号进行处理。包括宏,条件编译,包含的头文件,以及一些特殊符号。基本上是一个replace的过程。
第二步 编译及优化
命令: gcc -o test.s -S test.i
或者 /路径/cc1 -o test.s test.i
结果: 生成汇编文件test.s(可打开后查看源文件生成的汇编码)
作用: 通过词法和语法分析,确认所有指令符合语法规则(否则报编译错),之后翻译成对应的中间码,在linux中被称为RTL(Register Transfer Language),通常是平台无关的,这个过程也被称为编译前端。编译后端对RTL树进行裁减,优化,得到在目标机上可执行的汇编代码。gcc采用as作为其汇编器,所以汇编码是AT&T格式的,而不是Intel格式,所以在用gcc编译嵌入式汇编时,也要采用AT&T格式。
第三步 汇编
命令: gcc -o test.o -c test.s
或者 as -o test.o test.s
结果: 生成目标机器指令文件test.o(可用objdump查看)
作用: 把汇编语言代码翻译成目标机器指令, 用file test.o 可以看到test.o是一个relocatable的ELF文件,通常包含.text .rodata代码段和数据段。可用readelf -r test.o查看需要relocation的部分。
第四步 链接
命令: gcc -o test test.o
或者 ld -o test test.o
结果: 生成可执行文件test (可用objdump查看)
作用: 将在一个文件中引用的符号同在另外一个文件中该符号的定义链接起来,使得所有的这些目标文件链接成为一个能被操作系统加载到内存的执行体。(如果有不到的符号定义,或者重复定义等,会报链接错)。用file test 可以看到test是一个executable的ELF文件。
当然链接的时候还会用到静态链接库,和动态连接库。静态库和动态库都是.o目标文件的集合。
静态库:
命令:ar -v -q test.a test.o
结果: 生成静态链接库test.a
作用: 静态库是在链接过程中将相关代码提取出来加入可执行文件的库(即在链接的时候将函数的代码将从其所在地静态链接库中被拷贝到最终的可执行程序中),ar只是将一些别的文件集合到一个文件中。可以打包,当然也可以解包。
动态库:
命令: gcc -shared test.so test.o
或者/PATH/collect2 -shared test.so test.o (省略若干参数)
结果: 生成动态连接库test.so
作用: 动态库在链接时只创建一些符号表,而在运行的时候才将有关库的代码装入内存,映射到运行时相应进程的虚地址空间。如果出错,如找不到对应的.so文件,会在执行的时候报动态连接错(可用LD_LIBRARY_PATH指定路径)。用file test.so可以看到test.so是shared object的ELF文件。
当然以上各步可以一步或若干步一起完成,如gcc -o test test.c直接得到可执行文件。
附:
ELF文件格式
ELF文件格式是ABI(Application Binary Interface)的一部分,被Tool Interface Standards committee作为在32位Intel架构下可移植的目标文件格式。其格式比较复杂,这里就不细讲了,只说说其类型。
在specification 1.1中定义了的类型。表示在ELF header中的e_type
Name Value Meaning
==== ===== =======
ET_NONE 0 No file type
ET_REL 1 Relocatable file
ET_EXEC 2 Executable file
ET_DYN 3 Shared object file
ET_CORE 4 Core file
ET_LOPROC 0xff00 Processor-specific
ET_HIPROC 0xffff Processor-specific
主要的有4种
1. Relocatable file 保留了代码和数据,被用来和其他的object file一起创建可执行的文件或者是shared object file. (也就是我们常见的.o文件)
2. Executable file 保留了用来执行的程序,该文件可以被系统exec()加载用以创建程序进程。(也就是我们常说的可执行文件)
3. Shared object file 保留了代码和数据,以在两种情况下被连接,一是link editor如ld,可以用它与其他的Relocateble或者Shared的object file一起创建另一个object file. 二是与Executable file或者其他的Shared object file动态链接成为一个进程映像。(也就是我们常说的动态链接库,或者.so文件)
4. Core file 的内容在规范中没有指明,目前多用来记录core dump信息。
原文地址:http://blog.csdn.net/lychee007/article/details/4123018
在Unix系统上,从源文件、目标文件、可执行文件的编译过程的更多相关文章
- 使用apache daemon让java程序在unix系统上以服务方式运行
通过使用apache_commons_daemon,可以让Java程序在unix系统上以服务器的方式运行. 当然,通过wrapper也是可以达到这样的目的,wrapper还可以指定java应用中用到的 ...
- linux下的 sudo ln -s 源文件 目标文件
这是linux中一个非常重要命令,请大家一定要熟悉.它的功能是为某一个文件或目录在另外一个位置建立一个同步的链接,类似Windows下的超级链接. 这个命令最常用的参数是-s,具体用法是:sudo l ...
- 在Windows系统上一批可以下载但是需要经过编译再安装的第三方的直接编译后的版本(UCI页面)
在Windows系统上一批可以下载但是需要经过编译再安装的第三方的直接编译后的版本(UCI页面) (https://www.lfd.uci.edu/~gohlke/pythonlibs/) win10 ...
- UNIX系统上的抓包工具tcpdump常用命令说明
tcpdump 介绍 tcpdump采用命令行方式对接口的数据包进行筛选抓取,其丰富特性表现在灵活的表达式上. 不带任何选项的tcpdump,默认会抓取第一个网络接口,且只有将tcpdump进程终止才 ...
- UNIX 系统上的文本操作简介
http://www.oschina.net/question/129540_53561 UNIX 的基本哲学之一就是创建只做一件事并将这一件事做好的程序(或进程).这一哲学要求认真考虑接口以及结合这 ...
- Unix系统编程()复制文件描述符
Bourne shell的IO重定向语法2>&1,意在通知shell把标准错误(文件描述符2)重定向到标准输出(文件描述符1).因此下列命令将把标准输出和标准错误写入result.log ...
- Linux和类Unix系统上5个最佳开源备份工具
一个好的备份最基本的目的就是为了能够从一些错误中恢复: 人为的失误 磁盘阵列或是硬盘故障 文件系统崩溃 数据中心被破坏等等. 所以,我为大家罗列了一些开源的软件备份工具. 当为一个企业选择备份工具的时 ...
- Linux 和类 Unix 系统上5个最佳开源备份工具
转载:http://linux.cn/article-4623-weixin.html#rd?sukey=cbbc36a2500a2e6cb7678c4d38b691a9fa7403b259f898e ...
- Unix系统编程()在文件特定偏移量处的IO:pread和pwrite
首先我想问的是这两个p代表的是什么? 系统调用pread和pwrite完成与read和write相类似的工作,只是前两者会在offset参数所指定的位置进行文件IO操作,而非始于文件的当前偏移量处,并 ...
随机推荐
- net Core 2.0应用程序发布到IIS
.net Core 2.0应用程序发布到IIS上注意事项 .net Core2.0应用程序发布window服务器报错容易错过的配置. 1.应用程序发布. 2.IIS上新建网站. 3.应用程序池选择 ...
- AngularJS(一):概述
本文也同步发表在我的公众号“我的天空” 在我们之前学习的前端代码编写过程中,总是通过HTML与CSS来进行页面布局,而使用JS来控制页面逻辑,因此,我们习惯于在JS中来操作页面元素,如以下代码,我们希 ...
- LNK2005错误——重复定义错误
编程中经常能遇到LNK2005错误——重复定义错误,其实LNK2005错误并不是一个很难解决的错误.弄清楚它形成的原因,就可以轻松解决它了. 造成LNK2005错误主要有以下几种情况: 1.重复定义全 ...
- deb软件安装
deb是debian linux的安装格式,跟red hat的rpm非常相似,最基本的安装命令是:dpkg -i file.deb dpkg 是Debian Package的简写,是为Debian 专 ...
- 获取cell中的button在整个屏幕上的位置
编写cell中得button点击事件 - (IBAction)showButtonClick:(id)sender { UIButton *button = (UIButton *)sender; U ...
- u-boot剖析(一)----Makefile分析
由于u-boot比较庞大,所以我们分开来分析,对于一个大型的项目我们想快速的了解其代码架构和内容,最方便的方法就是分析Makefile,所以我们今天以三星的s3c2440来分析Makefile.我们今 ...
- 【extjs6学习笔记】0.1 准备:基础概念(02)
Ext 类 Ext 是一个全局单例的对象,在 Sencha library 中它封装了所有的类和许多实用的方法.许多常用的函数都定义在 Ext 对象里.它还提供了像其他类中一些频繁使用的方法的快速调用 ...
- Eclipse IDE配置PHP开发、调试环境
前言 使用java语言开发的朋友想必对Eclipse开发工具已经不陌生了,那么Eclipse作为java主流的开发工具,是否能够开发PHP项目呢?答案如你所想,肯定是可以的!以下就是该IDE下如何配置 ...
- Wireshark漫谈(一)
可能有人会说,一个软件的安装有什么好谈的,无非就是"同意,同意,同意......是,是,是"诸如此类的选项.的确,Wireshark软件的安装步骤是挺简单的,不过本文不是想谈安装步 ...
- MySQL表的碎片整理和空间回收小结
MySQL表碎片化(Table Fragmentation)的原因 关于MySQL中表碎片化(Table Fragmentation)产生的原因,简单总结一下,MySQL Engine不同,碎片化的原 ...