MIPSクロスコンパイラで実行ファイルの作成

MIPSのクロスコンパイラで実行ファイルの作成ってどうやるか
学生時代からわからなかったんですけど、ようやくわかったのでそのメモ。

結論からいうとリンカスクリプトを指定するだけで良いようです。
以下のようなプログラムをコンパイルしてみます。

準備

GCC 4.6 MIPS クロスコンパイラを作る - Life is very short

で作成したクロスコンパイラを使います。

実行ファイルの作成

#include <stdio.h>
int main(void)
{
    printf("hello world\n");
    return 0;
}

コンパイルします。

  % mipsel-linux-elf-gcc -T idt.ld hello.c

エラーが出ずにコンパイル、リンクができましたので実行します。

  % mipsel-linux-elf-run a.out
  hello world

となりました。

リンカスクリプト

GNU ld(普通これを使っていると思いますが)を使っている場合は
以下のコマンドでデフォルトのリンカスクリプトを確認することができます

  % ld --verbose
  % mipsel-linux-elf-ld --verbose

正直何が問題なのかはよくわからないですけど、mipsのクロスコンパイラの
場合 libc、libgccとのリンク、ランタイムルーチン(crt0.o)とのリンクを
明示的に書いてやる必要があるようです。デフォルトのリンカスクリプト
にはそれらの指定がないので実行ファイルを生成できませんでした。


idt.ld(今回は ${インストールディレクトリ}/mipsel-linux-elf/libにある)
を見るとそのあたりが明示的に示されています。いろいろなリンカスクリプト
同じディレクトリにあるのですが、これが一番うまくいってます。


見た感じどれもそこまで違わなくてテキスト領域の開始アドレスが違う
程度のように見えます。ユーザランドなんでよほど変な位置に置かない限り
大丈夫なんじゃないかなって思うんだけど、微妙な差が見られました。
シミュレータ側(GDB run)の問題かもしれないですが、詳しいところは
わかっていません。

もう少し複雑なプログラムでテスト

N-Queenは動きました。ものによっては unmapな領域を触った等のエラーが
出て実行できませんでした。printしたり、計算ゴリゴリっていうのであれば
おそらく動くんじゃないかなと思います。

#include <stdio.h>

static void nqueen(int n, int now, int *board, int *answer)
{
    int i, j;

    for (i = 0; i < n; i++) {
        int flag = 1;

        for (j = 0; j < now; j++) {
            if ((board[j] == i)
                || (board[j] - (now - j) == i)
                || (board[j] + (now - j) == i)) {
                flag = 0;
            }
        }

        if (flag) {
            board[now] = i;
            if (now + 1 == n) {
                (*answer)++;
            } else {
                nqueen(n, now + 1, board, answer);
            }
        }
    }
}

#ifndef PROBLEMS
#define PROBLEMS 10
#endif

int main (void)
{
    int board[PROBLEMS];
    int answer[PROBLEMS];
    int ans = 0, i;

    for (i = 0; i < PROBLEMS; i++) {
        nqueen(i + 1, 0, board, &ans);
        answer[i] = ans;
        ans = 0;
    }

    for (i = 0; i < PROBLEMS; i++) {
        printf("answer %2d = %8d\n", i + 1, answer[i]);
    }
    return 0;
}

runは引数が渡せないっぽいので、コンパイル時に定数を埋め込んでやる
方法をとっています。


コンパイル & 実行は以下の通り

  % mipsel-linux-elf-gcc -T idt.ld -O2 nqueen.c
  % mipsel-linux-elf-run a.out
  answer  1 =        1
  answer  2 =        0
  answer  3 =        0
  answer  4 =        2
  answer  5 =       10
  answer  6 =        4
  answer  7 =       40
  answer  8 =       92
  answer  9 =      352
  answer 10 =      724

ホストマシンの性能のためか、わりと早いです。

まとめ

MIPSロスコンパイラで実行ファイルを作成する方法を示しました。
spimなんかでもいいかと思うのですが、やっぱり printfとかできると
嬉しいものですね。