この前作ったテスト用のバイナリを読める程度の最低限動くプログラムを書いてみました。manのELF(5)が参考資料です。
メインPC自体は64bitなのですが、作ってるカーネルは32bitなので、テスト用のバイナリも32bitでコンパイルしてます。
これを自作カーネルに持っていきます。
テストプログラムはデータとか全然ないし、静的なバイナリなのでreadelf -Sでセクションを見るとほとんどデータはありません。
There are 6 section headers, starting at offset 0xcc: Section Headers: [Nr] Name Type Addr Off Size ES Flg Lk Inf Al [ 0] NULL 00000000 000000 000000 00 0 0 0 [ 1] .text PROGBITS 08048074 000074 000011 00 AX 0 0 4 [ 2] .comment PROGBITS 00000000 000085 00001c 01 MS 0 0 1 [ 3] .shstrtab STRTAB 00000000 0000a1 00002a 00 0 0 1 [ 4] .symtab SYMTAB 00000000 0001bc 000080 10 5 4 4 [ 5] .strtab STRTAB 00000000 00023c 000026 00 0 0 1 Key to Flags: W (write), A (alloc), X (execute), M (merge), S (strings) I (info), L (link order), G (group), x (unknown) O (extra OS processing required) o (OS specific), p (processor specific)
ELFの読み込みは大体以下のような感じでやってます。
ELFヘッダ->プログラムヘッダテーブル->文字列テーブル->セクションヘッダ->シンボルテーブル
最初にELFヘッダから読んでますが、これはファイルの先頭から構造体のサイズ分を一気に読んでるだけです。
次にプログラムヘッダの読み込みですが、
最初にElf32_Ehdrのe_phnumを見てこいつが0じゃなければ、データを読み込みます。
プログラムヘッダのサイズはe_phentsize*e_phnumになっています。
ここから先に出てくる変数fileはELFファイルをmmpaしたデータです。
static int read_program_header(struct elf *data, const unsigned char *file) { unsigned long size = get_program_table_size(data); // Is there program header. if (!data->e_hdr.e_phnum) return 1; data->p_hdr = malloc(size); if (!data->p_hdr) return -1; memcpy(data->p_hdr, file + data->e_hdr.e_phoff, size); return 0; }
その次はプログラムヘッダです。最初はさっきと同じようにチェックがあり、e_shnumが0じゃなければプログラムヘッダの読み込みをします。プログラムヘッダのサイズは、Elf32_Ehdrのe_shentsizeとe_shnumを掛け算します。
static int read_section_header(struct elf *data, const unsigned char *file) { unsigned long section_size = get_section_size(data); // Is there any section header? if (!data->e_hdr.e_shnum) return 1; data->s_hdr = malloc(section_size); if (!data->s_hdr) return -1; memcpy(data->s_hdr, file + data->e_hdr.e_shoff, section_size); return 0; }
次はストリングテーブルです。このテーブルはn個あるセクションヘッダのどこかにいて、そのインデックスはElf32_Ehdrのdata->e_hdr.e_shstrndxで示されてます。
なので、セクションヘッダの配列[Elf32_Ehdr.e_shstrndx]という感じでアクセスします。
sh_offsetはテーブルのアドレス(ファイルの先頭から)で、sh_sizeはテーブルのサイズです。
offsetとsizeはシンボルテーブルなんかでも同様の方法で扱います。
static int read_string_table(struct elf *data, const unsigned char *file) { unsigned long offset = data->s_hdr[data->e_hdr.e_shstrndx].sh_offset; unsigned long size = data->s_hdr[data->e_hdr.e_shstrndx].sh_size; data->str_table.string_tbl = malloc(size); if (!data->str_table.string_tbl) return -1; memcpy(data->str_table.string_tbl, file + offset, size); return 0; }
次にストリングテーブルですが、こんな感じに書いてみました。
get_section_header()でシンボル名を指定して、欲しいセクションヘッダの構造体を取得します。
あとは、さっきのoffsetとsize方式でデータのアクセスしてます。
static int read_symbol_table(struct elf *data, const unsigned char *file) { const Elf32_Shdr *sym = get_section_header(data, ".symtab"); if (!sym) return -2; data->sym = malloc(sym->sh_size); if (!data->sym) return -1; memcpy(data->sym, file + sym->sh_offset, sym->sh_size); data->sym_count = sym->sh_size / sizeof(Elf32_Sym); return 0; }
get_section_header()ではget_section_name()を使って、セクション名を取得します。
static const Elf32_Shdr *get_section_header(const struct elf *data, const char *section) { const Elf32_Shdr *sym = NULL; unsigned char *str_tbl = data->str_table.string_tbl; int i; for (i = 0; i < data->e_hdr.e_shnum; i++) { const Elf32_Shdr *p = data->s_hdr + i; char buf[64] = { 0 }; get_section_name(p, str_tbl, buf); if (!strcmp(buf, section)) { sym = p; break; } } return sym; }
ELFのストリングテーブルは単に文字列が連続しているだけなんですが、文字列の終端は0x0が入っています。
また、sh_nameというのは名前からして、char型で文字列が入っているイメージなんですが、そういうわけではなくて、
ストリングテーブル先頭からのオフセットが入っています。なので、ストリングテーブルの開始アドレス+sh_nameをstrcpyの2番目の引数にしてあげれば、サイズが分からなくてもデータの取得はできます。
static void get_section_name(const Elf32_Shdr *data, const unsigned char *table, char *buf) { strcpy(buf, (const char *) table + data->sh_name); }
大体こんな感じでやれば他のデータも読めるようになりました。
作ったプログラムはこれです。エラー処理とかはあまり気にしてません。
#include <elf.h> #include <stdio.h> #include <string.h> #include <stdlib.h> #include <assert.h> #include <sys/mman.h> #include <sys/stat.h> #include <sys/types.h> #include <unistd.h> #include <fcntl.h> static const char test_file[] = "/home/masami/experiment/miko/test/test_program/hello"; struct string_tables { unsigned char *string_tbl; unsigned char *symbol_str_tbl; }; struct section { unsigned char *data; size_t size; }; struct sections { struct section *text; struct section *bss; }; struct elf { Elf32_Ehdr e_hdr; Elf32_Phdr *p_hdr; Elf32_Shdr *s_hdr; Elf32_Sym *sym; int sym_count; Elf32_Rel *rel; Elf32_Rela *rela; struct string_tables str_table; struct sections section_data; }; static unsigned long get_file_size(void); static void *map2memory(unsigned long size); static inline unsigned long get_program_table_size(const struct elf *data); static inline unsigned long get_section_size(const struct elf *data); static int read_section_header(struct elf *data, const unsigned char *file); static int read_program_header(struct elf *data, const unsigned char *file); static int read_string_table(struct elf *data, const unsigned char *file); static int read_symbol_string_table(struct elf *data, const unsigned char *file); static int read_text_section(struct elf *data, const unsigned char *file); static int read_bss_section(struct elf *data, const unsigned char *file); static struct section *read_section(struct elf *data, const unsigned char *file, const char *name); static int is_elf(const struct elf *data); static int read_header(struct elf *data, const unsigned char *file); static int read_symbol_table(struct elf *data, const unsigned char *file); static void get_section_name(const Elf32_Shdr *data, const unsigned char *table, char *buf); static void get_symbol_string_name(const Elf32_Sym *data, const unsigned char *table, char *buf); static const Elf32_Shdr *get_section_header(const struct elf *data, const char *section); static void print_symbol_table(const struct elf *data, const unsigned char *file); static void print_section_header(const struct elf *data, const unsigned char *file); static void print_program_header(const struct elf *data); static void print_header(const struct elf *data); static void print_text_section(const struct elf *data); static void print_bss_section(const struct elf *data); static void free_elf_data(struct elf *data); static void free_string_tables(struct string_tables *tables); static void free_section(struct section *section); static void free_sections(struct sections *sections); static unsigned long get_file_size(void) { struct stat st; assert(stat(test_file, &st) != -1); return st.st_size; } static void *map2memory(unsigned long size) { int fd; void *ret; fd = open(test_file, O_RDONLY); assert(fd >= 0); ret = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0); assert(ret != MAP_FAILED); close(fd); return ret; } static void free_string_tables(struct string_tables *tables) { if (tables->string_tbl) free(tables->string_tbl); if (tables->symbol_str_tbl) free(tables->symbol_str_tbl); } static void free_section(struct section *section) { if (!section) return ; if (section->data) free(section->data); section->size = 0; free(section); } static void free_sections(struct sections *sections) { free_section(sections->text); free_section(sections->bss); } static void free_elf_data(struct elf *data) { if (!data) return ; if (data->p_hdr) free(data->p_hdr); if (data->s_hdr) free(data->s_hdr); if (data->sym) free(data->sym); if (data->rel) free(data->rel); if (data->rela) free(data->rela); free_string_tables(&data->str_table); free_sections(&data->section_data); free(data); } static const Elf32_Shdr *get_section_header(const struct elf *data, const char *section) { const Elf32_Shdr *sym = NULL; unsigned char *str_tbl = data->str_table.string_tbl; int i; for (i = 0; i < data->e_hdr.e_shnum; i++) { const Elf32_Shdr *p = data->s_hdr + i; char buf[64] = { 0 }; get_section_name(p, str_tbl, buf); if (!strcmp(buf, section)) { sym = p; break; } } return sym; } static inline unsigned long get_program_table_size(const struct elf *data) { return data->e_hdr.e_phentsize * data->e_hdr.e_phnum; } static inline unsigned long get_section_size(const struct elf *data) { return data->e_hdr.e_shentsize * data->e_hdr.e_shnum; } static int read_section_header(struct elf *data, const unsigned char *file) { unsigned long section_size = get_section_size(data); // Is there any section header? if (!data->e_hdr.e_shnum) return 1; data->s_hdr = malloc(section_size); if (!data->s_hdr) return -1; memcpy(data->s_hdr, file + data->e_hdr.e_shoff, section_size); return 0; } static struct section *read_section(struct elf *data, const unsigned char *file, const char *name) { const Elf32_Shdr *sym = get_section_header(data, name); struct section *ret; if (!sym) return NULL; ret = malloc(sizeof(*ret)); ret->size = sym->sh_size; if (!sym->sh_size) { printf("%s section size is 0\n", name); return NULL; } ret->data = malloc(sym->sh_size); if (!ret->data) return NULL; memcpy(ret->data, file + sym->sh_offset, sym->sh_size); return ret; } static int read_text_section(struct elf *data, const unsigned char *file) { data->section_data.text = read_section(data, file, ".text"); if (!data->section_data.text) return -1; return 0; } static int read_bss_section(struct elf *data, const unsigned char *file) { data->section_data.bss = read_section(data, file, ".bss"); if (!data->section_data.bss) return -1; return 0; } static int read_program_header(struct elf *data, const unsigned char *file) { unsigned long size = get_program_table_size(data); // Is there program header. if (!data->e_hdr.e_phnum) return 1; data->p_hdr = malloc(size); if (!data->p_hdr) return -1; memcpy(data->p_hdr, file + data->e_hdr.e_phoff, size); return 0; } static int read_string_table(struct elf *data, const unsigned char *file) { unsigned long offset = data->s_hdr[data->e_hdr.e_shstrndx].sh_offset; unsigned long size = data->s_hdr[data->e_hdr.e_shstrndx].sh_size; data->str_table.string_tbl = malloc(size); if (!data->str_table.string_tbl) return -1; memcpy(data->str_table.string_tbl, file + offset, size); return 0; } static int read_symbol_string_table(struct elf *data, const unsigned char *file) { const Elf32_Shdr *sym = get_section_header(data, ".strtab"); if (!sym) return -2; data->str_table.symbol_str_tbl = malloc(sym->sh_size); if (!data->str_table.symbol_str_tbl) return -1; memcpy(data->str_table.symbol_str_tbl, file + sym->sh_offset, sym->sh_size); return 0; } static int is_elf(const struct elf *data) { if (data->e_hdr.e_ident[0] == 0x7f && data->e_hdr.e_ident[1] == 'E' && data->e_hdr.e_ident[2] == 'L' && data->e_hdr.e_ident[3] == 'F') return 1; return 0; } static int read_header(struct elf *data, const unsigned char *file) { memcpy(&data->e_hdr, file, sizeof(data->e_hdr)); return is_elf(data) == 1 ? 0 : -1; } static int read_symbol_table(struct elf *data, const unsigned char *file) { const Elf32_Shdr *sym = get_section_header(data, ".symtab"); if (!sym) return -2; data->sym = malloc(sym->sh_size); if (!data->sym) return -1; memcpy(data->sym, file + sym->sh_offset, sym->sh_size); data->sym_count = sym->sh_size / sizeof(Elf32_Sym); return 0; } static void get_section_name(const Elf32_Shdr *data, const unsigned char *table, char *buf) { strcpy(buf, (const char *) table + data->sh_name); } static void get_symbol_string_name(const Elf32_Sym *data, const unsigned char *table, char *buf) { strcpy(buf, (const char *) table + data->st_name); } static void print_text_section(const struct elf *data) { int i; if (!data->section_data.text) return ; printf("[-------Text section size(0x%lx)-------]\n", data->section_data.text->size); for (i = 0; i < data->section_data.text->size; i++) { printf("0x%02x ", data->section_data.text->data[i]); if (i != 0 && (i % 16) == 0) printf("\n"); } } static void print_bss_section(const struct elf *data) { int i; if (!data->section_data.bss) return ; printf("[-------BSS section size(0x%lx)-------]\n", data->section_data.bss->size); for (i = 0; i < data->section_data.bss->size; i++) { printf("0x%02x ", data->section_data.bss->data[i]); if (i != 0 && (i % 16) == 0) printf("\n"); } } static void print_symbol_table(const struct elf *data, const unsigned char *file) { int i; const Elf32_Sym *p; unsigned char *sym_str_tbl = data->str_table.symbol_str_tbl; for (i = 0; i < data->sym_count; i++) { p = data->sym + i; char buf[128] = { 0 }; get_symbol_string_name(p, sym_str_tbl, buf); printf("[-------Symbol Table %d-------]\nst_name: %s\nst_value: 0x%x\n" "st_size: 0x%x\nst_info: 0x%x\nst_other: 0x%x\nst_shndx: 0x%x\n", i, buf, p->st_value, p->st_size, p->st_info, p->st_other, p->st_shndx); } } static void print_section_header(const struct elf *data, const unsigned char *file) { int i; const unsigned char *str_tbl = data->str_table.string_tbl; for (i = 0; i < data->e_hdr.e_shnum; i++) { const Elf32_Shdr *p = data->s_hdr + i; char buf[64] = { 0 }; get_section_name(p, str_tbl, buf); printf("[-------Section Header %d-------]\nsh_name: %s\nsh_type: 0x%x\nsh_flags: 0x%x\n" "sh_addr: 0x%x\nsh_offset 0x%x\nsh_size: 0x%x\nsh_link: 0x%x\n" "sh_info: 0x%x\nsh_addralign: 0x%x\nsh_entsize: 0x%x\n", i, buf, p->sh_type, p->sh_flags, p->sh_addr, p->sh_offset, p->sh_size, p->sh_link, p->sh_info, p->sh_addralign, p->sh_entsize); } } static void print_program_header(const struct elf *data) { int i; for (i = 0; i < data->e_hdr.e_phnum; i++) { const Elf32_Phdr *p = data->p_hdr + i; printf("[-------Program Header %d-------]\np_type: 0x%x\np_offset: 0x%x\n" "p_vaddr: 0x%x\np_paddr: 0x%x\np_memsz: 0x%x\np_flags: 0x%x\np_align: 0x%x\n", i, p->p_type, p->p_offset, p->p_vaddr, p->p_paddr, p->p_memsz, p->p_flags, p->p_align); } } static void print_header(const struct elf *data) { printf("[-------ELF Header-------]\n"); printf("EI_CLASS: 0x%x\n", data->e_hdr.e_ident[4]); printf("EI_DATA: 0x%x\n", data->e_hdr.e_ident[5]); printf("EI_VERSION: 0x%x\n", data->e_hdr.e_ident[6]); printf("EI_OSABI: 0x%x\n", data->e_hdr.e_ident[7]); printf("e_type: 0x%x\n", data->e_hdr.e_type); printf("e_machine: 0x%x\n", data->e_hdr.e_machine); printf("e_version: 0x%x\n", data->e_hdr.e_version); printf("e_entry: 0x%x\n", data->e_hdr.e_entry); printf("e_phoff: 0x%x\n", data->e_hdr.e_phoff); printf("e_shoff: 0x%x\n", data->e_hdr.e_shoff); printf("e_ehsize: 0x%x\n", data->e_hdr.e_ehsize); printf("e_phentsize: 0x%x\n", data->e_hdr.e_phentsize); printf("e_phnum: 0x%x\n", data->e_hdr.e_phnum); printf("e_shentsize: 0x%x\n", data->e_hdr.e_shentsize); printf("e_shnum: 0x%x\n", data->e_hdr.e_shnum); printf("e_shstrndx: 0x%x\n", data->e_hdr.e_shstrndx); } struct elf *init_elf_data(void) { struct elf *data; data = malloc(sizeof(*data)); if (!data) { printf("couldn't allocate for data\n"); return NULL; } memset(data, 0, sizeof(*data)); return data; } int main(int argc, char **argv) { unsigned long file_size = get_file_size(); unsigned char *file = NULL; struct elf *data; data = init_elf_data(); if (!data) return 0; printf("file %s size is %ld\n", test_file, file_size); file = map2memory(file_size); printf("file %s has been mmapped\n", test_file); if (read_header(data, file) < 0) { munmap(file, file_size); printf("this is not ELF file\n"); return -1; } print_header(data); read_program_header(data, file); print_program_header(data); read_section_header(data, file); read_string_table(data, file); print_section_header(data, file); read_symbol_table(data, file); read_symbol_string_table(data, file); print_symbol_table(data, file); read_text_section(data, file); print_text_section(data); read_bss_section(data, file); print_bss_section(data); free_elf_data(data); munmap(file, file_size); return 0; }
これを実行すると、下のようになります。
file /home/masami/experiment/miko/test/test_program/hello size is 610 file /home/masami/experiment/miko/test/test_program/hello has been mmapped [-------ELF Header-------] EI_CLASS: 0x1 EI_DATA: 0x1 EI_VERSION: 0x1 EI_OSABI: 0x0 e_type: 0x2 e_machine: 0x3 e_version: 0x1 e_entry: 0x8048074 e_phoff: 0x34 e_shoff: 0xcc e_ehsize: 0x34 e_phentsize: 0x20 e_phnum: 0x2 e_shentsize: 0x28 e_shnum: 0x6 e_shstrndx: 0x3 [-------Program Header 0-------] p_type: 0x1 p_offset: 0x0 p_vaddr: 0x8048000 p_paddr: 0x8048000 p_memsz: 0x85 p_flags: 0x5 p_align: 0x1000 [-------Program Header 1-------] p_type: 0x6474e551 p_offset: 0x0 p_vaddr: 0x0 p_paddr: 0x0 p_memsz: 0x0 p_flags: 0x6 p_align: 0x4 [-------Section Header 0-------] sh_name: sh_type: 0x0 sh_flags: 0x0 sh_addr: 0x0 sh_offset 0x0 sh_size: 0x0 sh_link: 0x0 sh_info: 0x0 sh_addralign: 0x0 sh_entsize: 0x0 [-------Section Header 1-------] sh_name: .text sh_type: 0x1 sh_flags: 0x6 sh_addr: 0x8048074 sh_offset 0x74 sh_size: 0x11 sh_link: 0x0 sh_info: 0x0 sh_addralign: 0x4 sh_entsize: 0x0 [-------Section Header 2-------] sh_name: .comment sh_type: 0x1 sh_flags: 0x30 sh_addr: 0x0 sh_offset 0x85 sh_size: 0x1c sh_link: 0x0 sh_info: 0x0 sh_addralign: 0x1 sh_entsize: 0x1 [-------Section Header 3-------] sh_name: .shstrtab sh_type: 0x3 sh_flags: 0x0 sh_addr: 0x0 sh_offset 0xa1 sh_size: 0x2a sh_link: 0x0 sh_info: 0x0 sh_addralign: 0x1 sh_entsize: 0x0 [-------Section Header 4-------] sh_name: .symtab sh_type: 0x2 sh_flags: 0x0 sh_addr: 0x0 sh_offset 0x1bc sh_size: 0x80 sh_link: 0x5 sh_info: 0x4 sh_addralign: 0x4 sh_entsize: 0x10 [-------Section Header 5-------] sh_name: .strtab sh_type: 0x3 sh_flags: 0x0 sh_addr: 0x0 sh_offset 0x23c sh_size: 0x26 sh_link: 0x0 sh_info: 0x0 sh_addralign: 0x1 sh_entsize: 0x0 [-------Symbol Table 0-------] st_name: st_value: 0x0 st_size: 0x0 st_info: 0x0 st_other: 0x0 st_shndx: 0x0 [-------Symbol Table 1-------] st_name: st_value: 0x8048074 st_size: 0x0 st_info: 0x3 st_other: 0x0 st_shndx: 0x1 [-------Symbol Table 2-------] st_name: st_value: 0x0 st_size: 0x0 st_info: 0x3 st_other: 0x0 st_shndx: 0x2 [-------Symbol Table 3-------] st_name: hello.c st_value: 0x0 st_size: 0x0 st_info: 0x4 st_other: 0x0 st_shndx: 0xfff1 [-------Symbol Table 4-------] st_name: __bss_start st_value: 0x8049085 st_size: 0x0 st_info: 0x10 st_other: 0x0 st_shndx: 0xfff1 [-------Symbol Table 5-------] st_name: main st_value: 0x8048074 st_size: 0x11 st_info: 0x12 st_other: 0x0 st_shndx: 0x1 [-------Symbol Table 6-------] st_name: _edata st_value: 0x8049085 st_size: 0x0 st_info: 0x10 st_other: 0x0 st_shndx: 0xfff1 [-------Symbol Table 7-------] st_name: _end st_value: 0x8049088 st_size: 0x0 st_info: 0x10 st_other: 0x0 st_shndx: 0xfff1 [-------Text section size(0x11)-------] 0x55 0x89 0xe5 0xb8 0x01 0x00 0x00 0x00 0xcd 0x80 0xb8 0x00 0x00 0x00 0x00 0x5d 0xc3