○顛末 ×瑣末

漢字難しすぎわろた。
そんな瑣末なことは、(ry


結果→クロスコンパイラのビルドに成功しました
ここまでのまとめ:

  • x86-Linux-64bitのサンプルに設定
  • C++/Java/Fortranはいらないので消す
  • Debug系の項目はいらないしエラーが出るので消す
  • ct-ng build
  • 1〜2時間くらい待つ


特に設定した覚えはないけど、どうやらホームディレクトリに勝手にできていたx-toolsの中に入っているようだ。クロスコンパイルツールなのでくろす・つーるずと読んであげよう。x-tools/x86_64-unknown-linux-gnu/binに

x86_64-unknown-linux-gnu-addr2line*
x86_64-unknown-linux-gnu-ar*
x86_64-unknown-linux-gnu-as*
x86_64-unknown-linux-gnu-cc@
x86_64-unknown-linux-gnu-c++filt*
x86_64-unknown-linux-gnu-cpp*
x86_64-unknown-linux-gnu-ct-ng.config*
x86_64-unknown-linux-gnu-gcc*
x86_64-unknown-linux-gnu-gcc-4.4.3*
...

gccもあるしこれっぽいのでここにパスを通す。


test.c

#include <stdio.h>

#define PSIZE(type) printf("%16s: %u\n", #type, sizeof(type))

int main() {
        PSIZE(char);
        PSIZE(short);
        PSIZE(int);
        PSIZE(long);
        PSIZE(long long);
        PSIZE(float);
        PSIZE(double);
        PSIZE(void *);
        return 0;
}

なんかインデントの空白多すぎないか。Emacsの設定ェ…。正直言って上の方いらない。とりあえずいつもの32bit-gccコンパイルしていつもの結果を確認。次にx86_64-unknown-linux-gnu-gccコンパイル。%uがunsigned intを要求するが、sizeof(type)がunsigned long intだと警告が出た。%uを%luにしたら、今度はsizeof(type)がunsigned intだと32bitの方で出た。無理ゲーなのでスルー*1
…と同時に、「cannot find /lib64/libc.so.6」とエラーを吐いて死んだ。libcはCライブラリで、.soはshared object、WindowsでいうDLLみたいなもんで、どっかに生成されてはいるはずだがリンカが見つけられなかった系なので、-staticオプションで静的ライブラリを使うようにしたら耐えた(理由になってねーぞ)。

% ./a.out
./a.out: Exec format error. Wrong Architecture.

残当。32bitマシンで64bitの機械語が動くわけがなかった。が、これでクロスコンパイラが正常動作している可能性は上がった。仕方ないので-Sオプションでアセンブラを吐かせる。ポインタのサイズが8バイトになっているのを確認。また、longが8バイトというのも特徴的だ。


あとは渡されたドライバのMakefileをいじってこっちのgccを使うようにするだけだが…。

ifneq ($(KERNELRELEASE),)
        obj-m := rmtproc.o
else
        KERNELDIR ?= /lib/modules/$(shell uname -r)/build
        PWD     := $(shell pwd)

default:
        $(MAKE) -C $(KERNELDIR) M=$(PWD) modules

……。なにこれ。
調べたところによるとこれLinuxデバイスドライバをmakeするときの決まり文句みたいなもんらしい…。makeの中からmakeコマンドに丸投げですか。-Cでデバイスドライバ用のMakefileとヘッダとかいろいろがあるLinuxソースのディレクトリへカレントディレクトリを移動し、変数Mにソースコードがあるディレクトリ(←pwd)を設定し、modulesターゲットを実行するらしい。いつもはgccが入ってる$(CC)を変えてやればいいと思っていた時期が私にもありました。
どうもこのLinuxデバドラ用Makefileの流儀に従わなければならないらしく、なんとか決まり文句を発見。ARCHとCROSS_COMPILEにうまく真似して設定してやる。

default:
    $(MAKE) ARCH=x86 CROSS_COMPILE=x86_64-unknown-linux-gnu-
        -C $(KERNELDIR) M=$(PWD) modules

$(KERNELDIR)をなんかソースがインクルードしてるlinux/pci.hとかがあるx-tools内の場所に変えようとしたけど、一番大事そうなlinux/init.hやlinux/module.hが無い上に、もちろんMakefileもないので断念。カーネルのヘッダをインストールとか言ってたから期待してたのに…。仕方ない、64bitLinuxのソースを落としてくるか…と思ったら別にbitはおろかCPUに関わらず共通であった。そりゃそうだった。じゃあ別にこの32bitPCに入ってるヘッダとか使って大丈夫じゃね?????とKERNELDIRは32bitマシンの、コンパイラはx86-64bit用でmakeぶっぱ。
…………。
make通って.ko*2できちゃった…。


しかしググってたら出てきたふざけたドライバ*3もかなり普通の(?)形で32/64bitLinux対応って書いてあったから、実はドライバの64bit移植って(Linuxのバージョンが同じなら)ヘッダや型や構造体の変更とかそういうのはなかった疑惑。ねえ…普通に64bitマシン用意してmakeぱなせばCROSS_COMPILEとかこんな面倒なことなかったよね…。まあしかし、ハードウェアに対してunsigned long(→64bit Linuxでは8バイト)のアドレス値を32bit書き込み関数で書き込んでたりしてるようだから、そういうのを洗う必要はありそう。ハードウェアでn bitとか決まってる系ならshort int longでなくて、サイズ明示の何かに変更すべきかなあ。
本当にそれだけでいいのなら、合計1000行弱の関数を一つずつ丁寧に見ていくだけの、程よい作業になりそう。元々そんなに急を要するものじゃないし、
もうこれ以上は64bitの実機でやれ。


もう二度と無駄にクロスコンパイルなんてしないよ。
結論:オライリー本(Linux デバイスドライバ)くらい手元にないときつくね?(研究室にあった気はする)まあソースがあればある程度は耐える。

*1:注:size_tは%zdでprintfできるらしい。size_tがらみではCライブラリやSTLの返すsize_tをunsigned intで受け取ったりしているコードが64bitで死ぬ例が散見されるようなので注意だ。

*2:カーネルオブジェクト。カーネルコードの一部として実行される特別なオブジェクトファイル。付け外しが可能。

*3:読み取るとルイズコピペがランダムに出てくる狂った装置。こういう簡単な例のソースが公開されてるとものすごく役に立つからすごく困る。