PowerPC
主要部分を抜粋します。
_start: # write(STDOUT_FILENO, message, MESSAGE_SIZE) li %r0, 4 # r0: write(2) syscall number. li %r3, 1 # r3: first argument. addis %r4, %r0, message@h # r4: second argument. ori %r4, %r4, message@l li %r5, MESSAGE_SIZE # r5: third argument. sc # exit(EXIT_SUCCESS) li %r0, 1 # r0: exit(2) syscall number. li %r3, 0 # r3: first argument. sc
MIPS
MIPSに移植しました。
_start: # write(STDOUT_FILENO, message, MESSAGE_SIZE) li $v0, 4 # v0: write(2) syscall number. li $a0, 1 # a0: first argument. lui $a1, %hi(message) # a1: second argument. ori $a1, $a1, %lo(message) li $a2, MESSAGE_SIZE # a2: third argument. syscall # exit(EXIT_SUCCESS) li $v0, 1 # v0: exit(2) syscall number. li $a0, 0 # a0: first argument. syscall
多少文法は異なりますが、処理内容はほとんど同じです。
動作確認
動作確認はgxemul上のNetBSD/evbmips環境で行いました。
# uname -a NetBSD 6.1.2 NetBSD 6.1.2 (MALTA) evbmips # gcc -nostdlib mips.s ld: warning: cannot find entry symbol __start; defaulting to 00000000004000f0 # file a.out a.out: ELF 32-bit LSB executable, MIPS, MIPS-I version 1 (SYSV), statically linked, for NetBSD 5.99.56, not stripped # ./a.out Hello, world
gxemulへのインストール方法などは以下を参照してください。
調査方法
MIPSのアセンブリには慣れていないので、書き方から調べました。即値やアドレスの代入方法はC言語で簡単なサンプルを書いて確認しました。
test.c
main() { int a = 0x12345678; const char *b = "abc"; }
アセンブリを出力します。gxemul上で処理するとファイル転送が面倒なため、Windows上でクロスコンパイラを動かしています。(gcc-4.5.4-msys-cross-mipsel-netbsd-6.1.tar.xz)
$ mipsel-netbsd-gcc -S test.c
出力されたtest.sを見ると、即値の代入部分はすぐ分かります。
li $2,305397760 # 0x12340000 ori $2,$2,0x5678 sw $2,8($fp)
直後に文字列の代入部分があります。
lw $2,%got($LC0)($28) nop addiu $2,$2,%lo($LC0) sw $2,12($fp)
GOT経由でポインタを取得しています。これはちょっと複雑なので、GOTを使わないように指示します。
$ mipsel-netbsd-gcc -S test.c -mno-abicalls
単純になりました。
lui $2,%hi($LC0) addiu $2,$2,%lo($LC0) sw $2,4($fp)
これを参考にサンプルを移植しました。
余談
バイナリ勉強会で最初に提示するサンプルとやっていることは同じです。
https://bitbucket.org/7shi/ikebin/wiki/pdp11/hello
/ write(1, hello, 6); mov $1, r0 sys write hello 6 / exit(0); mov $0, r0 sys exit .data hello: <hello\n>
NetBSDのサンプルの存在は後で教えてもらいました。最小限のハローワールドを書こうとするとwriteとexitだけになってしまうのは、自然な成り行きでしょうか。