交叉编译器

交叉编译器【交叉编译器】交叉编译器简介 在一种计算机环境中运行的编译程式,能编译出在另外一种环境下运行的代码
基本介绍中文名:交叉编译器
分类1:预处理器
分类2:编译器前端
分类3:编译器后端
简介高级计算机语言便于人编写,阅读,维护 。低级机器语言是计算机能直接解读、运行的 。编译器将源程式(Source program)作为输入,翻译产生使用目标语言(Target language)的等价程式 。原始码一般为高级语言 (High-level language),如 Pascal、C、C++、C#、Java等,而目标语言则是彙编语言或目标机器的目标代码(Object code),有时也称作机器代码(Machine code) 。原理编译是从原始码(通常为高级语言)到能直接被计算机或虚拟机执行的目标代码(通常为低阶语言或机器语言)的翻译过程 。然而,也存在从低阶语言到高阶语言的编译器,这类编译器中用来从由高阶语言生成的低阶语言代码重新生成高阶语言代码的又被叫做反编译器 。也有从一种高阶语言生成另一种高阶语言的编译器,或者生成一种需要进一步处理的的中间代码的编译器(又叫级联) 。典型的编译器输出是由包含入口点的名字和地址,以及外部调用(到不在这个目标档案中的函式调用)的机器代码所组成的目标档案 。一组目标档案,不必是同一编译器产生,但使用的编译器必需採用同样的输出格式,可以连结在一起并生成可以由用户直接执行的可执行程式 。分类编译器可以生成用来在与编译器本身所在的计算机和作业系统(平台)相同的环境下运行的目标代码,这种编译器又叫做“本地”编译器 。另外,编译器也可以生成用来在其它平台上运行的目标代码,这种编译器又叫做交叉编译器 。交叉编译器在生成新的硬体平台时非常有用 。“源码到源码编译器”是指用一种高阶语言作为输入,输出也是高阶语言的编译器 。例如: 自动并行化编译器经常採用一种高阶语言作为输入,转换其中的代码,并用并行代码注释对它进行注释(如OpenMP)或者用语言构造进行注释(如FORTRAN的DOALL指令) 。预处理器(preprocessor)作用是通过代入预定义等程式段将源程式补充完整 。编译器前端(frontend)前端主要负责解析(parse)输入的原始码,由语法分析器和语意分析器协同工作 。语法分析器负责把原始码中的‘单词’(Token)找出来,语意分析器把这些分散的单词按预先定义好的语法组装成有意义的表达式,语句,函式等等 。例如“a = b + c;”前端语法分析器看到的是“a,=,b,+,c;”,语意分析器按定义的语法,先把他们组装成表达式“b + c”,再组装成“a = b + c”的语句 。前端还负责语义(semantic checking)的检查,例如检测参与运算的变数是否是同一类型的,简单的错误处理 。最终的结果常常是一个抽象的语法树(abstract syntax tree,或 AST),这样后端可以在此基础上进一步最佳化和处理 。编译器后端(backend)编译器后端主要负责分析,最佳化中间代码(Intermediate representation)以及生成机器代码(Code Generation) 。一般说来所有的编译器分析,最佳化,变型都可以分成两大类:函式内(intraprocedural)还是函式之间(interprocedural)进行 。很明显,函式间的分析,最佳化更準确,但需要更长的时间来完成 。代码分析编译器分析(compiler analysis)的对象是前端生成并传递过来的中间代码,现代的最佳化型编译器(optimizing compiler)常常用好几种层次的中间代码来表示程式,高层的中间代码(high levelIR)接近输入的源程式的格式,与输入语言相关(language dependent),包含更多的全局性的信息,和源程式的结构;中层的中间代码(middle level IR)与输入语言无关,低层的中间代码(Low level IR)与机器语言类似 。不同的分析,最佳化发生在最适合的那一层中间代码上 。常见的编译分析有函式调用树(call tree),控制流程图(Control flow graph),以及在此基础上的 变数定义-使用,使用-定义链(define-use/use-define or u-d/d-u chain),变数别名分析(alias analysis),指针分析(pointer analysis),数据依赖分析(data dependenceanalysis)等 。程式分析结果是编译器最佳化(compileroptimization)和程式变形(compiler transformation)的前提条件 。常见的最佳化和变形有:函式内嵌(inlining),无用代码删除(Dead code elimination),标準化循环结构(loop normalization),循环体展开(loop unrolling),循环体合併,分裂(loop fusion,loop fission),数组填充(array padding),等等 。最佳化和变形的目的是减少代码的长度,提高记忆体(memory),快取(cache)的使用率,减少读写磁碟,访问网路数据的频率 。更高级的最佳化甚至可以把序列化的代码(serial code)变成并行运算,多执行绪的代码(parallelized,multi-threaded code) 。机器代码的生成是最佳化变型后的中间代码转换成机器指令的过程 。现代编译器主要採用生成彙编代码(assembly code)的策略,而不直接生成二进制的目标代码(binary object code) 。即使在代码生成阶段,高级编译器仍然要做很多分析,最佳化,变形的工作 。例如如何分配暂存器(register allocatioin),如何选择合适的机器指令(instruction selection),如何合併几句代码成一句等等 。工作方法首先编译器进行语法分析,也就是要把那些字元串分离出来 。然后进行语义分析,就是把各个由语法分析分析出的语法单元的意义搞清楚 。最后生成的是目标档案,也称为obj档案 。再经过连结器的连结就可以生成最后的可执行代码了 。有些时候需要把多个档案产生的目标档案进行连结,产生最后的代码 。这一过程称为交叉连结 。语言对比许多人将高阶程式语言分为两类:编译型语言和 直译型语言。然而,实际上,这些语言中的大多数既可用编译型实现也可用直译型实现,分类实际上反映的是那种语言常见的实现方式 。(但是,某些直译型语言,很难用编译型实现 。比如那些允许 线上代码更改 的直译型语言 。) 发展历史20世纪50年代,IBM的John Backus带领一个研究小组对FORTRAN语言及其编译器进行开发 。但由于当时人们对编译理论了解不多,开发工作变得既複杂又艰苦 。与此同时,Noam Chomsky开始了他对自然语言结构的研究 。他的发现最终使得编译器的结构异常简单,甚至还带有了一些自动化 。Chomsky的研究导致了根据语言文法的难易程度以及识别它们所需要的算法来对语言分类 。正如所称的Chomsky架构(Chomsky Hierarchy),它包括了文法的四个层次:0型文法、1型文法、2型文法和3型文法,且其中的每一个都是其前者的特殊情况 。2型文法(或上下文无关文法)被证明是程式设计语言中最有用的,而且今天它已代表着程式设计语言结构的标準方式 。分析问题(parsing problem,用于上下文无关文法识别的有效算法)的研究是在60年代和70年代,它相当完善的解决了这个问题 。它已是编译原理中的一个标準部分 。有限状态自动机(Finite Automation)和正则表达式(Regular Expression)同上下文无关文法紧密相关,它们与Chomsky的3型文法相对应 。对它们的研究与Chomsky的研究几乎同时开始,并且引出了表示程式设计语言的单词的符号方式 。人们接着又深化了生成有效目标代码的方法,这就是最初的编译器,它们被一直使用至今 。人们通常将其称为最佳化技术(Optimization Technique),但因其从未真正地得到过被最佳化了的目标代码而仅仅改进了它的有效性,因此实际上应称作代码改进技术(Code Improvement Technique) 。当分析问题变得好懂起来时,人们就在开发程式上花费了很大的功夫来研究这一部分的编译器自动构造 。这些程式最初被称为编译器的编译器(Compiler-compiler),但更确切地应称为分析程式生成器(Parser Generator),这是因为它们仅仅能够自动处理编译的一部分 。这些程式中最着名的是Yacc(Yet Another Compiler-compiler),它是由Steve Johnson在1975年为Unix系统编写的 。类似的,有限状态自动机的研究也发展了一种称为扫描程式生成器(Scanner Generator)的工具,Lex(与Yacc同时,由Mike Lesk为Unix系统开发)是这其中的佼佼者 。在20世纪70年代后期和80年代早期,大量的项目都贯注于编译器其它部分的生成自动化,这其中就包括了代码生成 。这些尝试并未取得多少成功,这大概是因为操作太複杂而人们又对其不甚了解 。编译器设计最近的发展包括:首先,编译器包括了更加複杂算法的应用程式它用于推断或简化程式中的信息;这又与更为複杂的程式设计语言的发展结合在一起 。其中典型的有用于函式语言编译的Hindley-Milner类型检查的统一算法 。其次,编译器已越来越成为基于视窗的互动开发环境(Interactive Development Environment,IDE)的一部分,它包括了编辑器、连线程式、调试程式以及项目管理程式 。这样的IDE标準并没有多少,但是对标準的视窗环境进行开发已成为方向 。另一方面,儘管在编译原理领域进行了大量的研究,但是基本的编译器设计原理在近20年中都没有多大的改变,它正迅速地成为计算机科学课程中的中心环节 。在20世纪90年代,作为GNU项目或其它开放原始码项目标一部分,许多免费编译器和编译器开发工具被开发出来 。这些工具可用来编译所有的电脑程式语言 。它们中的一些项目被认为是高质量的,而且对现代编译理论感兴趣的人可以很容易的得到它们的免费原始码 。大约在1999年,SGI公布了他们的一个工业化的并行化最佳化编译器Pro64的原始码,后被全世界多个编译器研究小组用来做研究平台,并命名为Open64 。Open64的设计结构好,分析最佳化全面,是编译器高级研究的理想平台 。交叉编译在一种计算机环境中运行的编译程式,能编译出在另外一种环境下运行的代码,我们就称这种编译器支持交叉编译 。这个编译过程就叫交叉编译 。简单地说,就是在一个平台上生成另一个平台上的可执行代码 。这里需要注意的是所谓平台,实际上包含两个概念:体系结构(Architecture)、作业系统(Operating System) 。同一个体系结构可以运行不同的作业系统;同样,同一个作业系统也可以在不同的体系结构上运行 。举例来说,我们常说的x86 Linux平台实际上是Intel x86体系结构和Linux for x86作业系统的统称;而x86 WinNT平台实际上是Intel x86体系结构和Windows NT for x86作业系统的简称 。有时是因为目的平台上不允许或不能够安装我们所需要的编译器,而我们又需要这个编译器的某些特徵;有时是因为目的平台上的资源贫乏,无法运行我们所需要编译器;有时又是因为目的平台还没有建立,连作业系统都没有,根本谈不上运行什幺编译器 。交叉编译这个概念的出现和流行是和嵌入式系统的广泛发展同步的 。我们常用的计算机软体,都需要通过编译的方式,把使用高级计算机语言编写的代码(比如C代码)编译(compile)成计算机可以识别和执行的二进制代码 。比如,我们在Windows平台上,可使用Visual C++开发环境,编写程式并编译成可执行程式 。这种方式下,我们使用PC平台上的Windows工具开发针对Windows本身的可执行程式,这种编译过程称为native compilation,中文可理解为本机编译 。然而,在进行嵌入式系统的开发时,运行程式的目标平台通常具有有限的存储空间和运算能力,比如常见的 ARM 平台,其一般的静态存储空间大概是16到32MB,而CPU的主频大概在100MHz到500MHz之间 。这种情况下,在ARM平台上进行本机编译就不太可能了,这是因为一般的编译工具链(compilation tool chain)需要很大的存储空间,并需要很强的CPU运算能力 。为了解决这个问题,交叉编译工具就应运而生了 。通过交叉编译工具,我们就可以在CPU能力很强、存储空间足够的主机平台上(比如PC上)编译出针对其他平台的可执行程式 。要进行交叉编译,我们需要在主机平台上安装对应的交叉编译工具链(cross compilation tool chain),然后用这个交叉编译工具链编译我们的原始码,最终生成可在目标平台上运行的代码 。举例交叉编译1、在Windows PC上,利用ADS(ARM开发环境),使用armcc编译器,则可编译出针对ARM CPU的可执行代码 。2、在Linux PC上,利用arm-linux-gcc编译器,可编译出针对Linux ARM平台的可执行代码 。3、在Windows PC上,利用cygwin环境,运行arm-elf-gcc编译器,可编译出针对ARM CPU的可执行代码 。4、在Windows系统上,利用Keil Uvison工具,开发出运行在89C51单片机上的程式 。5、在Windows系统上,利用CodeWarrior IDE工具,开发出运行在Freescale XS128单片机上的程式 。