MIPSのクロスコンパイラで実行ファイルの作成ってどうやるか
学生時代からわからなかったんですけど、ようやくわかったのでそのメモ。
結論からいうとリンカスクリプトを指定するだけで良いようです。
以下のようなプログラムをコンパイルしてみます。
実行ファイルの作成
#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
ホストマシンの性能のためか、わりと早いです。