【お知らせ】プログラミング記事の投稿はQiitaに移行しました。

NetBSDでMIPSのハローワールド

NetBSDにはアセンブリ言語のサンプルが入っています。

PowerPC用ですが、MIPSに移植してみます。

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

writeシステムコールとexitシステムコールを呼んでいます。

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だけになってしまうのは、自然な成り行きでしょうか。