PowerPC64アセンブリ開発環境の整備

目的

PowerPC64アセンブリの演習のための環境を手元で構築する.

最新情報へのリンク

2015/01/11 以下のように事情が変化している。

前提

Ubuntu 13.10 (x86_64) を利用していると仮定する.

crosstool-ngの導入

必要なパッケージの準備
$ sudo apt-get install build-essential gperf texinfo gawk libtool automake libncurses5-dev
ソースコードを入手してビルド
$ wget http://crosstool-ng.org/download/crosstool-ng/crosstool-ng-1.19.0.tar.bz2
$ tar jxvf crosstool-ng-1.19.0.tar.bz2
$ cd crosstool-ng-1.19.0
$ ./configure
$ make
$ sudo make install
bash用の補完プラグインを入れる

入れなくても良い

sudo cp ct-ng.comp /etc/bash_completion.d/

PowerPC64用クロスコンパイラのビルド

必要なパッケージを入れる

これらはcross-gdbのビルドに必要(入れていないとビルドの最後のほうで失敗して泣く)

$ sudo apt-get install libexpat1-dev python2.7-dev
ソースを補完するディレクトリを掘る

デフォルトだとホームの下のsrcに設定されている.ディレクトリがないと保存されないので掘っておく

$ mkdir ~/src
作業ディレクトリを掘る
$ mkdir ppc64-toolchain
$ cd ppc64-toolchain
powerpc64-unknown-linux-gnu用に設定する
$ ct-ng powerpc64-unknown-linux-gnu
設定をいじる
$ ct-ng menuconfig

設定画面の使い方

  • 上下: 選択肢の移動
  • スペース: 選択肢の選択
  • 左右: <Select> / <Exit> / <Help> の間を移動
  • Enter: <Select> / <Exit> / <Help> のうち選択されているものを実行

推奨の設定: FortranJavaを外す

  • 上下キーで C compiler ---> に移動,スペースで中に入る
  • 上下キーで [*] Fortran に移動,スペースでチェックを消す
  • 上下キーで [*] Java に移動,スペースでチェックを消す
  • 左右キーで < Exit > を選択,Enterで実行→外に出る
  • 左右キーで < Exit > を選択,Enterで実行→設定終了

終了時に設定を保存するか聞かれるのでYesと答える

ビルド実行
$ ct-ng build

あとはひたすら待つと勝手にビルドとインストールを実行してくれる

インストール先はデフォルトでは次のような構成

~/x-tools
~/x-tools/powerpc64-unknown-linux-gnu
~/x-tools/powerpc64-unknown-linux-gnu/bin
~/x-tools/powerpc64-unknown-linux-gnu/include
~/x-tools/powerpc64-unknown-linux-gnu/lib
~/x-tools/powerpc64-unknown-linux-gnu/libexec
~/x-tools/powerpc64-unknown-linux-gnu/powerpc64-unknown-linux-gnu
~/x-tools/powerpc64-unknown-linux-gnu/share

例えば,binの中身は次のようになっている

powerpc64-unknown-linux-gnu-addr2line
powerpc64-unknown-linux-gnu-ar
powerpc64-unknown-linux-gnu-as
powerpc64-unknown-linux-gnu-c++
powerpc64-unknown-linux-gnu-cc
powerpc64-unknown-linux-gnu-c++filt
powerpc64-unknown-linux-gnu-cpp
powerpc64-unknown-linux-gnu-ct-ng.config
powerpc64-unknown-linux-gnu-embedspu
powerpc64-unknown-linux-gnu-g++
powerpc64-unknown-linux-gnu-gcc
powerpc64-unknown-linux-gnu-gcc-4.5.2
powerpc64-unknown-linux-gnu-gccbug
powerpc64-unknown-linux-gnu-gcov
powerpc64-unknown-linux-gnu-gdb
powerpc64-unknown-linux-gnu-gdbtui
powerpc64-unknown-linux-gnu-gprof
powerpc64-unknown-linux-gnu-ld
powerpc64-unknown-linux-gnu-ldd
powerpc64-unknown-linux-gnu-nm
powerpc64-unknown-linux-gnu-objcopy
powerpc64-unknown-linux-gnu-objdump
powerpc64-unknown-linux-gnu-populate
powerpc64-unknown-linux-gnu-ranlib
powerpc64-unknown-linux-gnu-readelf
powerpc64-unknown-linux-gnu-size
powerpc64-unknown-linux-gnu-strings
powerpc64-unknown-linux-gnu-strip

コンパイルする

PATHの追加

