如何为虚拟机创建二进制文件?

时间:2013-09-11 22:33:56

标签: java c++ jvm vm-implementation

最近我对语言开发非常感兴趣,我有多个工作前端,并且有各种系统来执行代码。我已经决定尝试开发虚拟机类型系统。 (有点像JVM,但当然要简单得多)所以我设法创建了一个带有堆栈和寄存器的基本工作指令集,但我只是对如何实现某些事情感到好奇。

在Java中,例如在编写程序之后,使用java编译器编译它,它会为JVM创建一个二进制(.class)来执行。我不明白这是怎么做的,JVM如何解释这个二进制文件,从人类可读指令到这个二进制文件的转换是什么,我怎样才能创建类似的东西?

感谢您的任何帮助/建议!

3 个答案:

答案 0 :(得分:2)

好吧,我会咬这个通用的问题。

实现编译器/汇编程序/ vm组合是一项很高的要求,特别是如果你自己这样做的话。 话虽这么说:如果你保持你的语言规范足够简单,它是非常可行的;也是你自己。

基本上,要创建二进制文件,请完成以下操作(这是 tad 位简化*:

1)输入源被读取,列举和标记化

2)分析程序逻辑的语义正确性。

E.g。而以下C ++将解析& tokenize,它会失败语义分析

float int* double = const (_identifier >><<) operator& * 

3)构建一个抽象语法树来表示语句

4)构建符号表并解析标识符

5)可选:优化代码

6)以您选择的输出格式生成代码;例如二进制操作码/操作数,字符串表。无论哪种格式最适合您的需求。或者,您可以为现有VM或本机CPU创建字节码。

修改 如果要设计自己的字节码格式,可以编写,例如:

1) File Header
DWORD filesize
DWORD checksum
BYTE  endianness;
DWORD entrypoint <-- Entry point for first instruction in main() or whatever
2) String table
DWORD numstrings
<strings>
DWORD stringlen
<string bytes/words>

3) Instructions
DWORD numinstructions
<instructions>
DWORD opcode
DWORD numops <--- or deduce from opcode
DWORD op1_type <--- stack index, integer literal, index to string table, etc
DWORD operand1
DWORD op1_type
DWORD operand2
...

<强> END

总的来说,这些步骤是可以管理的,但是,与往常一样,魔鬼在细节中。

一些很好的参考资料是:

The Dragon Book - 这在理论上很重要,所以这是一个干读,但值得的

Game Scripting Mastery - 指导您在更实际的事情中开发所有三个组件。但是,示例代码充斥着安全问题,内存泄漏和整体糟糕的编码风格(imho)。但是,你可以从本书中汲取很多概念,值得一读。

The Art of Compiler Design - 我个人没有看过这个,但听到了积极的一面。

如果您决定走这条路,请确保您知道自己正在做什么。这不是一些胆小的人,也不是一个刚接触编程的人。它需要很多的概念思维和事先计划。然而,这是非常有益和有趣的

答案 1 :(得分:1)

@APott -

1)虚拟机不会创建二进制文件。 Java编译器创建二进制.class文件;正在运行的JVM加载并执行类文件。

2)Java JVM没有什么特别“新”或独特的东西。从概念上讲,它与UCSD Pascal或IBM MV / 370没有什么不同。这是VM的短暂历史:

3)如果您感兴趣,完整的JVM规范是在线的,并且有许多书籍/链接可以详细讨论它:

答案 2 :(得分:0)

编译器所做的就是将字符串转换为字符串,无论目标是真实机器还是虚拟机。由于您正在构建自己的目标VM,因此您可能使用与现有虚拟或物理机器指令集不同的编码方式,但这并没有真正改变。所有物理机器指令集都可以用软件模拟,所有虚拟机指令集都可以在硬件中运行(虽然这在实践中可能稍微困难一些,因为为虚拟机设计的指令集可能比硬件预算允许的复杂得多)。毕竟,CPU只是指令集的解释器。

任何编译器书籍都应该对此进行扩展,但物理或虚拟机的编译过程是相同的。通常,您需要先将解析源语言开始到源代码抽象语法树(AST)中,然后需要转换将此源AST转换为目标AST (虽然目标语言通常比源语言更平坦,所以你可能实际上不需要树但是数组通常就足够了),那么你需要代码生成来将目标AST转换为字节码(这通常只是从目标AST节点到字节码的一对一转换。对于具有复杂语法的语言,您可能需要具有中间解析阶段以形成具体的语法树a.k.a.解析树,然后才能形成源AST;并且一些编译器可以使用多个翻译阶段,并且可以在其间包括优化翻译器;那些是微小的差异。