ラベル SBCL_Internals の投稿を表示しています。 すべての投稿を表示
ラベル SBCL_Internals の投稿を表示しています。 すべての投稿を表示

2010年2月24日水曜日

SBCLでバイト列を逆アセンブルする

Kernel/VM探検隊に参加しました。皆さんスペック高すぎです。参加したおかげで上がったテンションのまま、SBCLで遊んでみた。

1.SBCLにはネイティブコードコンパイラが備わっている。

2.CommonLispでは仕様でdisassemble関数が定義されている。

つまり、SBCLでバイト列を逆アセンブルことは簡単。たぶん。

ということでやってみた。

(defun disassemble-u8-list (u8-list)
(let ((len (length u8-list)))
(let ((alien (sb-alien:make-alien sb-alien:unsigned-char len)))
(unwind-protect
(progn
(loop :for i from 0
:for u in u8-list
:do (setf (sb-alien:deref alien i) u))
(sb-disassem:disassemble-memory (sb-alien:alien-sap alien) len))
(sb-alien:free-alien alien)))))

準備として、アセンブラで適当にコードを書く。

bits 32
section .text

global _start
_start:
mov eax, 4 ;write
mov ebx, 1 ;fd
mov ecx, ptr ;buf
mov edx, 6 ;size
int 0x80
mov eax, 1 ;exit
mov ebx, 0
int 0x80
ptr db 'Hello',10
>nasm -f elf32 test.asm
>ld -o test test.o
>./test
Hello
>nasm -f bin -o test.bin test.asm
>hd test.bin
00000000 b8 04 00 00 00 bb 01 00 00 00 b9 22 00 00 00 ba |..........."....|
00000010 06 00 00 00 cd 80 b8 01 00 00 00 bb 00 00 00 00 |................|
00000020 cd 80 48 65 6c 6c 6f 0a |..Hello.|
00000028

このバイト列を逆アセンブルしてみる。

>(disassemble-u8-list
'(#xb8 #x04 #x00 #x00 #x00 #xbb #x01 #x00 #x00 #x00
#xb9 #x22 #x00 #x00 #x00 #xba #x06 #x00 #x00 #x00
#xcd #x80 #xb8 #x01 #x00 #x00 #x00 #xbb #x00 #x00
#x00 #x00 #xcd #x80 #x48 #x65 #x6c #x6c #x6f #x0a))
; 08089CA8: B804000000 MOV EAX, 4
; AD: BB01000000 MOV EBX, 1
; B2: B922000000 MOV ECX, 34
; B7: BA06000000 MOV EDX, 6
; BC: CD80 INT 128
; BE: B801000000 MOV EAX, 1
; C3: BB00000000 MOV EBX, 0
; C8: CD80 INT 128
; CA: 48 DEC EAX
; CB: 65 GS-SEGMENT-PREFIX
; CC: 6C INSB
; CD: 6C INSB
; CE: 6F OUTSD
; CF: 0A00 OR AL, [EAX]
NIL

2回目のINT命令以降は文字列。objdumpの結果とくらべても間違いはなさそう。最後の0x0Aが0A00とされているが、メモリ上の次の値を読んでいそう。

2010年2月8日月曜日

SBCL1.0.35のlispobj

SBCLのsrc/runtimeディレクトリのC言語で記述された箇所を読む努力をしてみる。脳みそのスペックと英語力と注意力が無いので多々憶測を含む。

lispobjは名前のとおりlispのオブジェクトを表すものだと思われるが、こいつがどのように利用されているか見ていく。

N_WORD_BITS == 32とする(genesis/config.h)。 runtime.hやgenesis/constants.hあたりにタグなどの定義がある。

定義(runtime.h)

/* fake it on alpha32 */
typedef unsigned int lispobj;
#define LOW_WORD(c) ((long)(c) & 0xFFFFFFFFL)

LOWTAG

LOWTAG_MASKはconstants.h(自動生成される)で7と定義されている。 lispobjの下位3ビットを利用したタグ。

  • タグの値:定義されている定数の名前
  • 0:EVEN_FIXNUM_LOWTAG
  • 1:INSTANCE_POINTER_LOWTAG
  • 2:OTHER_IMMEDIATE_0_LOWTAG
  • 3:LIST_POINTER_LOWTAG
  • 4:ODD_FIXNUM_LOWTAG
  • 5:FUN_POINTER_LOWTAG
  • 6:OTHER_IMMEDIATE_1_LOWTAG
  • 7:OTHER_POINTER_LOWTAG

ビット0が立っていれば何らかのポインタである。

整数をN_FIXNUM_TAG_BITS(2)左にシフトすることで偶数ならEVEN_FIXNUM_LOWTAGが、奇数ならODD_FIXNUM_LOWTAGがLOWTAGにセットされる。なのでfixnumが表現できるのは29ビット30ビット。 OTHER_IMMEDIATE_x_LOWTAGはWIDETAGで何を示すのかを表すっぽい。

WIDETAG

WIDETAG_MASKが255と定義されているので、lispobjの下位8ビットがWIDETAG。 xxx_WIDETAGと定義されている定数の下位3ビットが2か6になっているので、LOWTAGが OTHER_IMMEDIATE_x_LOWTAGのときはWIDETAGで値の種類を判別するのだと思う。

メモ

lispobjとはとくに関係ないけど、runtime.hのQSHOW_SIGNALSを真にすると実行中にいろいろメッセージを吐くようになる。 make.shを実行すると、runtimeディレクトリにsbclという実行ファイルが作られる。 --core引数をつけずに実行すると、search_for_core関数でコアファイルを探そうとする。 コアファイルはcoreparse.cのload_core_file関数で読み込まれるが、その際コアファイルと実行ファイルのビルドIDが一致しなければnever_returns [
__attribute__((noreturn))]であるlose関数を呼び出す。

loseはわりといろんなところから呼ばれている。処理を続行できないエラーの場合に呼び出しているのだろう。

load_core_fileは最初に実行すべき関数のlispobjを返す。このlispobjを main関数の終わり付近でcreate_initial_thread関数の引数として利用する。