環境変数PATHを追加する.

$ export PATH=$HOME/x-tools/powerpc64-unknown-linux-gnu/bin:$PATH

これを毎回実行するのは面倒なので,~/.bashrcに追記する.

$ echo 'export PATH=$HOME/x-tools/powerpc64-unknown-linux-gnu/bin:$PATH' >> ~/.bashrc
サンプルソースコード

次のようなCソースコードをhello.cという名前で作成する.

#include <stdio.h>

int main() {
  puts("Hello, world!");
  return 0;
}
コンパイル

コンパイルする

$ powerpc64-unknown-linux-gnu-gcc hello.c
$ file a.out
a.out: ELF 64-bit MSB executable, 64-bit PowerPC or cisco 7500, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.31, not stripped

確かにPowerPC64用のバイナリである.

アセンブリソースの出力

アセンブリのソースを出力してみる

$ powerpc64-unknown-linux-gnu-gcc -S hello.c
$ cat hello.s
	.file	"hello.c"
	.section	".toc","aw"
	.section	".text"
	.section	.rodata
	.align 3
.LC0:
	.string	"Hello, world!"
	.section	".toc","aw"
.LC1:
	.tc .LC0[TC],.LC0
	.section	".text"
	.align 2
	.globl main
	.section	".opd","aw"
	.align 3
main:
	.quad	.L.main,.TOC.@tocbase,0
	.previous
	.type	main, @function
.L.main:
	mflr 0
	std 0,16(1)
	std 31,-8(1)
	stdu 1,-128(1)
	mr 31,1
	ld 3,.LC1@toc(2)
	bl puts
	nop
	li 0,0
	mr 3,0
	addi 1,31,128
	ld 0,16(1)
	mtlr 0
	ld 31,-8(1)
	blr
	.long 0
	.byte 0,0,0,1,128,1,0,1
	.size	main,.-.L.main
	.ident	"GCC: (crosstool-NG 1.19.0) 4.5.2"

確かにPowerPC64のアセンブリコードが出力されている.

実行

生成されたバイナリはPowerPC64用なのでそのままでは実行できない.

$ ./a.out
bash: ./a.out: cannot execute binary file

そこで,エミュレーターQEMUユーザーモードエミュレーション機能を用いる.

qemu-userのインストール
$ sudo apt-get install qemu-user
QEMU_LD_PREFIXの設定

このままでは先ほどのa.outを実行できない.というのも,依存しているライブラリがあり,QEMUがその場所を把握していないからである.

QEMU_LD_PREFIX環境変数を設定

$ export QEMU_LD_PREFIX=$HOME/x-tools/powerpc64-unknown-linux-gnu/powerpc64-unknown-linux-gnu/

これも毎回実行するのは面倒なので,~/.bashrcに追記しておく.

$ echo 'export QEMU_LD_PREFIX=$HOME/x-tools/powerpc64-unknown-linux-gnu/powerpc64-unknown-linux-gnu/' >> ~/.bashrc
a.outを実行してみる
$ qemu-ppc64 ./a.out
Hello, world!

無事実行できた!

デバッガの利用

powerpc64-unknwon-linux-gnu-gdb自体はPowerPC64コードを実行する機能をもたないようなので,qemu-ppc64で実行しているものを遠隔操作する方法を用いる.(リモートデバッグという)

デバッグオプションをつけてコンパイル

デバッガを使う上で-gをつけないと余りに不便.

$ powerpc64-unknown-linux-gnu-gcc -g hello.c
プログラムを実行

12345のところはポート番号なので,他の数字でもよい.このポートにgdbから接続する.

$ qemu-ppc64 -g 12345 ./a.out

こうすると,qemu-ppc64は待機状態になる.

デバッガを起動

別の端末を開いて,gdbを起動する.

$ powerpc64-unknwon-linux-gnu-gdb a.out

実行中のqemu-ppc64に接続する.

(gdb) target remote localhost:12345

これでデバッグの準備が整った.

デバッガを使う

ブレークポイントを設定してみる

(gdb) b main
Breakpoint 1 at 0x1000051c: file hello.c, line 4.

実行してみる

(gdb) c
Continuing.

Breakpoint 1, main () at hello.c:4
4	  puts("Hello, world!");

r (run) ではなくc (continue) であることに注意!

もう一回continueするとputsが実行されて終了する.このとき,qemu-ppc64を実行している端末でHello, world!が出力されるので確認すること.

(gdb) c
Continuing.
[Inferior 1 (Remote target) exited normally]

gdbを終了する.

(gdb) q