home ホーム search 検索 -  login ログイン  | reload edit datainfo version cmd icon diff delete  | help ヘルプ

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

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

日記 / 2010 / 01 / 13 / jonesforthのgasによるコンパイルと"--build-id"オプションなど
id: 551 所有者: msakamoto-sf    作成日: 2010-01-13 02:06:46
カテゴリ: Assembler 

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

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

ldとgasのバージョンは以下の通り:

$ ld --version
GNU ld version 2.17.50.0.6-6.el5 20061020
...
$ as --version
GNU assembler 2.17.50.0.6-6.el5 20061020
...

jonesforthのサイトURLは以下:

"Download"の"jonesforth.s.txt"をローカルにダウンロードし、"jonesforth.S"にリネームする。
ソースコードを見てみると、FORTH処理系の概要や実装方法、gasアセンブラの簡単な説明からコンパイルオプションまで至れりつくせりなドキュメントが埋め込まれている。
・・・これでコンパイルできないって、かなり涙目。

ということで、ソースコード中で指示されている通りにgccを動かしてみる。

$ gcc -m32 -nostdlib -static -Wl,-Ttext,0 -Wl,--build-id=none -o jonesforth jonesforth.S
/usr/bin/ld: unrecognized option '--build-id=none'
/usr/bin/ld: use the --help option for usage information
collect2: ld はステータス 1 で終了しました

・・・涙目。

binutilsのldコマンドが "--build-id" オプションを認識出来なくて失敗している。p0tの記事でも調査されたようだが、うまく解決できていないようだ。

Google先生の助けを借りて、ldや"--build-id"について検索していく内に、やはりldコマンドのバージョンが若干古いらしいことと、"--build-id"オプションは指定しなくても問題ない事が分かってきた。

まずGNU binutils本家のサイトを調べてみると、バージョン2.18で"--build-id"オプションが追加された旨がldのNEWSファイルに記されていた。

Changes in 2.18:

* Linker sources now released under version 3 of the GNU General Public
  License.

* ELF: New --build-id option to generate a unique per-binary identifier
  embedded in a note section.
...

GNU binutils本家から辿れる、最新2.20のldのinfoファイルを見てみる。

--build-id
--build-id=style
    Request creation of .note.gnu.build-id ELF note section. 
    ...

ファイルを識別する為のユニークなビットフィールドを ".note.gnu.build-id" というELFのnoteセクションに埋め込む。
styleには以下の値を指定出来る。

"uuid" 128 random bits
"sha1" 160-bit SHA1 hash
"md5" 128-bit MD5 hash
"0x" + hexdigits 任意の16進数

styleが省略された場合は "sha1" を指定したものと見なされる。
特別なstyleとして "none" を指定すると、"--build-id" の機能を無効化する。

"--build-id"を調べていると、最近のLinuxディストリビューションで"--build-id"の有効化についてのドキュメントが見つかった。

".note.gnu.build-id"セクションは実行時にプロセスイメージにロードされる。もしkernelがサポートしていればコアダンプファイルに含まれるようになる。
OpenSUSEやFedoraProjectでこの機能を使うようになった背景として、FedoraProjectのページの"1.1 Summary"に以下の2点が挙げられている。(OpenSUSEの方も似たような事情のようだ)

  • コアダンプから、正しいバージョンのバイナリを探せるようにする
  • デバッグ情報のチェック処理の最適化 - ファイル全体のチェックサム計算などを回避する為。

前掲のFedoraProjectのページを見てみると

The Linux binutils-2.17.50.0.17 release includes this, in f8test1.
binutils-2.17.50.0.17-3 and later in Rawhide have the feature.

というような記述があるが、バックポートでもされたのだろうか。

いずれにせよ、自分が使っているバージョンのld(2.17.50.0.6)ではサポートされていないのは確実である。

jonesforthに戻ると、以上の情報から "-Wl,--build-id=none" オプションはそもそも"--build-id"機能を無効化している。よって、"--build-id"オプションをサポートしていないバージョンのldでは指定する必要が無いと予想される。

実際にこのオプションを削った状態でコンパイルに挑戦してみる。

$ gcc -m32 -nostdlib -static -Wl,-Ttext,0  -o jonesforth jonesforth.S
$ echo $?
0
$ file jonesforth
jonesforth: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), statically linked, not stripped

コンパイルに成功した。

続けてjonesforthのサイトからサンプルスクリプトファイルをダウンロードし、実行してみる。以下のコマンドサンプルがjonesforth.Sファイル中のドキュメントにあったので、試してみた。

