日記/2010/01/13/jonesforthのgasによるコンパイルと"--build-id"オプションなど - Glamenv-Septzen.net

docs.komagata.orgの「ガチ鬱プログラマー日記」のRSSフィードを読んでいると、暫く前からjonesforthというアセンブラで書かれたForth処理系をLinuxのgasでコンパイルしようとして上手く行かなかったり、謎のオプションでエラーになったりトラブルに突き当たっている。

(...)

同じくアセンブラに手を出した人間として、捨てておくわけにはいかない。
というわけで自分もCentOS5.2(古っ)上でjonesforthのコンパイルに挑戦してみた。

ELFのbuild ID noteって何? - p0tというエントリにmsakamoto-sfさんからコメント頂きました。

タイトルの疑問にズバリ回答を示してくださって、衝撃を受けました。

ナレーション「この時、komagataに電流走る・・・っっっ!!」

この、「これは何なんだろう?」という疑問と「これはこういう仕組みになっている」というところがつながった時の快感は何物にも代え難いモノがあります。中毒、というか病気です。仕方ないね・・・。

ELFのbuild ID noteとは何か

最近のldから入った機能で、ビルド毎のバイナリを識別するためのIDのようです。msakamoto-sfさんが書かれているように、デバッグの時に重宝しそうな機能です。僕の環境はDebian lennyのGNU ld 2.18.0で、--build-idオプションに対応しているため、入れても入れなくても動きました。

% ld -v
GNU ld (GNU Binutils for Debian) 2.18.0.20080103

helloworld.sの-e mainオプションについて

helloworld.sをasでコンパイルするときに-e mainというエントリポイントを指定するオプションを使っていましたが、エントリポイントを_start(cで書かれたソースはこのエントリポイントにコンパイルされる)にしたら必要無かったです。多分デフォルトのエントリポイント名なんだと思います。

.code32
.text
.globl _start
_start:
(...)

helloworldの結果が1になってる件

ちょっと気になってたんですが、以前のhelloworldは実行した返り値が1なんですよね。エラーです。exit 1になっていたようなのでexit 0になるように変えました。

変更前:

  movl $1, %eax
int $0x80

変更後:

  movl $1, %eax
xor %ebx, %ebx # exit 0
int $0x80

ldの-Ttext 0オプションについて

これは僕も未だに謎です。

jonesforthのコメントにはjonesforthが学習に向いている点として素のPCから直接FORTHを立ち上げられることを上げています。(僕のショボイ英語力で間違ってなければ・・・)

jonesforth.S

You could boot such a FORTH on a bare PC and it would
come up with a prompt where you could start doing useful work.

その時にtextセクションが0から始まっている必要があったりするのかもしれません。

asでjonesforthのコンパイルが失敗する理由

僕もコメントが怪しいと踏んでjonesforthに使われている複数のコメントスタイルをhelloworld.sに入れてasでコンパイルしてみました。

.code32
.text
.globl _start
_start:
movl $4, %eax
movl $1, %ebx
movl $msg, %ecx
movl $len, %edx
int $0x80

# foo
/* bar */
// baz
movl $1, %eax # foo
xor %ebx, %ebx /* bar */
int $0x80 // buz

.data
msg: .string "Hello, world!\n"
msglen: len = msglen - msg

asでコンパイル。

