/* * Copyright (C) 2002 Richard Henderson * Copyright (C) 2001 Rusty Russell, 2002 Rusty Russell IBM. * * 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 */ #include #include #include #include #include #include #include #include #include #include static unsigned int find_sec(Elf_Ehdr *hdr, Elf_Shdr *sechdrs, const char *secstrings, const char *name) { unsigned int i; for (i = 1; i < hdr->e_shnum; i++) { debug("find_sec: %s %s\n", name, secstrings+sechdrs[i].sh_name); /* Alloc bit cleared means "ignore it." */ if ((sechdrs[i].sh_flags & SHF_ALLOC) && !strcmp(secstrings+sechdrs[i].sh_name, name)) return i; } return 0; } /* Provided by the linker */ extern const struct kernel_symbol __usymtab_start[]; extern const struct kernel_symbol __usymtab_end[]; /* lookup symbol in given range of kernel_symbols */ static const struct kernel_symbol *lookup_symbol(const char *name, const struct kernel_symbol *start, const struct kernel_symbol *stop) { const struct kernel_symbol *ks = start; for (; ks < stop; ks++) { if (strcmp(ks->name, name) == 0) return ks; } return NULL; } static unsigned long resolve_symbol(Elf32_Shdr *sechdrs, const char *name) { const struct kernel_symbol *ks; debug("%s: %s\n", __FUNCTION__, name); ks = lookup_symbol(name, __usymtab_start, __usymtab_end); if (ks) return ks->value; return 0; } /* Change all symbols so that sh_value encodes the pointer directly. */ static int simplify_symbols(Elf32_Shdr *sechdrs, unsigned int symindex, const char *strtab) { Elf32_Sym *sym = (void *)sechdrs[symindex].sh_addr; unsigned long secbase; unsigned int i, n = sechdrs[symindex].sh_size / sizeof(Elf32_Sym); int ret = 0; for (i = 1; i < n; i++) { switch (sym[i].st_shndx) { case SHN_COMMON: /* We compiled with -fno-common. These are not supposed to happen. */ printf("Common symbol: %s\n", strtab + sym[i].st_name); printf("please compile with -fno-common\n"); ret = -1; break; case SHN_ABS: /* Don't need to do anything */ debug("Absolute symbol: 0x%08lx\n", (long)sym[i].st_value); break; case SHN_UNDEF: sym[i].st_value = resolve_symbol(sechdrs, strtab + sym[i].st_name); debug("undef : %20s 0x%08x 0x%08lx\n", strtab + sym[i].st_name, sym[i].st_value); /* Ok if resolved. */ if (sym[i].st_value != 0) break; /* Ok if weak. */ if (ELF32_ST_BIND(sym[i].st_info) == STB_WEAK) break; printf("Unknown symbol %s\n", strtab + sym[i].st_name); ret = -1; break; default: secbase = sechdrs[sym[i].st_shndx].sh_addr; debug("default: %20s 0x%08x 0x%08lx\n", strtab + sym[i].st_name, sym[i].st_value, secbase); sym[i].st_value += secbase; break; } } return ret; } #define ALIGN(x,a) __ALIGN_MASK(x,(typeof(x))(a)-1) #define __ALIGN_MASK(x,mask) (((x)+(mask))&~(mask)) /* Update size with this section: return offset. */ static long get_offset(unsigned long *size, Elf32_Shdr *sechdr) { long ret; ret = ALIGN(*size, sechdr->sh_addralign ?: 1); *size = ret + sechdr->sh_size; return ret; } static void layout_sections( struct module *mod, const Elf32_Ehdr *hdr, Elf32_Shdr *sechdrs, const char *secstrings) { static unsigned long const masks[][2] = { /* NOTE: all executable code must be the first section * in this array; otherwise modify the text_size * finder in the two loops below */ { SHF_EXECINSTR | SHF_ALLOC }, { SHF_ALLOC, SHF_WRITE }, { SHF_WRITE | SHF_ALLOC }, { SHF_ALLOC, 0 } }; unsigned int m, i; for (i = 0; i < hdr->e_shnum; i++) sechdrs[i].sh_entsize = ~0UL; debug("Core section allocation order:\n"); for (m = 0; m < ARRAY_SIZE(masks); ++m) { for (i = 0; i < hdr->e_shnum; ++i) { Elf32_Shdr *s = &sechdrs[i]; if ((s->sh_flags & masks[m][0]) != masks[m][0] || (s->sh_flags & masks[m][1]) || s->sh_entsize != ~0UL || strncmp(secstrings + s->sh_name, ".init", 5) == 0) continue; s->sh_entsize = get_offset(&mod->core_size, s); debug("\t%s 0x%08x\n", secstrings + s->sh_name, s->sh_entsize); } } debug("core_size: %ld\n", mod->core_size); } LIST_HEAD(module_list); struct module * load_module(void *mod_image, unsigned long len) { struct module *module = NULL; Elf32_Ehdr *ehdr; /* Elf header structure pointer */ Elf32_Shdr *sechdrs; /* Section header structure pointer */ Elf32_Sym *sym; unsigned int numsyms; char *strtab = 0; /* String table pointer */ int i; /* Loop counter */ unsigned int strindex = 0; unsigned int symindex = 0; unsigned int modindex; char *secstrings; void *ptr = NULL; int err; int cmdindex; if (len < sizeof(*ehdr)) return NULL; ehdr = (Elf32_Ehdr *)mod_image; if (len < ehdr->e_shoff + ehdr->e_shnum * sizeof(Elf_Shdr)) return NULL; /* Find the section header string table for output info */ sechdrs = (Elf32_Shdr *) (mod_image + ehdr->e_shoff + (ehdr->e_shstrndx * sizeof (Elf32_Shdr))); if (sechdrs->sh_type == SHT_SYMTAB) strtab = (char *) (mod_image + sechdrs->sh_offset); /* Convenience variables */ sechdrs = (void *)ehdr + ehdr->e_shoff; secstrings = (void *)ehdr + sechdrs[ehdr->e_shstrndx].sh_offset; sechdrs[0].sh_addr = 0; for (i = 0; i < ehdr->e_shnum; i++) { debug("%d addr: 0x%08x size: 0x%08x ofs: 0x%08x\n", i, sechdrs[i].sh_addr, sechdrs[i].sh_size, sechdrs[i].sh_offset); /* Mark all sections sh_addr with their address in the temporary image. */ sechdrs[i].sh_addr = (size_t)ehdr + sechdrs[i].sh_offset; if (sechdrs[i].sh_type == SHT_SYMTAB) { symindex = i; strindex = sechdrs[i].sh_link; strtab = mod_image + sechdrs[strindex].sh_offset; } } modindex = find_sec(ehdr, sechdrs, secstrings, ".gnu.linkonce.this_module"); if (!modindex) { printf("No module found in object\n"); err = -ENOEXEC; goto cleanup; } module = (void *)sechdrs[modindex].sh_addr; if (symindex == 0) { printf("module has no symbols (stripped?)\n"); err = -ENOEXEC; goto cleanup; } /* Determine total sizes, and put offsets in sh_entsize. For now this is done generically; there doesn't appear to be any special cases for the architectures. */ layout_sections(module, ehdr, sechdrs, secstrings); ptr = xzalloc(module->core_size); module->module_core = ptr; /* Transfer each section which specifies SHF_ALLOC */ debug("final section addresses:\n"); for (i = 0; i < ehdr->e_shnum; i++) { void *dest; if (!(sechdrs[i].sh_flags & SHF_ALLOC)) continue; dest = module->module_core + sechdrs[i].sh_entsize; debug("0x%08x dest 0x%p\n", sechdrs[i].sh_addr, dest); if (sechdrs[i].sh_type != SHT_NOBITS) memcpy(dest, (void *)sechdrs[i].sh_addr, sechdrs[i].sh_size); /* Update sh_addr to point to copy in image. */ sechdrs[i].sh_addr = (unsigned long)dest; } /* Fix up syms, so that st_value is a pointer to location. */ err = simplify_symbols(sechdrs, symindex, strtab); if (err < 0) goto cleanup; for (i = 0; i < ehdr->e_shnum; i++) { if (sechdrs[i].sh_type == SHT_REL) { apply_relocate(sechdrs, strtab, symindex, i, module); } if (sechdrs[i].sh_type == SHT_RELA) apply_relocate_add(sechdrs, strtab, symindex, i, module); } numsyms = sechdrs[symindex].sh_size / sizeof(Elf32_Sym); sym = (void *)sechdrs[symindex].sh_addr; cmdindex = find_sec(ehdr, sechdrs, secstrings, ".barebox_cmd"); if (cmdindex) { struct command *cmd =(struct command *)sechdrs[cmdindex].sh_addr; for (i = 0; i < sechdrs[cmdindex].sh_size / sizeof(struct command); i++) { register_command(cmd); cmd++; } } for (i = 0; i < numsyms; i++) { if (!strcmp(strtab + sym[i].st_name, MODULE_SYMBOL_PREFIX "init_module")) { printf("found init_module() at 0x%08x\n", sym[i].st_value); module->init = (void *)sym[i].st_value; } } list_add_tail(&module->list, &module_list); return module; cleanup: if (ptr) free(ptr); if (module) free(module); return NULL; }