基于Java的汇编与反汇编框架外文翻译资料

 2022-03-25 08:03

英语原文共 22 页,剩余内容已隐藏,支付完成后下载完整资料


基于Java的汇编与反汇编框架

摘要

Java编程语言主要用于跨平台编程。但它还为平台特定的功能提供了许多生产力,可维护性和性能优势,例如机器代码生成。

我们为SPARC,AMD64, IA32和PowerPC创建了可靠的汇编器,它们支持用户模式和特权指令和64位模式。这些汇编器是由一个使用Java语言编写的可扩展汇编器框架生成的Java源代码。汇编程序生成器还可以生成精确指定每个操作数的合法值的javadoc注释。

我们的设计基于自编的Klein汇编器系统。汇编器是根据规范生成的,就像表驱动的反汇编器和单元测试一样。驱动生成器以Java语言对象的形式提供,因此不需要额外的解析器,开发人员不需要学习任何新的语法来扩展额外的ISA框架。

通过比较两者的输出,每个生成的汇编器都针对已存在的汇编器进行测试。每条指令的测试用例都来自其潜在操作数值的叉积。大多数测试结果是正面的(即结果是合法指令编码)。该框架还会生成负面测试,这些测试预计会被汇编器检测到错误。与Klein汇编器系统一样,我们在外部汇编器以及ISA参考手册中发现了一些错误。

我们的框架产生了数以千万计的测试。对于符号操作数,我们的测试包括了所有适用的预定义常量。对于积分操作数,测试了重要的边界值,例如相应的最小值,最大值,0,1和-1. 完整的测试可能需要几个小时才能完成,但是我们对正确性有很高的信心。

关键词:交叉汇编;汇编生成器;反汇编;自动测试;特定领域框架;系统编程

1.介绍与动机

尽管Java编程语言是为了跨平台而设计的,但是它的很多内容显然更普遍适用,因此也转而适用于平台特定的任务。例如,用Java语言编写的流行的集成开发环境(IDE)已经被扩展以支持C/C 等语言的开发,C/C 可以被静态编译成特定平台的机器代码。除了传统的程序重用外,我们没有理由不让编译器在这样的环境下享受Java语言开发软件所带来的优势(与C/C 相比)。此外,一些Java虚拟机已经使用Java语言进行编写(e.g.,[3,22,15]),包括从字节码到机器码的编译器。

通过本文的中的贡献,我们打算鼓励并支持Java中未来的编译器架构研究和开发。我们的软件为程序员提供了所有平台特有的最重要的任务,即遵守现有通用指令集(ISA)规范的机器指令的正确生成。

我们专注于这个底层问题,与其他高层次的任务完全分离,如指令选择、指令调度、寻址模式选择,寄存器分配或其他种类的优化。这种关注的分离使我们能够直接将我们的规范与现有文档(参考手册)相匹配,并利用已存在的文本汇编器进行系统的全面测试。因此我们的系统几乎消除一整类非常难以发现的错误,让用户有信心去建立未来的编译器层的基础。

考虑到构建汇编器不同的方法,我们分为如下几类:

独立汇编程序:这种汇编器采用文本输入并生成二进制输出文件。与其他两种变体相比,它们相对较慢且启动时间较长。因此独立汇编程序主要用于静态代码生成。另一方面,它们通常提供超过单纯指令编码的最丰富的功能集:对象文件格式输出,分段与链接指令,数据与代码对齐,宏等等。

内联汇编程序:用于高级编程语言(HLLs)的某些编译器,例如C/C 可以直接在HLL源代码中嵌入汇编语言源代码。通常它们也有语法规定来在汇编源代码中定位和操作HLL实体。

汇编程序库:这些是已发行二进制汇编指令的HLL例程集合(e.g.,[10,14]).他们的特征可能并不总是直接与文本汇编语言一致。例如:作为HotSpot Java虚拟机的一部分的x86汇编程序库中的许多方法[19]是用C 编写的,它将广义位置描述符作为参数,而不是明确分解的操作数。更进一步,单纯的汇编器和以下类别之间的差别变得相当分散。

代码生成库:这些代码生成器整合了更高级别的任务,通常是指令选择和调度,管理ABI的合法性等,甚至是一些代码优化技术。

我们观察到对于现代Java程序员只有上述第一种类型的汇编器是真正可用的:也就是独立汇编器,它通过System.exec()方法调用。这种方法对于一些有限的静态代码生成目标可能是足够的,但是他缺乏语言集成和由于具有单独的程序而导致的管理开销。关于动态代码生成,外部汇编进程的启动成本是过高的。

