LSDA

Language Specific Data Area (LSDA)

[Coins Java - 例外処理]

LSDAとは

例外処理構造のうち、言語に依存する部分を記述するデータ構造。今回はJavaについてのみ取り扱うことにする。

LSDAの構造

LSDAは3つの部分に分かれていて、大まかには以下のような構造になっている。

パート 内容
ヘッダ エンコーディング形式や、各パートの開始位置など
コールサイト キャッチする範囲や条件、キャッチが成立した場合の着地地点
アクションテーブル キャッチする条件や、失敗時の挙動
トラップタイプ キャッチできる型のテーブル

ヘッダ部

各部分のエンコーディング形式や、開始位置などを記述しておく部分。

名前 形式 備考
lpstart_encoding byte lpstartのエンコーディング形式
lpstart lpstart_encodingで指定したもの landing pad(後述)
ttype_encoding byte キャッチする型のエンコーディング形式
TType uleb128 キャッチする型の開始位置(後述)
call_site_encoding byte コールサイト部のエンコーディング形式
action_table uleb128 アクションテーブル部の開始位置

簡単に各項目を紹介する。

lpstart
Landing Pad(着地地点)と呼ばれるものの起点となる場所を指定する。絶対アドレスではなく、関数の開始位置からの相対アドレスで指定すればよい。この値は、コールサイト部で使用されるcs_lpの起点となる。lpstart_encodingの値がomitならばこの値は指定しない。その際のデフォルト値は関数の先頭となる。
TType
キャッチする型を表すクラスを列挙したテーブルの位置を指定する。指定すべきアドレスは、テーブルの終端直後のアドレスである。TTypeの指定が終わった直後のアドレスからの相対アドレスで記述する。ttype_encodingの値がomitならばこの値は指定しない。その際は、起点アドレスが0となる(動作は不明)
action_table
アクションテーブル部の開始位置を指定する。絶対アドレスではなく、action_tableの指定が終わった直後のアドレスからの相対アドレスで記述する。

コールサイト部

トラップ対象のプログラムカウンタの範囲や、アクションテーブル内のレコードの指定、キャッチが成立した場合の着地地点などを記述する。なお、この部分は1回以上でいくら繰り返しても良い。

名前 形式 備考
cs_start call_site_encoding トラップ開始位置(関数の先頭からの相対)
cs_len call_site_encoding トラップ範囲
cs_lp call_site_encoding キャッチ成立時の着地地点(lpstartからの相対)
cs_action uleb128 アクションテーブルレコード
cs_action
アクションテーブル内のどのレコードを使用するか指定する。action_tableで指定したアドレスからの相対位置 - 1 を記述すればよい。ここで0を指定すると、必ずキャッチが成立することになる。その場合のswitching_value(後述)は0になる。

アクションテーブル部

キャッチする型と、キャッチ失敗時の処理を記述する。なお、この部分は1回以上でいくら繰り返しても良い。

名前 形式 備考
ar_filter sleb128 キャッチする型を指定する
ar_disp sleb128 ar_filterでキャッチできなかった場合の処理を指定する
ar_filter
キャッチする型を指定する。トラップタイプ部の末尾の値を1番目として、次の値を2番、と逆順に番号がつけられている。0番を指定すると、必ずキャッチする。ここで指定した値は、switching_valueとして、正常処理に戻ったときに特定のレジスタに格納されている。
ar_disp
ar_filterでキャッチできなかった場合の処理を指定する。具体的には、キャッチできなかった場合に次に走査するアクションテーブルレコードの位置を指定する。この位置は現在のar_dispの位置からの相対位置で記述する。相対位置0を指定すると、キャッチに失敗したことになり、フレームの巻き戻しが行われる。

トラップタイプ部

キャッチする型のテーブルを記述する。なお、この部分は1回以上でいくら繰り返しても良い。

名前 形式 備考
ttype ttype_encoding キャッチする型

一般的にはjava.lang.Classのインスタンスで記述するが、クラス名によるシンボル参照も可能である。その場合は、クラス名を表したUtf8Constへのポインタで指定しても良いが、java.lang.Classのインスタンスと区別をつけるため、ポインタの値に1を加算した値で格納しておく必要がある。このことより、java.lang.Classのインスタンスは最低でも2バイト単位で整列している必要がありそうだ。

LSDAの処理終了後の状態

キャッチ成功時

キャッチが成功した場合、ヘッダ部で指定したlpstartの値とコールサイト部で指定したcs_lpの値の和になる位置にプログラムカウンタが指定される。

また、GCC内部でのレジスタ番号0番と1番に、それぞれ例外処理の情報と、キャッチした時のar_filterの値が格納されて帰ってくる。

例外処理の情報は以下のような構造をしている。

struct java_exception_header
{
  /* Cache handler details between Phase 1 and Phase 2.  */
  _Unwind_Ptr landingPad;
  int handlerSwitchValue;

  /* The object being thrown.  Compiled code expects this to be immediately
     before the generic exception header.  Which is complicated by the fact
     that _Unwind_Exception is ((aligned)).  */

  char pad[sizeof(jthrowable) < sizeof(alignment_test_struct)
	   ? sizeof(alignment_test_struct) - sizeof(jthrowable) : 0]
    __attribute__((aligned));

  jthrowable value;

  /* The generic exception header.  */
  _Unwind_Exception unwindHeader;
};

このうち、実際に帰ってくる値は unwindHeader へのポインタである。しかし、Javaで使用する値は jthrowable value なので、この値を取得したい場合は、unwindHeaderへのポインタからポインタサイズだけ引いた値が指すメモリの内容を取得すればよい。

また、GCC内部でのレジスタ番号は、例外処理時のそれとはまったく別物であるので注意する必要がある。

キャッチ失敗時

自動でフレームの巻き戻しが行われ、呼び出しもとのフレームで同様の例外処理が開始される。

Copyright (C) 2002-2006 s.arakawa