/ «2004-04-27 (Tue) ^ 2004-04-30 (Fri)» ?
   西田 亙の本:GNU 開発ツール -- hello.c から a.out が誕生するまで --

Categories Books | Hard | Hardware | Linux | MCU | Misc | Publish | Radio | Repository | Thoughts | Time | UNIX | Writing | プロフィール


2004-04-29 (Thu)

[Writing] Linux徹底詳解再訪 続報・その1

読者の方々からの質問

先日、インターフェース 2002年7月号の特集「Linux徹底詳解」の内容について、読者の方からメールを頂きました。内容は「Decompressing kernel... の部分でハングアップしてしまう」というもので、これまでにも同様のメールを他の方々からも頂いています。

発表後、2年近く立っているにもかかわらず、私の記事を辿りながら Linux 起動の仕組みを探求して頂いていることに感謝すると共に、全くフォローアップ出来ていないことを猛省・・。

ということで、「Linux徹底詳解」の世界を、このゴールデンウィークの間に再度探訪してみたいと思います。私自身もこの2年間で新しい知見を手に入れましたし、Linux もカーネル 2.6 で様変わりしています。ページ数の関係で、雑誌上では十分解説できていない部分もありましたので、この機会に補講しておくことにしましょう。「ツッコミ」も表示可能にしましたので、不明な点などありましたら、ドシドシ書き込んでやってください。

それでは、参りましょう!

インターフェース 2002年7月号未購入の方々へ

2002年7月号は、発売後間もなく完売になってしまいました。お読みになりたい方は、図書館や、CD-ROM版インターフェースなどをご利用頂ければと思います。ちなみに、CQ出版はコピーサービスも行っていますが、本特集の場合、総額8000円を超えてしまうことになります・・。

サイズ・プレフィックス

プログラムの作成に入る前に、特集記事で触れることが出来なかった「サイズ・プレフィックス」について補講しておきます。

x86 がリアルモードとプロテクトモードという、2つの顔を持っていることはよく知られていますが、これとは別に x86 には「16 bit mode / 32 bit mode」というモードが存在します。

具体的にリアルモードで説明してみましょう。当然のことながら、リアルモード上は 16 ビットの世界です。オペランド値もレジスターも 16 ビット。セグメント機構を使っても、たかだか 20 ビットまでしかアクセスすることは出来ません。

・・というのが、巷の解説本に書かれている内容ですが、これは真実について半分しか言及していません。余り知られていませんが、「リアルモード上でも 32 ビットレジスタにはアクセスできる」のです。「うっそ〜〜〜!」という声が聞こえてきそうですが、ホンマです。ちなみに、逆もまた真なりで、プロテクトモード上で 16 ビットレジスターを操作することも可能です。

命令コードの共有化

さて、ここからが面白いところです。「ふたつのビット長モードが存在するということは、16 ビット/32 ビットのそれぞれに対応する2種類の命令コードが存在する」ということになります。確認してみましょう。次のような、簡単な mov1.s を用意してください。

movl    0x12345678, %eax

これは、32 ビットの EAX レジスターに 32 ビット即値を転送するための命令です(32 ビット長のため、mov 命令に l (Long) suffix が後置されている点に注意)。それでは、アセンブルです。

$ as -o mov1.o mov1.s
$ objdump -D mov1.o

mov1.o:     file format elf32-i386

Disassembly of section .text:

00000000 <.text>:
   0:   a1 78 56 34 12          mov    0x12345678,%eax
Disassembly of section .data:
$ wc -c mov1.o
    449 mov1.o

全部で5バイトの機械語にアセンブルされました。即値の EAX レジスターへの転送命令は A1、その後ろに 0x12345678 が Little-endian 型式で格納されていることが分かります。

ところで、生成された mov1.o オブジェクトファイルのサイズを見てみると、449バイトあります。444バイトも水増しされていることになりますが、この原因は GCC プログラミング工房の読者の方々にはお馴染みの ELF (Executable and Linking Format)の冗長性にあります。「そんな無駄、ワシは許さ〜〜ん!」という方は、次の命令を実行あれ。

$ objcopy -O binary mov1.o mov1.bin
$ wc -c mov1.bin
      5 mov1.bin
$ hexdump -C mov1.bin 
00000000  a1 78 56 34 12                                    |.xV4.|
00000005

objcopy コマンドを使って、ELF オブジェクトファイル形式を単なるバイナリー型式に変換しています。-O (Output target) オプションがミソです。余分な情報はバッサリ切り捨てられ、丸坊主状態ですが、もう一度逆アセンブルして、その中身を確かめてみましょう。

$ objdump -D mov1.bin
objdump: mov1.bin: File format not recognized

「ファイル形式が認識できない」という、エラーメッセージが表示されてしまいました。それもそのはず、mov1.bin には、実行コード以外には付帯情報が全く添付されていません。

このような場合のために、objdump には -b オプションが用意されています。これは BFD name の略であり、ファイル形式を指定するためのオプションです。mov1.bin はバイナリー型式ですから、binary を指定してください。

$ objdump -b binary -D mov1.bin

mov1.bin:     file format binary

objdump: Can't disassemble for architecture UNKNOWN!

少し進みましたが、今度は「アーキテクチャが分からん」と言っています。全く世話の焼けるやつですが、アーキテクチャを指定するためのオプションとして -m (Machine)が用意されています。

$ objdump -b binary -m i386 -D mov1.bin

mov1.bin:     file format binary

Disassembly of section .data:

00000000 <.data>:
   0:   a1 78 56 34 12          mov    0x12345678,%eax

お〜〜〜、5バイトの羅列が、見事逆アセンブルされました!こういう芸当は、統合開発環境では到底不可能です。さすがは、我らが GNU 開発ツール。

さて、ここで私からのちょっとした宿題です。次のコマンドを実行してみてください。

$ objdump -b binary -m i8086 -D mov1.bin

mov1.bin:     file format binary

Disassembly of section .data:

00000000 <.data>:
   0:   a1 78 56                mov    0x5678,%ax
   3:   34 12                   xor    $0x12,%al

i8086 というのは、名前から類推できる通り、x86 の 16 ビットアーキテクチャを意味しています。この逆アセンブルリストは、実に深い意味を孕んでいるのですが、私達の目の前で一体何が起こったのでしょうか?各自、一晩ゆっくり考えてみてください。