我们没有在任何Java编译器中找到内联汇编器,据我们所知,Java程序包中只有极少数汇编程序库。

根据我们上面提出的关注点分离,构建一个代码生成器框架不是替代我们现有工作,而是现有工作的延伸。

在本文中,我们提供了一个新的汇编程序库(以Java包的形式),涵盖了最流行的指令集(适用于任何大于手持设备的系统):SPARC,PowerPC,AMD64 和 IA32。此外,我们的库包含相应的反汇编器,全面的汇编器测试程序,以及一个可扩展的框架,其中包含依赖于ISA细节的所有模块的规范驱动的生成器。以上所有内容共同构成了Maxwell 汇编器系统项目(PMAS).

PMAS的设计在很大程度上源于Klein汇编系统(KAS),该系统是我们之前作为Klein虚拟机的一部分开发的[20]。KAS使用Self编写,支持两种RISC指令集架构:SPARC和PowerPC,是一种基于原型,面向对象的动态类型语言。

图1.PMAS软件包概述

提供具有编程接口的汇编器与基于文本输入的汇编器相比效率显著提升,因为解析成本是在Java源代码编译期间产生的,而不是在汇编器执行期间产生的。此外,我们将讨论如何合适的使用Java类型系统才能将输入验证的一些成本转移到Java源编译器。

2.概述

图1概述了PMAS封装结构。其中.gen包及其子包中包含了ISA的说明和其他生成器和调试程序。.dis子树通过重用.gen实现反汇编。其他五个.asm的直接子包包含了汇编器,而它们并没有对上述两个子树进行引用。以x86结尾的每个包都提供了.amd64和.ia32的共享包。

通过减少框架的手段来支持任意ISA的子集是最简单直接的,仅仅需要通过收集那些没有任何相关名称,但仅与被排除的ISA相关的包。

在下一个章节中将介绍如何在Java程序中使用生成的汇编程序。如章节4中所述,对于每一个汇编程序都有一个互补的反汇编程序。我们将在章节5中描述创建这个汇编程序和反汇编程序的框架。首先介绍我们的ISA表示的结构(章节5.1),然后我们绘制由其生成的指令模板(章节5.2)。我们认为后者是我们框架的核心,因为其在三个主要功能单元之间共享,这三个主要功能单元提供了后续三个部分的主题:生成的汇编源代码的结构(章节5.3),然后是主要的反汇编算法(章节5.4)和全自动的汇编和反汇编测试(章节5.5)。章节6描述了如何为系统添加另一个ISA。我们在章节7中将简要讨论相关的工作,而本文也总结了值得注意的观测指标和后期的工作(章节8)。

3.如何使用汇编器

每个汇编程序都由顶层包com.sun.max.asm和匹配其ISA的子包组成,如上图1所示。另外,com.sun.max.asm.x86包在AMD64和IA32编辑器之间是共享的。因此,要使用AMD64汇编器,需要以下软件包:comsun.max.asm, com.sun.max.asm.amd64 和com.sun.max.asm.x86。没有汇编程序需要任何.gen和.dis下的包

要使用汇编程序,首先要实例化一个显示在其中的叶类图,如下图2所示。顶层类Assembler为所有汇编程序提供了通用方法,例如标签绑定和输出到流或字节数组。中间生成的类包含ISA特定的汇编程序。为了便于使用,这些方法有针对性地在现有的组装参考手册中定位,其方法名称将模仿个别符号和整型操作数直接对应的助记符和参数。

图2. Assembler类的层级结构

图3. 拆开的AMD64指令

下面是AMD64的一个例子,它在一个Java字节数组里创建了一小部分机器代码指令(如上图3所示):

汇编器输出也可以直接指向流(例如写入文件或存储器),而不使用字节数组:

上面的例子说明了两种不同的标签用法。标签loop绑定到bindLabel( )调用之后的指令。相反,标签subroutine绑定到绝对地址。在这两种情况下,汇编程序都会创建与PC相关的代码,但通过计算相应的偏移量参数,一个明确的费标签参数可以通过使用int(或有时候较长)的值而不是标签来表示,如下所示:

这里使用的call( )的变体在我们的汇编器的原始汇编器(AMD64RawAssembler)超类中定义,它需要一个”raw”int参数:

相比之下,第一个实例中使用的call( )方法是在标签汇编器(AMD64LabelAssembler)中定义的,它位于我们的汇编器类和原始汇编器类之间:

该方法建立在原始的call( )方法上,如其正文中所示。

与许多其他方法一样,这些方法在语法上通过参数重载进行区分。这个Java语言功能还可以区分寄存器是直接使用还是间接使用,还是作基址或者索引。例如,上面的表达式RSP.indirect( )会产生与普通RSP不同的Java类型,从而阐明给定的mov指令必须使用哪种寻址模式。同样,RCX.base( )指定了一个基址类型的寄存器等等。

如果有一个参数的有效值范围相对有限,则将匹配的枚举类而非原始Java类型定义为参数类型。例如上述SIB寻址表达式中关于SCALE 8的情况。它的类型声明如下:

每个RISC汇编器根据相应的参考手册提供综合指令。例如,可以编写这些语句开创建一些合成的SPARC指令【21】:

我们来看看其中一种方法的生成源代码:

正如我们在这里所看到的的,我们的汇编生成器创建了一个javadoc注释,它公开了每条指令的文本外部汇编语法,并指出了参考手册中的确切位置(“G.3节”)【21】找到详细的说明。

另外,假设生成器具有生成类的完整模型,我们使用@see标记在javadoc中创建链接到相关元素。例如,在上面的示例中,会生成一个链接以显示合成inc指令所来源的原始添加指令。

像RISC(但不是x86)汇编程序方法,通常情况下,我们采取动态检查,如果是超范围的参数被传递,将导致运行时异常。在其他的所有情况下,我们的汇编程序都是静态类型安全的。

4.如何使用反汇编器

由于反汇编器的性能不如汇编器那么重要,因此我们的反汇编器不会生成Java源代码。相反它们是手动编写的程序,在每次程序重启时都依赖于PMAS框架来生成指令模版表(参阅5.2)。因此使用反汇编器需要加载.gen下的包。

4.1. 文本反汇编

此Java语句序列从输入流中反汇编AMD64指令,给出与PC相关的译码的起始地址,并将文本输出至控制台。

应用于AMD64代码实力中的说明3,产生输出图3.

我们针对AMD64(和IA32)的反汇编语法融合了所谓的Intel和ATamp;T语法[7],并做了一些修改。地址的呈现方式模仿C/Java语法,我们发现这将会更加直观。简单的索引由rsquo;[rsquo;和rsquo;]rsquo;来表示,类似于Java语言中的数组访问:

寄存器等以Intel语法命名,小写时不带ATamp;T语法的rsquo;%rsquo;前缀。间接访问看起来像没有基址的索引(或带有隐式基址0):

位移是从索引/间接操作数中加/减的:

比例显示为索引的乘积:

比例值为2,4或8.比例1是隐含的。

尽管位移值和偏移值被呈现为带符号的十进制整数,我们更喜欢在直接内存引用(指针)和立即操作数中使用无符号的十六进制整数。

与位移不同,偏移在符号和数字之间没有空格,例如:

这样的话也方便我们将RIP(指令指针相对位置)寻址表达为偏移操作数和间接寻址的组合,例如:

操作数遵循Intel语法,将目的操作数放在左侧,源操作数放在右侧。一些助记符可能有大小后缀,如ATamp;T语法中所示:

因此,与Intel语法一样,不需要指针的操作数大小指示符(例如DWORD PTR).

反汇编器显示反汇编地址范围内所有目标地址的合成标签,该地址范围是指令的起始地址。与此标签重合的操作数将显示相应的标签,如标签L1(图3)。打印引用标签的指令会同时给出标签及其基础原始值,例如:

SPARC和PowerPC的反汇编器创建的文本输出实际上除了提供标签合成外,与参考手册中的语法相同。无论如何,为了调整反汇编器的输出以满足个人喜好需要修改的源代码非常少。

4.2. 程序反汇编

反汇编器也可用来分析和操作反汇编指令,而不依赖于创建文本输出。

单独反汇编的指令由DisassembledInstruction类及其子类来模拟,这些类专

全文共6034字,剩余内容已隐藏,支付完成后下载完整资料


资料编号:[15352],资料为PDF文档或Word文档,PDF文档可免费转换为Word

原文和译文剩余内容已隐藏,您需要先支付 30元 才能查看原文和译文全部内容!立即支付

以上是毕业论文外文翻译,课题毕业论文、任务书、文献综述、开题报告、程序设计、图纸设计等资料可联系客服协助查找。