ぽんこつメモ

https://github.com/kanorimon

PDP-11のインタプリタを作った話をする

何のエントリ?

池袋バイナリ勉強会でPDP-11のバイナリをインタプリタ実行するプログラム(以下、インタプリタ)を作った記録です。
間違いや思い込み、より良いやり方があったらご指摘いただけると助かります。
作ったものはkanorimon/pdp11 · GitHubにあります。

誰?

一応動くプログラムが作れる程度の文系SE。

勉強会に参加した発端

パタヘネ読み始めてMIPSで詰み、偶然見つけた池袋バイナリ勉強会へ。

どんな感じでバイナリハックしてたか

  1. 勉強会(だいたい隔週日曜午後)
    • カリキュラムに合わせた課題をもらう
    • 課題や周辺知識を説明してもらう
    • わからない部分を質問しながら課題をもくもく
  2. 自宅
    • わからない部分をメモしながら課題をもくもく
  3. 参考書

もくもくでやってたこと

  1. PDP-11の仕様書とV6本を見ながら、命令とシステムコールを実装する。
  2. インタプリタを実行する。
  3. 期待する結果が出なかった場合、実行ログ(命令実行時に命令・レジスタ・フラグを出力したもの)を出力する。
  4. @7shiさん作成のインタプリタ(7run)の実行ログと見比べ、未実装の箇所や実装のミスを見つける。

バイナリハックのカリキュラム

  1. Brainf*ckのインタプリタ作成
  2. PDP-11の逆アセンブラ作成
  3. PDP-11のインタプリタ作成
  4. UNIX V6のカーネルビルド

詳しくは池袋バイナリ勉強会wiki7shi / ikebin / wiki / Home — Bitbucket)を参照。
次からカリキュラムに沿ってやったことと、わかったことを書いていく。

Brainf*ckのインタプリタ作成

Brainf*ckの以下のコードを読み込んで、標準出力に"HellowWorld"を出力するプログラムを作成した。

++++++++[>+++++++++<-]>.
<+++++[>++++++<-]>-.
<++[>+++<-]>+.
.
+++.
<++++++[>----<-]>.
<++++++[>++++<-]>.
+++.
------.
--------.

わかったこと

  • CPU
    • CPUは0と1(電圧の高低)しか認識できない
  • メモリ
    • メモリ≒byte配列
  • バイナリ

PDP-11の逆アセンブラ作成

