一、背景
我们知道,通过javap命令可以查看字节码信息,我们通过字节码可以知道对于一个程序,虚拟机做了些什么事情。但是各大虚拟机厂商对其的实现细节可能会大不相同,字节码只能从语义上解释程序的执行。如果我们要分析程序在宿主环境中到底是如何运行的,字节码则不能给我们提供足够详细的信息了,这个时候我们就需要站在汇编的角度考虑问题。
二、HSDIS
HSDIS是sun推荐的HotSpot虚拟机JIT编译代码的反汇编插件,它包含在HotSpot虚拟机的源码中,但没有提供编译后的程序。HotSpot的-XX:PrintAssemBly指令可以调用它来把本地代码还原为汇编代码输出,同时还生成了一些注释。由于编译比较麻烦,这里就直接贴出可用于windows64位的下载地址:
链接: https://pan.baidu.com/s/1POT2B96z2FFu3qpfbb7pBw
提取码: 2xe9
下载该文件后放到JDK_HOME/jre/bin/server或JDK_HOME/jre/bin/client下即可。
三、实践
首先我们定义一个类(代码本身没有什么实际的意义):
public class Test {
public static void print(int n) {
for (int i = 0; i < n; i++) {
System.out.println(i);
}
}
public static void main(String[] args) {
Test.print(10);
}
}
我们首先需要使用javac编译源码文件:
javac Test.java
然后使用HSDIS插件进行反汇编:
java -XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly -XX:+LogCompilation -XX:LogFile=b.log -Xcomp -XX:CompileCommand=compileonly,Test.print Test
其中的参数含义如下:
-XX:+UnlockDiagnosticVMOptions:Product版的HotSpot则需要加上此参数,如果是Debug或FastDebug版的HotSpot,则可以忽略它
-XX:+PrintAssembly:表示输出反汇编内容
-XX:+LogCompilation:表示生成日志文件
-XX:LogFile:日志文件路径(生成日志文件之后,可以用其它工具做详细分析,比如JITWatch)
-Xcomp:让虚拟机以编译模式执行代码,使用它我们可以不必做预热操作就能触发JIT编译(如果该参数在实际使用的虚拟机中已经被移除,那么还是需要做预热才能触发JIT编译,简单做个循环预热就可以了)
-XX:CompileCommand:用于指定compile指令。我们配置compileonly,表示只编译Test#print方法
对应的输出结果如下(没有贴完哈),为AT&T风格,%开头表示寄存器,$开头表示立即数:
Decoding compiled method 0x0000000002fb1fd0:
Code:
[Disassembling for mach='i386:x86-64']
[Entry Point]
[Verified Entry Point]
[Constants]
# {method} {0x00000000195402b8} 'print' '(I)V' in 'Test'
# parm0: rdx = int
# [sp+0x40] (sp of caller)
0x0000000002fb2160: mov %eax,-0x6000(%rsp)
0x0000000002fb2167: push %rbp
0x0000000002fb2168: sub $0x30,%rsp
0x0000000002fb216c: mov %edx,0x24(%rsp)
0x0000000002fb2170: movabs $0x19540488,%r8 ; {metadata(method data for {method} {0x00000000195402b8} 'print' '(I)V' in 'Test')}
0x0000000002fb217a: mov 0xdc(%r8),%esi
0x0000000002fb2181: add $0x8,%esi
0x0000000002fb2184: mov %esi,0xdc(%r8)
0x0000000002fb218b: movabs $0x195402b0,%r8 ; {metadata({method} {0x00000000195402b8} 'print' '(I)V' in 'Test')}
0x0000000002fb2195: and $0x0,%esi
0x0000000002fb2198: cmp $0x0,%esi
0x0000000002fb219b: je 0x0000000002fb2312 ;*iconst_0
; - Test::print@0 (line 3)
0x0000000002fb21a1: mov $0x0,%esi
0x0000000002fb21a6: jmpq 0x0000000002fb22ce ;*iload_1
; - Test::print@2 (line 3)
0x0000000002fb21ab: nopl 0x0(%rax,%rax,1)
0x0000000002fb21b0: jmpq 0x0000000002fb2386 ; {no_reloc}
0x0000000002fb21b5: add %al,(%rax)
0x0000000002fb21b7: add %al,(%rax)
0x0000000002fb21b9: add %ah,0xf(%rsi)
0x0000000002fb21bc: (bad)
0x0000000002fb21bd: add %r8b,(%rax)
0x0000000002fb21c0: jmpq 0x0000000002fb23a1 ; implicit exception: dispatches to 0x0000000002fb2390
0x0000000002fb21c5: nop
0x0000000002fb21c6: nop
0x0000000002fb21c7: shl $0x3,%rdi ;*getstatic out
; - Test::print@7 (line 4)
0x0000000002fb21cb: cmp (%rdi),%rax ; implicit exception: dispatches to 0x0000000002fb23ab
0x0000000002fb21ce: mov %rdi,%r8
0x0000000002fb21d1: movabs $0x19540488,%rbx ; {metadata(method data for {method} {0x00000000195402b8} 'print' '(I)V' in 'Test')}
0x0000000002fb21db: mov 0x8(%r8),%r8d
0x0000000002fb21df: shl $0x3,%r8
0x0000000002fb21e3: cmp 0x130(%rbx),%r8
0x0000000002fb21ea: jne 0x0000000002fb21f9
0x0000000002fb21ec: addq $0x1,0x138(%rbx)
0x0000000002fb21f4: jmpq 0x0000000002fb225f
0x0000000002fb21f9: cmp 0x140(%rbx),%r8
0x0000000002fb2200: jne 0x0000000002fb220f
0x0000000002fb2202: addq $0x1,0x148(%rbx)
0x0000000002fb220a: jmpq 0x0000000002fb225f
0x0000000002fb220f: cmpq $0x0,0x130(%rbx)
0x0000000002fb221a: jne 0x0000000002fb2233
0x0000000002fb221c: mov %r8,0x130(%rbx)
0x0000000002fb2223: movq $0x1,0x138(%rbx)
0x0000000002fb222e: jmpq 0x0000000002fb225f
0x0000000002fb2233: cmpq $0x0,0x140(%rbx)
0x0000000002fb223e: jne 0x0000000002fb2257
0x0000000002fb2240: mov %r8,0x140(%rbx)
0x0000000002fb2247: movq $0x1,0x148(%rbx)
0x0000000002fb2252: jmpq 0x0000000002fb225f
0x0000000002fb2257: addq $0x1,0x128(%rbx)
0x0000000002fb225f: mov %rsi,%r8
0x0000000002fb2262: mov %rdi,%rdx ;*invokevirtual println
; - Test::print@11 (line 4)
0x0000000002fb2265: mov %esi,0x20(%rsp)
0x0000000002fb2269: nop
0x0000000002fb226a: nop
0x0000000002fb226b: nop
0x0000000002fb226c: nop
0x0000000002fb226d: movabs $0xffffffffffffffff,%rax
0x0000000002fb2277: callq 0x0000000002ef63e0 ; OopMap{off=284}
;*invokevirtual println
; - Test::print@11 (line 4)
; {virtual_call}
0x0000000002fb227c: mov 0x20(%rsp),%esi
0x0000000002fb2280: inc %esi
0x0000000002fb2282: movabs $0x19540488,%rdi ; {metadata(method data for {method} {0x00000000195402b8} 'print' '(I)V' in 'Test')}
0x0000000002fb228c: mov 0xe0(%rdi),%ebx
0x0000000002fb2292: add $0x8,%ebx
0x0000000002fb2295: mov %ebx,0xe0(%rdi)
0x0000000002fb229b: movabs $0x195402b0,%rdi ; {metadata({method} {0x00000000195402b8} 'print' '(I)V' in 'Test')}
0x0000000002fb22a5: and $0xfff8,%ebx
0x0000000002fb22ab: cmp $0x0,%ebx
0x0000000002fb22ae: je 0x0000000002fb23b0 ; OopMap{off=340}
;*goto
; - Test::print@17 (line 3)
0x0000000002fb22b4: test %eax,-0x1f821ba(%rip) # 0x0000000001030100
; {poll}
0x0000000002fb22ba: movabs $0x19540488,%rdi ; {metadata(method data for {method} {0x00000000195402b8} 'print' '(I)V' in 'Test')}
0x0000000002fb22c4: incl 0x158(%rdi) ;*goto
; - Test::print@17 (line 3)
0x0000000002fb22ca: mov 0x24(%rsp),%edx
0x0000000002fb22ce: cmp %edx,%esi
0x0000000002fb22d0: movabs $0x19540488,%r8 ; {metadata(method data for {method} {0x00000000195402b8} 'print' '(I)V' in 'Test')}
0x0000000002fb22da: movabs $0x108,%rdi
0x0000000002fb22e4: jge 0x0000000002fb22f4
0x0000000002fb22ea: movabs $0x118,%rdi
0x0000000002fb22f4: mov (%r8,%rdi,1),%rbx
0x0000000002fb22f8: lea 0x1(%rbx),%rbx
0x0000000002fb22fc: mov %rbx,(%r8,%rdi,1)
0x0000000002fb2300: jl 0x0000000002fb21b0 ;*if_icmpge
; - Test::print@4 (line 3)
0x0000000002fb2306: add $0x30,%rsp
0x0000000002fb230a: pop %rbp
0x0000000002fb230b: test %eax,-0x1f82211(%rip) # 0x0000000001030100
; {poll_return}
0x0000000002fb2311: retq
0x0000000002fb2312: mov %r8,0x8(%rsp)
0x0000000002fb2317: movq $0xffffffffffffffff,(%rsp)
0x0000000002fb231f: callq 0x0000000002fb0120 ; OopMap{off=452}
;*synchronization entry
; - Test::print@-1 (line 3)
; {runtime_call}
0x0000000002fb2324: jmpq 0x0000000002fb21a1
0x0000000002fb2329: movabs $0x0,%r8 ; {oop(NULL)}
0x0000000002fb2333: push %rax
0x0000000002fb2334: push %rbx
0x0000000002fb2335: mov 0x48(%r8),%rbx
0x0000000002fb2339: push %rdi
0x0000000002fb233a: push %rsi
0x0000000002fb233b: push %rdx
0x0000000002fb233c: push %rcx
0x0000000002fb233d: push %r8
0x0000000002fb233f: push %r9
0x0000000002fb2341: push %r10
0x0000000002fb2343: mov %rsp,%r10
0x0000000002fb2346: and $0xfffffffffffffff0,%rsp
0x0000000002fb234a: push %r10
0x0000000002fb234c: push %r11
0x0000000002fb234e: mov $0x7,%ecx
0x0000000002fb2353: movabs $0x7ff9f36a4c90,%r10 ; {runtime_call}
0x0000000002fb235d: callq *%r10
0x0000000002fb2360: pop %r11
0x0000000002fb2362: pop %rsp
0x0000000002fb2363: pop %r10
0x0000000002fb2365: pop %r9
0x0000000002fb2367: pop %r8
0x0000000002fb2369: pop %rcx
0x0000000002fb236a: pop %rdx
0x0000000002fb236b: pop %rsi
0x0000000002fb236c: pop %rdi
0x0000000002fb236d: cmp 0x118(%rbx),%rax
0x0000000002fb2374: pop %rbx
0x0000000002fb2375: pop %rax
0x0000000002fb2376: jne 0x0000000002fb2386
0x0000000002fb237c: jmpq 0x0000000002fb21ba
0x0000000002fb2381: mov $0xa535d00,%eax
0x0000000002fb2386: callq 0x0000000002faf4a0 ; OopMap{off=555}
;*getstatic out
; - Test::print@7 (line 4)
; {runtime_call}
0x0000000002fb238b: jmpq 0x0000000002fb21b0
0x0000000002fb2390: callq 0x0000000002fab8c0 ; OopMap{r8=Oop off=565}
;*getstatic out
; - Test::print@7 (line 4)
; {runtime_call}
0x0000000002fb2395: mov 0x0(%r8),%edi
0x0000000002fb239c: mov $0x7050c00,%eax
0x0000000002fb23a1: callq 0x0000000002faec20 ; OopMap{r8=Oop off=582}
;*getstatic out
; - Test::print@7 (line 4)
; {runtime_call}
0x0000000002fb23a6: jmpq 0x0000000002fb21c0
0x0000000002fb23ab: callq 0x0000000002fab8c0 ; OopMap{rdi=Oop off=592}
;*invokevirtual println
; - Test::print@11 (line 4)
; {runtime_call}
0x0000000002fb23b0: mov %rdi,0x8(%rsp)
0x0000000002fb23b5: movq $0x11,(%rsp)
0x0000000002fb23bd: callq 0x0000000002fb0120 ; OopMap{off=610}
;*goto
; - Test::print@17 (line 3)
; {runtime_call}
0x0000000002fb23c2: jmpq 0x0000000002fb22b4
0x0000000002fb23c7: nop
0x0000000002fb23c8: nop
0x0000000002fb23c9: mov 0x2a8(%r15),%rax
0x0000000002fb23d0: movabs $0x0,%r10
0x0000000002fb23da: mov %r10,0x2a8(%r15)
0x0000000002fb23e1: movabs $0x0,%r10
0x0000000002fb23eb: mov %r10,0x2b0(%r15)
0x0000000002fb23f2: add $0x30,%rsp
0x0000000002fb23f6: pop %rbp
0x0000000002fb23f7: jmpq 0x0000000002f1e2e0 ; {runtime_call}
0x0000000002fb23fc: hlt
0x0000000002fb23fd: hlt
......