From 70b7ffee1bfc1552ed61b6259dd533d9e577ad64 Mon Sep 17 00:00:00 2001 From: Ian Dowse Date: Sun, 29 Aug 2004 01:21:51 +0000 Subject: [PATCH] Add support for completing the installation of ELF relocatable object format modules that were read in by the loader. Loading modules via the loader should now work on the amd64 platform. --- sys/kern/link_elf_obj.c | 211 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 208 insertions(+), 3 deletions(-) diff --git a/sys/kern/link_elf_obj.c b/sys/kern/link_elf_obj.c index e5fa30e9804..ad4ea35831a 100644 --- a/sys/kern/link_elf_obj.c +++ b/sys/kern/link_elf_obj.c @@ -82,6 +82,7 @@ typedef struct { typedef struct elf_file { struct linker_file lf; /* Common fields */ + int preloaded; caddr_t address; /* Relocation address */ vm_object_t object; /* VM object to hold file pages */ Elf_Shdr *e_shdr; @@ -168,15 +169,206 @@ static int link_elf_link_preload(linker_class_t cls, const char *filename, linker_file_t *result) { + Elf_Ehdr *hdr; + Elf_Shdr *shdr; + Elf_Sym *es; + void *modptr, *baseptr, *sizeptr; + char *type; + elf_file_t ef; + linker_file_t lf; + Elf_Addr off; + int error, i, j, pb, ra, rl, shstrindex, symstrindex, symtabindex; + + /* Look to see if we have the file preloaded */ + modptr = preload_search_by_name(filename); + if (modptr == NULL) + return ENOENT; + + type = (char *)preload_search_info(modptr, MODINFO_TYPE); + baseptr = preload_search_info(modptr, MODINFO_ADDR); + sizeptr = preload_search_info(modptr, MODINFO_SIZE); + hdr = (Elf_Ehdr *)preload_search_info(modptr, MODINFO_METADATA | + MODINFOMD_ELFHDR); + shdr = (Elf_Shdr *)preload_search_info(modptr, MODINFO_METADATA | + MODINFOMD_SHDR); + if (type == NULL || (strcmp(type, "elf" __XSTRING(__ELF_WORD_SIZE) + " obj module") != 0 && + strcmp(type, "elf obj module") != 0)) { + return (EFTYPE); + } + if (baseptr == NULL || sizeptr == NULL || hdr == NULL || + shdr == NULL) + return (EINVAL); + + lf = linker_make_file(filename, &link_elf_class); + if (lf == NULL) + return (ENOMEM); + + ef = (elf_file_t)lf; + ef->preloaded = 1; + ef->address = *(caddr_t *)baseptr; + lf->address = *(caddr_t *)baseptr; + lf->size = *(size_t *)sizeptr; + + if (hdr->e_ident[EI_CLASS] != ELF_TARG_CLASS || + hdr->e_ident[EI_DATA] != ELF_TARG_DATA || + hdr->e_ident[EI_VERSION] != EV_CURRENT || + hdr->e_version != EV_CURRENT || + hdr->e_type != ET_REL || + hdr->e_machine != ELF_TARG_MACH) { + error = EFTYPE; + goto out; + } + ef->e_shdr = shdr; + + /* Scan the section header for information and table sizing. */ + symtabindex = -1; + symstrindex = -1; + for (i = 0; i < hdr->e_shnum; i++) { + switch (shdr[i].sh_type) { + case SHT_PROGBITS: + case SHT_NOBITS: + ef->nprogtab++; + break; + case SHT_SYMTAB: + symtabindex = i; + symstrindex = shdr[i].sh_link; + break; + case SHT_REL: + ef->nrel++; + break; + case SHT_RELA: + ef->nrela++; + break; + } + } + + shstrindex = hdr->e_shstrndx; + if (ef->nprogtab == 0 || symstrindex < 0 || + symstrindex >= hdr->e_shnum || + shdr[symstrindex].sh_type != SHT_STRTAB || shstrindex == 0 || + shstrindex >= hdr->e_shnum || + shdr[shstrindex].sh_type != SHT_STRTAB) { + printf("%s: bad/missing section headers\n", filename); + error = ENOEXEC; + goto out; + } + + /* Allocate space for tracking the load chunks */ + if (ef->nprogtab != 0) + ef->progtab = malloc(ef->nprogtab * sizeof(*ef->progtab), + M_LINKER, M_WAITOK | M_ZERO); + if (ef->nrel != 0) + ef->reltab = malloc(ef->nrel * sizeof(*ef->reltab), M_LINKER, + M_WAITOK | M_ZERO); + if (ef->nrela != 0) + ef->relatab = malloc(ef->nrela * sizeof(*ef->relatab), M_LINKER, + M_WAITOK | M_ZERO); + if ((ef->nprogtab != 0 && ef->progtab == NULL) || + (ef->nrel != 0 && ef->reltab == NULL) || + (ef->nrela != 0 && ef->relatab == NULL)) { + error = ENOMEM; + goto out; + } + + /* XXX, relocate the sh_addr fields saved by the loader. */ + off = 0; + for (i = 0; i < hdr->e_shnum; i++) { + if (shdr[i].sh_addr != 0 && (off == 0 || shdr[i].sh_addr < off)) + off = shdr[i].sh_addr; + } + for (i = 0; i < hdr->e_shnum; i++) { + if (shdr[i].sh_addr != 0) + shdr[i].sh_addr = shdr[i].sh_addr - off + + (Elf_Addr)ef->address; + } + + ef->ddbsymcnt = shdr[symtabindex].sh_size / sizeof(Elf_Sym); + ef->ddbsymtab = (Elf_Sym *)shdr[symtabindex].sh_addr; + ef->ddbstrcnt = shdr[symstrindex].sh_size; + ef->ddbstrtab = (char *)shdr[symstrindex].sh_addr; + ef->shstrcnt = shdr[shstrindex].sh_size; + ef->shstrtab = (char *)shdr[shstrindex].sh_addr; + + /* Now fill out progtab and the relocation tables. */ + pb = 0; + rl = 0; + ra = 0; + for (i = 0; i < hdr->e_shnum; i++) { + switch (shdr[i].sh_type) { + case SHT_PROGBITS: + case SHT_NOBITS: + ef->progtab[pb].addr = (void *)shdr[i].sh_addr; + if (shdr[i].sh_type == SHT_PROGBITS) + ef->progtab[pb].name = "<>"; + else + ef->progtab[pb].name = "<>"; + ef->progtab[pb].size = shdr[i].sh_size; + ef->progtab[pb].sec = i; + if (ef->shstrtab && shdr[i].sh_name != 0) + ef->progtab[pb].name = + ef->shstrtab + shdr[i].sh_name; + + /* Update all symbol values with the offset. */ + for (j = 0; j < ef->ddbsymcnt; j++) { + es = &ef->ddbsymtab[j]; + if (es->st_shndx != i) + continue; + es->st_value += (Elf_Addr)ef->progtab[pb].addr; + } + pb++; + break; + case SHT_REL: + ef->reltab[rl].rel = (Elf_Rel *)shdr[i].sh_addr; + ef->reltab[rl].nrel = shdr[i].sh_size / sizeof(Elf_Rel); + ef->reltab[rl].sec = shdr[i].sh_info; + rl++; + break; + case SHT_RELA: + ef->relatab[ra].rela = (Elf_Rela *)shdr[i].sh_addr; + ef->relatab[ra].nrela = + shdr[i].sh_size / sizeof(Elf_Rela); + ef->relatab[ra].sec = shdr[i].sh_info; + ra++; + break; + } + } + if (pb != ef->nprogtab) + panic("lost progbits"); + if (rl != ef->nrel) + panic("lost rel"); + if (ra != ef->nrela) + panic("lost rela"); + + /* Local intra-module relocations */ + link_elf_reloc_local(lf); + + *result = lf; + return (0); + +out: /* preload not done this way */ - return (EFTYPE); + linker_file_unload(lf, LINKER_UNLOAD_FORCE); + return (error); } static int link_elf_link_preload_finish(linker_file_t lf) { - /* preload not done this way */ - return (EFTYPE); + elf_file_t ef; + int error; + + ef = (elf_file_t)lf; + error = relocate_file(ef); + if (error) + return error; + + /* Notify MD code that a module is being loaded. */ + error = elf_cpu_load_file(lf); + if (error) + return (error); + + return (0); } static int @@ -606,6 +798,19 @@ link_elf_unload_file(linker_file_t file) /* Notify MD code that a module is being unloaded. */ elf_cpu_unload_file(file); + if (ef->preloaded) { + if (ef->reltab) + free(ef->reltab, M_LINKER); + if (ef->relatab) + free(ef->relatab, M_LINKER); + if (ef->progtab) + free(ef->progtab, M_LINKER); + if (file->filename != NULL) + preload_delete_name(file->filename); + /* XXX reclaim module memory? */ + return; + } + for (i = 0; i < ef->nrel; i++) if (ef->reltab[i].rel) free(ef->reltab[i].rel, M_LINKER);