Universal eXecutable Format - 01

  • (by K, 2016.09.27)

COFFとの比較

  • まず、ベースとなるソースプログラムを提示します。
    int uxfdl_printf(const char *fmt, ...);
    
    int entry(int argc, const char **argv)
    {
        uxfdl_printf("hello\n");
        return 0;
    }
  • たったこれだけの128バイトのプログラムです。
  • これをgcc 3.4.5で普通にコンパイルすると(=リンクはしない)、以下の500バイトのCOFFファイルが得られます。
     offset   +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +A +B +C +D +E +F  0123456789ABCDEF
    ---------------------------------------------------------------------------
    00000000  4C 01 04 00 00 00 00 00 F8 00 00 00 0D 00 00 00  L...............
    00000010  00 00 04 01 2E 74 65 78 74 00 00 00 00 00 00 00  .....text.......
    00000020  00 00 00 00 20 00 00 00 B4 00 00 00 E4 00 00 00  .... ...........
    00000030  00 00 00 00 02 00 00 00 20 00 00 60 2E 64 61 74  ........ ..`.dat
    00000040  61 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  a...............
    00000050  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
    00000060  40 00 00 C0 2E 62 73 73 00 00 00 00 00 00 00 00  @....bss........
    00000070  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
    00000080  00 00 00 00 00 00 00 00 80 00 00 C0 2E 72 64 61  .............rda
    00000090  74 61 00 00 00 00 00 00 00 00 00 00 10 00 00 00  ta..............
    000000A0  D4 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
    000000B0  40 00 00 40 55 89 E5 68 00 00 00 00 E8 00 00 00  @..@U..h........
    000000C0  00 C9 31 C0 C3 90 90 90 90 90 90 90 90 90 90 90  ..1.............
    000000D0  90 90 90 90 68 65 6C 6C 6F 0A 00 00 00 00 00 00  ....hello.......
    000000E0  00 00 00 00 04 00 00 00 0A 00 00 00 06 00 09 00  ................
    000000F0  00 00 0C 00 00 00 14 00 2E 66 69 6C 65 00 00 00  .........file...
    00000100  00 00 00 00 FE FF 00 00 67 01 68 65 6C 6C 6F 2E  ........g.hello.
    00000110  63 00 00 00 00 00 00 00 00 00 00 00 5F 65 6E 74  c..........._ent
    00000120  72 79 00 00 00 00 00 00 01 00 20 00 02 01 00 00  ry........ .....
    00000130  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
    00000140  2E 74 65 78 74 00 00 00 00 00 00 00 01 00 00 00  .text...........
    00000150  03 01 11 00 00 00 02 00 00 00 00 00 00 00 00 00  ................
    00000160  00 00 00 00 2E 64 61 74 61 00 00 00 00 00 00 00  .....data.......
    00000170  02 00 00 00 03 01 00 00 00 00 00 00 00 00 00 00  ................
    00000180  00 00 00 00 00 00 00 00 2E 62 73 73 00 00 00 00  .........bss....
    00000190  00 00 00 00 03 00 00 00 03 01 00 00 00 00 00 00  ................
    000001A0  00 00 00 00 00 00 00 00 00 00 00 00 2E 72 64 61  .............rda
    000001B0  74 61 00 00 00 00 00 00 04 00 00 00 03 01 07 00  ta..............
    000001C0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
    000001D0  00 00 00 00 04 00 00 00 00 00 00 00 00 00 20 00  .............. .
    000001E0  02 00 12 00 00 00 5F 75 78 66 64 6C 5F 70 72 69  ......_uxfdl_pri
    000001F0  6E 74 66 00                                      ntf.
  • 128バイトから500バイトになるなんて!
  • 中身は見ての通りゼロばかりでスカスカです。
  • この中には32バイトの.textセクションと、0バイトの.dataセクションと16バイトの.rdataセクションと0バイトの.bssセクションがあります。
  • 128バイトのソースから作るからスカスカなんだという反論は可能で、だからもっと長いソースから作ればスカスカ率は改善するかもしれないです。
    • 手元にあるobj2uxf.cのコンパイルで実験したところ、15.9KBのソースに対して6.52KBのCOFFファイルでした。
  • こうなった原因の一つに、asがあまり優秀ではないということがあります。無駄なパディングが多すぎるのです。これはasがダメなのであって、COFFの仕様のせいではないです。
  • というこで、gccにアセンブラソースを出力させて、gas2naskを使ってnask向けソースに変換し、objを作ってみました。
     offset   +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +A +B +C +D +E +F  0123456789ABCDEF
    ---------------------------------------------------------------------------
    00000000  4C 01 03 00 00 00 00 00 B8 00 00 00 0A 00 00 00  L...............
    00000010  00 00 00 00 2E 74 65 78 74 00 00 00 00 00 00 00  .....text.......
    00000020  00 00 00 00 11 00 00 00 93 00 00 00 A4 00 00 00  ................
    00000030  00 00 00 00 02 00 00 00 20 00 10 60 2E 64 61 74  ........ ..`.dat
    00000040  61 00 00 00 00 00 00 00 00 00 00 00 07 00 00 00  a...............
    00000050  8C 00 00 00 A4 00 00 00 00 00 00 00 00 00 00 00  ................
    00000060  40 00 30 C0 2E 62 73 73 00 00 00 00 00 00 00 00  @.0..bss........
    00000070  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
    00000080  00 00 00 00 00 00 00 00 80 00 10 C0 68 65 6C 6C  ............hell
    00000090  6F 0A 00 55 89 E5 68 00 00 00 00 E8 00 00 00 00  o..U..h.........
    000000A0  C9 31 C0 C3 04 00 00 00 04 00 00 00 06 00 09 00  .1..............
    000000B0  00 00 08 00 00 00 14 00 2E 66 69 6C 65 00 00 00  .........file...
    000000C0  00 00 00 00 FE FF 00 00 67 01 68 65 6C 6C 6F 2E  ........g.hello.
    000000D0  63 00 00 00 00 00 00 00 00 00 00 00 2E 74 65 78  c............tex
    000000E0  74 00 00 00 00 00 00 00 01 00 00 00 03 01 11 00  t...............
    000000F0  00 00 02 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
    00000100  2E 64 61 74 61 00 00 00 00 00 00 00 02 00 00 00  .data...........
    00000110  03 01 07 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
    00000120  00 00 00 00 2E 62 73 73 00 00 00 00 00 00 00 00  .....bss........
    00000130  03 00 00 00 03 01 00 00 00 00 00 00 00 00 00 00  ................
    00000140  00 00 00 00 00 00 00 00 00 00 00 00 04 00 00 00  ................
    00000150  00 00 00 00 00 00 00 00 02 00 5F 65 6E 74 72 79  .........._entry
    00000160  00 00 00 00 00 00 01 00 00 00 02 00 12 00 00 00  ................
    00000170  5F 75 78 66 64 6C 5F 70 72 69 6E 74 66 00        _uxfdl_printf.
  • 128バイトが382バイトになりました。でもまあスカスカ感はあまり変わりません。
  • .textセクションが17バイトに、.dataセクションが7バイトになりました。.bssは0ですが、.rdataはありません(naskでは.rdataに対応しておらず、すべて.dataに読み替えられます)。
  • これをuxfに変換するとこうなります。
     offset   +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +A +B +C +D +E +F  0123456789ABCDEF
    ---------------------------------------------------------------------------
    00000000  01 00 11 07 00 55 89 E5 68 00 00 00 00 E8 00 00  .....U..h.......
    00000010  00 00 C9 31 C0 C3 02 01 11 07 19 70 72 69 6E 74  ...1.......print
    00000020  66 68 65 6C 6C 6F 0A 00 00                       fhello...
  • 超スッキリです!41バイトです。しかも最初の11,07,00で、.text、.data、 .bssのサイズがすぐにわかります。
  • uxfでは_uxfdl_で始まるラベルについては、_uxfdl_が省略されて格納されます。
    • uxfdlというのはuxfのダイナミックリンクという意味です。

