안녕하세요. 이 글은 웹으로 읽는 것을 추천드립니다.
자바 클래스 파일 읽는 방법에 대해서 알려드리겠습니다.
보통 자바를 컴파일하고 실행할 때, Java Class라는 파일을 생성합니다.
Raw파일로 일반 에디터(ex. Atom)으로 읽을 수 없고, 리눅스 OS 시스템의 경우,
CLI에서 "od -tx1 파일명"이라는 방식으로 읽을 수 있습니다.
그럼 간단한 예시로 알려드리겠습니다.
$ od -tx1 Runnable.class
0000000 ca fe ba be 00 00 00 34 00 0b 07 00 09 07 00 0a
0000020 01 00 03 72 75 6e 01 00 03 28 29 56 01 00 0a 53
0000040 6f 75 72 63 65 46 69 6c 65 01 00 0d 52 75 6e 6e
0000060 61 62 6c 65 2e 6a 61 76 61 01 00 19 52 75 6e 74
0000100 69 6d 65 56 69 73 69 62 6c 65 41 6e 6e 6f 74 61
0000120 74 69 6f 6e 73 01 00 1f 4c 6a 61 76 61 2f 6c 61
0000140 6e 67 2f 46 75 6e 63 74 69 6f 6e 61 6c 49 6e 74
0000160 65 72 66 61 63 65 3b 01 00 12 6a 61 76 61 2f 6c
0000200 61 6e 67 2f 52 75 6e 6e 61 62 6c 65 01 00 10 6a
0000220 61 76 61 2f 6c 61 6e 67 2f 4f 62 6a 65 63 74 06
0000240 01 00 01 00 02 00 00 00 00 00 01 04 01 00 03 00
0000260 04 00 00 00 02 00 05 00 00 00 02 00 06 00 07 00
0000300 00 00 06 00 01 00 08 00 00
0000311
그럼 여기서 읽는 방식에 대해서 알려드리겠습니다.
자바 공식 홈페이지에 있는 관련 도큐먼트를 보면 이런 내용이 있습니다.
(관련 홈페이지 URL: Chapter 4. The class File Format)
ClassFile { u4 magic; u2 minor_version; u2 major_version; u2 constant_pool_count; cp_info constant_pool[constant_pool_count-1]; u2 access_flags; u2 this_class; u2 super_class; u2 interfaces_count; u2 interfaces[interfaces_count]; u2 fields_count; field_info fields[fields_count]; u2 methods_count; method_info methods[methods_count]; u2 attributes_count; attribute_info attributes[attributes_count]; }
이것은 자바의 자료구조에 대해서 설명하는 부분입니다.
u4는 4바이트로 된 자료라는 것이고, u2는 2바이트로 된 자료라는 뜻입니다.
그래서 Magic은 4바이트이므로 ca fe ba be 입니다.
(참고로, 자바의 Magic은 항상 CAFE BABE입니다. )
만약에 Magic이 CAFE BABE가 아닐 경우, 이 파일은 자바 클래스 파일이 아님을 뜻합니다.
그리고 그 다음 2바이트는 Minor_version을 뜻하는 것이고, 다음 2바이트는 Major_version을 뜻하는 것입니다.
그래서 순서를 보자면, Magic 4바이트 -> Minor_version 2바이트 -> Major_version 2바이트 -> 등등 이렇습니다.
문제가 되는 부분은 Constant_pool[constant_pool_count - 1]을 읽는 방법입니다.
그 바로 앞에 constant_pool_count라는 2바이트로 된 데이터가 있는데 보통 이를 통해서, 다음에 오는 데이터,
즉, Constant_pool[constant_pool_count - 1]이 몇개 있는지 알 수 있습니다. 예를 들어, 00 0b개가 있을 경우,
16진법 0b는 11이므로 Constant_pool은 10개의 태그와 데이터를 가진다는 사실을 알 수 있습니다.
그럼 Constant_pool[constant_pool_count - 1]은 어떻게 읽을 수 있을까요?
Tag byte | Additional bytes | Description of constant |
---|---|---|
1 | 2+x bytes (variable) | UTF-8 (Unicode) string: a character string prefixed by a 16-bit number (type u2) indicating the number of bytes in the encoded string which immediately follows (which may be different than the number of characters). Note that the encoding used is not actually UTF-8, but involves a slight modification of the Unicode standard encoding form. |
3 | 4 bytes | Integer: a signed 32-bit two's complement number in big-endian format |
4 | 4 bytes | Float: a 32-bit single-precision IEEE 754 floating-point number |
5 | 8 bytes | Long: a signed 64-bit two's complement number in big-endian format (takes two slots in the constant pool table) |
6 | 8 bytes | Double: a 64-bit double-precision IEEE 754 floating-point number (takes two slots in the constant pool table) |
7 | 2 bytes | Class reference: an index within the constant pool to a UTF-8 string containing the fully qualified class name (in internal format) (big-endian) |
8 | 2 bytes | String reference: an index within the constant pool to a UTF-8 string (big-endian too) |
9 | 4 bytes | Field reference: two indexes within the constant pool, the first pointing to a Class reference, the second to a Name and Type descriptor. (big-endian) |
10 | 4 bytes | Method reference: two indexes within the constant pool, the first pointing to a Class reference, the second to a Name and Type descriptor. (big-endian) |
11 | 4 bytes | Interface method reference: two indexes within the constant pool, the first pointing to a Class reference, the second to a Name and Type descriptor. (big-endian) |
12 | 4 bytes | Name and type descriptor: two indexes to UTF-8 strings within the constant pool, the first representing a name (identifier) and the second a specially encoded type descriptor. |
15 | 3 bytes | Method handle: this structure is used to represent a method handle and consists of one byte of type descriptor, followed by an index within the constant pool.[5] |
16 | 2 bytes | Method type: this structure is used to represent a method type, and consists of an index within the constant pool.[5] |
18 | 4 bytes | InvokeDynamic: this is used by an invokedynamic instruction to specify a bootstrap method, the dynamic invocation name, the argument and return types of the call, and optionally, a sequence of additional constants called static arguments to the bootstrap method.[5] |
다행스럽게도, Java Class File 위키백과에 자세하게 나와있습니다.
(관련 홈페이지 URL : Java class file)
각각의 Constant 테이블은 1개의 태그를 가지는데 그 태그에 따라서 읽어야하는 데이터가 달라지는 것입니다.
예를 들어, 태그가 07인 경우, 위의 표에서 볼 수 있듯이 2개의 바이트를 추가로 데이터로 가지고 있습니다.
태그가 01인 경우, UTF8을 뜻하는데 태그 후에 2개의 바이트를 읽고, 그 바이트에 따라서,
추가로 데이터를 더 읽어야 합니다. 예를 들어:
01 00 03 72 75 6e의 경우 '01' 태그를 읽고 2바이트를 읽습니다.
그럼 00 03이라는 2개의 바이트를 추가로 읽을 수 있는데, 이는 3개의 바이트를 추가로 취한다는 뜻입니다.
그래서 그 1개의 Constant_pool[0]은 즉 이렇게 됩니다:
$ od -tx1 Runnable.class
0000000 ca fe ba be 00 00 00 34 00 0b 07 00 09 07 00 0a
0000020 01 00 03 72 75 6e 01 00 03 28 29 56 01 00 0a 53
0000040 6f 75 72 63 65 46 69 6c 65 01 00 0d 52 75 6e 6e
0000060 61 62 6c 65 2e 6a 61 76 61 01 00 19 52 75 6e 74
0000100 69 6d 65 56 69 73 69 62 6c 65 41 6e 6e 6f 74 61
0000120 74 69 6f 6e 73 01 00 1f 4c 6a 61 76 61 2f 6c 61
0000140 6e 67 2f 46 75 6e 63 74 69 6f 6e 61 6c 49 6e 74
0000160 65 72 66 61 63 65 3b 01 00 12 6a 61 76 61 2f 6c
0000200 61 6e 67 2f 52 75 6e 6e 61 62 6c 65 01 00 10 6a
0000220 61 76 61 2f 6c 61 6e 67 2f 4f 62 6a 65 63 74 06
0000240 01 00 01 00 02 00 00 00 00 00 01 04 01 00 03 00
0000260 04 00 00 00 02 00 05 00 00 00 02 00 06 00 07 00
0000300 00 00 06 00 01 00 08 00 00
0000311
다음 Constant_pool[1]은:
$ od -tx1 Runnable.class
0000000 ca fe ba be 00 00 00 34 00 0b 07 00 09 07 00 0a
0000020 01 00 03 72 75 6e 01 00 03 28 29 56 01 00 0a 53
0000040 6f 75 72 63 65 46 69 6c 65 01 00 0d 52 75 6e 6e
0000060 61 62 6c 65 2e 6a 61 76 61 01 00 19 52 75 6e 74
0000100 69 6d 65 56 69 73 69 62 6c 65 41 6e 6e 6f 74 61
0000120 74 69 6f 6e 73 01 00 1f 4c 6a 61 76 61 2f 6c 61
0000140 6e 67 2f 46 75 6e 63 74 69 6f 6e 61 6c 49 6e 74
0000160 65 72 66 61 63 65 3b 01 00 12 6a 61 76 61 2f 6c
0000200 61 6e 67 2f 52 75 6e 6e 61 62 6c 65 01 00 10 6a
0000220 61 76 61 2f 6c 61 6e 67 2f 4f 62 6a 65 63 74 06
0000240 01 00 01 00 02 00 00 00 00 00 01 04 01 00 03 00
0000260 04 00 00 00 02 00 05 00 00 00 02 00 06 00 07 00
0000300 00 00 06 00 01 00 08 00 00
0000311
다음 Constant_pool[2]는:
$ od -tx1 Runnable.class
0000000 ca fe ba be 00 00 00 34 00 0b 07 00 09 07 00 0a
0000020 01 00 03 72 75 6e 01 00 03 28 29 56 01 00 0a 53
0000040 6f 75 72 63 65 46 69 6c 65 01 00 0d 52 75 6e 6e
0000060 61 62 6c 65 2e 6a 61 76 61 01 00 19 52 75 6e 74
0000100 69 6d 65 56 69 73 69 62 6c 65 41 6e 6e 6f 74 61
0000120 74 69 6f 6e 73 01 00 1f 4c 6a 61 76 61 2f 6c 61
0000140 6e 67 2f 46 75 6e 63 74 69 6f 6e 61 6c 49 6e 74
0000160 65 72 66 61 63 65 3b 01 00 12 6a 61 76 61 2f 6c
0000200 61 6e 67 2f 52 75 6e 6e 61 62 6c 65 01 00 10 6a
0000220 61 76 61 2f 6c 61 6e 67 2f 4f 62 6a 65 63 74 06
0000240 01 00 01 00 02 00 00 00 00 00 01 04 01 00 03 00
0000260 04 00 00 00 02 00 05 00 00 00 02 00 06 00 07 00
0000300 00 00 06 00 01 00 08 00 00
0000311
그렇게 쭉쭉 10개의 태그를 읽게되면 그 후에는 access_flags를 만나게 됩니다. 이 자료의 경우:
$ od -tx1 Runnable.class
0000000 ca fe ba be 00 00 00 34 00 0b 07 00 09 07 00 0a
0000020 01 00 03 72 75 6e 01 00 03 28 29 56 01 00 0a 53
0000040 6f 75 72 63 65 46 69 6c 65 01 00 0d 52 75 6e 6e
0000060 61 62 6c 65 2e 6a 61 76 61 01 00 19 52 75 6e 74
0000100 69 6d 65 56 69 73 69 62 6c 65 41 6e 6e 6f 74 61
0000120 74 69 6f 6e 73 01 00 1f 4c 6a 61 76 61 2f 6c 61
0000140 6e 67 2f 46 75 6e 63 74 69 6f 6e 61 6c 49 6e 74
0000160 65 72 66 61 63 65 3b 01 00 12 6a 61 76 61 2f 6c
0000200 61 6e 67 2f 52 75 6e 6e 61 62 6c 65 01 00 10 6a
0000220 61 76 61 2f 6c 61 6e 67 2f 4f 62 6a 65 63 74 06
0000240 01 00 01 00 02 00 00 00 00 00 01 04 01 00 03 00
0000260 04 00 00 00 02 00 05 00 00 00 02 00 06 00 07 00
0000300 00 00 06 00 01 00 08 00 00
0000311
access_flags는 06 01입니다. 이와 같은 방식으로 this_class, super_class, interfaces_count 등등을 읽으면 됩니다.
안타깝게도 Programming을 하면서 한국어로 된 자료를 찾기가 어려운게 현실입니다.
조금이라도 빠른 이해에 도움이 되었으면 좋겠습니다. 미국에 있는 공대생들 화이팅입니다.
[오피니언] 준수한 개발자는 무엇인가? (0) | 2022.11.15 |
---|---|
시스템 가상 머신(System Virtual Machine)의 역사 (0) | 2019.04.21 |
전송 계층과 네트워크 계층 간의 차이점 (0) | 2018.10.01 |
16진수 2진수 표현방법 (0) | 2018.09.15 |