DalvikVM
google android에서 사용한다는 virtual machine.
android가 java기반 개발 환경이라고는 하지만, 실제로 java vm이 들어 있는게 아니고 DalvikVM이라는 android 전용 virtual machine이 들어 있다. class는 loading시점에 dalvik vm의 byte code로 변환되거나, 준비된 tool을 통해 dalvik vm의 format으로 변환되어 download될 수도 있는 것으로 보인다. jar file과 apk file이 공존하는 것으로 봐서 말이다. 그리고 결정적으로 실제 실행되는 code는 cache라는 이름이 붙은 directory에서 실행이 되는 것으로 보아, jar의 경우 compile(translation)된 class를 caching하는 정책을 사용하는 듯하다.
그런데 이름은.. 너무 촌스럽다. 달빅이 뭐냐 달빅이.. 음냥..
java vm과는 다르게 register 기반의 vm이라고 하는데, stack 기반의 vm과 큰 차이가 있는것으로 보이지만 bytecode 내용을 까보니 실제적으로 그렇게 달라 보이지는 않는다. llvm과 흡사한 instruction set을 가졌으리라고 생각했었는데, architecture는 동일하지만 level이 다르다. llvm은 risc cpu level, dalvik vm은 java vm level...(object management(create/gabage collection)이 위에 있느냐 아래에 있느냐의 차이.. 정도 되겠다) 하긴 요즘들어 llvm도 윗 기능들이 하나 하나 vm 아래로 내려가기 시작하니까.. 조만간 이런 형태가 되지 말란 법도 없긴 하지만 말이다.
일단 core.jar에 있는 class들을 까보니, 누군가의 짐작처럼 apache harmony의 class file들을 가져다 사용하고 있었다. org.apache.harmony package의 class들이 잔뜩 들어 있는 것으로 보아 말이다. core.jar에 대략 3000개의 class가 있는데 그 대표적인 package는 아래와 같다.
- SQLite
- android.access
- android.app
- android.dalvik
- android.net
- android.os
- android.security
- android.xml
- com.ibm.icu4jni
- java.io
- java.lang
- java.math
- java.net
- java.nio
- java.security
- java.sql
- java.text
- java.util
- javax.crypto
- javax.net
- javax.security
- javax.sound
- javax.sql
- javax.xml
- junit.
- org.apache.commons
- org.apache.harmony
- org.bouncycastle
- org.json
- org.kxml2
- org.w3c.dom
- org.xml.sax
- org.xmlpull
- sun.misc.Unsafe
정도 된다.
눈에 띄는건 위에서 말한 harmony 부분과 bouncycastle(harmony에서도 bouncycastle을 썼었는지 기억은 안나지만) 그리고 xmlpull parser를 지원한다는것 정도 되겠다.
다음으로 대표적인 class인 java.lang.Object class의 byte code 내용을 까보면,
Class #281 -
Class name : 'java/lang/Object'
Access flags : 0x20001 (PUBLIC OPTIMIZED)
Interfaces -
Static fields -
Instance fields -
Direct methods -
#0 : (in java/lang/Object)
name : ''
type : '()V'
access : 0x0001 (PUBLIC)
code -
registers : 2
ins : 1
outs : 0
source_idx : 21099
insns : 1 16-bit code units
434ebc: |[434ebc] java/lang/Object.:()V
434ec0: 0e00 |0000: return-void
exceptions : (none)
positions : 1
0x0000 line=32
locals : 1
0x0000 - 0x0001 reg=1 this Ljava/lang/Object;
#1 : (in java/lang/Object)
name : 'internalClone'
type : '(Ljava/lang/Cloneable;)Ljava/lang/Object;'
access : 0x0102 (PRIVATE NATIVE)
code : (none)
Virtual methods -
#0 : (in java/lang/Object)
name : 'clone'
type : '()Ljava/lang/Object;'
access : 0x0004 (PROTECTED)
code -
registers : 4
ins : 1
outs : 2
source_idx : 21099
insns : 21 16-bit code units
434ec4: |[434ec4] java/lang/Object.clone:()Ljava/lang/Object;
434ec8: 1f31 ac04 |0000: instance-of v1, v3, java/lang/Cloneable // class@04ac
434ecc: 3e01 0a00 |0002: if-nez v1, 000c // +000a
434ed0: 2101 6102 |0004: new-instance v1, java/lang/CloneNotSupportedException // class@0261
434ed4: 1802 1d0b |0006: const-string v2, "Class doesn't implement Cloneable" // string@0b1d
434ed8: 6f02 840c 2100 |0008: invoke-direct {v1, v2}, java/lang/CloneNotSupportedException.:(Ljava/lang/
String;)V // method@0c84
434ede: 3201 |000b: throw v1
434ee0: 0730 |000c: move-object v0, v3
434ee2: 1e00 ac04 |000d: check-cast v0, java/lang/Cloneable // class@04ac
434ee6: 0701 |000f: move-object v1, v0
434ee8: 6f02 bc0d 1300 |0010: invoke-direct {v3, v1}, java/lang/Object.internalClone:(Ljava/lang/Cloneable;)Lj
ava/lang/Object; // method@0dbc
434eee: 0c01 |0013: move-result-object v1
434ef0: 1101 |0014: return-object v1
exceptions : (none)
positions : 3
0x0000 line=46
0x0004 line=47
0x000c line=50
locals : 1
0x0000 - 0x0015 reg=3 this Ljava/lang/Object;
#1 : (in java/lang/Object)
name : 'equals'
type : '(Ljava/lang/Object;)Z'
access : 0x0001 (PUBLIC)
code -
registers : 3
ins : 2
outs : 0
source_idx : 21099
insns : 6 16-bit code units
434ef4: |[434ef4] java/lang/Object.equals:(Ljava/lang/Object;)Z
434ef8: 3821 0400 |0000: if-ne v1, v2, 0004 // +0004
434efc: 1210 |0002: const/4 v0, #int 1 // #1
434efe: 0f00 |0003: return v0
434f00: 1200 |0004: const/4 v0, #int 0 // #0
434f02: 33fe |0005: goto 0003 // -0002
exceptions : (none)
positions : 1
0x0000 line=70
locals : 2
0x0000 - 0x0006 reg=1 this Ljava/lang/Object;
0x0000 - 0x0006 reg=2 o Ljava/lang/Object;
#2 : (in java/lang/Object)
name : 'finalize'
type : '()V'
access : 0x0004 (PROTECTED)
code -
registers : 2
ins : 1
outs : 0
source_idx : 21099
insns : 1 16-bit code units
434f04: |[434f04] java/lang/Object.finalize:()V
434f08: 0e00 |0000: return-void
exceptions : (none)
positions : 1
0x0000 line=88
locals : 1
0x0000 - 0x0001 reg=1 this Ljava/lang/Object;
#3 : (in java/lang/Object)
name : 'getClass'
type : '()Ljava/lang/Class;'
access : 0x0111 (PUBLIC FINAL NATIVE)
code : (none)
#4 : (in java/lang/Object)
name : 'hashCode'
type : '()I'
access : 0x0101 (PUBLIC NATIVE)
code : (none)
#5 : (in java/lang/Object)
name : 'notify'
type : '()V'
access : 0x0111 (PUBLIC FINAL NATIVE)
code : (none)
#6 : (in java/lang/Object)
name : 'notifyAll'
type : '()V'
access : 0x0111 (PUBLIC FINAL NATIVE)
code : (none)
#7 : (in java/lang/Object)
name : 'toString'
type : '()Ljava/lang/String;'
access : 0x0001 (PUBLIC)
code -
registers : 3
ins : 1
outs : 2
source_idx : 21099
insns : 40 16-bit code units
434f0c: |[434f0c] java/lang/Object.toString:()Ljava/lang/String;
434f10: 2100 b407 |0000: new-instance v0, java/lang/StringBuilder // class@07b4
434f14: 6f01 540f 0000 |0002: invoke-direct {v0}, java/lang/StringBuilder.:()V // method@0f54
434f1a: f801 0300 0200 |0005: +invoke-virtual-quick {v2}, [0003] // vtable #0003
434f20: 0c01 |0008: move-result-object v1
434f22: f801 2900 0100 |0009: +invoke-virtual-quick {v1}, [0029] // vtable #0029
434f28: 0c01 |000c: move-result-object v1
434f2a: f802 3700 1000 |000d: +invoke-virtual-quick {v0, v1}, [0037] // vtable #0037
434f30: 0c00 |0010: move-result-object v0
434f32: 1301 4000 |0011: const/16 v1, #int 64 // #40
434f36: f802 3100 1000 |0013: +invoke-virtual-quick {v0, v1}, [0031] // vtable #0031
434f3c: 0c00 |0016: move-result-object v0
434f3e: f801 0400 0200 |0017: +invoke-virtual-quick {v2}, [0004] // vtable #0004
434f44: 0a01 |001a: move-result v1
434f46: 7001 2f0d 0100 |001b: invoke-static {v1}, java/lang/Integer.toHexString:(I)Ljava/lang/String; // method@0d2f
434f4c: 0c01 |001e: move-result-object v1
434f4e: f802 3700 1000 |001f: +invoke-virtual-quick {v0, v1}, [0037] // vtable #0037
434f54: 0c00 |0022: move-result-object v0
434f56: f801 0700 0000 |0023: +invoke-virtual-quick {v0}, [0007] // vtable #0007
434f5c: 0c00 |0026: move-result-object v0
434f5e: 1100 |0027: return-object v0
exceptions : (none)
positions : 1
0x0000 line=145
locals : 1
0x0000 - 0x0028 reg=2 this Ljava/lang/Object;
#8 : (in java/lang/Object)
name : 'wait'
type : '()V'
access : 0x0011 (PUBLIC FINAL)
code -
registers : 4
ins : 1
outs : 4
source_idx : 21099
insns : 7 16-bit code units
434f60: |[434f60] java/lang/Object.wait:()V
434f64: 1500 0000 |0000: const-wide/16 v0, #int 0 // #0
434f68: 1202 |0002: const/4 v2, #int 0 // #0
434f6a: f804 0a00 0321 |0003: +invoke-virtual-quick {v3, v0, v1, v2}, [000a] // vtable #000a
434f70: 0e00 |0006: return-void
exceptions : (none)
positions : 2
0x0000 line=170
0x0006 line=171
locals : 1
0x0000 - 0x0007 reg=3 this Ljava/lang/Object;
#9 : (in java/lang/Object)
name : 'wait'
type : '(J)V'
access : 0x0011 (PUBLIC FINAL)
code -
registers : 4
ins : 3
outs : 4
source_idx : 21099
insns : 5 16-bit code units
434f74: |[434f74] java/lang/Object.wait:(J)V
434f78: 1200 |0000: const/4 v0, #int 0 // #0
434f7a: f804 0a00 2103 |0001: +invoke-virtual-quick {v1, v2, v3, v0}, [000a] // vtable #000a
434f80: 0e00 |0004: return-void
exceptions : (none)
positions : 2
0x0000 line=195
0x0004 line=196
locals : 2
0x0000 - 0x0005 reg=1 this Ljava/lang/Object;
0x0000 - 0x0005 reg=2 time J
#10 : (in java/lang/Object)
name : 'wait'
type : '(JI)V'
access : 0x0111 (PUBLIC FINAL NATIVE)
code : (none)
와 같다. 외곽 구조는
- Class name
- Access flag
- Interface list
- Static fields
- Instance fields
- Direct methods
- Virtual methods
그리고 각 method 안에는
name, type, access, code, register갯수, argument 갯수, return 갯수등의 정보가 들어 있다.
정도로 java byte code의 format과 거의 비슷한 것으로 보인다. 거의 유일하게 차이나는 것이 instruction set인데, 은근히 비슷하면서도 꽤 다르다. java vm의 byte code instruction을 참고하긴 했으나, 베끼지는 않았다는 흔적이 남아 있는 듯하다. byte code는 stack->register로 피해가고, java class는 haromny를 통해 apache의 등에 숨고.. 나름 상당히 고민한 흔적이 있다. SUN과의 충돌도 피하기 힘들 듯 하다.
다음 리스트는 core.jar에 사용된 instruction의 list이다. 앞의 + prefix(의미는 모르겠다)나, 뒤의 /range, /2addr /lit8, /lit16, /4, /8, /16, /32, /from16, 등의 qualifier를 제외한 리스트이다. (143개)
시간날때 천천히 살펴보면 어떻게 생겨먹었는지 짐작이 갈 듯 하다.
add-double
add-float
add-int
add-long
aget
aget-boolean
aget-byte
aget-char
aget-object
aget-short
aget-wide
and-int
and-long
aput
aput-boolean
aput-byte
aput-char
aput-object
aput-short
aput-wide
array-length
check-cast
cmp-long
cmpg-double
cmpg-float
cmpl-double
cmpl-float
const
const-class
const-string
const-wide
div-double
div-float
div-int
div-long
double-to-float
double-to-int
double-to-long
execute-inline
filled-new-array
float-to-double
float-to-int
float-to-long
goto
if-eq
if-eqz
if-ge
if-gez
if-gt
if-gtz
if-le
if-lez
if-lt
if-ltz
if-ne
if-nez
iget-object-quick
iget-quick
iget-wide-quick
instance-of
int-to-byte
int-to-char
int-to-double
int-to-float
int-to-long
int-to-short
invoke-direct
invoke-direct-empty
invoke-interface
invoke-static
invoke-super-quick
invoke-virtual-quick
iput-object-quick
iput-quick
iput-wide-quick
long-to-double
long-to-float
long-to-int
monitor-enter
monitor-exit
move
move-exception
move-object
move-result
move-result-object
move-result-wide
move-wide
mul-double
mul-float
mul-int
mul-long
neg-double
neg-float
neg-int
neg-long
new-array
new-array-boolean
new-array-byte
new-array-char
new-array-double
new-array-float
new-array-int
new-array-long
new-array-short
new-instance
nop
or-int
or-long
packed-switch
packed-switch-data
rem-int
rem-long
return
return-object
return-void
return-wide
sget
sget-boolean
sget-byte
sget-char
sget-object
sget-wide
shl-int
shl-long
shr-int
shr-long
sparse-switch
sparse-switch-data
sput
sput-boolean
sput-byte
sput-char
sput-object
sput-wide
sub-double
sub-float
sub-int
sub-long
throw
ushr-int
ushr-long
xor-int
xor-long
좀더 자세한 내용을 원한다면, emulator를 띄우면서 -C option을 주면 console prompt가 떨어지므로 거기에서 dexdump command를 이용해 dex file을 dump해 볼 수 있다. dumper를 통해 본 내용이므로 정확한 binary format을 알 수는 없지만 대략 어떤 종류의 정보가 있으며, 어떤 class/method가 있는지 다 뜯어 볼 수 있다.