我主要对流行的和广泛使用的编译器感兴趣,例如gcc。但如果不同的编译器对事情做了不同的事情,我也想知道。
以gcc为例,它是否将用C编写的短程序直接编译为 machine 代码,或者首先将其转换为人类可读的程序集,然后才使用(在 - 内置?)汇编程序将汇编程序转换为二进制,机器代码 - 一系列指令给CPU?
使用汇编代码创建二进制可执行文件是一项非常昂贵的操作吗?或者这是一个相对简单快速的事情吗?
(假设我们只处理x86系列处理器,所有程序都是为Linux编写的。)
我非常感谢有关此事的任何帮助和想法。谢谢!
答案 0 :(得分:45)
gcc实际上生成汇编程序并使用 as 汇编程序汇编它。并非所有编译器都这样做 - MS编译器直接生成目标代码,但您可以使它们生成汇编程序输出。将汇编程序转换为目标代码是一个非常简单的过程,至少与编译相比。
有些编译器生成其他高级语言代码作为输出 - 例如, cfront ,第一个C ++编译器生成C作为其输出,然后由C编译器编译。
请注意,直接编译或汇编实际上都不会生成可执行文件。这是由链接器完成的,它接受编译/汇编产生的各种目标代码文件,解析它们包含的所有名称并生成最终的可执行二进制文件。
答案 1 :(得分:14)
几乎所有编译器(包括gcc)都会生成汇编代码,因为它更容易生成和调试编译器。主要的例外情况通常是即时编译器或交互式编译器,其作者不希望性能开销或分支整个过程运行汇编程序的麻烦。一些有趣的例子包括
Standard ML of New Jersey,以交互方式运行并动态编译每个表达式。
tinycc compiler,其设计足够快,可以在100毫秒内完成编译,加载和运行C脚本,因此不需要调用汇编器和链接器的开销
这些案例的共同点是对“瞬时”反应的渴望。汇编程序和链接器速度很快,但不足以进行交互式响应。爱好。
还有一大类语言,例如Smalltalk,Java和Lua,它们编译为字节码,而不是汇编代码,但其实现可能稍后将该字节码直接转换为机器代码,而无需汇编
(脚注:在20世纪90年代早期,Mary Fernandez和我写了New Jersey Machine Code Toolkit code在线,它生成了C库,编译器编写者可以使用这些库绕过标准汇编器和链接器Mary在生成a.out
时使用它来大致加倍优化链接器的速度。如果你不写入磁盘,加速甚至更大......)
答案 2 :(得分:6)
编译器通常将源代码解析为抽象语法树(AST),然后解析为某种中间语言。只有这样,通常在一些优化之后,它们才会发出目标语言。
关于gcc,它可以编译为各种各样的目标。我不知道对于x86它是否首先编译为汇编,但我确实给了你一些关于编译器的见解 - 你也要求它。
答案 3 :(得分:6)
根据chapter 2的Introduction to Reverse Engineering Software(由Mike Perry和Nasko Oskov提供),gcc和cl.exe(MSVC ++的后端编译器)都具有 -S 您可以使用switch来输出每个编译器生成的程序集。
您还可以在详细模式(gcc -v
)中运行gcc以获取它执行的命令列表,以查看它在幕后执行的操作。
答案 4 :(得分:5)
GCC编译成汇编程序。其他一些编译器则没有。例如,LLVM-GCC编译为LLVM-assembly或LLVM-bytecode,然后将其编译为机器代码。几乎所有的编译器都有某种内部表示,LLVM-GCC使用LLVM,而IIRC,GCC使用的是GIMPLE。
答案 5 :(得分:2)
没有一个答案澄清了这样一个事实:ASSEMBLER是BINARY CODE和MACHINE DEPENDENT SYMBOLIC CODE之间的第一层抽象。编译器是MACHINE DEPENDENT SYMBOLIC CODE和MACHINE INDEPENDENT SYMBOLIC CODE之间的第二层抽象。
如果编译器直接将代码转换为二进制代码,根据定义,它将被称为汇编程序而不是编译器。
更合适的是说编译器使用INTERMEDIATE CODE,它可能是也可能不是汇编语言,例如Java使用字节代码作为中间代码,字节代码是java虚拟机(JVM)的汇编程序。
编辑:您可能想知道为什么汇编程序总是生成与机器相关的代码以及编译器为什么能够生成与机器无关的代码。答案很简单。汇编程序是机器代码的直接映射,因此它生成的汇编语言始终取决于机器。相反,我们可以为不同的机器编写多个版本的编译器。因此,要独立于机器运行我们的代码,我们必须编译相同的代码,但必须编译为该机器编写的编译器版本。答案 6 :(得分:1)
Visual C ++有switch输出汇编代码,所以我认为它会在输出机器代码之前生成汇编代码。
答案 7 :(得分:1)
您可能有兴趣收听此播客:Internals of GCC
答案 8 :(得分:1)
在代码生成步骤中生成大多数multi-pass compilers汇编语言。这允许您编写一次词法分析器,语法和语义阶段,然后使用单个汇编程序后端生成可执行代码。这在交叉编译器中经常使用,例如为一系列不同的cpu生成的C编译器。
几乎每个编译器都有某种形式,这是一个隐含或明确的步骤。
答案 9 :(得分:1)
编译有很多阶段。在摘要中,有一个前端读取源代码,将其分解为令牌,最后分解为解析树。
后端负责首先生成一个顺序代码,如三个地址代码,例如:
代码:
x = y + z + w
成:
reg1 = y + z
x = reg1 + w
然后优化它,将其转换为装配,最后转换为机器语言。所有步骤都经过仔细分层,以便在需要时可以替换其中一个
答案 10 :(得分:0)
Java编译器编译为java字节代码(二进制格式),然后使用虚拟机(jvm)运行它。
虽然这可能看起来很慢 - 但它可以更快,因为JVM可以利用以后的CPU指令和新的优化。 C ++编译器不会这样做 - 你必须在编译时定位指令集。
答案 11 :(得分:0)
虽然所有编译器都没有将源代码转换为中间级代码,但是有一个桥梁将源代码转换为多个编译器中的机器级代码
答案 12 :(得分:0)
上面的某些答案使我感到困惑,因为在某些答案中提到GCC(GNU编译器集合)是一个工具,但它是一组工具,如GNU汇编器(也称为GAS),链接器,编译器和调试器,它们被使用。一起产生可执行文件。是的,GCC不会直接将C源文件转换为机器代码。
它分4个步骤完成:
答案 13 :(得分:0)
列表文件是编译器生成的包含汇编语言的文本文件 编译器生成的代码。大多数编译器支持在编译过程中生成列表文件。对于某些编译器,例如 GCC,这是编译过程的标准部分,因为编译器不会直接生成对象 文件,而是生成一个汇编语言文件,然后对其进行处理 由汇编程序。在这样的编译器中,请求一个列表文件仅仅意味着 汇编程序完成后,编译器不得将其删除。其他 编译器(例如 Microsoft 或 Intel 编译器),列表文件是可选的 必须通过命令行启用的功能。