ユニバーサルバイナリとは
今の理解は、 PPC 用のバイナリと Intel 用のバイナリをくっつけたバイナリファイルの形式。
こういう複数のアーキテクチャのコードを含むバイナリを「FAT バイナリ」や「マルチアーキテクチャバイナリ」という。
つまり、ユニバーサルバイナリは FAT バイナリの一種。
ユニバーサルバイナリのヘッダ
ユニバーサルファイルの先頭には FAT ヘッダが付く。
ヘッダの形式は、以下の構造体の形式は /usr/include/mach-o/fat.h にある
struct fat_header { uint32_t magic; /* FAT_MAGIC */ uint32_t nfat_arch; /* number of structs that follow */ }; struct fat_arch { cpu_type_t cputype; /* cpu specifier (int) */ cpu_subtype_t cpusubtype; /* machine specifier (int) */ uint32_t offset; /* file offset to this object file */ uint32_t size; /* size of this object file */ uint32_t align; /* alignment as a power of 2 */ };
まず、マジックがあって、アーキテクチャの数があって。
アーキテクチャの数だけ fat_arch 構造体の形式のデータが続く
cputype と cpusubtype にアーキテクチャの種類が書かれている。
cputype と cpusubtype については /usr/include/mach/machine.h に定数の一覧が書かれている
: #define CPU_TYPE_X86 ((cpu_type_t) 7) #define CPU_TYPE_I386 CPU_TYPE_X86 /* compatibility */ #define CPU_TYPE_X86_64 (CPU_TYPE_X86 | CPU_ARCH_ABI64) #define CPU_TYPE_MC98000 ((cpu_type_t) 10) #define CPU_TYPE_SPARC ((cpu_type_t) 14) #define CPU_TYPE_I860 ((cpu_type_t) 15) #define CPU_TYPE_POWERPC ((cpu_type_t) 18) #define CPU_TYPE_POWERPC64 (CPU_TYPE_POWERPC | CPU_ARCH_ABI64) : #define CPU_SUBTYPE_PENTIUM_M CPU_SUBTYPE_INTEL(9, 0) #define CPU_SUBTYPE_PENTIUM_4 CPU_SUBTYPE_INTEL(10, 0) #define CPU_SUBTYPE_PENTIUM_4_M CPU_SUBTYPE_INTEL(10, 1) #define CPU_SUBTYPE_ITANIUM CPU_SUBTYPE_INTEL(11, 0) #define CPU_SUBTYPE_ITANIUM_2 CPU_SUBTYPE_INTEL(11, 1) #define CPU_SUBTYPE_XEON CPU_SUBTYPE_INTEL(12, 0) #define CPU_SUBTYPE_XEON_MP CPU_SUBTYPE_INTEL(12, 1) :
自分のコンピュータのアーキテクチャを知る
以下のようにするらしい
#include <stdio.h> #include <mach/mach.h> int main() { mach_port_t host_port, task_port; kern_return_t result; struct host_basic_info info; unsigned int count; host_port = mach_host_self(); count = HOST_BASIC_INFO_COUNT; result = host_info(host_port, HOST_BASIC_INFO, (host_t)&info, &count); task_port = mach_task_self(); mach_port_deallocate(task_port, host_port); if (result != KERN_SUCCESS) exit(-1); printf("host_basic_info:\n"); printf("\tmax_cpus: %d\n", info.max_cpus); printf("\tavail_cpus: %d\n", info.avail_cpus); printf("\tmemory_size: %u\n", info.memory_size); printf("\tcpu_type: %d\n", info.cpu_type); printf("\tcpu_subtype: %d\n", info. cpu_subtype); printf("\tphysical_cpu: %d\n", info.physical_cpu); printf("\tphysical_cpu_max: %d\n", info.physical_cpu_max); printf("\tlogical_cpu: %d\n", info.logical_cpu); printf("\tlogical_cpu_max: %d\n", info.logical_cpu_max); printf("\tmax_mem: %u\n", info.max_mem); return 0; }
実行するとこうなる
$ ./a.out host_basic_info: max_cpus: 2 host_basic_info: 2 memory_size: 2147483648 cpu_type: 7 cpu_subtype: 2 physical_cpu: 2 physical_cpu_max: 2 logical_cpu: 2 logical_cpu_max: 2 max_mem: 2147483648
この mach_task_self, mach_host_self, mach_port_deallocate は Mach というマイクロカーネルの API ということだ。
意味ふ><
ちょっと寄り道して Mach およびマイクロカーネルについて調べてみる。
で、どういうことなのかなあ
この二つってどういう関係なんだろう。
同じ Mach カーネルの上に構築された Unix Hurd の説明を見ると
The GNU Hurd FAQ: 定義と概要
POSIX のシステムコールもマイクロカーネル API を使ってるにすぎないようなことが書いてあった。
マイクロカーネルの API ってシステムコールよりも下のレイヤということになるのかな??
さらに
プロセス(カーネルを含む)がサーバとクライアントになって通信し合うような API らしい。
だから、システムコールをする場合は、カーネル(サーバ)にプロセスがクライアントとして通信しているという感じなのかな?
通信はポートというものを使う、ポートには権限(ポートライト)があって、送信できる権利(センドライト)と受信できる権利(レシーブライト)があるらしい
具体的なサーバとクライアントの書き方
ここの下のほうにありました。
http://www.rtmach.org/publications/transtech-mach-1.pdf
つまりさっきの
host_port = mach_host_self(); count = HOST_BASIC_INFO_COUNT; result = host_info(host_port, HOST_BASIC_INFO, (host_t)&info, &count); task_port = mach_task_self(); mach_port_deallocate(task_port, host_port);
は、
mach_host_self() で取得したポートを介して、通信によってサーバとなっているプロセスの host_info を呼び出しているということか。
mach_host_self() で得られるポートについては以下に書いてあります。
Host Interface - The GNU Mach Reference Manual
task's host ってなんだろう。
寄り道しすぎた
さてと、話は FAT ヘッダまで行ったと。。
次は自分の cputype にあった fat_arch を選ぶ
host_info.cpu_type == fat_arch.cputype
Mach-O とは
Mach-O とは Mac OS X で使われるオブジェクトファイルの形式。
ユニバーサルバイナリじゃない場合は全体がこの形式。
ユニバーサルバイナリの場合はこの形式のファイルが結合されて、先頭に FAT ヘッダがつく。
Mach-O のヘッダの形式
/usr/include/mach-o/loader.h にあった
/* * The 32-bit mach header appears at the very beginning of the object file for * 32-bit architectures. */ struct mach_header { uint32_t magic; /* mach magic number identifier */ cpu_type_t cputype; /* cpu specifier */ cpu_subtype_t cpusubtype; /* machine specifier */ uint32_t filetype; /* type of file */ uint32_t ncmds; /* number of load commands */ uint32_t sizeofcmds; /* the size of all the load commands */ uint32_t flags; /* flags */ }; /* Constant for the magic field of the mach_header (32-bit architectures) */ #define MH_MAGIC 0xfeedface /* the mach magic number */ #define MH_CIGAM 0xcefaedfe /* NXSwapInt(MH_MAGIC) */ /* * The 64-bit mach header appears at the very beginning of object files for * 64-bit architectures. */ struct mach_header_64 { uint32_t magic; /* mach magic number identifier */ cpu_type_t cputype; /* cpu specifier */ cpu_subtype_t cpusubtype; /* machine specifier */ uint32_t filetype; /* type of file */ uint32_t ncmds; /* number of load commands */ uint32_t sizeofcmds; /* the size of all the load commands */ uint32_t flags; /* flags */ uint32_t reserved; /* reserved */ }; /* Constant for the magic field of the mach_header_64 (64-bit architectures) */ #define MH_MAGIC_64 0xfeedfacf /* the 64-bit mach magic number */ #define MH_CIGAM_64 0xcffaedfe /* NXSwapInt(MH_MAGIC_64) */
magic はマジックナンバー。
cputype と cpusubtype は fat_arch にもあった情報。
filetype はなんだろう。 filetype には、以下の値が入るらしい
#define MH_OBJECT 0x1 /* relocatable object file */ #define MH_EXECUTE 0x2 /* demand paged executable file */ #define MH_FVMLIB 0x3 /* fixed VM shared library file */ #define MH_CORE 0x4 /* core file */ #define MH_PRELOAD 0x5 /* preloaded executable file */ #define MH_DYLIB 0x6 /* dynamically bound shared library */ #define MH_DYLINKER 0x7 /* dynamic link editor */ #define MH_BUNDLE 0x8 /* dynamically bound bundle file */ #define MH_DYLIB_STUB 0x9 /* shared library stub for static */
なるほどね。
実行形式か、再配置可能なオブジェクトか、ダイナミックリンクライブラリか、などなどいろいろなファイルの種類があるなあ。
flags もめっちゃ種類ある
/* Constants for the flags field of the mach_header */ #define MH_NOUNDEFS 0x1 /* the object file has no undefined references */ #define MH_INCRLINK 0x2 /* the object file is the output of an incremental link against a base file and can't be link edited again */ #define MH_DYLDLINK 0x4 /* the object file is input for the dynamic linker and can't be staticly link edited again */ #define MH_BINDATLOAD 0x8 /* the object file's undefined references are bound by the dynamic linker when loaded. */ #define MH_PREBOUND 0x10 /* the file has its dynamic undefined references prebound. */ #define MH_SPLIT_SEGS 0x20 /* the file has its read-only and read-write segments split */ #define MH_LAZY_INIT 0x40 /* the shared library init routine is to be run lazily via catching memory faults to its writeable segments (obsolete) */ #define MH_TWOLEVEL 0x80 /* the image is using two-level name space bindings */ #define MH_FORCE_FLAT 0x100 /* the executable is forcing all images to use flat name space bindings */ #define MH_NOMULTIDEFS 0x200 /* this umbrella guarantees no multiple defintions of symbols in its sub-images so the two-level namespace hints can always be used. */ #define MH_NOFIXPREBINDING 0x400 /* do not have dyld notify the prebinding agent about this executable */ #define MH_PREBINDABLE 0x800 /* the binary is not prebound but can have its prebinding redone. only used when MH_PREBOUND is not set. */ #define MH_ALLMODSBOUND 0x1000 /* indicates that this binary binds to all two-level namespace modules of its dependent libraries. only used when MH_PREBINDABLE and MH_TWOLEVEL are both set. */ #define MH_SUBSECTIONS_VIA_SYMBOLS 0x2000/* safe to divide up the sections into sub-sections via symbols for dead code stripping */ #define MH_CANONICAL 0x4000 /* the binary has been canonicalized via the unprebind operation */ #define MH_WEAK_DEFINES 0x8000 /* the final linked image contains external weak symbols */ #define MH_BINDS_TO_WEAK 0x10000 /* the final linked image uses weak symbols */ #define MH_ALLOW_STACK_EXECUTION 0x20000/* When this bit is set, all stacks in the task will be given stack execution privilege. Only used in MH_EXECUTE filetypes. */ #define MH_ROOT_SAFE 0x40000 /* When this bit is set, the binary declares it is safe for use in processes with uid zero */ #define MH_SETUID_SAFE 0x80000 /* When this bit is set, the binary declares it is safe for use in processes when issetugid() is true */ #define MH_NO_REEXPORTED_DYLIBS 0x100000 /* When this bit is set on a dylib, the static linker does not need to examine dependent dylibs to see if any are re-exported */ #define MH_PIE 0x200000 /* When this bit is set, the OS will load the main executable at a random address. Only used in MH_EXECUTE filetypes. */
うわーーーー。全部調べたいけど大変そう><
PIE (アセンブラ中に相対参照しかない実行コード)とかはフラグがあるんだ。ぱっとみ分かるのが PIE くらいしかない><
時間があるときに調べてみよう。
で、 ncmds, sizecmds っていうのがロードコマンドの数を表しているらしい。
ということは、このあとにロードコマンドが続いていくというのが分かる
ロードコマンドとは
仮想記憶のレイアウト、シンボルテーブルの場所、などのセグメントの初期化情報やレジスタの初期値などが入っているらしい
ロードコマンドの形式
(つづく)
確認用に書いてるコード
// エラー処理は一切ありません #include <mach/mach.h> #include <stdio.h> #include <mach-o/loader.h> #include <mach-o/fat.h> #include <libc.h> #include <sys/mman.h> #define FILE_NAME "/bin/ls" #define SWAP32(a) (((a) << 24) | (((a) << 8) & 0x00ff0000) | (((a) >> 8) & 0x0000ff00) | ((unsigned int)(a) >> 24) ) int main() { int little_endian = FALSE; int i; int fd; struct stat st; unsigned long size; char *pstart; char *p; uint32_t magic; struct fat_header fh; struct fat_arch fa; mach_port_t port; struct host_basic_info hi; unsigned int c; uint32_t obj_offset = 0; uint32_t obj_size = 0; struct mach_header mh; struct mach_header_64 mh64; #ifdef __LITTLE_ENDIAN__ little_endian = TRUE; #endif // 自分のマシンの cpu_type と cpu_subtype を調べる // hi.cpu_type と hi.cpu_subtype でを見ればいい port = mach_host_self(); c = HOST_BASIC_INFO_COUNT; host_info(port, HOST_BASIC_INFO, (host_t)&hi, &c); mach_port_deallocate(mach_task_self(), port); printf("host_basic_hi:\n"); printf("\tmax_s: %d\n", hi.max_cpus); printf("\tavail_cpu: %d\n", hi.avail_cpus); printf("\tmemory_size: %u\n", hi.memory_size); printf("\tcpu_type: %d\n", hi.cpu_type); printf("\tcpu_subtype: %d\n", hi. cpu_subtype); printf("\tphysical_cpu: %d\n", hi.physical_cpu); printf("\tphysical_cpu_max: %d\n", hi.physical_cpu_max); printf("\tlogical_cpu: %d\n", hi.logical_cpu); printf("\tlogical_cpu_max: %d\n", hi.logical_cpu_max); printf("\tmax_mem: %u\n", hi.max_mem); // ファイルを開く fd = open(FILE_NAME, O_RDONLY); // ファイルサイズ fstat(fd, &st); size = st.st_size; // ファイルをメモリにマップする pstart = mmap(0, size, PROT_READ, MAP_FILE|MAP_PRIVATE, fd, 0); p = pstart; // ファイルを閉じる close(fd); // 最初の 32 byte を読む // uint32_t のポインタとして代入 magic = *(uint32_t *)p; // magic が FAT ヘッダの magic か見る if ((FAT_MAGIC == magic && !little_endian) || (FAT_CIGAM == magic && little_endian)) { // FAT のマジックを読む printf("fat magic:\t0x%x\n", magic); // fat_header を読む fh = *(struct fat_header *)p; // big endian で書かれているので if (little_endian) { fh.nfat_arch = SWAP32(fh.nfat_arch); } printf("arch num:\t%d\n", fh.nfat_arch); // メモリの読み込む位置を fat_header のサイズ分進める p += sizeof(struct fat_header); for (i = 0; i < fh.nfat_arch; i ++) { // fat_arch を読む fa = *(struct fat_arch *)p; // big endian で書かれているので if (little_endian) { fa.cputype = SWAP32(fa.cputype); fa.cpusubtype = SWAP32(fa.cpusubtype); fa.offset = SWAP32(fa.offset); fa.size = SWAP32(fa.size); fa.align = SWAP32(fa.align); } printf("arch %d\n", i); printf("\tcputype %d:\t\n", fa.cputype); printf("\tcpusubtype %d:\t\n", fa.cpusubtype); printf("\toffset %d:\t\n", fa.offset); printf("\talign %d:\t\n", fa.align); // cputype が等しいのを選ぶ // cpusubtype の判定はかなりめんどくさそうだったので省略 if(fa.cputype == hi.cpu_type) { puts("\tthis is my cputype object!"); obj_offset = fa.offset; obj_size = fa.size; } // 次の fat_arch に進む p += sizeof(struct fat_arch); } // 自分の cputype に合うオブジェクトがない場合は終了 if (obj_offset == 0) { exit(-1); } } else { obj_offset = 0; obj_size = size; } p = pstart + obj_offset; // 32 bit 読む magic = *(uint32_t*)p; if (MH_MAGIC == magic) { // Mach-O ヘッダを読み込む mh = *(struct mach_header*)p; printf("mach-o magic:\t0x%x\n", mh.magic); printf("\tcputype: %d\n", mh.cputype); printf("\tcpusubtype: %d\n", mh.cpusubtype); printf("\tfiletype: 0x%x\n", mh.filetype); printf("\tncmds: %d\n", mh.ncmds); printf("\tsizeofcmds: %d\n", mh.sizeofcmds); printf("\tflags: 0x%x\n", mh.flags); } else if (MH_MAGIC_64 == magic) { // Mach-O ヘッダを読み込む mh64 = *(struct mach_header_64*)p; printf("mach-o magic:\t0x%x\n", mh64.magic); printf("\tcputype: %d\n", mh64.cputype); printf("\tcpusubtype: %d\n", mh64.cpusubtype); printf("\tfiletype: %x\n", mh64.filetype); printf("\tncmds: %d\n", mh64.ncmds); printf("\tsizeofcmds: %d\n", mh64.sizeofcmds); printf("\tflags: 0x%x\n", mh64.flags); } else { // Mach-O じゃない exit(-1); } return 0; }
(つづく)