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

How to create binaries for virtual machines?

本文关键字:创建 二进制文件 虚拟机      更新时间:2023-10-16

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

例如,在

Java中,在编写程序后,使用java编译器对其进行编译,并创建一个二进制文件(.class)供JVM执行。我不明白这是如何完成的,JVM如何解释这个二进制文件,从人类可读指令到这个二进制文件的过渡是什么,我怎么能创建类似的东西?

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

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

实现编译器/汇编器/vm 组合是一项艰巨的任务,尤其是当您自己完成时。话虽如此:如果你保持你的语言规范足够简单,它是完全可行的;也是你自己。

基本上,要创建二进制文件,请执行以下操作(这有点简化*):

1) 输入源被读取、词法化和标记化

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

例如,虽然以下C++可以解析和标记化,但它将无法进行语义分析

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
...

结束

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

一些很好的参考是:

龙之书 - 这是一本理论重书,所以读起来很枯燥,但值得一读

游戏脚本精通 - 指导您,同时以更实际的方式开发所有三个组件。但是,示例代码充斥着安全问题、内存泄漏和整体糟糕的编码风格(恕我直言)。但是,你可以从这本书中拿走很多概念,值得一读。

编译器设计的艺术 - 我没有亲自读过这篇文章,但听说过关于它的积极方面。

如果你决定走这条路,确保你知道自己正在陷入什么。这不是一些胆小的人,也不是编程新手的事情。它需要大量的概念思维和事先规划。然而,这是非常有益和有趣的

@APott -

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

2)Java JVM没有什么特别"新"或独特的东西。 从概念上讲,它与UCSD Pascal或IBM MV/370没有什么不同。 以下是 VM 的良好简短历史记录:

  • http://cap-lore.com/Software/CP.html

3)如果你有兴趣,完整的JVM规范是在线的,有很多书籍/链接详细讨论它:

  • http://docs.oracle.com/javase/specs/jvms/se7/html/

  • http://www.artima.com/insidejvm/ed2/jvmP.html

  • http://en.wikipedia.org/wiki/Java_virtual_machine

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

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