以下のアセンブリコンパイルしたPDP-11のバイナリ(a.out)を読み込んで、標準出力に逆アセンブル結果を出力するプログラムを作成した。(単純な逆アセンブラ

mov $1, r0
sys write
hello
6

mov $0, r0
sys exit

.data
hello: <hello\n> 

以下のアセンブリコンパイルしたPDP-11のバイナリ(a.out)を読み込んで、標準出力に逆アセンブル結果を出力するプログラムを作成した。(アドレス指定方式の取り込み)

mov $1, r0
sys write
hello
6

mov $hello, r0
mov $62510, r1
mov r1, (r0)
mov $1, r0
sys write
hello
6

mov $0, r0
sys exit

.data
hello: <hello\n>

わかったこと

PDP-11のインタプリタ作成

以下のアセンブリコンパイルしたPDP-11のバイナリ(a.out)を読み込んで、標準出力に"hello"を出力するプログラムを作成した。(単純なインタプリタ

mov $1, r0
sys write
hello
6

mov $0, r0
sys exit

.data
hello: <hello\n>

以下のアセンブリコンパイルしたPDP-11のバイナリ(a.out)を読み込んで、標準出力に"Hello"を出力するプログラムを作成した。(アドレス指定方式の取り込み)

mov $1, r0
sys write
hello
6

mov $hello, r0
mov $62510, r1
mov r1, (r0)
mov $1, r0
sys write
hello
6

mov $0, r0
sys exit

.data
hello: <hello\n>

以下のソースをコンパイルしたPDP-11のバイナリ(a.out)を読み込んで、結果を標準出力に出力するプログラムを作成した。(各種命令の実装、システムコールの実装)

main(){
    write(1, "hello\n", 6);
}
main(){
    putchar('a');
}
main(){
    printf("hello\n");
}
main(){
    int a;
    a = 1234;
    printf("a=%d\n", a);
}

以下のソースをコンパイルしたPDP-11のバイナリ(a.out)を読み込んで、結果を標準出力に出力するプログラムを作成した。(コマンドライン引数の実装)

main(argc, argv){
    char **argv;
{
    int i;
    for(i = 0; i < argc; i++){
        printf("argv[%d]=%s\n", i, argv[i]);
}

以下のソースをコンパイルしたPDP-11のバイナリ(a.out)を読み込んで、結果を標準出力に出力するプログラムを作成した。(関数呼び出しの実装)

main(){
    printo(012345);
}

printo(v){
    int i;
    putchar(v < 0 ? '1' : '0');
    for(i = 0; i < 5; i++){
        putchar(((v >> 12) & 7) + '0');
    }
}


PDP-11のバイナリ(nm)を読み込んで、標準出力にオブジェクトファイルのシンボルリストを出力するプログラムを作成した。(各種命令の実装、システムコールの実装)

# nm a.out

PDP-11のバイナリ(cc)を読み込んで、Cのソースをコンパイルするプログラムを作成した。(更に命令の種類とシステムコールが増える)

# cc test.c

わかったこと

  • バイナリ
    • 原則として、1命令目から順番に処理される
  • UNIX V6
    • カーネル
    • プロセス
    • ユーザー空間とカーネル空間は異なる
    • プロセスごとに仮想アドレス空間を持ち、MMUがメモリの物理アドレスに変換する
    • 仮想アドレス空間はテキスト/空き領域(マジックナンバ0410の場合)/データ+bss/ヒープ/空き領域/スタックとなっている
    • ファイルはinodeで管理されている
    • ファイル入出力等の基本機能はシステムコールを呼び出すことで実現している
    • 関数呼び出しをする場合は、スタックに引数と戻り先のアドレスを格納してからジャンプする
  • システムコール
    • exit()
    • read()
    • write()
    • open()
    • close()
    • creat()
    • link()
    • unlink()
    • chmod()
    • brk()
    • stat()
    • lseek()
    • getpid()
    • dup()
    • signal()
  • コンパイラ
  • 実装方法
    • ccがライブラリを呼び出す場合、何も対応をしないと/lib,/bin,/tmpを見に行ってしまうので、インタプリタで呼び出したい/lib,/bin,/tmpの場所を指定できるようにする必要がある

UNIX V6のカーネルビルド

UNIX V6のカーネルのソースを読み込んで、コンパイルするプログラムを作成した。

わかったこと

  • システムコール
    • fork()
    • exec()
    • wait()
  • Java
    • Stringの処理はコストがかかるので、できるだけ使わない方が高速
    • インスタンスをnewするとコストがかかるので、できるだけnewせずに使いまわす方が高速

総括

  • できるようになったこと
    • OSが何をやっているのか、なんとなくわかるようになった。
    • コンパイルの手順(コンパイラアセンブラ⇒リンカ)を覚えられた。
    • アセンブリ言語の雰囲気がつかめるようになった。
    • セキュリティ系の本(HACKING本とか)を読めるようになった。
  • まだできないこと
    • アセンブリ言語をすらすら読めるわけではない。
    • 2進数からの直翻訳はできない。(是枝くん@王様達のヴァイキングはすごい)
    • C言語アセンブリの対応はよくわからない。
  • 感じたこと
    • 自作インタプリタのバグを見つけるのは正直しんどかった。まさに苦行。
    • なんとか完成できたのは、@7shiさんをはじめ、要所要所でアドバイスいただいた池袋バイナリ勉強会の方々のおかげです。ありがとうございます。

最後に

バイナリに興味がある方へ

素人でも半年くらいあればなんとかなるので、バイナリ・アセンブラコンパイラ等々に興味がある方はぜひ池袋へどうぞ!

今後の話

コンピュータの仕組みとかパフォーマンスを勉強するのがすごく面白かったので、今年の春から大学生(2回目)してきます。
低レイヤとかセキュリティとかもっと知りたいです。
池袋バイナリ勉強会にはこれからもお世話になります。よろしくお願いします!