低レイヤ教育研究会2015-11-08作業メモ

前回まで

とりあえず頂いたお題のBrainf*ckを作ったので、次のお題に入ることにした。

Visual Brainf*ck JS

次のお題は、逆アセンブラを作ること。

アセンブラを作るための作業工程

  • ファイル内容を16進数で表示できること
  • Cross-Compiler環境を構築すること
  • 以下、続く...

ファイル内容を16進数で表示できること

どのCPU/OS向けの逆アセンブラを作るにしても、ファイル内容の16進数表示は必要。 まずはファイルを読んで、16進数で標準出力へ出力するプログラムを書いた。

github.com

Cross-Compiler環境を構築すること

Cross-Compiler環境を構築することの意義

アセンブラを作るときに、Cross-Compiler環境を構築することの意義を確認します。 どのCPU用逆アセンブラを作るか選ぶときに、作成の難易度が現実的なことを大切にしました。 現実的なところを狙うと、最新のCPUと比較して古いCPUの方が、仕様が小さくていいだろうとなります。 では古いCPU用実行ファイルの逆アセンブラを作りましょうとなると、お手本や分析の対象になる、古いCPU用実行ファイルが欲しいところです。 ですが現代的な開発環境では、古いCPU用へのコンパイルができません。 その古いCPU用実行ファイルを自前で整えられるようにするのが、Cross-Compiler環境を構築する意義です。

作業環境

Mac OS X EI Capitan 10.11.1 x86_64

作業記録

こちらを参考に環境構築をしました。

qiita.com

$ # 作業用ディレクトリ作成

$ mkdir cross
$ cd cross

$ # ソースの取得と展開

$ curl -O http://ftp.gnu.org/gnu/binutils/binutils-2.25.tar.bz2
$ curl -O http://ftp.gnu.org/gnu/gcc/gcc-4.6.4/gcc-core-4.6.4.tar.bz2
$ tar xvf binutils-2.25.tar.bz2
$ tar xvf gcc-core-4.6.4.tar.bz2

$ # ビルド用ディレクトリ作成

$ mkdir powerpc-elf
$ cd powerpc-elf/

$ # binutilsをビルド

$ mkdir binutils
$ cd binutils/
$ ../../binutils-2.25/configure --prefix=/opt/cross --target=powerpc-elf
$ make -j2
$ sudo make install

$ # gccをビルド

$ cd ..
$ mkdir gcc
$ cd gcc
$ ../../gcc-4.6.4/configure --prefix=/opt/cross --target=powerpc-elf
configure: error: Building GCC requires GMP 4.2+, MPFR 2.3.1+ and MPC 0.8.0+.
Try the --with-gmp, --with-mpfr and/or --with-mpc options to specify
their locations.  Source code for these libraries can be found at
their respective hosting sites as well as at
ftp://gcc.gnu.org/pub/gcc/infrastructure/.  See also
http://gcc.gnu.org/install/prerequisites.html for additional info.  If
you obtained GMP, MPFR and/or MPC from a vendor distribution package,
make sure that you have installed both the libraries and the header
files.  They may be located in separate packages.

$ # エラーになった。エラーメッセージや参考記事にもある依存ライブラリをインストール

$ brew install gmp
$ brew install xz ; # brew info mpfrのDependenciesを見ると要りそうなので導入
$ brew install mpfr
$ brew install libmpc
$ brew info gmp
gmp: stable 6.0.0a (bottled)
GNU multiple precision arithmetic library
https://gmplib.org/
/usr/local/Cellar/gmp/6.0.0a (15 files, 3.2M) *
  Poured from bottle
From: https://github.com/Homebrew/homebrew/blob/master/Library/Formula/gmp.rb
==> Options
--32-bit
    Build 32-bit only
--c++11
    Build using C++11 mode
$ brew info xz
xz: stable 5.2.1 (bottled)
General-purpose data compression with high compression ratio
http://tukaani.org/xz/
/usr/local/Cellar/xz/5.2.1 (59 files, 1.6M) *
  Poured from bottle
From: https://github.com/Homebrew/homebrew/blob/master/Library/Formula/xz.rb
==> Options
--universal
    Build a universal binary
