尽管在Java语言中,存在一个“Java虚拟机规范”,规范了Java中每一条指令所能执行的动作以及堆栈的分布做了规范,但是随着技术的发展,高性能虚拟机真正的细节实现方式已经渐渐与虚拟机规范所描述产生越来越大的差距,虚拟机规范中的描述逐渐成了虚拟机实现的“概念模型”——即实现只能保证规范描述等效。基于上面的原因,我们分析程序的执行语义问题(虚拟机做了什么)时,在字节码层面上分析完全可行,但分析程序的执行行为问题(虚拟机是怎样做的、性能如何)时,在字节码层面上分析就没有什么意义了,需要通过其他方式解决。 分析程序如何执行,通过软件调试工具(GDB、Windbg等)来断点调试是最常见的手段,但是这样的调试方式在JVM中会遇到很大困难,因为大量执行代码是通过JIT编译器动态生成到CodeBuffer中的,没有很简单的手段来处理这种混合模式的调试(不过相信虚拟机开发团队内部肯定是有内部工具的)。因此我们要通过一些曲线手段来解决问题,基于这种背景下,本文的主角——HSDIS插件就正式登场了。
1.准备工作
HSDIS是一份Sun官方推荐的HotSpot VM JIT编译代码的反汇编插件,它包含在HotSpot VM的源码之中,在Project Kenai(http://kenai.com/projects/base-hsdis)也可以下载到单独的源码。它的作用是让HotSpot的-XX:+PrintAssembly指令调用它来把动态生成的本地代码还原为汇编代码输出,同时还生成了大量非常有价值的注释,这样我们就可以通过输出的代码来分析问题。 读者可以根据自己的操作系统和CPU类型从Kenai的网站上下载编译好的插件,直接放到JDK_HOME/jre/bin/client和JDK_HOME/jre/bin/server目录中即可。如果没有找到所需操作系统 (譬如Windows的就没有)的成品,那就得自己拿源码编译一下,或者去HLLVM圈子(http://hllvm.group.iteye.com/)中下载也可以。 如果你使用的是Debug或者FastDebug版的HotSpot,那可以直接通过-XX:+PrintAssembly指令使用的插件; 如果你使用的是Product版的HotSpot,那还要额外加入一个-XX:+UnlockDiagnosticVMOptions参数。 我使用的Java版本是:E:\data\bak>java -version
java version "1.7.0_06"
Java(TM) SE Runtime Environment (build 1.7.0_06-b24)
Java HotSpot(TM) Client VM (build 23.2-b09, mixed mode, sharing)
2.案例分析
第一个案例的问题是“在Java虚拟机规范中把虚拟机内存划分为Java Heap、Java VM Stack、Method Area等多个运行时区域,那当ByteCode编译为Native Code后,Java堆、栈、方法区还是原来那个吗?在Java堆、栈、方法区中的数据是如何访问的?” 我们通过下面这段简单代码的实验来回答这个问题:public class Bar {
int a = 1;
static int b = 2;
public int sum(int c) {
return a + b + c;
}
public static void main(String[] args) {
new Bar().sum(3);
}
}
代码很简单,sum()方法使用到3个变量a、b、c,按照概念模型中的划分,其中a是实例变量,来自Java Heap,b是类变量,来自Method Area,c是参数,来自VM Stack。那我们来看看JIT之后,它们是怎么访问的。使用下面命令来执行上述代码:java -Xcomp -XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly -Xcomp -XX:CompileCommand=dontinline,*Bar.sum -XX:CompileCommand=compileonly,*Bar.sum Bar
其中,参数-Xcomp是让虚拟机以编译模式执行代码,这样代码可以偷懒,不需要执行足够次数来预热都能触发JIT编译。两个 -XX:CompileCommand意思是让编译器不要内联sum()并且只编译sum(),-XX:+PrintAssembly就是输出反汇编内 容。如果一切顺利的话,屏幕上出现类似下面的内容:整个JAVA文件汇编后的完整代码我放gist上了,地址如下:Java代码汇编后的完整代码。
3.解读
代码并不多,一句一句来看:1. mov %eax,-0x4000(%esp):检查栈溢。2. push %ebp:保存上一栈帧基址。3. sub $0x18,%esp:给新帧分配空间。4. mov 0x8(%ecx),%eax:取实例变量a,这里0x8(%ecx)就是ecx+0x8的意思,前面“[Constants]”节中提示了 “this:ecx = 'test/Bar'”,即ecx寄存器中放的就是this对象的地址。偏移0x8是越过this对象的对象头,之后就是实例变量a的内存位置。这次是访
问“Java堆”中的数据。5. mov $0x239ae978,%esi:取test.Bar在方法区的指针。6. mov 0x70(%esi),%esi:取类变量b,这次是访问“方法区”中的数据。7. add %esi,%eax 、add %edx,%eax:做2次加法,求a+b+c的值,前面的代码把a放在eax中,把b放在esi中,而c在[Constants]中提示了,“parm0:edx = int”,说明c在edx中。8. add $0x18,%esp:撤销栈帧。9. pop %ebp:恢复上一栈帧。10. test %eax,0x120100:轮询方法返回处的SafePoint11. ret:方法返回。 从汇编代码中可见,访问Java堆、栈和方法区中的数据,都是直接访问某个内存地址或者寄存器,之间并没有看见有什么隔阂。HotSpot虚拟机本身 是一个运行在物理机器上的程序,Java堆、栈、方法区都在Java虚拟机进程的内存中分配。在JIT编译之后,Native Code面向的是HotSpot这个进程的内存,说变量a还在Java
Heap中,应当理解为a的位置还在原来的那个内存位置上,但是Native Code是不理会Java Heap之类的概念的,因为那并不是同一个层次的概念。附:此文只是最简单的讲解,文章内容来自Iteye上LLVM圈子的讨论,如果需要更多的信息,可以参考此文:使用IdealGraphVisualizer观察HotSpot
Server Compiler编译过程的一个例子,注意,如果想比较方便的看到汇编代码,最好使用fastDebug版本的JDK,而不是正式版。如图所示:idealgraphvisualizer显示了JAVA从语法解析到最终生成本地代码的完整过程另有以下地址可参考:https://wikis.oracle.com/display/HotSpotInternals/PrintAssemblyhttp://ssw.jku.at/General/Staff/TW/igv.html
分享到:
相关推荐
总之,"Java代码直接转化成smali代码工具"是一个实用的开发资源,它提供了从高级语言到汇编语言的桥梁,帮助开发者深入探索和操纵Android应用的底层机制。在逆向工程、调试和优化等方面都有其独特的价值。通过理解和...
Eclipse 和 IntelliJ IDEA 作为两大主流的 Java 开发工具,提供了查看代码运行时汇编指令的功能,帮助开发者洞察程序的底层运行机制。本教程将围绕如何在 Eclipse 和 IDEA 中查看汇编指令展开,以增强对程序执行细节...
Java 反编译器、汇编器和反汇编器Krakatau 为 Java 字节码提供了汇编器和反汇编器,它允许您将二进制类文件转换为人类可读的文本格式,进行更改,并将其转换回类文件,即使是混淆的代码也是如此。您还可以通过手动...
为了深入了解和实现这个项目,可以查看该文件以获取具体细节和代码实现。通过这个模拟器,你可以亲身体验到CPU如何执行指令、管理内存以及与外部设备交互的过程,这对于深入理解计算机科学至关重要。
2. **代码完整性和准确性**:"smali2java"生成的Java代码可能并不完全等同于原始的Java源代码,因为它无法恢复注释、部分变量名和方法名,以及某些特定的优化细节。 3. **调试和分析**:虽然转换后的Java代码便于...
了解汇编语言可以提升对编译器优化的理解,例如函数内联、循环展开等技术,以及为什么某些C++或Java代码在运行时可能会慢。 书中的源码部分可能包括示例程序,演示了如何使用C++和Java实现特定的汇编操作,比如直接...
- **JAD**:Java反汇编器,可以将字节码反编译为类似源代码的文本。 4. **反编译的用途**: - **学习和研究**:查看他人库的内部工作原理,尤其是没有提供源代码的库。 - **调试**:当只有.jar文件而没有源代码...
Java源代码反编译工具是开发者在特定场景下非常有用的工具,主要用于查看和理解已编译的Java字节码(.class文件)是如何由原始的Java源代码编译而来的。这种工具对于软件逆向工程、学习开源项目、调试、修复错误或者...
本压缩包"idea查看代码运行的汇编指令工具.zip"显然是为了帮助Idea用户了解和分析程序在运行时的底层细节,特别是汇编指令层面的信息。了解汇编语言能够帮助开发者深入理解计算机的工作原理,优化性能关键部分的代码...
在Mac环境下,使用JITWatch来查看JDK 1.8的汇编代码是一个非常有用的技巧,这有助于我们深入理解Java程序的运行机制,特别是JIT(Just-In-Time)...结合汇编代码的分析,可以帮助我们编写更高效、更优化的Java代码。
在描述中提到的博客链接,作者可能详细介绍了该反汇编器的实现细节,包括如何解析MIPS的32位指令格式,如何处理各种寻址方式,以及如何构建汇编代码的语法结构。可能还涵盖了如何处理分支、跳转和其他控制流程指令,...
在`jisuanqi.asm`文件中,我们可以看到具体的汇编代码实现,包括定义数据段和代码段,设置栈指针,初始化变量,以及处理输入和输出的细节。`jisuanqi.exe`则是经过汇编器和链接器处理后的可执行文件,可以直接在支持...
Java代码编译和反编译的那些事儿 Java代码编译和反编译是Java开发者必须掌握的基本概念。编译是将高级语言转换成低级语言的过程,而反编译则是将低级语言还原到高级语言。下面是关于Java代码编译和反编译的详细知识...
通过对上述C语言源代码的反汇编分析,我们不仅了解了函数调用的一般流程,还学习了如何在汇编级别上追踪函数调用的具体细节。这种能力对于理解和分析二进制程序至关重要,尤其是在安全领域中进行漏洞挖掘或恶意软件...
- **反汇编器**:将机器可读的目标代码转换为人类可读的汇编代码,如Java开发工具包(JDK)自带的`javap`工具就是一个例子。 - **反编译器**:将目标代码转换为接近原始源代码的形式。现代反编译器不仅功能强大,还...
尽管高级编程语言如C++、Java等在现代软件开发中占据主导地位,但汇编编译器依然有着不可替代的作用。在系统底层开发、嵌入式系统、实时操作系统、性能敏感的代码优化等领域,汇编语言的直接性和高效性被广泛利用。...
在高级语言如C、Python或Java中,许多底层细节被抽象掉了,而在汇编语言中,这些细节都是显而易见的。因此,理解和分析这样的程序需要对计算机系统有深入的理解。 综上所述,这个“非常牛的汇编程序”揭示了汇编...
- **JAD**:Java反汇编器,提供命令行界面,可以将.class文件反编译为伪Java源代码。 - **Procyon**:除了反编译,还支持编译和除错,提供了一整套开发工具集。 - **FernFlower**:一款开源的Java反编译器,反...
2. **JAD**:JAD是Java反汇编器的简称,它能够将字节码反编译为类似于Java的源代码。JAD也有命令行版本,适合于自动化脚本。 3. **FernFlower**:这是一个开源的Java反编译器,能产生高质量的源代码近似表示。同样...
相比于高级语言如C、Java等,汇编语言更贴近于底层硬件操作,能够实现对硬件资源的精细控制,但在编写和维护上更为复杂。高级语言则通过编译器自动处理了许多底层细节,使得程序员可以更加专注于算法设计和逻辑实现...