NetBSDにはアセンブリ言語のサンプルが入っています。
PowerPC用ですが、MIPSに移植してみます。
主要部分を抜粋します。
_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
writeシステムコールとexitシステムコールを呼んでいます。
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)
これを参考にサンプルを移植しました。