$ cat jonesforth.f - | ./jonesforth
強制終了

強制終了してしまった。試しにjonesforth単体で動かしてみる。

$ ./jonesforth
強制終了
$ echo $?
137

ぱっと見、次に怪しいのはこのオプションである。

-Wl,-Ttext,0

ldのヘルプを見てみると

-Ttext ADDRESS              Set address of .text section

とあるし、infoファイルにも

`-Ttext ORG'
     Same as -section-start, with `.bss', `.data' or `.text' as the
     SECTIONNAME.

とある。0が指定されているのだから、".text"セクションの開始アドレスを"0"にする意味になる。

"--save-temps", "-v"オプションをつけて途中生成物の保存&gccドライバの詳細メッセージを表示させる。

$ gcc -v --save-temps -m32 -nostdlib -static -Wl,-Ttext,0  -o jonesforth jonesforth.S
Using built-in specs.
Target: i386-redhat-linux
...
 as -V -Qy -o jonesforth.o jonesforth.s
GNU assembler version 2.17.50.0.6-6.el5 (i386-redhat-linux) using BFD version 2.17.50.0.6-6.el5 20061020
 /usr/libexec/gcc/i386-redhat-linux/4.1.2/collect2 \
     -m elf_i386
     --hash-style=gnu
     -static
     -o jonesforth
     -L/usr/lib/gcc/i386-redhat-linux/4.1.2
     -L/usr/lib/gcc/i386-redhat-linux/4.1.2
     -L/usr/lib/gcc/i386-redhat-linux/4.1.2/../../..
     -Ttext 0
     jonesforth.o

$ ls
jonesforth*  jonesforth.S  jonesforth.f  jonesforth.o  jonesforth.s

$ readelf -S jonesforth
There are 8 section headers, starting at offset 0x1d04:

Section Headers:
  [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al
  [ 0]                   NULL            00000000 000000 000000 00      0   0  0
  [ 1] .text             PROGBITS        00000000 001000 0005ba 00  AX  0   0  4
  [ 2] .rodata           PROGBITS        000005bc 0015bc 0006d0 00   A  0   0  4
  [ 3] .data             PROGBITS        00001c8c 001c8c 000044 00  WA  0   0  4
  [ 4] .bss              NOBITS          00002000 001cd0 003000 00  WA  0   0 4096
  [ 5] .shstrtab         STRTAB          00000000 001cd0 000034 00      0   0  1
  [ 6] .symtab           SYMTAB          00000000 001e44 001650 10      7  40  4
  [ 7] .strtab           STRTAB          00000000 003494 000dcc 00      0   0  1
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings)
  I (info), L (link order), G (group), x (unknown)
  O (extra OS processing required) o (OS specific), p (processor specific)

$ readelf -h jonesforth
ELF Header:
  Magic:   7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
  Class:                             ELF32
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              EXEC (Executable file)
  Machine:                           Intel 80386
  Version:                           0x1
  Entry point address:               0xe
  Start of program headers:          52 (bytes into file)
  Start of section headers:          7428 (bytes into file)
  Flags:                             0x0
  Size of this header:               52 (bytes)
  Size of program headers:           32 (bytes)
  Number of program headers:         2
  Size of section headers:           40 (bytes)
  Number of section headers:         8
  Section header string table index: 5

".text"セクションがアドレス0から開始され、エントリポイントのアドレスが0xeになっている。".text"セクションを逆アセンブルしてみる。

$ objdump -d -S -j .text jonesforth | less
jonesforth:     file format elf32-i386

Disassembly of section .text:

00000000 <DOCOL>:
   0:   8d 6d fc                lea    0xfffffffc(%ebp),%ebp
   3:   89 75 00                mov    %esi,0x0(%ebp)
   6:   83 c0 04                add    $0x4,%eax
   9:   89 c6                   mov    %eax,%esi
   b:   ad                      lods   %ds:(%esi),%eax
   c:   ff 20                   jmp    *(%eax)

0000000e <_start>:
   e:   fc                      cld
   f:   89 25 98 1c 00 00       mov    %esp,0x1c98
  15:   bd 00 40 00 00          mov    $0x4000,%ebp
  1a:   e8 7e 05 00 00          call   59d <set_up_data_segment>
  1f:   be bc 05 00 00          mov    $0x5bc,%esi
  24:   ad                      lods   %ds:(%esi),%eax
  25:   ff 20                   jmp    *(%eax)
...

gdbでデバッグしてみる。

$ gdb jonesforth
GNU gdb Red Hat Linux (6.5-37.el5_2.2rh)
...

(gdb) b _start
Breakpoint 1 at 0xe
(gdb) run
Starting program: /home/msakamoto/in_vitro/lang.asm/jonesforth/jonesforth
Couldn't get registers: そのようなプロセスはありません.

・・・なーんか、そろそろ深みに嵌りそうな気がしてきた・・・。

ちょっと "-Wl,-Ttext,0" による影響の詳細追跡は置いておき、とにかく怪しいオプションなのだから削ってみる。

$ gcc -v -m32 -nostdlib -static -o jonesforth jonesforth.S
Using built-in specs.
Target: i386-redhat-linux
...
 as -V -Qy -o /tmp/ccgxvdKK.o /tmp/ccEEORGU.s
GNU assembler version 2.17.50.0.6-6.el5 (i386-redhat-linux) using BFD version 2.17.50.0.6-6.el5 20061020
 /usr/libexec/gcc/i386-redhat-linux/4.1.2/collect2 
     -m elf_i386
     --hash-style=gnu
     -static
     -o jonesforth
     -L/usr/lib/gcc/i386-redhat-linux/4.1.2
     -L/usr/lib/gcc/i386-redhat-linux/4.1.2
     -L/usr/lib/gcc/i386-redhat-linux/4.1.2/../../..
     /tmp/ccgxvdKK.o

$ readelf -S jonesforth
There are 8 section headers, starting at offset 0xd78:

Section Headers:
  [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al
  [ 0]                   NULL            00000000 000000 000000 00      0   0  0
  [ 1] .text             PROGBITS        08048074 000074 0005ba 00  AX  0   0  4
  [ 2] .rodata           PROGBITS        08048630 000630 0006d0 00   A  0   0  4
  [ 3] .data             PROGBITS        08049d00 000d00 000044 00  WA  0   0  4
  [ 4] .bss              NOBITS          0804a000 000d44 003000 00  WA  0   0 4096
  [ 5] .shstrtab         STRTAB          00000000 000d44 000034 00      0   0  1
  [ 6] .symtab           SYMTAB          00000000 000eb8 001650 10      7  40  4
  [ 7] .strtab           STRTAB          00000000 002508 000dcc 00      0   0  1
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings)
  I (info), L (link order), G (group), x (unknown)
  O (extra OS processing required) o (OS specific), p (processor specific)

$ readelf -h jonesforth
ELF Header:
  Magic:   7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
  Class:                             ELF32
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              EXEC (Executable file)
  Machine:                           Intel 80386
  Version:                           0x1
  Entry point address:               0x8048082
  Start of program headers:          52 (bytes into file)
  Start of section headers:          3448 (bytes into file)
  Flags:                             0x0
  Size of this header:               52 (bytes)
  Size of program headers:           32 (bytes)
  Number of program headers:         2
  Size of section headers:           40 (bytes)
  Number of section headers:         8
  Section header string table index: 5

$ objdump -d -S -j .text jonesforth | less

jonesforth:     file format elf32-i386

Disassembly of section .text:

08048074 <DOCOL>:
 8048074:       8d 6d fc                lea    0xfffffffc(%ebp),%ebp
 8048077:       89 75 00                mov    %esi,0x0(%ebp)
 804807a:       83 c0 04                add    $0x4,%eax
 804807d:       89 c6                   mov    %eax,%esi
 804807f:       ad                      lods   %ds:(%esi),%eax
 8048080:       ff 20                   jmp    *(%eax)

08048082 <_start>:
 8048082:       fc                      cld
 8048083:       89 25 0c 9d 04 08       mov    %esp,0x8049d0c
 8048089:       bd 00 c0 04 08          mov    $0x804c000,%ebp
 804808e:       e8 7e 05 00 00          call   8048611 <set_up_data_segment>
 8048093:       be 30 86 04 08          mov    $0x8048630,%esi
 8048098:       ad                      lods   %ds:(%esi),%eax
 8048099:       ff 20                   jmp    *(%eax)
...

いつも見慣れた"0x80480.."近辺のアドレスに"_start"シンボルが移動した。雰囲気的にイケそうな感じがするので、試してみる。

$ ./jonesforth
(Ctrl-D)
$ echo $?
0

イケそうなので、サンプルスクリプトに再挑戦してみる。

$ cat jonesforth.f - | ./jonesforth
JONESFORTH VERSION 47
14499 CELLS REMAINING
OK
$ echo $?
0

・・・ようやく動いた・・・。

ところが話はここで終わっていない。以下の記事によると、そもそもgasの時点でエラーが沢山表示されてしまうとのこと。

$ as -o jonesforth.o jonesforth.S

実際に試してみたところ、同様に大量のエラーメッセージが表示された。
しかし・・・".S"ファイルを直接asに食べさせているのが気になる。もう一度、上でコンパイル&実行に成功したオプションでのgccの詳細を見てみる。

$ gcc -v -m32 -nostdlib -static -o jonesforth jonesforth.S
Using built-in specs.
Target: i386-redhat-linux
...
 /usr/libexec/gcc/i386-redhat-linux/4.1.2/cc1 -E -lang-asm -quiet -v jonesforth.S -m32 -mtune=generic -o /tmp/ccFU9F94.s
存在しないディレクトリ "/usr/lib/gcc/i386-redhat-linux/4.1.2/../../../../i386-redhat-linux/include" を無視します
#include "..." の探索はここから始まります:
#include <...> の探索はここから始まります:
 /usr/local/include
 /usr/lib/gcc/i386-redhat-linux/4.1.2/include
 /usr/include
探索リストの終わり
 as -V -Qy -o /tmp/ccC32TCa.o /tmp/ccFU9F94.s
GNU assembler version 2.17.50.0.6-6.el5 (i386-redhat-linux) using BFD version 2.17.50.0.6-6.el5 20061020
 /usr/libexec/gcc/i386-redhat-linux/4.1.2/collect2 (省略)

一旦"cc1", プリプロセッサを通してその出力をasに食わせている。

試しに手動で同じコマンドを実行してみる。まずプリプロセッサの結果を"jonesforth.asm"に保存してみる。

$ cpp -E -lang-asm -quiet -v jonesforth.S -m32 -mtune=generic -o jonesforth.asm
Using built-in specs.
cpp: unrecognized option '-quiet'
Target: i386-redhat-linux
...

 /usr/libexec/gcc/i386-redhat-linux/4.1.2/cc1 -E -lang-asm -quiet -v jonesforth.S -o jonesforth.asm -m32 -mtune=generic
存在しないディレクトリ "/usr/lib/gcc/i386-redhat-linux/4.1.2/../../../../i386-redhat-linux/include" を無視します
#include "..." の探索はここから始まります:
#include <...> の探索はここから始まります:
 /usr/local/include
 /usr/lib/gcc/i386-redhat-linux/4.1.2/include
 /usr/include
探索リストの終わり
cpp: -lang-asm: リンクが完了しなかったのでリンカの入力ファイルは使われませんでした

$ cat jonesforth.asm
# 1 "jonesforth.S"
# 1 "<built-in>"
# 1 "<コマンドライン>"
# 1 "jonesforth.S"
(...)
 .set JONES_VERSION,47
# 306 "jonesforth.S"
 .macro NEXT
 lodsl
 jmp *(%eax)
 .endm
(...)

うまく動いたようだ。続いてこれを、gasに入力してみる。

$ as -V -Qy -o jonesforth_asm.o jonesforth.asm
GNU assembler version 2.17.50.0.6-6.el5 (i386-redhat-linux) using BFD version 2.17.50.0.6-6.el5 20061020

$ echo $?
0

$ file jonesforth_asm.o
jonesforth_asm.o: ELF 32-bit LSB relocatable, Intel 80386, version 1 (SYSV), not stripped

ここまでも無事動いているようだ。最後に、リンクしてみる。

$ ld \
     -m elf_i386 \
     --hash-style=gnu \
     -static \
     -o jonesforth_byhand \
     ./jonesforth_asm.o

$ cat jonesforth.f - | ./jonesforth_byhand
JONESFORTH VERSION 47
14499 CELLS REMAINING
OK

$ echo $?
0

問題ないようだ。

jonesforthのソースコードでは、

/* ... */

形式のコメントが大量に使われている。一方、msakamoto-sf自身がこれまで経験してきたアセンブラのコメント形式は

#...

形式だった。

恐らく

/* ... */

形式のコメントを除去する為にプリプロセッサを通す必要があり、そのため、直接asに入力してもエラーとなってしまう。それが、"asでコンパイル出来ない"問題の原因(の、少なくとも一つ)であることが予想される。


プレーンテキスト形式でダウンロード
現在のバージョン : 1
更新者: msakamoto-sf
更新日: 2010-01-13 02:27:55
md5:a3e14d47766830d815191eab160beb6f
sha1:01b200c2ed207701b2c747a29fb558f7ccd7b816
コメント
コメントを投稿するにはログインして下さい。