diff options
Diffstat (limited to 'mdk-stage1/insmod-modutils/obj/obj_load.c')
-rw-r--r-- | mdk-stage1/insmod-modutils/obj/obj_load.c | 354 |
1 files changed, 354 insertions, 0 deletions
diff --git a/mdk-stage1/insmod-modutils/obj/obj_load.c b/mdk-stage1/insmod-modutils/obj/obj_load.c new file mode 100644 index 000000000..4db20a998 --- /dev/null +++ b/mdk-stage1/insmod-modutils/obj/obj_load.c @@ -0,0 +1,354 @@ +/* Elf file reader. + Copyright 1996, 1997 Linux International. + + Contributed by Richard Henderson <rth@tamu.edu> + obj_free() added by Björn Ekwall <bj0rn@blox.se> March 1999 + Support for kallsyms Keith Owens <kaos@ocs.com.au> April 2000 + + This file is part of the Linux modutils. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#ident "$Id$" + +#include <alloca.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> + +#include "obj.h" +#include "util.h" + +/*======================================================================*/ + +struct obj_file * +obj_load (int fp, Elf32_Half e_type, const char *filename) +{ + struct obj_file *f; + ElfW(Shdr) *section_headers; + int shnum, i; + char *shstrtab; + + /* Read the file header. */ + + f = arch_new_file(); + memset(f, 0, sizeof(*f)); + f->symbol_cmp = strcmp; + f->symbol_hash = obj_elf_hash; + f->load_order_search_start = &f->load_order; + + gzf_lseek(fp, 0, SEEK_SET); + if (gzf_read(fp, &f->header, sizeof(f->header)) != sizeof(f->header)) + { + error("error reading ELF header %s: %m", filename); + return NULL; + } + + if (f->header.e_ident[EI_MAG0] != ELFMAG0 + || f->header.e_ident[EI_MAG1] != ELFMAG1 + || f->header.e_ident[EI_MAG2] != ELFMAG2 + || f->header.e_ident[EI_MAG3] != ELFMAG3) + { + error("%s is not an ELF file", filename); + return NULL; + } + if (f->header.e_ident[EI_CLASS] != ELFCLASSM + || f->header.e_ident[EI_DATA] != ELFDATAM + || f->header.e_ident[EI_VERSION] != EV_CURRENT + || !MATCH_MACHINE(f->header.e_machine)) + { + error("ELF file %s not for this architecture", filename); + return NULL; + } + if (f->header.e_type != e_type && e_type != ET_NONE) + { + switch (e_type) { + case ET_REL: + error("ELF file %s not a relocatable object", filename); + break; + case ET_EXEC: + error("ELF file %s not an executable object", filename); + break; + default: + error("ELF file %s has wrong type, expecting %d got %d", + filename, e_type, f->header.e_type); + break; + } + return NULL; + } + + /* Read the section headers. */ + + if (f->header.e_shentsize != sizeof(ElfW(Shdr))) + { + error("section header size mismatch %s: %lu != %lu", + filename, + (unsigned long)f->header.e_shentsize, + (unsigned long)sizeof(ElfW(Shdr))); + return NULL; + } + + shnum = f->header.e_shnum; + f->sections = xmalloc(sizeof(struct obj_section *) * shnum); + memset(f->sections, 0, sizeof(struct obj_section *) * shnum); + + section_headers = alloca(sizeof(ElfW(Shdr)) * shnum); + gzf_lseek(fp, f->header.e_shoff, SEEK_SET); + if (gzf_read(fp, section_headers, sizeof(ElfW(Shdr))*shnum) != sizeof(ElfW(Shdr))*shnum) + { + error("error reading ELF section headers %s: %m", filename); + return NULL; + } + + /* Read the section data. */ + + for (i = 0; i < shnum; ++i) + { + struct obj_section *sec; + + f->sections[i] = sec = arch_new_section(); + memset(sec, 0, sizeof(*sec)); + + sec->header = section_headers[i]; + sec->idx = i; + + switch (sec->header.sh_type) + { + case SHT_NULL: + case SHT_NOTE: + case SHT_NOBITS: + /* ignore */ + break; + + case SHT_PROGBITS: + case SHT_SYMTAB: + case SHT_STRTAB: + case SHT_RELM: + if (sec->header.sh_size > 0) + { + sec->contents = xmalloc(sec->header.sh_size); + gzf_lseek(fp, sec->header.sh_offset, SEEK_SET); + if (gzf_read(fp, sec->contents, sec->header.sh_size) != sec->header.sh_size) + { + error("error reading ELF section data %s: %m", filename); + return NULL; + } + } + else + sec->contents = NULL; + break; + +#if SHT_RELM == SHT_REL + case SHT_RELA: + if (sec->header.sh_size) { + error("RELA relocations not supported on this architecture %s", filename); + return NULL; + } + break; +#else + case SHT_REL: + if (sec->header.sh_size) { + error("REL relocations not supported on this architecture %s", filename); + return NULL; + } + break; +#endif + + default: + if (sec->header.sh_type >= SHT_LOPROC) + { + if (arch_load_proc_section(sec, fp) < 0) + return NULL; + break; + } + + error("can't handle sections of type %ld %s", + (long)sec->header.sh_type, filename); + return NULL; + } + } + + /* Do what sort of interpretation as needed by each section. */ + + shstrtab = f->sections[f->header.e_shstrndx]->contents; + + for (i = 0; i < shnum; ++i) + { + struct obj_section *sec = f->sections[i]; + sec->name = shstrtab + sec->header.sh_name; + } + + for (i = 0; i < shnum; ++i) + { + struct obj_section *sec = f->sections[i]; + + /* .modinfo and .modstring should be contents only but gcc has no + * attribute for that. The kernel may have marked these sections as + * ALLOC, ignore the allocate bit. + */ + if (strcmp(sec->name, ".modinfo") == 0 || + strcmp(sec->name, ".modstring") == 0) + sec->header.sh_flags &= ~SHF_ALLOC; + + if (sec->header.sh_flags & SHF_ALLOC) + obj_insert_section_load_order(f, sec); + + switch (sec->header.sh_type) + { + case SHT_SYMTAB: + { + unsigned long nsym, j; + char *strtab; + ElfW(Sym) *sym; + + if (sec->header.sh_entsize != sizeof(ElfW(Sym))) + { + error("symbol size mismatch %s: %lu != %lu", + filename, + (unsigned long)sec->header.sh_entsize, + (unsigned long)sizeof(ElfW(Sym))); + return NULL; + } + + nsym = sec->header.sh_size / sizeof(ElfW(Sym)); + strtab = f->sections[sec->header.sh_link]->contents; + sym = (ElfW(Sym) *) sec->contents; + + /* Allocate space for a table of local symbols. */ + j = f->local_symtab_size = sec->header.sh_info; + f->local_symtab = xmalloc(j *= sizeof(struct obj_symbol *)); + memset(f->local_symtab, 0, j); + + /* Insert all symbols into the hash table. */ + for (j = 1, ++sym; j < nsym; ++j, ++sym) + { + const char *name; + if (sym->st_name) + name = strtab+sym->st_name; + else + name = f->sections[sym->st_shndx]->name; + + obj_add_symbol(f, name, j, sym->st_info, sym->st_shndx, + sym->st_value, sym->st_size); + + } + } + break; + } + } + + /* second pass to add relocation data to symbols */ + for (i = 0; i < shnum; ++i) + { + struct obj_section *sec = f->sections[i]; + switch (sec->header.sh_type) + { + case SHT_RELM: + { + unsigned long nrel, j; + ElfW(RelM) *rel; + struct obj_section *symtab; + char *strtab; + if (sec->header.sh_entsize != sizeof(ElfW(RelM))) + { + error("relocation entry size mismatch %s: %lu != %lu", + filename, + (unsigned long)sec->header.sh_entsize, + (unsigned long)sizeof(ElfW(RelM))); + return NULL; + } + + nrel = sec->header.sh_size / sizeof(ElfW(RelM)); + rel = (ElfW(RelM) *) sec->contents; + symtab = f->sections[sec->header.sh_link]; + strtab = f->sections[symtab->header.sh_link]->contents; + + /* Save the relocate type in each symbol entry. */ + for (j = 0; j < nrel; ++j, ++rel) + { + ElfW(Sym) *extsym; + struct obj_symbol *intsym; + unsigned long symndx; + symndx = ELFW(R_SYM)(rel->r_info); + if (symndx) + { + extsym = ((ElfW(Sym) *) symtab->contents) + symndx; + if (ELFW(ST_BIND)(extsym->st_info) == STB_LOCAL) + { + /* Local symbols we look up in the local table to be sure + we get the one that is really intended. */ + intsym = f->local_symtab[symndx]; + } + else + { + /* Others we look up in the hash table. */ + const char *name; + if (extsym->st_name) + name = strtab + extsym->st_name; + else + name = f->sections[extsym->st_shndx]->name; + intsym = obj_find_symbol(f, name); + } + intsym->r_type = ELFW(R_TYPE)(rel->r_info); + } + } + } + break; + } + } + + f->filename = xstrdup(filename); + + return f; +} + +void obj_free(struct obj_file *f) +{ + struct obj_section *sec; + struct obj_symbol *sym; + struct obj_symbol *next; + int i; + int n; + + if (f->sections) { + n = f->header.e_shnum; + for (i = 0; i < n; ++i) { + if ((sec = f->sections[i]) != NULL) { + if (sec->contents) + free(sec->contents); + free(sec); + } + } + free(f->sections); + } + + for (i = 0; i < HASH_BUCKETS; ++i) { + for (sym = f->symtab[i]; sym; sym = next) { + next = sym->next; + free(sym); + } + } + + if (f->local_symtab) + free(f->local_symtab); + + if (f->filename) + free((char *)(f->filename)); + + if (f->persist) + free((char *)(f->persist)); + + free(f); +} |