| /**************************************************************************** |
| * libs/libc/elf/elf_insert.c |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. The |
| * ASF licenses this file to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance with the |
| * License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| * License for the specific language governing permissions and limitations |
| * under the License. |
| * |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Included Files |
| ****************************************************************************/ |
| |
| #include <nuttx/config.h> |
| #include <assert.h> |
| #include <debug.h> |
| #include <errno.h> |
| #include <sys/param.h> |
| |
| #include <nuttx/lib/lib.h> |
| #include <nuttx/lib/elf.h> |
| |
| #include "elf.h" |
| |
| /**************************************************************************** |
| * Public Functions |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Name: libelf_dumploadinfo |
| * |
| * Description: |
| * Dump the load information to debug output. |
| * |
| ****************************************************************************/ |
| |
| #ifdef CONFIG_DEBUG_BINFMT_INFO |
| void libelf_dumploadinfo(FAR struct mod_loadinfo_s *loadinfo) |
| { |
| int i; |
| |
| binfo("LOAD_INFO:\n"); |
| binfo(" textalloc: %08lx\n", (long)loadinfo->textalloc); |
| binfo(" datastart: %08lx\n", (long)loadinfo->datastart); |
| binfo(" textsize: %ld\n", (long)loadinfo->textsize); |
| binfo(" datasize: %ld\n", (long)loadinfo->datasize); |
| binfo(" textalign: %zu\n", loadinfo->textalign); |
| binfo(" dataalign: %zu\n", loadinfo->dataalign); |
| binfo(" filelen: %ld\n", (long)loadinfo->filelen); |
| binfo(" filfd: %d\n", loadinfo->filfd); |
| binfo(" symtabidx: %d\n", loadinfo->symtabidx); |
| binfo(" strtabidx: %d\n", loadinfo->strtabidx); |
| |
| binfo("ELF Header:\n"); |
| binfo(" e_ident: %02x %02x %02x %02x\n", |
| loadinfo->ehdr.e_ident[0], loadinfo->ehdr.e_ident[1], |
| loadinfo->ehdr.e_ident[2], loadinfo->ehdr.e_ident[3]); |
| binfo(" e_type: %04x\n", loadinfo->ehdr.e_type); |
| binfo(" e_machine: %04x\n", loadinfo->ehdr.e_machine); |
| binfo(" e_version: %08x\n", loadinfo->ehdr.e_version); |
| binfo(" e_entry: %08lx\n", (long)loadinfo->ehdr.e_entry); |
| binfo(" e_phoff: %ju\n", (uintmax_t)loadinfo->ehdr.e_phoff); |
| binfo(" e_shoff: %ju\n", (uintmax_t)loadinfo->ehdr.e_shoff); |
| binfo(" e_flags: %08x\n", loadinfo->ehdr.e_flags); |
| binfo(" e_ehsize: %d\n", loadinfo->ehdr.e_ehsize); |
| binfo(" e_phentsize: %d\n", loadinfo->ehdr.e_phentsize); |
| binfo(" e_phnum: %d\n", loadinfo->ehdr.e_phnum); |
| binfo(" e_shentsize: %d\n", loadinfo->ehdr.e_shentsize); |
| binfo(" e_shnum: %d\n", loadinfo->ehdr.e_shnum); |
| binfo(" e_shstrndx: %d\n", loadinfo->ehdr.e_shstrndx); |
| |
| if (loadinfo->shdr && loadinfo->ehdr.e_shnum > 0) |
| { |
| for (i = 0; i < loadinfo->ehdr.e_shnum; i++) |
| { |
| FAR Elf_Shdr *shdr = &loadinfo->shdr[i]; |
| binfo("Sections %d:\n", i); |
| # ifdef CONFIG_ARCH_USE_SEPARATED_SECTION |
| if (loadinfo->ehdr.e_type == ET_REL) |
| { |
| binfo(" sh_alloc: %08jx\n", |
| (uintmax_t)loadinfo->sectalloc[i]); |
| } |
| # endif |
| |
| binfo(" sh_name: %08x\n", shdr->sh_name); |
| binfo(" sh_type: %08x\n", shdr->sh_type); |
| binfo(" sh_flags: %08jx\n", (uintmax_t)shdr->sh_flags); |
| binfo(" sh_addr: %08jx\n", (uintmax_t)shdr->sh_addr); |
| binfo(" sh_offset: %ju\n", (uintmax_t)shdr->sh_offset); |
| binfo(" sh_size: %ju\n", (uintmax_t)shdr->sh_size); |
| binfo(" sh_link: %d\n", shdr->sh_link); |
| binfo(" sh_info: %d\n", shdr->sh_info); |
| binfo(" sh_addralign: %ju\n", (uintmax_t)shdr->sh_addralign); |
| binfo(" sh_entsize: %ju\n", (uintmax_t)shdr->sh_entsize); |
| } |
| } |
| } |
| |
| /**************************************************************************** |
| * Name: libelf_dumpmodule |
| ****************************************************************************/ |
| |
| void libelf_dumpmodule(FAR struct module_s *modp) |
| { |
| binfo("Module:\n"); |
| #ifdef HAVE_LIBC_ELF_NAMES |
| binfo(" modname: %s\n", modp->modname); |
| #endif |
| binfo(" textalloc: %08lx\n", (long)modp->textalloc); |
| #if defined(CONFIG_FS_PROCFS) && !defined(CONFIG_FS_PROCFS_EXCLUDE_MODULE) |
| binfo(" dataalloc: %08lx\n", (long)modp->dataalloc); |
| binfo(" textsize: %ld\n", (long)modp->textsize); |
| #endif |
| |
| #ifdef CONFIG_ARCH_USE_SEPARATED_SECTION |
| binfo(" sectalloc: %p\n", modp->sectalloc); |
| binfo(" nsect: %ld\n", (long)modp->nsect); |
| for (int i = 0; i < modp->nsect; i++) |
| { |
| binfo(" sectalloc[%d]: %p\n", i, modp->sectalloc[i]); |
| } |
| |
| #endif |
| |
| #if CONFIG_LIBC_ELF_MAXDEPEND > 0 |
| binfo(" dependents: %d\n", modp->dependents); |
| for (int i = 0; i < modp->dependents; i++) |
| { |
| # ifdef HAVE_LIBC_ELF_NAMES |
| binfo("%d %s\n", i, modp->dependencies[i]->modname); |
| # else |
| binfo("%d\n", i); |
| # endif |
| libelf_dumpmodule(modp->dependencies[i]); |
| } |
| #endif |
| |
| binfo(" finiarr: %08lx\n", (long)modp->finiarr); |
| binfo(" nfini: %d\n", modp->nfini); |
| } |
| |
| #endif |
| /**************************************************************************** |
| * Name: elf_dumpentrypt |
| ****************************************************************************/ |
| |
| #ifdef CONFIG_LIBC_ELF_DUMPBUFFER |
| void libelf_dumpentrypt(FAR struct mod_loadinfo_s *loadinfo) |
| { |
| FAR const uint8_t *entry; |
| #ifdef CONFIG_ARCH_ADDRENV |
| int ret; |
| |
| /* If CONFIG_ARCH_ADDRENV=y, then the loaded ELF lies in a virtual address |
| * space that may not be in place now. libelf_addrenv_select() will |
| * temporarily instantiate that address space. |
| */ |
| |
| if (loadinfo->addrenv != NULL) |
| { |
| ret = libelf_addrenv_select(loadinfo); |
| if (ret < 0) |
| { |
| berr("ERROR: libelf_addrenv_select() failed: %d\n", ret); |
| return; |
| } |
| } |
| #endif |
| |
| if (loadinfo->ehdr.e_type == ET_REL) |
| { |
| entry = (FAR const uint8_t *) |
| ((uintptr_t)loadinfo->textalloc + loadinfo->ehdr.e_entry); |
| } |
| else if (loadinfo->ehdr.e_type == ET_EXEC) |
| { |
| entry = (FAR const uint8_t *)loadinfo->ehdr.e_entry; |
| } |
| else |
| { |
| entry = (FAR const uint8_t *)loadinfo->textalloc; |
| } |
| |
| libelf_dumpbuffer("Entry code", entry, |
| MIN(loadinfo->textsize - loadinfo->ehdr.e_entry, 512)); |
| |
| #ifdef CONFIG_ARCH_ADDRENV |
| /* Restore the original address environment */ |
| |
| if (loadinfo->addrenv != NULL) |
| { |
| ret = libelf_addrenv_restore(loadinfo); |
| if (ret < 0) |
| { |
| berr("ERROR: libelf_addrenv_restore() failed: %d\n", ret); |
| } |
| } |
| #endif |
| } |
| #endif |
| |
| /**************************************************************************** |
| * Name: libelf_loadsymtab |
| * |
| * Description: |
| * Load the symbol table into memory. |
| * |
| ****************************************************************************/ |
| |
| static int libelf_loadsymtab(FAR struct module_s *modp, |
| FAR struct mod_loadinfo_s *loadinfo) |
| { |
| FAR Elf_Shdr *symhdr = &loadinfo->shdr[loadinfo->symtabidx]; |
| FAR Elf_Sym *sym = lib_malloc(symhdr->sh_size); |
| int ret; |
| int i; |
| |
| if (sym == NULL) |
| { |
| return -ENOMEM; |
| } |
| |
| ret = libelf_read(loadinfo, (FAR uint8_t *)sym, symhdr->sh_size, |
| symhdr->sh_offset); |
| |
| if (ret < 0) |
| { |
| berr("Failed to read symbol table\n"); |
| lib_free(sym); |
| return ret; |
| } |
| |
| for (i = 0; i < symhdr->sh_size / sizeof(Elf_Sym); i++) |
| { |
| if (sym[i].st_shndx != SHN_UNDEF && |
| sym[i].st_shndx < loadinfo->ehdr.e_shnum) |
| { |
| FAR Elf_Shdr *s = &loadinfo->shdr[sym[i].st_shndx]; |
| |
| sym[i].st_value = sym[i].st_value + s->sh_addr; |
| } |
| } |
| |
| ret = libelf_insertsymtab(modp, loadinfo, symhdr, sym); |
| lib_free(sym); |
| if (ret != 0) |
| { |
| binfo("Failed to export symbols program binary: %d\n", ret); |
| return ret; |
| } |
| |
| return ret; |
| } |
| |
| /**************************************************************************** |
| * Name: libelf_insert |
| * |
| * Description: |
| * Verify that the file is an ELF module binary and, if so, load the |
| * module into kernel memory and initialize it for use. |
| * |
| * NOTE: libelf_setsymtab() had to have been called in board-specific OS |
| * logic prior to calling this function from application logic (perhaps via |
| * boardctl(BOARDIOC_OS_SYMTAB). Otherwise, insmod will be unable to |
| * resolve symbols in the OS module. |
| * |
| * Input Parameters: |
| * |
| * filename - Full path to the module binary to be loaded |
| * modname - The name that can be used to refer to the module after |
| * it has been loaded. |
| * |
| * Returned Value: |
| * A non-NULL module handle that can be used on subsequent calls to other |
| * module interfaces is returned on success. If libelf_insert() was |
| * unable to load the module libelf_insert() will return a NULL handle |
| * and the errno variable will be set appropriately. |
| * |
| ****************************************************************************/ |
| |
| FAR void *libelf_insert(FAR const char *filename, FAR const char *modname) |
| { |
| FAR const struct symtab_s *exports; |
| struct mod_loadinfo_s loadinfo; |
| FAR struct module_s *modp; |
| FAR void (**array)(void); |
| int nexports; |
| int ret; |
| int i; |
| |
| DEBUGASSERT(filename != NULL && modname != NULL); |
| binfo("Loading file: %s\n", filename); |
| |
| /* Get exclusive access to the module registry */ |
| |
| libelf_registry_lock(); |
| |
| /* Check if this module is already installed */ |
| |
| #ifdef HAVE_LIBC_ELF_NAMES |
| if (libelf_registry_find(modname) != NULL) |
| { |
| libelf_registry_unlock(); |
| set_errno(EEXIST); |
| return NULL; |
| } |
| #endif |
| |
| /* Initialize the ELF library to load the program binary. */ |
| |
| ret = libelf_initialize(filename, &loadinfo); |
| libelf_dumploadinfo(&loadinfo); |
| if (ret != 0) |
| { |
| berr("ERROR: Failed to initialize to load module: %d\n", ret); |
| goto errout_with_loadinfo; |
| } |
| |
| /* Allocate a module registry entry to hold the module data */ |
| |
| modp = lib_zalloc(sizeof(struct module_s)); |
| if (modp == NULL) |
| { |
| berr("Failed to allocate struct module_s\n"); |
| ret = -ENOMEM; |
| goto errout_with_loadinfo; |
| } |
| |
| #ifdef HAVE_LIBC_ELF_NAMES |
| /* Save the module name in the registry entry */ |
| |
| strlcpy(modp->modname, modname, sizeof(modp->modname)); |
| #endif |
| |
| /* Load the program binary */ |
| |
| ret = libelf_load(&loadinfo); |
| libelf_dumploadinfo(&loadinfo); |
| if (ret != 0) |
| { |
| binfo("Failed to load ELF program binary: %d\n", ret); |
| goto errout_with_registry_entry; |
| } |
| |
| /* Get the symbol table */ |
| |
| libelf_getsymtab(&exports, &nexports); |
| |
| /* Bind the program to the kernel symbol table */ |
| |
| ret = libelf_bind(modp, &loadinfo, exports, nexports); |
| if (ret != 0) |
| { |
| binfo("Failed to bind symbols program binary: %d\n", ret); |
| goto errout_with_load; |
| } |
| |
| ret = libelf_loadsymtab(modp, &loadinfo); |
| if (ret != 0) |
| { |
| binfo("Failed to load symbol table: %d\n", ret); |
| goto errout_with_load; |
| } |
| |
| /* Save the load information */ |
| |
| modp->textalloc = (FAR void *)loadinfo.textalloc; |
| modp->dataalloc = (FAR void *)loadinfo.datastart; |
| #ifdef CONFIG_ARCH_USE_SEPARATED_SECTION |
| modp->sectalloc = (FAR void **)loadinfo.sectalloc; |
| modp->nsect = loadinfo.ehdr.e_shnum; |
| #endif |
| |
| #if defined(CONFIG_FS_PROCFS) && !defined(CONFIG_FS_PROCFS_EXCLUDE_MODULE) |
| modp->textsize = loadinfo.textsize; |
| modp->datasize = loadinfo.datasize; |
| #endif |
| |
| /* Call the module initializer */ |
| |
| switch (loadinfo.ehdr.e_type) |
| { |
| case ET_REL : |
| case ET_DYN : |
| |
| /* Process any preinit_array entries */ |
| |
| array = (FAR void (**)(void))loadinfo.preiarr; |
| for (i = 0; i < loadinfo.nprei; i++) |
| { |
| array[i](); |
| } |
| |
| /* Process any init_array entries */ |
| |
| array = (FAR void (**)(void))loadinfo.initarr; |
| for (i = 0; i < loadinfo.ninit; i++) |
| { |
| array[i](); |
| } |
| |
| modp->initarr = loadinfo.initarr; |
| modp->ninit = loadinfo.ninit; |
| modp->finiarr = loadinfo.finiarr; |
| modp->nfini = loadinfo.nfini; |
| break; |
| } |
| |
| /* Add the new module entry to the registry */ |
| |
| libelf_registry_add(modp); |
| |
| libelf_uninitialize(&loadinfo); |
| libelf_registry_unlock(); |
| return modp; |
| |
| errout_with_load: |
| libelf_unload(&loadinfo); |
| #if CONFIG_LIBC_ELF_MAXDEPEND > 0 |
| libelf_undepend(modp); |
| #endif |
| errout_with_registry_entry: |
| lib_free(modp); |
| errout_with_loadinfo: |
| libelf_uninitialize(&loadinfo); |
| libelf_registry_unlock(); |
| set_errno(-ret); |
| return NULL; |
| } |