% as -o helloworld.o helloworld.s
helloworld.s: Assembler messages:
helloworld.s:15: Error: junk `//buz' after register

どうやら命令の後に//スタイルのコメントを書いた場合だけasでは“余計なオペランドがある”みたいな感じでエラーになってしまうようです。そこだけ削除したらコンパイルできました。

gccを使わずにjonesforthをコンパイルする

上記を踏まえてcプリプロセッサ、as、ldを直接使ってコンパイルできるようにやっとなりました。

cppで余計なコメントを削除。恐らくcで使う#includeとか#defineも使えるんだと思います。でも.macro ... .endmも十分便利ですよね。

% cpp -v -o jonesforth.s jonesforth.S
Using built-in specs.
Target: i486-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Debian 4.3.2-1.1' --with-bugurl=file:///usr/share/doc/gcc-4.3/README.Bugs --enable-languages=c,c++,fortran,objc,obj-c++ --prefix=/usr --enable-shared --with-system-zlib --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --enable-nls --with-gxx-include-dir=/usr/include/c++/4.3 --program-suffix=-4.3 --enable-clocale=gnu --enable-libstdcxx-debug --enable-objc-gc --enable-mpfr --enable-targets=all --enable-cld --enable-checking=release --build=i486-linux-gnu --host=i486-linux-gnu --target=i486-linux-gnu
Thread model: posix
gcc version 4.3.2 (Debian 4.3.2-1.1)
COLLECT_GCC_OPTIONS='-v' '-E' '-o' 'jonesforth.s' '-mtune=generic'
/usr/lib/gcc/i486-linux-gnu/4.3.2/cc1 -E -lang-asm -quiet -v jonesforth.S -o jonesforth.s -mtune=generic -fno-directives-only
ignoring nonexistent directory "/usr/local/include/i486-linux-gnu"
ignoring nonexistent directory "/usr/lib/gcc/i486-linux-gnu/4.3.2/../../../../i486-linux-gnu/include"
ignoring nonexistent directory "/usr/include/i486-linux-gnu"
#include "..." search starts here:
#include <...> search starts here:
/usr/local/include
/usr/lib/gcc/i486-linux-gnu/4.3.2/include
/usr/lib/gcc/i486-linux-gnu/4.3.2/include-fixed
/usr/include
End of search list.
COMPILER_PATH=/usr/lib/gcc/i486-linux-gnu/4.3.2/:/usr/lib/gcc/i486-linux-gnu/4.3.2/:/usr/lib/gcc/i486-linux-gnu/:/usr/lib/gcc/i486-linux-gnu/4.3.2/:/usr/lib/gcc/i486-linux-gnu/:/usr/lib/gcc/i486-linux-gnu/4.3.2/:/usr/lib/gcc/i486-linux-gnu/
LIBRARY_PATH=/usr/lib/gcc/i486-linux-gnu/4.3.2/:/usr/lib/gcc/i486-linux-gnu/4.3.2/:/usr/lib/gcc/i486-linux-gnu/4.3.2/../../../../lib/:/lib/../lib/:/usr/lib/../lib/:/usr/lib/gcc/i486-linux-gnu/4.3.2/../../../:/lib/:/usr/lib/
COLLECT_GCC_OPTIONS='-v' '-E' '-o' 'jonesforth.s' '-mtune=generic'

プリプロセッサを通したので普通にasでコンパイル出来る。

% as -o jonesforth.o jonesforth.s

普通にlinkする。-m elf_i386は無くても大丈夫でした。-nostdlibもそもそも使ってないからサイズも変わりませんでした。-staticも動的ライブラリを使ってないので関係無いみたいです。

% ld -o jonesforth jonesforth.o

strip -s jonesforthで3.6KBぐらいになりました。

% wc -c jonesforth
12983 jonesforth
% strip -s jonesforth
% wc -c jonesforth
3672 jonesforth

ちゃんと動くのでshell上から使う分には問題無さそうです。

% cat jonesforth.f - | ./jonesforth
JONESFORTH VERSION 47
14499 CELLS REMAINING
OK 1 2 + .
3

今の困り事

msakamoto-sfさんが示してくださったように、build ID noteもldのinfoにバッチリ載っていました。しかし、info全体の検索方法が分からない・・・。これは我ながら情けない。

Debianのinfoの罠

gccやcppやgdbのドキュメントはnon-freeなのでmainだけでやってたときハマりました。商用UNIX関係の記述がその辺から来ているのが原因なんでしょうか。インストールしてるとvrmsにも怒られてしまいます。

Comments


Option