FLATバイナリ
FLATバイナリ、uClinuxと言う組み込みに特化したカーネルで使われるフォーマットだそうです。故にELFのような高度機能は必要なく、実行イメージも小さくなるような工夫がされています。なお、fedoraではbinfmt_flatでサポートされています。
FLATバイナリは、2と4の2つのバージョンがあり、バージョン4では、4つか1つのシェアードライブラリのサポートがされています。
FLATバイナリは、flat_hdrの内容のイメージを先頭する、以降に実行イメージ/データイメージ/BSSイメージを有するファイルとなります。
magicはbFLTで、revにOLD_FLAT_VERSION/FLAT_VERSIONのどちらかを設定しなければなりません。
flagに以下の設定をすることで、イメージをメモリに展開して実行するか、そのまま実行するか。PIC(どこに配置しても、実行的にOK。GOT(グローバルテーブル。PICでもグローバル変数を参照する場合、再配置が必要です。)の処理が必要か。text/data領域のイメージが圧縮されているか。data領域のイメージが圧縮されているか。そしてエラー内容としてrlog(printk)に出力するか。の設定が可能です。
FLATバイナリは、2と4の2つのバージョンがあり、バージョン4では、4つか1つのシェアードライブラリのサポートがされています。
FLATバイナリは、flat_hdrの内容のイメージを先頭する、以降に実行イメージ/データイメージ/BSSイメージを有するファイルとなります。
magicはbFLTで、revにOLD_FLAT_VERSION/FLAT_VERSIONのどちらかを設定しなければなりません。
#define OLD_FLAT_VERSION 0x00000002L #define FLAT_VERSION 0x00000004L
flagに以下の設定をすることで、イメージをメモリに展開して実行するか、そのまま実行するか。PIC(どこに配置しても、実行的にOK。GOT(グローバルテーブル。PICでもグローバル変数を参照する場合、再配置が必要です。)の処理が必要か。text/data領域のイメージが圧縮されているか。data領域のイメージが圧縮されているか。そしてエラー内容としてrlog(printk)に出力するか。の設定が可能です。
#define FLAT_FLAG_RAM 0x0001 /* load program entirely into RAM */ #define FLAT_FLAG_GOTPIC 0x0002 /* program is PIC with GOT */ #define FLAT_FLAG_GZIP 0x0004 /* all but the header is compressed */ #define FLAT_FLAG_GZDATA 0x0008 /* only data/relocs are compressed (for XIP) */ #define FLAT_FLAG_KTRACE 0x0010 /* output useful kernel trace for debugging */ struct flat_hdr { char magic[4]; unsigned long rev; /* version (as above) */ unsigned long entry; /* Offset of first executable instruction with text segment from beginning of file */ unsigned long data_start; /* Offset of data segment from beginning of file */ unsigned long data_end; /* Offset of end of data segment from beginning of file */ unsigned long bss_end; /* Offset of end of bss segment from beginning of file */ /* (It is assumed that data_end through bss_end forms the bss segment.) */ unsigned long stack_size; /* Size of stack, in bytes */ unsigned long reloc_start; /* Offset of relocation records from beginning of file */ unsigned long reloc_count; /* Number of relocation records */ unsigned long flags; unsigned long build_date; /* When the program/library was built */ unsigned long filler[5]; /* Reservered, set to zero */ };以下は、linux_binfmt flat_formatローダとなる、load_flat_binary()からコールされるload_flat_file()の内容で、どのような処理がされているかを追ってみると、なんとなくFLATバイナリ概要が見えてきます。なお、再配置等の処理は良くわかりませんし、正直ここまで追うのに疲れました。またそのうちにと言うことで・・・
static struct linux_binfmt flat_format = { .module = THIS_MODULE, .load_binary = load_flat_binary, .core_dump = flat_core_dump, .min_coredump = PAGE_SIZE }; static int load_flat_file(struct linux_binprm * bprm, struct lib_info *libinfo, int id, unsigned long *extra_stack) { struct flat_hdr * hdr; unsigned long textpos = 0, datapos = 0, result; unsigned long realdatastart = 0; unsigned long text_len, data_len, bss_len, stack_len, flags; unsigned long memp = 0; /* for finding the brk area */ unsigned long extra, rlim; unsigned long *reloc = 0, *rp; struct inode *inode; int i, rev, relocs = 0; loff_t fpos; unsigned long start_code, end_code; hdr = ((struct flat_hdr *) bprm->buf); /* exec-header */ inode = bprm->file->f_dentry->d_inode; text_len = ntohl(hdr->data_start); data_len = ntohl(hdr->data_end) - ntohl(hdr->data_start); bss_len = ntohl(hdr->bss_end) - ntohl(hdr->data_end); stack_len = ntohl(hdr->stack_size); if (extra_stack) { stack_len += *extra_stack; *extra_stack = stack_len; } relocs = ntohl(hdr->reloc_count); flags = ntohl(hdr->flags); rev = ntohl(hdr->rev); if (flags & FLAT_FLAG_KTRACE) printk("BINFMT_FLAT: Loading file: %s\n", bprm->filename); マジック番号のチェック if (strncmp(hdr->magic, "bFLT", 4) || (rev != FLAT_VERSION && rev != OLD_FLAT_VERSION)) { if (strncmp(hdr->magic, "#!", 2)) printk("BINFMT_FLAT: bad magic/rev (0x%x, need 0x%x)\n", rev, (int) FLAT_VERSION); return -ENOEXEC; } シェアードライブラリの処理で、OLD_FLAT_VERSIONはシェアードライブラリは未サポート(id!=0はシェアードライブラリのロード) if (rev == OLD_FLAT_VERSION && id != 0) { printk("BINFMT_FLAT: shared libraries are not available before rev 0x%x\n", (int) FLAT_VERSION); return -ENOEXEC; } OLD_FLAT_VERSIONは、FLAT_FLAG_RAMしかサポートしていないため、flagsに何か設定されていれば、 OLD_FLAT_VERSIONは、flagとしての機能はサポートされてないため、FLAT_FLAG_RAMとして再設定する。 if (rev == OLD_FLAT_VERSION && flat_old_ram_flag(flags)) flags = FLAT_FLAG_RAM; 圧縮モードでflatは、カーネルは、CONFIG_BINFMT_ZFLATコンパイルしなければならない。 #ifndef CONFIG_BINFMT_ZFLAT if (flags & (FLAT_FLAG_GZIP|FLAT_FLAG_GZDATA)) { printk("Support for ZFLAT executables is not enabled.\n"); return -ENOEXEC; } #endif データサイズ+BSSサイズのプロセスリソースのリミットチェック rlim = current->rlim[RLIMIT_DATA].rlim_cur; if (rlim >= RLIM_INFINITY) rlim = ~0; if (data_len + bss_len > rlim) return -ENOMEM; 本体のロード時、処理してるカレントプロセスの対応する設定をbpmで初期化する。 if (id == 0) { result = flush_old_exec(bprm); if (result) return result; /* OK, This is the point of no return */ set_personality(PER_LINUX); } BSS+スタックか再配置テーブルを大きい方を、エクストラスペースとする。エクストラスペースは再配置後、必要ないため、実装としてBSS+スタックと領域を共有する。 extra = max(bss_len + stack_len, relocs * sizeof(unsigned long)); if ((flags & (FLAT_FLAG_RAM|FLAT_FLAG_GZIP)) == 0) { イメージのROMをメインメモリーとして使用および、圧縮タイプでない。とメモリー上にロードする必要がない。 down_write(¤t->mm->mmap_sem); flatイメージのtext領域を、メモリーマップする。 textpos = do_mmap(bprm->file, 0, text_len, PROT_READ|PROT_EXEC, 0, 0); up_write(¤t->mm->mmap_sem); if (!textpos || textpos >= (unsigned long) -4096) { if (!textpos) textpos = (unsigned long) -ENOMEM; printk("Unable to mmap process text, errno %d\n", (int)-textpos); return(textpos); } down_write(¤t->mm->mmap_sem); flatイメージのdata領域+extra+MAX_SHARED_LIBSの領域を、確保する。 (do_mmapの第1/2引数の0は、仮想空間の0から検索して、空いているメモリー空間を確保するだけである。) realdatastart = do_mmap(0, 0, data_len + extra + MAX_SHARED_LIBS * sizeof(unsigned long), PROT_READ|PROT_WRITE|PROT_EXEC, 0, 0); up_write(¤t->mm->mmap_sem); if (realdatastart == 0 || realdatastart >= (unsigned long)-4096) { if (!realdatastart) realdatastart = (unsigned long) -ENOMEM; printk("Unable to allocate RAM for process data, errno %d\n", (int)-datapos); do_munmap(current->mm, textpos, text_len); return realdatastart; } 上で確保したメモリー空間アドレス+ MAX_SHARED_LIBSをデータ領域の開始アドレスとする。 datapos = realdatastart + MAX_SHARED_LIBS * sizeof(unsigned long); DBG_FLT("BINFMT_FLAT: Allocated data+bss+stack (%d bytes): %x\n", (int)(data_len + bss_len + stack_len), (int)datapos); fpos = ntohl(hdr->data_start); #ifdef CONFIG_BINFMT_ZFLAT DATAが圧縮モードの時、解凍して上のメモリー空間に展開 if (flags & FLAT_FLAG_GZDATA) { result = decompress_exec(bprm, fpos, (char *) datapos, data_len + (relocs * sizeof(unsigned long)), 0); } else #endif { DATA開始位置から、上のメモリー空間に読み込む result = bprm->file->f_op->read(bprm->file, (char *) datapos, data_len + (relocs * sizeof(unsigned long)), &fpos); } if (result >= (unsigned long)-4096) { printk("Unable to read data+bss, errno %d\n", (int)-result); do_munmap(current->mm, textpos, text_len); do_munmap(current->mm, realdatastart, data_len + extra); return result; } reloc = (unsigned long *) (datapos+(ntohl(hdr->reloc_start)-text_len)); memp = realdatastart; } else { 全イメージはRAMへ複写して、そこで実行するため、textサイズからMAX_SHARED_LIBSまでの 全必要サイズのメモリー空間を割り当てる。 down_write(¤t->mm->mmap_sem); textpos = do_mmap(0, 0, text_len + data_len + extra + MAX_SHARED_LIBS * sizeof(unsigned long), PROT_READ | PROT_EXEC | PROT_WRITE, 0, 0); up_write(¤t->mm->mmap_sem); if (!textpos || textpos >= (unsigned long) -4096) { if (!textpos) textpos = (unsigned long) -ENOMEM; printk("Unable to allocate RAM for process text/data, errno %d\n", (int)-textpos); return(textpos); } 実データ開始アドレスは、上で割り当てた開始アドレスのイメージ内のデータ開始位置を加えたもの。 realdatastart = textpos + ntohl(hdr->data_start); datapos = realdatastart + MAX_SHARED_LIBS * sizeof(unsigned long); reloc = (unsigned long *) (textpos + ntohl(hdr->reloc_start) + MAX_SHARED_LIBS * sizeof(unsigned long)); memp = textpos; #ifdef CONFIG_BINFMT_ZFLAT if (flags & FLAT_FLAG_GZIP) { 全圧縮モード時の処理 result = decompress_exec(bprm, sizeof (struct flat_hdr), (((char *) textpos) + sizeof (struct flat_hdr)), (text_len + data_len + (relocs * sizeof(unsigned long)) - sizeof (struct flat_hdr)), 0); memmove((void *) datapos, (void *) realdatastart, data_len + (relocs * sizeof(unsigned long))); } else if (flags & FLAT_FLAG_GZDATA) { データ圧縮モード時の処理、textpos(上で割り当てたメモリ開始位置)に、textサイズ読み込んで、 残りのdataサイズを解凍展開する。 fpos = 0; result = bprm->file->f_op->read(bprm->file, (char *) textpos, text_len, &fpos); if (result < (unsigned long) -4096) result = decompress_exec(bprm, text_len, (char *) datapos, data_len + (relocs * sizeof(unsigned long)), 0); } else #endif { 全イメージをメモリに展開。text領域を読み込んで、data領域等を読み込む。 fpos = 0; result = bprm->file->f_op->read(bprm->file, (char *) textpos, text_len, &fpos); if (result < (unsigned long) -4096) { fpos = ntohl(hdr->data_start); result = bprm->file->f_op->read(bprm->file, (char *) datapos, data_len + (relocs * sizeof(unsigned long)), &fpos); } } if (result >= (unsigned long)-4096) { printk("Unable to read code+data+bss, errno %d\n",(int)-result); do_munmap(current->mm, textpos, text_len + data_len + extra + MAX_SHARED_LIBS * sizeof(unsigned long)); return result; } } if (flags & FLAT_FLAG_KTRACE) printk("Mapping is %x, Entry point is %x, data_start is %x\n", (int)textpos, 0x00ffffff&ntohl(hdr->entry), ntohl(hdr->data_start)); 実行本体のロードの場合、各種アドレスを、プロセスのmm_structに設定し、 シェアードライブラリおよびリロケーションの処理を行う。たぶん(後日) start_code = textpos + sizeof (struct flat_hdr); end_code = textpos + text_len; if (id == 0) { current->mm->start_code = start_code; current->mm->end_code = end_code; current->mm->start_data = datapos; current->mm->end_data = datapos + data_len; current->mm->start_brk = datapos + data_len + bss_len; current->mm->brk = (current->mm->start_brk + 3) & ~3; current->mm->context.end_brk = memp + ksize((void *) memp) - stack_len; current->mm->rss = 0; } if (flags & FLAT_FLAG_KTRACE) printk("%s %s: TEXT=%x-%x DATA=%x-%x BSS=%x-%x\n", id ? "Lib" : "Load", bprm->filename, (int) start_code, (int) end_code, (int) datapos, (int) (datapos + data_len), (int) (datapos + data_len), (int) (((datapos + data_len + bss_len) + 3) & ~3)); text_len -= sizeof(struct flat_hdr); /* the real code len */ libinfo->lib_list[id].start_code = start_code; libinfo->lib_list[id].start_data = datapos; libinfo->lib_list[id].start_brk = datapos + data_len + bss_len; libinfo->lib_list[id].text_len = text_len; libinfo->lib_list[id].loaded = 1; libinfo->lib_list[id].entry = (0x00ffffff & ntohl(hdr->entry)) + textpos; libinfo->lib_list[id].build_date = ntohl(hdr->build_date); if (flags & FLAT_FLAG_GOTPIC) { for (rp = (unsigned long *)datapos; *rp != 0xffffffff; rp++) { unsigned long addr; if (*rp) { addr = calc_reloc(*rp, libinfo, id, 0); if (addr == RELOC_FAILED) return -ENOEXEC; *rp = addr; } } } if (rev > OLD_FLAT_VERSION) { for (i=0; i < relocs; i++) { unsigned long addr, relval; relval = ntohl(reloc[i]); addr = flat_get_relocate_addr(relval); rp = (unsigned long *) calc_reloc(addr, libinfo, id, 1); if (rp == (unsigned long *)RELOC_FAILED) return -ENOEXEC; addr = flat_get_addr_from_rp(rp, relval); if (addr != 0) { if ((flags & FLAT_FLAG_GOTPIC) == 0) addr = ntohl(addr); addr = calc_reloc(addr, libinfo, id, 0); if (addr == RELOC_FAILED) return -ENOEXEC; /* Write back the relocated pointer. */ flat_put_addr_at_rp(rp, addr, relval); } } } else { for (i=0; i < relocs; i++) old_reloc(ntohl(reloc[i])); } flush_icache_range(start_code, end_code); /* zero the BSS, BRK and stack areas */ memset((void*)(datapos + data_len), 0, bss_len + (memp + ksize((void *) memp) - stack_len - /* end brk */ libinfo->lib_list[id].start_brk) + /* start brk */ stack_len); return 0; }