$  brew info mpfr
mpfr: stable 3.1.3 (bottled)
C library for multiple-precision floating-point computations
http://www.mpfr.org/
/usr/local/Cellar/mpfr/3.1.3 (24 files, 3.6M) *
  Poured from bottle
From: https://github.com/Homebrew/homebrew/blob/master/Library/Formula/mpfr.rb
==> Dependencies
Build: xz ✔
Required: gmp ✔
==> Options
--32-bit
    Build 32-bit only
$ brew info libmpc
libmpc: stable 1.0.3 (bottled)
C library for the arithmetic of high precision complex numbers
http://multiprecision.org
/usr/local/Cellar/libmpc/1.0.3 (10 files, 380K) *
  Poured from bottle
From: https://github.com/Homebrew/homebrew/blob/master/Library/Formula/libmpc.rb
==> Dependencies
Required: gmp ✔, mpfr ✔

$ # gccをビルド再挑戦

$ # --with-xxx=の指定は、brew info xxxの内容を参照した
$ ../../gcc-4.6.4/configure --prefix=/opt/cross --target=powerpc-elf --with-gmp=/usr/local/Cellar/GMP/6.0.0a --with-mpfr=/usr/local/Cellar/libmpc/1.0.3 --with-mpc=/usr/local/Cellar/mpfr/3.1.3
$ make -j2 all-gcc
$ sudo make install-gcc
$ /opt/cross/bin/powerpc-elf-gcc --version
powerpc-elf-gcc (GCC) 4.6.4
Copyright (C) 2011 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

$ # ビルドしたgccでCompileしてみるテスト

$ cd ..
$ mkdir test
$ cd test
$ cat add.c ; # ソースはある前提
int add(int a, int b) {
    return a + b;
}
$ export PATH="/opt/cross/bin:$PATH"
$ powerpc-elf-gcc -nostdlib -g -O add.c
/opt/cross/lib/gcc/powerpc-elf/4.6.4/../../../../powerpc-elf/bin/ld: 警告: エントリシンボル _start が見つかりません。デフォルトとして 0000000001800054 を使用します
$ powerpc-elf-objdump -S a.out

a.out:     ファイル形式 elf32-powerpc


セクション .text の逆アセンブル:

01800054 <add>:
int add(int a, int b) {
    return a + b;
}
 1800054:   7c 63 22 14     add     r3,r3,r4
 1800058:   4e 80 00 20     blr

$ # できたようだ

参考

http://wiki.osdev.org/GCC_Cross-Compiler

[追記] 同じことを--target=vax-netbsdelfでもやってみた

$ mkdir vax-netbsdelf
$ cd vax-netbsdelf

$ mkdir binutils
$ cd binutils
$ ../../binutils-2.25/configure --prefix=/opt/cross --target=vax-netbsdelf
$ make -j2
$ sudo make install

$ cd ..
$ mkdir gcc
$ cd gcc
$ ../../gcc-4.6.4/configure --prefix=/opt/cross --target=powerpc-elf --with-gmp=/usr/local/Cellar/GMP/6.0.0a --with-mpfr=/usr/local/Cellar/libmpc/1.0.3 --with-mpc=/usr/local/Cellar/mpfr/3.1.3
$ make -j2 all-gcc
$ sudo make install-gcc

$ cd ..
$ mkdir test
$ cd test
$ export PATH="/opt/cross/bin:$PATH"
$ cat add.c
int add(int a, int b) {
    return a + b;
}
$ vax-netbsdelf-gcc -nostdlib -g -O add.c
/opt/cross/lib/gcc/vax-netbsdelf/4.6.4/../../../../vax-netbsdelf/bin/ld: 警告: エントリシンボル _start が見つかりません。デフォルトとして 0000000000010054 を使用します
$ vax-netbsdelf-objdump -S a.out

a.out:     ファイル形式 elf32-vax


セクション .text の逆アセンブル:

00010054 <add>:
int add(int a, int b) {
   10054:   00 00           .word 0x0000 # Entry mask: < >
   10056:   c2 04 5e        subl2 $0x4,sp
    return a + b;
}
   10059:   c1 ac 04 ac     addl3 0x4(ap),0x8(ap),r0
   1005d:   08 50 
   1005f:   04              ret

参考

d.hatena.ne.jp