diff options
author | royger <royger@FreeBSD.org> | 2016-01-20 13:23:02 +0000 |
---|---|---|
committer | royger <royger@FreeBSD.org> | 2016-01-20 13:23:02 +0000 |
commit | 0ad0bd3893133b318d166ac9e4a4a468247c2107 (patch) | |
tree | 0f6c21d198dbf57fc7d64f7e183bc129440fc75a /sys/boot/common/load_elf.c | |
parent | 2f8109724febc7f9ea2083413f50d5eceb064058 (diff) | |
download | FreeBSD-src-0ad0bd3893133b318d166ac9e4a4a468247c2107.zip FreeBSD-src-0ad0bd3893133b318d166ac9e4a4a468247c2107.tar.gz |
MFC r277215, r277291, r277418, r280953 and r280954:
loader: implement multiboot support for Xen Dom0
Note that only the subset of the multiboot specification needed in order to
boot a Xen Dom0 is implemented.
Sponsored by: Citrix Systems R&D
Diffstat (limited to 'sys/boot/common/load_elf.c')
-rw-r--r-- | sys/boot/common/load_elf.c | 265 |
1 files changed, 214 insertions, 51 deletions
diff --git a/sys/boot/common/load_elf.c b/sys/boot/common/load_elf.c index edbad05..200a29c 100644 --- a/sys/boot/common/load_elf.c +++ b/sys/boot/common/load_elf.c @@ -76,7 +76,8 @@ static int __elfN(loadimage)(struct preloaded_file *mp, elf_file_t ef, u_int64_t static int __elfN(lookup_symbol)(struct preloaded_file *mp, elf_file_t ef, const char* name, Elf_Sym* sym); static int __elfN(reloc_ptr)(struct preloaded_file *mp, elf_file_t ef, Elf_Addr p, void *val, size_t len); -static int __elfN(parse_modmetadata)(struct preloaded_file *mp, elf_file_t ef); +static int __elfN(parse_modmetadata)(struct preloaded_file *mp, elf_file_t ef, + Elf_Addr p_start, Elf_Addr p_end); static symaddr_fn __elfN(symaddr); static char *fake_modname(const char *name); @@ -85,6 +86,61 @@ const char *__elfN(moduletype) = "elf module"; u_int64_t __elfN(relocation_offset) = 0; +static int +__elfN(load_elf_header)(char *filename, elf_file_t ef) +{ + ssize_t bytes_read; + Elf_Ehdr *ehdr; + int err; + + /* + * Open the image, read and validate the ELF header + */ + if (filename == NULL) /* can't handle nameless */ + return (EFTYPE); + if ((ef->fd = open(filename, O_RDONLY)) == -1) + return (errno); + ef->firstpage = malloc(PAGE_SIZE); + if (ef->firstpage == NULL) { + close(ef->fd); + return (ENOMEM); + } + bytes_read = read(ef->fd, ef->firstpage, PAGE_SIZE); + ef->firstlen = (size_t)bytes_read; + if (bytes_read < 0 || ef->firstlen <= sizeof(Elf_Ehdr)) { + err = EFTYPE; /* could be EIO, but may be small file */ + goto error; + } + ehdr = ef->ehdr = (Elf_Ehdr *)ef->firstpage; + + /* Is it ELF? */ + if (!IS_ELF(*ehdr)) { + err = EFTYPE; + goto error; + } + if (ehdr->e_ident[EI_CLASS] != ELF_TARG_CLASS || /* Layout ? */ + ehdr->e_ident[EI_DATA] != ELF_TARG_DATA || + ehdr->e_ident[EI_VERSION] != EV_CURRENT || /* Version ? */ + ehdr->e_version != EV_CURRENT || + ehdr->e_machine != ELF_TARG_MACH) { /* Machine ? */ + err = EFTYPE; + goto error; + } + + return (0); + +error: + if (ef->firstpage != NULL) { + free(ef->firstpage); + ef->firstpage = NULL; + } + if (ef->fd != -1) { + close(ef->fd); + ef->fd = -1; + } + return (err); +} + /* * Attempt to load the file (file) as an ELF module. It will be stored at * (dest), and a pointer to a module structure describing the loaded object @@ -93,54 +149,32 @@ u_int64_t __elfN(relocation_offset) = 0; int __elfN(loadfile)(char *filename, u_int64_t dest, struct preloaded_file **result) { + return (__elfN(loadfile_raw)(filename, dest, result, 0)); +} + +int +__elfN(loadfile_raw)(char *filename, u_int64_t dest, + struct preloaded_file **result, int multiboot) +{ struct preloaded_file *fp, *kfp; struct elf_file ef; Elf_Ehdr *ehdr; int err; - ssize_t bytes_read; fp = NULL; bzero(&ef, sizeof(struct elf_file)); + ef.fd = -1; - /* - * Open the image, read and validate the ELF header - */ - if (filename == NULL) /* can't handle nameless */ - return(EFTYPE); - if ((ef.fd = open(filename, O_RDONLY)) == -1) - return(errno); - ef.firstpage = malloc(PAGE_SIZE); - if (ef.firstpage == NULL) { - close(ef.fd); - return(ENOMEM); - } - bytes_read = read(ef.fd, ef.firstpage, PAGE_SIZE); - ef.firstlen = (size_t)bytes_read; - if (bytes_read < 0 || ef.firstlen <= sizeof(Elf_Ehdr)) { - err = EFTYPE; /* could be EIO, but may be small file */ - goto oerr; - } - ehdr = ef.ehdr = (Elf_Ehdr *)ef.firstpage; - - /* Is it ELF? */ - if (!IS_ELF(*ehdr)) { - err = EFTYPE; - goto oerr; - } - if (ehdr->e_ident[EI_CLASS] != ELF_TARG_CLASS || /* Layout ? */ - ehdr->e_ident[EI_DATA] != ELF_TARG_DATA || - ehdr->e_ident[EI_VERSION] != EV_CURRENT || /* Version ? */ - ehdr->e_version != EV_CURRENT || - ehdr->e_machine != ELF_TARG_MACH) { /* Machine ? */ - err = EFTYPE; - goto oerr; - } + err = __elfN(load_elf_header)(filename, &ef); + if (err != 0) + return (err); + ehdr = ef.ehdr; /* * Check to see what sort of module we are. */ - kfp = file_findfile(NULL, NULL); + kfp = file_findfile(NULL, __elfN(kerneltype)); #ifdef __powerpc__ /* * Kernels can be ET_DYN, so just assume the first loaded object is the @@ -177,6 +211,11 @@ __elfN(loadfile)(char *filename, u_int64_t dest, struct preloaded_file **result) } else if (ehdr->e_type == ET_DYN) { /* Looks like a kld module */ + if (multiboot != 0) { + printf("elf" __XSTRING(__ELF_WORD_SIZE) "_loadfile: can't load module as multiboot\n"); + err = EPERM; + goto oerr; + } if (kfp == NULL) { printf("elf" __XSTRING(__ELF_WORD_SIZE) "_loadfile: can't load module before kernel\n"); err = EPERM; @@ -209,10 +248,14 @@ __elfN(loadfile)(char *filename, u_int64_t dest, struct preloaded_file **result) err = EPERM; goto out; } - if (ef.kernel) + if (ef.kernel == 1 && multiboot == 0) setenv("kernelname", filename, 1); fp->f_name = strdup(filename); - fp->f_type = strdup(ef.kernel ? __elfN(kerneltype) : __elfN(moduletype)); + if (multiboot == 0) + fp->f_type = strdup(ef.kernel ? + __elfN(kerneltype) : __elfN(moduletype)); + else + fp->f_type = strdup("elf multiboot kernel"); #ifdef ELF_VERBOSE if (ef.kernel) @@ -240,7 +283,8 @@ __elfN(loadfile)(char *filename, u_int64_t dest, struct preloaded_file **result) out: if (ef.firstpage) free(ef.firstpage); - close(ef.fd); + if (ef.fd != -1) + close(ef.fd); return(err); } @@ -269,6 +313,8 @@ __elfN(loadimage)(struct preloaded_file *fp, elf_file_t ef, u_int64_t off) int symtabindex; Elf_Size size; u_int fpcopy; + Elf_Sym sym; + Elf_Addr p_start, p_end; dp = NULL; shdr = NULL; @@ -571,7 +617,15 @@ nosyms: COPYOUT(ef->hashtab + 1, &ef->nchains, sizeof(ef->nchains)); ef->buckets = ef->hashtab + 2; ef->chains = ef->buckets + ef->nbuckets; - if (__elfN(parse_modmetadata)(fp, ef) == 0) + + if (__elfN(lookup_symbol)(fp, ef, "__start_set_modmetadata_set", &sym) != 0) + return 0; + p_start = sym.st_value + ef->off; + if (__elfN(lookup_symbol)(fp, ef, "__stop_set_modmetadata_set", &sym) != 0) + return ENOENT; + p_end = sym.st_value + ef->off; + + if (__elfN(parse_modmetadata)(fp, ef, p_start, p_end) == 0) goto out; if (ef->kernel) /* kernel must not depend on anything */ @@ -634,7 +688,123 @@ struct mod_metadata32 { #endif int -__elfN(parse_modmetadata)(struct preloaded_file *fp, elf_file_t ef) +__elfN(load_modmetadata)(struct preloaded_file *fp, u_int64_t dest) +{ + struct elf_file ef; + int err, i, j; + Elf_Shdr *sh_meta, *shdr = NULL; + Elf_Shdr *sh_data[2]; + char *shstrtab = NULL; + size_t size; + Elf_Addr p_start, p_end; + + bzero(&ef, sizeof(struct elf_file)); + ef.fd = -1; + + err = __elfN(load_elf_header)(fp->f_name, &ef); + if (err != 0) + goto out; + + if (ef.ehdr->e_type == ET_EXEC) { + ef.kernel = 1; + } else if (ef.ehdr->e_type != ET_DYN) { + err = EFTYPE; + goto out; + } + + size = ef.ehdr->e_shnum * ef.ehdr->e_shentsize; + shdr = alloc_pread(ef.fd, ef.ehdr->e_shoff, size); + if (shdr == NULL) { + err = ENOMEM; + goto out; + } + + /* Load shstrtab. */ + shstrtab = alloc_pread(ef.fd, shdr[ef.ehdr->e_shstrndx].sh_offset, + shdr[ef.ehdr->e_shstrndx].sh_size); + if (shstrtab == NULL) { + printf("\nelf" __XSTRING(__ELF_WORD_SIZE) + "load_modmetadata: unable to load shstrtab\n"); + err = EFTYPE; + goto out; + } + + /* Find set_modmetadata_set and data sections. */ + sh_data[0] = sh_data[1] = sh_meta = NULL; + for (i = 0, j = 0; i < ef.ehdr->e_shnum; i++) { + if (strcmp(&shstrtab[shdr[i].sh_name], + "set_modmetadata_set") == 0) { + sh_meta = &shdr[i]; + } + if ((strcmp(&shstrtab[shdr[i].sh_name], ".data") == 0) || + (strcmp(&shstrtab[shdr[i].sh_name], ".rodata") == 0)) { + sh_data[j++] = &shdr[i]; + } + } + if (sh_meta == NULL || sh_data[0] == NULL || sh_data[1] == NULL) { + printf("\nelf" __XSTRING(__ELF_WORD_SIZE) + "load_modmetadata: unable to find set_modmetadata_set or data sections\n"); + err = EFTYPE; + goto out; + } + + /* Load set_modmetadata_set into memory */ + err = kern_pread(ef.fd, dest, sh_meta->sh_size, sh_meta->sh_offset); + if (err != 0) { + printf("\nelf" __XSTRING(__ELF_WORD_SIZE) + "load_modmetadata: unable to load set_modmetadata_set: %d\n", err); + goto out; + } + p_start = dest; + p_end = dest + sh_meta->sh_size; + dest += sh_meta->sh_size; + + /* Load data sections into memory. */ + err = kern_pread(ef.fd, dest, sh_data[0]->sh_size, + sh_data[0]->sh_offset); + if (err != 0) { + printf("\nelf" __XSTRING(__ELF_WORD_SIZE) + "load_modmetadata: unable to load data: %d\n", err); + goto out; + } + + /* + * We have to increment the dest, so that the offset is the same into + * both the .rodata and .data sections. + */ + ef.off = -(sh_data[0]->sh_addr - dest); + dest += (sh_data[1]->sh_addr - sh_data[0]->sh_addr); + + err = kern_pread(ef.fd, dest, sh_data[1]->sh_size, + sh_data[1]->sh_offset); + if (err != 0) { + printf("\nelf" __XSTRING(__ELF_WORD_SIZE) + "load_modmetadata: unable to load data: %d\n", err); + goto out; + } + + err = __elfN(parse_modmetadata)(fp, &ef, p_start, p_end); + if (err != 0) { + printf("\nelf" __XSTRING(__ELF_WORD_SIZE) + "load_modmetadata: unable to parse metadata: %d\n", err); + goto out; + } + +out: + if (shstrtab != NULL) + free(shstrtab); + if (shdr != NULL) + free(shdr); + if (ef.firstpage != NULL) + free(ef.firstpage); + if (ef.fd != -1) + close(ef.fd); + return (err); +} + +int +__elfN(parse_modmetadata)(struct preloaded_file *fp, elf_file_t ef, + Elf_Addr p_start, Elf_Addr p_end) { struct mod_metadata md; #if (defined(__i386__) || defined(__powerpc__)) && __ELF_WORD_SIZE == 64 @@ -644,20 +814,13 @@ __elfN(parse_modmetadata)(struct preloaded_file *fp, elf_file_t ef) #endif struct mod_depend *mdepend; struct mod_version mver; - Elf_Sym sym; char *s; int error, modcnt, minfolen; - Elf_Addr v, p, p_stop; - - if (__elfN(lookup_symbol)(fp, ef, "__start_set_modmetadata_set", &sym) != 0) - return 0; - p = sym.st_value + ef->off; - if (__elfN(lookup_symbol)(fp, ef, "__stop_set_modmetadata_set", &sym) != 0) - return ENOENT; - p_stop = sym.st_value + ef->off; + Elf_Addr v, p; modcnt = 0; - while (p < p_stop) { + p = p_start; + while (p < p_end) { COPYOUT(p, &v, sizeof(v)); error = __elfN(reloc_ptr)(fp, ef, p, &v, sizeof(v)); if (error == EOPNOTSUPP) |