FreeBSDでも、ふつパイラ
女房がこの間から、掃除機が壊れた々と言うので調べてみた。3年前に買ったやつだ。 そんなに早く壊れるかね? ソニータイマーなんて内蔵してない某PANA製だし。
女房からの訴えによると、掃除機じゃなくて、ごみを撒き散らすようになってしまったとの事。 Bugって、モーターが逆回転するようになった? まさかね!
説明書を引っ張り出してきて、調べてみても、ごみを撒き散らすようになった場合は、、、な んて故障事例は載っている訳がない。ふと、説明書をぱらぱら見てたら、紙フィルターを格納 するバスケットとやらが付いていない事に気が付いた。きっと、集塵したごみが隙間から漏れてしまって いるに違いないと予想。
こんな部品、補修用に取ってあるのかな? 半信半疑ながら、サポートセンターに電話して みたら、「ええ、ありますよ」との事。やっぱり、そそっかしい奥様向けのリクエストに 答えられるようになっているのね。(バスケット毎、紙フィルターを捨てちゃう、慌て者)
近くの電気店からオーダーして欲しいとの事で、チャリを飛ばして頼んできたら、次の日には 部品が届いてた。これにて、一件落着。
コンパイラ二題
最近、chickenとか、ふつパイラに凝っているせいか、コンパイラの記事に敏感になっている。
まずは、FreeBSDが将来、gcc から独立するかも? という話。gcc 4.2.2からは、GPLv3に ライセンスが変更されたとか。で、ベースシステムに取り込むのはいやだと言う話になり。。。 代替の有力候補として、LLVM Clangの試験が始まっているとか。ライセンスは勿論 BSD。 その他のGPLv3ライセンスのソフトも、ベースシステムには取り込まない方針とか。 これって、OpenBSDのあの人の影響かしらん。
もう一つの話題は、gaucheでx86アセンブラコードを出力するコンパイラ を開発中との情報。まだ、作業に取り掛かったばかりで、fibしかコンパイル出来ないそうですが 、開発者の方は、これからどういう方向を目指したらいいか、ちとお悩みの様子。 がんばってください。
ああ、コンパイラと言えば、ユニマガの最終巻が、coinsの特集だったような。手に入るうちに 仕入れておこう。
FreeBSDでも、ふつパイラ
前回は、ふつパイラをdebianでやった訳であるが、ふつパイラ本を読み進めている うちに、ひょっとしてFreeBSDでも動くんでないかいと、思えるようになってきた。 動くか早速やってみんべ。
動くか動かんかは、testが用意されているので調べるのは簡単。testじゅーよーって 事で、角谷宣教師さんに習ってみる。
[sakae@fb ~/cbc-1.0]$ make test cd test; make test ./run.sh ./run.sh:No such file or directory *** Error code 1 Stop in /home/sakae/cbc-1.0/test. *** Error code 1 Stop in /home/sakae/cbc-1.0.
ははは、いきなりのエラーだよ。run.shが無いってさ。run.shを調べてみたら、その 冒頭部分が、#!/bin/bashとなってた。FreeBSDのデフォshは、(t)csh なんだけど、 csh系は嫌いなので、/usr/local/bin/bash なんだよな。今後の事も考えて, リンク張ってごまかしておくか。
[sakae@fb ~/cbc-1.0]$ make test cd test; make test ./run.sh 01_exec..shunit[../bin/cbc ./zero.cb]: status 0 expected but was: 1 ..shunit[../bin/cbc ./one.cb]: status 0 expected but was: 1 02_print..shunit[../bin/cbc ./hello.cb]: status 0 expected but was: 1 ..shunit[../bin/cbc ./hello2.cb]: status 0 expected but was: 1 .^C
走り始めたけど、ことごとくエラーを背負っているなあ。どうもコンパイルを 失敗してるっぽいな。ちょっと、手打ちしてみっか。
[sakae@fb ~/cbc-1.0]$ cd test [sakae@fb ~/cbc-1.0/test]$ ../bin/cbc ./zero.cb readlink: illegal option -- f usage: readlink [-n] [file ...] Exception in thread "main" java.lang.NoClassDefFoundError: net/loveruby/cflat/compiler/Compiler Caused by: java.lang.ClassNotFoundException: net.loveruby.cflat.compiler.Compiler at java.net.URLClassLoader$1.run(URLClassLoader.java:200) at java.security.AccessController.doPrivileged(Native Method) at java.net.URLClassLoader.findClass(URLClassLoader.java:188) at java.lang.ClassLoader.loadClass(ClassLoader.java:306) at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:276) at java.lang.ClassLoader.loadClass(ClassLoader.java:251) at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:319)
ははは、出たな Java !! でも、その前にさりげなく出てるエラーの方が気になるぞ。 これはもう、../bin/cbc を見る鹿。多分、こやつは shell script だろう。
#!/bin/bash # cbc -- cflat compiler JAVA=${JAVA:-java} # cmd_path="$(readlink -f $0)" # srcdir_root="$(dirname "$(dirname "$cmd_path")")" srcdir_root=/home/sakae/cbc-1.0 "$JAVA" -classpath "$srcdir_root/lib/cbc.jar" \ net.loveruby.cflat.compiler.Compiler \ -I"$srcdir_root/import" \ -L"$srcdir_root/lib" \ "$@"
readlink -f って、FreeBSDには、無いけど、その意図は想像出来るよ。cbcのツリーが /usr/local 等の下あたりに置かれて、/usr/local/bin/cbc から、リンクされる。その リンクを追跡して、絶対PATHを返すんだな。後は、それを元に、dirnameを2回取って cbcのツリーPATHを求めてるのね。だから、先周りしちゃったよ。
[sakae@fb ~/cbc-1.0/test]$ ../bin/cbc ./zero.cb [sakae@fb ~/cbc-1.0/test]$
コンパイルは出来たな。ついでだから、ソースの確認と、実行もしてみっか。
[sakae@fb ~/cbc-1.0/test]$ ../bin/cbc ./zero.cb [sakae@fb ~/cbc-1.0/test]$ cat zero.cb int main(int argc, char **argv) { return 0; } [sakae@fb ~/cbc-1.0/test]$ ls -l zero* -rwxr-xr-x 1 sakae kuma 3373 10 6 10:43 zero* -rw-r--r-- 1 sakae kuma 50 12 24 2007 zero.cb -rw-r--r-- 1 sakae kuma 504 10 6 10:43 zero.o -rw-r--r-- 1 sakae kuma 175 10 6 10:43 zero.s [sakae@fb ~/cbc-1.0/test]$ cat zero.s .file "./zero.cb" .text .globl main .type main,@function main: pushl %ebp movl %esp, %ebp movl $0, %eax jmp .L0 .L0: movl %ebp, %esp popl %ebp ret .size main,.-main [sakae@fb ~/cbc-1.0/test]$ ./zero ELF interpreter /lib/ld-linux.so.2 not found Abort trap: 6 [sakae@fb ~/cbc-1.0/test]$
何もしないコードなんだけど、実行出来ないよ。ld-linux.so.2 って言われてもねぇ。 そんなの、ある訳ないっしょ。でも、FreeBSDと言えども、Tool chain は、rms系な 事は間違いないので、同等品を探せばいいんだな。
頭に ldが付くやつで、おいらが知ってるのは、ldd,ld.so あたりなので、ここらあたりを 手がかりに、man を手繰ってみた所、ld-elf.so.1 が、引っかかってきた。えぃ、こいつも、リンクしちゃ!
[sakae@fb ~]$ cd /lib [sakae@fb /lib]$ sudo ln -s /libexec/ld-elf.so.1 ld-linux.so.2
これをしたら、エラー無く、zeroが実行できたよ。うひょ。続けて、run.sh を実行。 時間がかかりそうだったので、LOGに落す事にした。
[sakae@fb ~/cbc-1.0/test]$ ./run.sh > LOG 2>&1
して、その結果と言えば
01_exec........ 02_print................................................................ 03_integer................ 04_funcall.................................... 05_string....stdout differ: string "-e ;;a;aa;b;";';\a\b\0033\f\n\r\t\v;ABCabc" and cmd "./string" --- tc.out.expected 2009-10-06 11:02:11.000000000 +0900 +++ tc.out.real 2009-10-06 11:02:11.000000000 +0900 @@ -1 +1,2 @@ --e ;;a;aa;b;";';\a\b\0033\f\n\r\t\v;ABCabc +;;a;aa;b;";';^[^L + ^K;ABCabc ---- ....stdout differ: string "-e ;;a;aa;b;";';\a\b\0033\f\n\r\t\v;ABCabc" and cmd " ./string" --- tc.out.expected 2009-10-06 11:02:13.000000000 +0900 +++ tc.out.real 2009-10-06 11:02:13.000000000 +0900 @@ -1 +1,2 @@ --e ;;a;aa;b;";';\a\b\0033\f\n\r\t\v;ABCabc +;;a;aa;b;";';^[^L + ^K;ABCabc ---- : 06_variables.................................................................... ....................................................shunit[../bin/cbc ./gvar.cb] : status 0 expected but was: 1 : 34_varargs..shunit[../bin/cbc ./varargs.cb]: status 0 expected but was: 1 35_invalidstmt...... 36_alloca..................................shunit[../bin/cbc -fPIE -pie alloca2. cb]: status 0 expected but was: 1 37_setjmp................ FAIL (7/1874 failed) -- fb.kuma.net / FreeBSD 6.4-STABLE i386
Stringのエラーはさておき、他のエラーをちょっとみてみる。
[sakae@fb ~/cbc-1.0/test]$ ../bin/cbc ./gvar.cb gvar.o(.text+0x7b): In function `main': : undefined reference to `stdin' cbc: error: /usr/bin/ld failed. (status 1) cbc: error: compile error [sakae@fb ~/cbc-1.0/test]$ ../bin/cbc ./varargs.cb varargs.o(.text+0x4b): In function `myprintf': : undefined reference to `stdout' cbc: error: /usr/bin/ld failed. (status 1) cbc: error: compile error [sakae@fb ~/cbc-1.0/test]$ ../bin/cbc -fPIE -pie alloca2.cb cbc: warning: alloca2.cb:16: unused variable: junk cbc: warning: alloca2.cb:28: unused variable: junk cbc: warning: alloca2.cb:41: unused variable: junk cbc: warning: alloca2.cb:54: unused variable: junk alloca2.s: Assembler messages: alloca2.s:198: Warning: setting incorrect section attributes for .text.__i686.get_pc_thunk.bx /usr/bin/ld: /usr/lib/Scrt1.o: No such file: No such file or directory cbc: error: /usr/bin/ld failed. (status 1) cbc: error: compile error
stdin,stdoutって、環境がらみ? Scrt1.oって、FreeBSDには、無いんだよ。これは 諦める鹿。
でも、
[sakae@fb ~/cbc-1.0/test]$ ls -l /usr/lib/*crt* -r--r--r-- 1 root wheel 2220 Sep 1 06:26 /usr/lib/crtbeginS.o -r--r--r-- 1 root wheel 1260 Sep 1 06:26 /usr/lib/crtendS.o -r--r--r-- 1 root wheel 1652 Sep 1 06:25 /usr/lib/gcrt1.o
怪しげなやつがあるんだよな。これのソースを読めば、何か分かるかも?
後、stdin,stdout は、stdio.hb に宣言されてるんだけど、取り込まれていないから エラーになるんだな。--dump-ast 付きでコンパイルしてみたけど、import は、現れ なかった。ちゃんと import を認識してるんかい? と思って、わざと importする ファイル名を変えたら、ちゃんとエラーになる。
[sakae@fb ~/cbc-1.0/test]$ ../bin/cbc varargs.cb cbc: error: no such library header file: Xstdio
ちゃんと見ててくれているよ。だったら、なぜにastに現れないん? 分かった積もり でも、やっぱり分かっていないや。
残りは、stringのエラーだな。テストコード(test_cbc.sh)を見ると
test_05_string() { assert_out "$(/bin/echo -e ';;a;aa;b;";'\'';\a\b\0033\f\n\r\t\v;ABCabc')" ./string
FrrBSD の echo コマンドに、-e なんてオプション無いんよ。ちなみに、-e って、バックスラッシュつきの文字を扱うみたい。って、事で、期待値が正しく扱われていないっぽい。 なお、テストの一方は、
[sakae@fb ~/cbc-1.0/test]$ cat string.cb import stdio; int main(int argc, char **argv) { printf(""); printf(";"); printf(";a"); printf(";aa;b"); printf(";\""); printf(";\'"); printf(";\a\b\e\f\n\r\t\v"); printf(";\101\102\103\141\142\143"); puts(""); return 0; }
こんなコードになってたよ。これって、エスケープシーケンスじゃ、ありませんか。 今、debian機も立ち上げたので、一応 stringの出力をdumpして貼っておく。
[sakae@fb ~/cbc-1.0/test]$ ./string >onBSD [sakae@fb ~/cbc-1.0/test]$ hd onBSD 00000000 3b 3b 61 3b 61 61 3b 62 3b 22 3b 27 3b 07 08 1b |;;a;aa;b;";';...| 00000010 0c 0a 0d 09 0b 3b 41 42 43 61 62 63 0a |.....;ABCabc.|
sakae@debian:~/cbc-1.0/test$ hexdump -C onLinux 00000000 3b 3b 61 3b 61 61 3b 62 3b 22 3b 27 3b 07 08 1b |;;a;aa;b;";';...| 00000010 0c 0a 0d 09 0b 3b 41 42 43 61 62 63 0a |.....;ABCabc.|
stdout,stdin ?
どうしても、上のstdoutやらが、どこからやってくるか調べておきたいのだ。 まずは、基準となる debian機で
sakae@debian:~/cbc-1.0/test$ ../bin/cbc -v varargs.cb as -o varargs.o varargs.s /usr/bin/ld -dynamic-linker /lib/ld-linux.so.2 /usr/lib/crt1.o /usr/lib/crti.o -L/home/sakae/cbc-1.0/lib varargs.o -lc -lcbc /usr/lib/crtn.o -o varargs
ふむ、*.cbをcbcでコンパイルすると、アセンブラソースが得られ、それをアセンブルすると *.oのオブジェクトが出てきて、そいつをリンカーを使って、ライブラリー類と結合するのか。
[sakae@fb ~/cbc-1.0/test]$ ../bin/cbc -v varargs.cb as -o varargs.o varargs.s /usr/bin/ld -dynamic-linker /lib/ld-linux.so.2 /usr/lib/crt1.o /usr/lib/crti.o -L/home/sakae/cbc-1.0/lib varargs.o -lc -lcbc /usr/lib/crtn.o -o varargs varargs.o(.text+0x4b): In function `myprintf': : undefined reference to `stdout' cbc: error: /usr/bin/ld failed. (status 1) cbc: error: compile error
リンカーのフェーズで失敗してるんだな。一応、varargs.oを確認しとく
sakae@debian:~/cbc-1.0/test$ cat varargs.s : movl 8(%ebp), %eax pushl %eax movl stdout, %eax pushl %eax call vfprintf : sakae@debian:~/cbc-1.0/test$ objdump -d varargs.o varargs.o: file format elf32-i386 Disassembly of section .text: : 46: 8b 45 08 mov 0x8(%ebp),%eax 49: 50 push %eax 4a: a1 00 00 00 00 mov 0x0,%eax 4f: 50 push %eax 50: e8 fc ff ff ff call 51 <myprintf+0x2a> :
movl stdout, %eax の、stdout が、リロケータブルファイルでは場所を開けてまってると いう事か。ちなみに、FreeBSDの varargs.o は、Debianのそれと全く同一のものが生成 されてたよ。まあ、ここまでは、問題無いわな。
で、リンクが済むと(実行形式になると)
sakae@debian:~/cbc-1.0/test$ objdump -d varargs varargs: file format elf32-i386 : 804835a: 8b 45 08 mov 0x8(%ebp),%eax 804835d: 50 push %eax 804835e: a1 60 95 04 08 mov 0x8049560,%eax 8048363: 50 push %eax 8048364: e8 6f ff ff ff call 80482d8 <vfprintf@plt> :
アドレスが確定されるんだな。ここで決まった(上の例では、0x8049560)やつは、何だ? ldが利用出来るようなヘッダーが付加されてるんだな。
sakae@debian:~/cbc-1.0/test$ readelf -s varargs | grep stdout 4: 08049560 4 OBJECT GLOBAL DEFAULT 19 stdout@GLIBC_2.0 (2) 52: 08049560 4 OBJECT GLOBAL DEFAULT 19 stdout@@GLIBC_2.0
そして、いよいよ実行時に
sakae@debian:~/cbc-1.0/test$ LD_DEBUG=reloc,symbols,bindings ./varargs 2>&1 | grep stdout 2521: symbol=_IO_stdout_; lookup in file=./varargs [0] 2521: symbol=_IO_stdout_; lookup in file=/lib/i686/cmov/libc.so.6 [0] 2521: binding file /lib/i686/cmov/libc.so.6 [0] to /lib/i686/cmov/libc.so.6 [0]: normal symbol `_IO_stdout_' [GLIBC_2.0] 2521: symbol=stdout; lookup in file=./varargs [0] 2521: binding file /lib/i686/cmov/libc.so.6 [0] to ./varargs [0]: normal symbol `stdout' [GLIBC_2.0] 2521: symbol=_IO_2_1_stdout_; lookup in file=./varargs [0] 2521: symbol=_IO_2_1_stdout_; lookup in file=/lib/i686/cmov/libc.so.6 [0] 2521: binding file /lib/i686/cmov/libc.so.6 [0] to /lib/i686/cmov/libc.so.6 [0]: normal symbol `_IO_2_1_stdout_' [GLIBC_2.1] 2521: symbol=stdout; lookup in file=/lib/i686/cmov/libc.so.6 [0] 2521: binding file ./varargs [0] to /lib/i686/cmov/libc.so.6 [0]: normal symbol `stdout' [GLIBC_2.0]
メモリー上で最終落ち着き先が決まる。(この解釈でいいんだろうか?)
FreeBSDでは、リンクに失敗してるので、勿論実行形式のファイルは作成されていない。その 原因は、libcに stdout が無い。いくら何でもそんな事は無いだろう。
疑問だ、疑問だ。 後は、どうやって調べよう?
思いついたぞ
sakae@debian:~/tmp$ cat test_stdout.c #include <stdio.h> main(){ fputs("Hello\n", stdout); }
こんなのを用意して、アセンブルしてみる。まずは、debian様から
sakae@debian:~/tmp$ cat test_stdout.s .file "test_stdout.c" .section .rodata .LC0: .string "Hello\n" .text .globl main .type main, @function main: leal 4(%esp), %ecx andl $-16, %esp pushl -4(%ecx) pushl %ebp movl %esp, %ebp pushl %ecx subl $20, %esp movl stdout, %eax movl %eax, 12(%esp) movl $6, 8(%esp) movl $1, 4(%esp) movl $.LC0, (%esp) call fwrite addl $20, %esp popl %ecx popl %ebp leal -4(%ecx), %esp ret .size main, .-main .ident "GCC: (Debian 4.3.2-1.1) 4.3.2" .section .note.GNU-stack,"",@progbits
fputsがfwriteに化けちゃったのは置いといて、Cのソース中に出てきた stdout は、その まま残っている。
一方、FreeBSDの方はどうかと言うと
[sakae@fb ~/tmp]$ cat test_stdout.s .file "test_stdout.c" .section .rodata .LC0: .string "Hello\n" .text .p2align 2,,3 .globl main .type main, @function main: pushl %ebp movl %esp, %ebp subl $8, %esp andl $-16, %esp movl $0, %eax addl $15, %eax addl $15, %eax shrl $4, %eax sall $4, %eax subl %eax, %esp subl $8, %esp pushl __stdoutp pushl $.LC0 call fputs addl $16, %esp leave ret .size main, .-main .ident "GCC: (GNU) 3.4.6 [FreeBSD] 20060305"
stdoutの名前が変形されちゃっているよ。変形した名前を受け付けるように、libcが調整 されてるから、ちゃんとリンクが出来ると。
今回は、アセンブラソースが、cbc経由でLinux用に出力されるんで、(FreeBSDの)libcとは 一致しない。その結果、エラーになったのね。いやー、勉強になるなあ。
果たして解決策はあるのか?
ちょいと、varargs.s の stdout を、__stdoutp に書き換えて、cbc varargs.s したら エラーなく、コンパイル&GO出来たよ。
FreeBSD の、stdio.h を見たら、#define stdout __stdoutp となってた。と言う事は stdio.hb にも、これを書いておくと良きに計らってくれるんでないかいと、やってみたら 青木さんが出てきて、怒られた。#define なんて仕様にないってば。
次は、cbcの何処かを改造して、sedでも挟んでやるか。まてまて、var名とぶつかったら どうする? うかつに出来ないよ。あれ? stdout って、予約語? 疑問が一杯。