FLATバイナリ


Rev.2を表示中。最新版はこちら

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のどちらかを設定しなければなりません。

#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に何か設定されていれば、flagを再設定する。
       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(&current->mm->mmap_sem);
flatイメージのtext領域を、メモリーマップする。
               textpos = do_mmap(bprm->file, 0, text_len, PROT_READ|PROT_EXEC, 0, 0);
               up_write(&current->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(&current->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(&current->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(&current->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(&current->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;
}

最終更新 2013/05/05 19:49:32 - north
(2013/05/05 19:48:05 作成)


検索

アクセス数
3691795
最近のコメント
コアダンプファイル - sakaia
list_head構造体 - yocto_no_yomikata
勧告ロックと強制ロック - wataash
LKMからのファイル出力 - 重松 宏昌
kprobe - ななし
ksetの実装 - スーパーコピー
カーネルスレッドとは - ノース
カーネルスレッドとは - nbyst
asmlinkageってなに? - ノース
asmlinkageってなに? - よろしく
Adsense
広告情報が設定されていません。