a.outのマジックナンバー

a.out*1のヘッダの先頭にはマジックナンバーが埋め込まれていて、これによっていわゆるOMAGICとかNMAGIC、ZMAGICとかに分類される(詳細はWikipediaここ)。OMAGICはヘッダのすぐ後にテキストとデータセグメントが続き、それら両方は書込み可能なメモリにロードされる。テキストセグメントがread onlyになったのはNMAGICから。ZMAGICではデマンドページングに対応している。

さて、V1カーネルのa.outはOMAGICよりもさらに古そうだ。OMAGICのマジックナンバーは0407だけど、V1のは0405なのだ。ちなみに、OMAGICになったのはV2からで、unix-jun72プロジェクトのカーネルではOMAGICサポートが追加されている。配布されているasはV2から持ってきたコードらしく、実際、asでアセンブルしたa.outはOMAGICだった。

これらマジックナンバーの0405などは適当に決めた値ではなく、実はbr命令でヘッダに続くテキストセグメントの先頭アドレスへジャンプすることを意味している。ヘッダが拡張されてオフセットが大きくなるにしたがいマジックナンバーも変更されているのだ。当然、この仕掛けはPDP-11以外のアーキテクチャでは機能しないので今となっては形骸化してしまっているが、こんなところにPDP-11の名残を留めているというのが面白い。

Plan9のa.outフォーマット」については以前書いたが、マジックナンバーにはアーキテクチャのIDがエンコードされていて、x86の場合は0x1ebになる。

#define HDR_MAGIC       0x00008000              /* header expansion */

#define _MAGIC(f, b)    ((f)|((((4*(b))+0)*(b))+7))
#define A_MAGIC         _MAGIC(0, 8)            /* 68020 */
#define I_MAGIC         _MAGIC(0, 11)           /* intel 386 */
#define J_MAGIC         _MAGIC(0, 12)           /* intel 960 (retired) */
#define K_MAGIC         _MAGIC(0, 13)           /* sparc */
#define V_MAGIC         _MAGIC(0, 16)           /* mips 3000 BE */
#define X_MAGIC         _MAGIC(0, 17)           /* att dsp 3210 (retired) */
#define M_MAGIC         _MAGIC(0, 18)           /* mips 4000 BE */
#define D_MAGIC         _MAGIC(0, 19)           /* amd 29000 (retired) */
#define E_MAGIC         _MAGIC(0, 20)           /* arm */
#define Q_MAGIC         _MAGIC(0, 21)           /* powerpc */
#define N_MAGIC         _MAGIC(0, 22)           /* mips 4000 LE */
#define L_MAGIC         _MAGIC(0, 23)           /* dec alpha */
#define P_MAGIC         _MAGIC(0, 24)           /* mips 3000 LE */
#define U_MAGIC         _MAGIC(0, 25)           /* sparc64 */
#define S_MAGIC         _MAGIC(HDR_MAGIC, 26)   /* amd64 */
#define T_MAGIC         _MAGIC(HDR_MAGIC, 27)   /* powerpc64 */

*1:もちろんELFとかCOFF以前の話である。