efg01(第二世代OSASK)との比較

  • efg01では"hello, world\n"を表示するのに必要なバイト数は16バイトでした。"hello\n"ならもっと小さくできるでしょう。それと比べたら、uxfは全然だめです。
  • でもこのサイズのhelloを作るのは結構大変で、C言語でちょろっとソースを書いてコンパイル、というわけにはいきませんでした。今回、uxf版は_uxfdl_という変なものがついてはいますが、でもそれ以外は普通の標準関数です。
  • efg01でも普通のC言語ソースでhelloを書いたらどうなるかの実験をしましたが、67バイトを要しました。worldの分の違いがあるとはいえ、41バイトとは有意な差です。
  • efg01は、APIも独自の仕様を選び、また内部のエンコードもgh4という4ビット体系を選び、とにかくサイズにこだわりました。いろいろと便利な機能がAPIにそろっていますが、仕様が分かりやすいわけではなく、またローダもそれなりに大きくなっていました(15.0KB)。
  • 今回はC言語で書ける範囲でしか頑張らないようにしていますし、ビット演算も少なくして8ビット単位のエンコードを採用しています。
  • uxfは「簡単な割には結構いけている」を目指しています。x86の究極はefg01です。
  • それに今や究極の小ささに関してはOSECPU-VMがあるので、efg01は中途半端で、だからuxfにも存在意義があると思っています。

