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;
}





