javaクラスファイルの読み方
TRANSCRIPT
Javaクラスファイル の読み⽅方Javaクラスファイル⼊入⾨門
2014/12/20
初⼼心者向け
1
このセッションの流れ
2
• JVM仕様とは何かについて • javapを使ってみましょう • classファイルフォーマットの読み方概要 • 後続セッションでの実践の準備
http://dev.classmethod.jp/server-side/java/classfile-reading/
追記: このセッションのフォローアップ記事を書きました Javaのクラスファイルをjavapとバイナリエディタで読む
Javaのコンパイルと実行
3
ハードウェア
OS OS OS
ハードウェア ハードウェア
JVM JVM JVM
中間コード 中間コード 中間コード
プラットフォーム共通
.class .class .class
中間コード.class
Javaプログラム
.java コンパイル
実行
JVM
4
ハードウェア
OS OS OS
ハードウェア ハードウェア
JVM JVM JVM
中間コード 中間コード 中間コード
プラットフォーム共通JVM プラットフォーム依存
.class .class .class
JVM仕様とは
5
JVM仕様は,実装の詳細には踏み込まない
入力であるクラスファイルのデータ構造と 出力である振る舞いのルールを定義するもの
このルールを満たす実装をJVMと呼ぶ 逆に言うと,特定のJVMの仕様を定めたものではない
classファイル・フォーマットとは
6
Javaのクラスファイルを表現するバイナリの並びを定義したもの
u4 magic
u2 minor_version
u2 major_version
u2 constant_pool_cont
など
2つのツールの違い
7
javap
バイナリエディタ
バイナリデータを読みやすく整形して表示する 入力として想定するのは,ファイルなら何でも
バイナリデータをJavaのclassファイルとして解釈し,JVM仕様で定義されたデータ構造とのマッピングを表示する 入力として想定するのは,Java classファイルのみ
どちらもバイナリデータを読み込む点は共通
javap
8
Javaの標準ツール OpenJDKベースのJDKに入っている
コマンドラインで実行する ターミナル,iTerm,コマンドプロンプト(Windows)
Javaクラスファイルを逆アセンブルするために使う
どんなときにjavapを使うか
9
• 複数のコンパイラを使う状況で,コンパイラごとに出力される中間コードの差分を見たいとき
• 処理系を作る際に,期待する入力と実際の中間コードの差分を見たいとき
バイナリとは異なる粒度でファイル間の差分を見る時に 使うのではないかと思います そういった作業をしなければ使う機会はあまりないと思います
(例)
javapを使ってみましょう
10
javapを使う前提条件
11
• Javaがインストールされていること • OpenJDK系のJDKがインストールされていること • JREだけでなく,JDKが必要です
PCをお持ちでない方は,javapの出力内容の説明に入るまで少々お待ちください.
javapを試す手順
12
1. HelloWorldを出力するJavaプログラムHello.javaを書いて,コンパイルしてください
2. 生成されたHello.classに対して,javapを実行してください
% vi Hello.java class Hello{ public static void main(String[] args){ System.out.pintln(“Hello World!”); } } !% javac Hello.java % javap Hello
javapの出力例
13
% javap Hello Compiled from "Hello.java" class Hello { Hello(); public static void main(java.lang.String[]); }
次のような出力が得られましたか?
そのままでは情報量が少ないため,vオプションをつけてもう一度実行してください
% javap -v Hello
javapの出力例の説明
14
javapの出力は,JVM仕様を参照しながらバイナリを読めるようになった後の方が理解しやすい javapの出力はすでにJVM仕様とマッピングされているが,バイナリエディタを使って見る作業では自分でマッピングするため
このタイミングで詳細が分からなくてもこだわらず, どんな項目が出力されるかを眺めておいてください どうしても気になる時は,JVM仕様内を検索してみてください
(注意事項)
15
Classfile /path/to/classfile/Hello.class Last modified 2014/12/20; size 409 bytes MD5 checksum 786366c9c8962af2a9d3e1cf3284d69c Compiled from "Hello.java" class Hello SourceFile: "Hello.java" minor version: 0 major version: 52 flags: ACC_SUPER Constant pool: #1 = Methodref #6.#15 // java/lang/Object."<init>":()V #2 = Fieldref #16.#17 // java/lang/System.out:Ljava/io/PrintStream; #3 = String #18 // hello #4 = Methodref #19.#20 // java/io/PrintStream.println:(Ljava/lang/String;)V #5 = Class #21 // Hello #6 = Class #22 // java/lang/Object
アクセス許可やクラスの属性
コンスタントプール
参考: Table 4.1-A. Class access and property modifiers
…
16
{ Hello(); descriptor: ()V flags: Code: stack=1, locals=1, args_size=1 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: return LineNumberTable: line 1: 0
自動生成されたデフォルトコンストラクタフィールドやメソッドの型
自動生成されたreturn文
Javaプログラムの行番号
バイトコードのindex
…
…
コンスタントプールのエントリへの参照
17
public static void main(java.lang.String[]); descriptor: ([Ljava/lang/String;)V flags: ACC_PUBLIC, ACC_STATIC Code: stack=2, locals=1, args_size=1 0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream; 3: ldc #3 // String hello 5: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 8: return LineNumberTable: line 3: 0 line 4: 8 }
バイトコードのindexは,メソッドごとに0から始まる
…mainメソッド
参考: 仮想マシン命令セット
18
参照: 「Chapter 6. The Java Virtual Machine Instruction Set」JVM仕様の目次をニーモニックでページ検索するとよいでしょう
0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream; 3: ldc #3 // String hello 5: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
getstatic: クラスのstaticフィールドの参照 ここではSystem.outインスタンスを取得する ldc: ランタイムからアイテムをpushする ここではhelloという文字列をpushする invokevirtual: インスタンスメソッドの呼び出し ここではPrintStream.printlnを呼び出す
ニーモニック
javapの便利オプション(基本)
19
-private
-c
-l(小文字のL)
すべてのアクセス制御子のコードを表示するデフォルトではpulicとprotectedとdefaultだけ
行とローカル変数テーブルを表示する
逆アセンブルされたコードを表示する
参考: local variable table
20
Javaが動作する時,各スレッドがスタックを持つ
ローカル変数の配列
オペランドスタック
参照
コンスタントプール
javapの便利オプション(その他)
21
-classpathカレントディレクトリからCLASSPATHへの相対or絶対パスを指定する
逆アセンブルしたいクラス名は特定しているが,それがどのjarに含まれているか不明な時に便利
classファイルフォーマット の読み方
22
Javaのコンパイルと実行(再掲)
23
ハードウェア
OS OS OS
ハードウェア ハードウェア
JVM JVM JVM
中間コード 中間コード 中間コード
プラットフォーム共通
.class .class .class
中間コード.class
Javaプログラム
.java コンパイル
実行
classファイルとは
24
中間コード.class
classファイルは8ビットのバイト・ストリームから 成り立っている.
値が意味を持つ最小構成単位が8ビットという意味
コンパイルによって生成されるファイル中間コード,中間ファイルとも呼ぶ
11001010111111101011101010111110
Java仮想マシン仕様 第2版
8 8 8 8
JVM仕様書
25
Java Language and Virtual Machine Specificationshttps://docs.oracle.com/javase/specs/
上記の「Chapter 4. The class File Format」を参照します
Hello.classをバイナリエディタで開いてください
26
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]; }
参照: 4.1. The ClassFile Structure
27
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]; }
全体は擬似構造体
型項目
符号なし 4バイト
クラスやインタフェースと1対1
可変長table
固定長配列
配列の要素数
型(u1, u2, u4)
28
1バイト=8ビットなので, u4型の項目は,8 * 4 = 32バイトで表現される
u1: 符号なし1バイト u2: 符号なし2バイト u4: 符号なし4バイト
29
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]; }
magic
magic
30
110010101111111010111010101111108 8 8 8
C A F E B A B E
• Java classファイルを識別するための定数値 • 固定で16進数「CAFEBABE」 • u4型なので,次のように4バイト読む • 以降の項目も,固定長の場合は同じように読む
参照: http://radio-weblogs.com/0100490/2003/01/28.html
31
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]; }
minor_version, major_version
classファイルフォーマットのバージョン
32
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]; }
constant_pool_countconstant_poolテーブルの要素より1多い数字
33
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]; }
cp_info
constant_pool[constant_pool_count-1]
34
• 可変長のtable • constant_pool_count-1個の要素数を持つ
• 中身を読まなければ,全体が何バイトあるかは分からない !
• 個々の要素は,javapの出力で「#n」というインデックスで参照されていた
cp_info
35
cp_info { u1 tag; u1 info[]; } 参照: 4.4. The Constant Pool
constant_poolテーブルには,上のような構造のデータが要素数分だけ入っている
1つめの項目であるtagによって構造が異なる参照: Table 4.4-A. Constant pool tags
constant_poolcp_info
36
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]; }
access_flags
access_flags
37
• アクセス許可やクラスの属性を表わす • javapにも出力されていた項目
!
• 和訳書籍の『JVMマシン仕様 第2版』ではaccess_flagsは5種類しか載っていませんが,これはJava1.2の頃の仕様で,現在は8種類です参考: Table 4.1-A. Class access and property modifiers
38
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]; }
this_class, super_class
this_class, super_class
39
• this_class:constant_poolテーブルへのインデックスインデックス先はConstant_Class_info構造体
• super_class: constant_poolテーブルへのインデックスか,0 (Object classのときだけ0)インデックス先はConstant_Class_info構造体
40
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]; }
interfaces_count, interfaces[interfaces_count]
interfaces_count, interfaces[interfaces_count]
41
• interfaces_count: このクラス,あるいはインタフェースの直接のsuper intarfaceの数
• interfaces[interfaces_count]: interfaces_count個の要素を持つ配列(固定長)
42
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]; }
fields_count, fields[fields_count]
fields_count, fields[fields_count]
43
• fields_count:このクラス,あるいはインタフェースで定義されているフィールドの数
• fields[fields_count]: fields_count個の要素を持つfield_info構造体のテーブル(可変長)
44
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]; }
methods_count, methods[methods_count]
methods_count, methods[methods_count]
45
• methods_count: このクラス,あるいはインタフェースで宣言されているメソッドの数(プログラム中に定義していない自動生成されたメソッドも含む)
• methods[methods_count]: methods_count個の要素を持つmethod_info構造体のテーブル(可変長)
46
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]; }
attributes_count, attributes[attributes_count]
attributes_count,attributes[attributes_count]
47
• attributes_count:attribute_info構造体の数
• attributes[attributes_count]: attributes_count個の要素を持つattribute_info構造体のテーブル(可変長)
新たなattributeを定義できるのが特徴 Javaの言語機能が増えるとattribute_info構造体の種類が増える
まとめ
48
• JVM仕様とは,「入力であるクラスファイルのデータ構造と出力である振る舞いのルールを定義するもの」 !
• javapは,classファイルのバイナリをJVM仕様とマッピングして表示してくれるツール
!
• classファイルフォーマットの表記ルールと読み進め方
参考資料
49
Java Language and Virtual Machine Specificationshttps://docs.oracle.com/javase/specs/
http://d.hatena.ne.jp/torazuka/20120820/cafebabeJavaのクラスファイルの読み方
http://dev.classmethod.jp/server-side/java/classfile-reading/
Javaのクラスファイルをjavapとバイナリエディタで読む(このセッションのフォローアップ記事です)