なぜuxfdlを付ける仕様を選んだのか?

  • gccでprintfを普通に使うと、putsやputcharに置き換えるという最適化を勝手にやることがあります。これが意図せず行われると期待と異なる結果にある場合があり(サイズ的に)、それを回避したいというのが理由の一つです。uxfdl_を付ければgccはそういうおせっかいはしません。
  • また単にprintfを書けばいいということにすると、uxf生成プログラムは、このprintfがエラーにするべき未解決リンクなのか、それともダイナミックリンクしてほしい意図的な未解決リンクなのか、簡単には判断できません。だからuxfdl_を付けたらエラーにはしない、というルールにしています。
  • さらにC言語でprintfと書くと、COFF内のシンボル名では頭にアンダースコアが付与されて「_printf」になります。これも気に入りません。標準関数のダイナミックリンクは使用頻度が高いのでアンダースコアなんてついてほしくないです。_printfだったらアンダースコアを取る?それって内部にテーブルを持つのか?そんなの面倒です。だったらuxfdl_を付ければいいじゃないですか。uxfファイルの生成時にこれをシンボル名から取り除くというルールにすればいいのです。

なぜ4バイトを書かずにsh8でエンコードしているのか?

  • 「シンプルを目指すのならsh8とかにしないで32ビットをそのまま書けばいいじゃないか!」それはその通りです。でもじゃあ64ビットアーキテクチャだったらどうでしょうか。64ビットをだらだらと書くのでしょうか。それって何か違うと私は思いました。
  • そう考えて、だから可変長にすることこそ本質だとして、じゃあどんな可変長なら受け入れられるかを考えました。その結論がsh8です。

uxfでCOFFの代用はできるか?

  • 今のuxfでは.rdataは記述できません。またデバッグ情報とかも載せられません。しかしそれを除けば、COFFの代用は可能です(つまりobjファイルをuxfで記述するということ)。
  • まあちょっと仕様変更すれば.rdataやデバッグ情報を積める余地が出てくるので、そうすればCOFFの完全代用も十分に可能です。きっと結構小さくなるでしょう。

こめんと欄


コメントお名前NameLink

トップ   編集 凍結 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2016-09-27 (火) 14:35:42 (2991d)