static inline int nxflat_clearbss(FAR struct nxflat_loadinfo_s *loadinfo) { #ifdef CONFIG_ARCH_ADDRENV int ret; #endif /* .bss resides within the D-Space allocation. If CONFIG_ARCH_ADDRENV=y, then * that D-Space allocation lies in an address environment that may not be * in place. So, in that case, we must call nxflat_addrenv_select to * temporarily instantiate that address space before the .bss can be * accessed. */ #ifdef CONFIG_ARCH_ADDRENV ret = nxflat_addrenv_select(loadinfo); if (ret < 0) { berr("ERROR: nxflat_addrenv_select() failed: %d\n", ret); return ret; } #endif /* Zero the BSS area */ memset((FAR void *)(loadinfo->dspace->region + loadinfo->datasize), 0, loadinfo->bsssize); /* Restore the original address environment */ #ifdef CONFIG_ARCH_ADDRENV ret = nxflat_addrenv_restore(loadinfo); if (ret < 0) { berr("ERROR: nxflat_addrenv_restore() failed: %d\n", ret); } return ret; #else return OK; #endif }
static inline int nxflat_gotrelocs(FAR struct nxflat_loadinfo_s *loadinfo) { FAR struct nxflat_reloc_s *relocs; FAR struct nxflat_reloc_s reloc; FAR struct nxflat_hdr_s *hdr; uint32_t offset; uint16_t nrelocs; int ret; int result; int i; /* The NXFLAT header is the first thing at the beginning of the ISpace. */ hdr = (FAR struct nxflat_hdr_s *)loadinfo->ispace; /* From this, we can get the offset to the list of relocation entries */ offset = ntohl(hdr->h_relocstart); nrelocs = ntohs(hdr->h_reloccount); binfo("offset: %08lx nrelocs: %d\n", (long)offset, nrelocs); /* The value of the relocation list that we get from the header is a * file offset. We will have to convert this to an offset into the * DSpace segment to get the pointer to the beginning of the relocation * list. */ DEBUGASSERT(offset >= loadinfo->isize); DEBUGASSERT(offset + nrelocs * sizeof(struct nxflat_reloc_s) <= (loadinfo->isize + loadinfo->dsize)); relocs = (FAR struct nxflat_reloc_s *) (offset - loadinfo->isize + loadinfo->dspace->region); binfo("isize: %08lx dpsace: %p relocs: %p\n", (long)loadinfo->isize, loadinfo->dspace->region, relocs); /* All relocations are performed within the D-Space allocation. If * CONFIG_ARCH_ADDRENV=y, then that D-Space allocation lies in an address * environment that may not be in place. So, in that case, we must call * nxflat_addrenv_select to temporarily instantiate that address space * before the relocations can be performed. */ #ifdef CONFIG_ARCH_ADDRENV ret = nxflat_addrenv_select(loadinfo); if (ret < 0) { berr("ERROR: nxflat_addrenv_select() failed: %d\n", ret); return ret; } #endif /* Now, traverse the relocation list of and bind each GOT relocation. */ ret = OK; /* Assume success */ for (i = 0; i < nrelocs; i++) { /* Handle the relocation by the relocation type */ #ifdef CONFIG_CAN_PASS_STRUCTS reloc = *relocs++; #else memcpy(&reloc, relocs, sizeof(struct nxflat_reloc_s)); relocs++; #endif result = OK; switch (NXFLAT_RELOC_TYPE(reloc.r_info)) { /* NXFLAT_RELOC_TYPE_REL32I Meaning: Object file contains a 32-bit offset * into I-Space at the offset. * Fixup: Add mapped I-Space address to the offset. */ case NXFLAT_RELOC_TYPE_REL32I: { result = nxflat_bindrel32i(loadinfo, NXFLAT_RELOC_OFFSET(reloc.r_info)); } break; /* NXFLAT_RELOC_TYPE_REL32D Meaning: Object file contains a 32-bit offset * into D-Space at the offset. * Fixup: Add allocated D-Space address to the * offset. */ case NXFLAT_RELOC_TYPE_REL32D: { result = nxflat_bindrel32d(loadinfo, NXFLAT_RELOC_OFFSET(reloc.r_info)); } break; /* NXFLAT_RELOC_TYPE_REL32ID Meaning: Object file contains a 32-bit offset * into I-Space at the offset that will * unfortunately be references relative * to the GOT * Fixup: Add allocated the mapped I-Space * address MINUS the allocated D-Space * address to the offset. */ #ifdef NXFLAT_RELOC_TYPE_REL32ID case NXFLAT_RELOC_TYPE_REL32ID: { result = nxflat_bindrel32id(loadinfo, NXFLAT_RELOC_OFFSET(reloc.r_info)); } break; #endif default: { berr("ERROR: Unrecognized relocation type: %d\n", NXFLAT_RELOC_TYPE(reloc.r_info)); result = -EINVAL; } break; } /* Check for failures */ if (result < 0 && ret == OK) { ret = result; } } /* Dump the relocation got */ #ifdef CONFIG_NXFLAT_DUMPBUFFER if (ret == OK && nrelocs > 0) { relocs = (FAR struct nxflat_reloc_s *)(offset - loadinfo->isize + loadinfo->dspace->region); nxflat_dumpbuffer("GOT", (FAR const uint8_t *)relocs, nrelocs * sizeof(struct nxflat_reloc_s)); } #endif /* Restore the original address environment */ #ifdef CONFIG_ARCH_ADDRENV ret = nxflat_addrenv_restore(loadinfo); if (ret < 0) { berr("ERROR: nxflat_addrenv_restore() failed: %d\n", ret); } #endif return ret; }
static inline int nxflat_bindimports(FAR struct nxflat_loadinfo_s *loadinfo, FAR const struct symtab_s *exports, int nexports) { FAR struct nxflat_import_s *imports; FAR struct nxflat_hdr_s *hdr; FAR const struct symtab_s *symbol; char *symname; uint32_t offset; uint16_t nimports; #ifdef CONFIG_ARCH_ADDRENV int ret; #endif int i; /* The NXFLAT header is the first thing at the beginning of the ISpace. */ hdr = (FAR struct nxflat_hdr_s *)loadinfo->ispace; /* From this, we can get the offset to the list of symbols imported by * this module and the number of symbols imported by this module. */ offset = ntohl(hdr->h_importsymbols); nimports = ntohs(hdr->h_importcount); binfo("Imports offset: %08x nimports: %d\n", offset, nimports); /* The import[] table resides within the D-Space allocation. If * CONFIG_ARCH_ADDRENV=y, then that D-Space allocation lies in an address * environment that may not be in place. So, in that case, we must call * nxflat_addrenv_select to temporarily instantiate that address space * before the import[] table can be modified. */ #ifdef CONFIG_ARCH_ADDRENV ret = nxflat_addrenv_select(loadinfo); if (ret < 0) { berr("ERROR: nxflat_addrenv_select() failed: %d\n", ret); return ret; } #endif /* Verify that this module requires imported symbols */ if (offset != 0 && nimports > 0) { /* It does.. make sure that exported symbols are provided */ DEBUGASSERT(exports && nexports > 0); /* If non-zero, the value of the imported symbol list that we get * from the header is a file offset. We will have to convert this * to an offset into the DSpace segment to get the pointer to the * beginning of the imported symbol list. */ DEBUGASSERT(offset >= loadinfo->isize && offset < loadinfo->isize + loadinfo->dsize); imports = (FAR struct nxflat_import_s *) (offset - loadinfo->isize + loadinfo->dspace->region); /* Now, traverse the list of imported symbols and attempt to bind * each symbol to the value exported by from the exported symbol * table. */ for (i = 0; i < nimports; i++) { binfo("Import[%d] (%08p) offset: %08x func: %08x\n", i, &imports[i], imports[i].i_funcname, imports[i].i_funcaddress); /* Get a pointer to the imported symbol name. The name itself * lies in the TEXT segment. But the reference to the name * lies in DATA segment. Therefore, the name reference should * have been relocated when the module was loaded. */ offset = imports[i].i_funcname; DEBUGASSERT(offset < loadinfo->isize); symname = (FAR char *)(offset + loadinfo->ispace + sizeof(struct nxflat_hdr_s)); /* Find the exported symbol value for this this symbol name. */ #ifdef CONFIG_SYMTAB_ORDEREDBYNAME symbol = symtab_findorderedbyname(exports, symname, nexports); #else symbol = symtab_findbyname(exports, symname, nexports); #endif if (!symbol) { berr("Exported symbol \"%s\" not found\n", symname); #ifdef CONFIG_ARCH_ADDRENV (void)nxflat_addrenv_restore(loadinfo); #endif return -ENOENT; } /* And put this into the module's import structure. */ imports[i].i_funcaddress = (uint32_t)symbol->sym_value; binfo("Bound import[%d] (%08p) to export '%s' (%08x)\n", i, &imports[i], symname, imports[i].i_funcaddress); } } /* Dump the relocation import table */ #ifdef CONFIG_NXFLAT_DUMPBUFFER if (nimports > 0) { nxflat_dumpbuffer("Imports", (FAR const uint8_t *)imports, nimports * sizeof(struct nxflat_import_s)); } #endif /* Restore the original address environment */ #ifdef CONFIG_ARCH_ADDRENV ret = nxflat_addrenv_restore(loadinfo); if (ret < 0) { berr("ERROR: nxflat_addrenv_restore() failed: %d\n", ret); } return ret; #else return OK; #endif }
int nxflat_load(struct nxflat_loadinfo_s *loadinfo) { off_t doffset; /* Offset to .data in the NXFLAT file */ uint32_t dreadsize; /* Total number of bytes of .data to be read */ uint32_t relocsize; /* Memory needed to hold relocations */ uint32_t extrasize; /* MAX(BSS size, relocsize) */ int ret = OK; /* Calculate the extra space we need to allocate. This extra space will be * the size of the BSS section. This extra space will also be used * temporarily to hold relocation information. So the allocated size of this * region will either be the size of .data + size of.bss section OR, the * size of .data + the relocation entries, whichever is larger * * This is the amount of memory that we have to have to hold the * relocations. */ relocsize = loadinfo->reloccount * sizeof(struct nxflat_reloc_s); /* In the file, the relocations should lie at the same offset as BSS. * The additional amount that we allocate have to be either (1) the * BSS size, or (2) the size of the relocation records, whicher is * larger. */ extrasize = MAX(loadinfo->bsssize, relocsize); /* Use this additional amount to adjust the total size of the dspace * region. */ loadinfo->dsize = loadinfo->datasize + extrasize; /* The number of bytes of data that we have to read from the file is * the data size plus the size of the relocation table. */ dreadsize = loadinfo->datasize + relocsize; /* We'll need this a few times. */ doffset = loadinfo->isize; /* We will make two mmap calls create an address space for the executable. * We will attempt to map the file to get the ISpace address space and * to allocate RAM to get the DSpace address space. If the filesystem does * not support file mapping, the map() implementation should do the * right thing. */ /* The following call will give as a pointer to the mapped file ISpace. * This may be in ROM, RAM, Flash, ... We don't really care where the memory * resides as long as it is fully initialized and ready to execute. */ loadinfo->ispace = (uint32_t)mmap(NULL, loadinfo->isize, PROT_READ, MAP_SHARED|MAP_FILE, loadinfo->filfd, 0); if (loadinfo->ispace == (uint32_t)MAP_FAILED) { bdbg("Failed to map NXFLAT ISpace: %d\n", errno); return -errno; } bvdbg("Mapped ISpace (%d bytes) at %08x\n", loadinfo->isize, loadinfo->ispace); /* The following call allocate D-Space memory and will provide a pointer * to the allocated (but still uninitialized) D-Space memory. */ ret = nxflat_addrenv_alloc(loadinfo, loadinfo->dsize); if (ret < 0) { bdbg("ERROR: nxflat_addrenv_alloc() failed: %d\n", ret); return ret; } bvdbg("Allocated DSpace (%d bytes) at %p\n", loadinfo->dsize, loadinfo->dspace->region); /* If CONFIG_ADDRENV=y, then the D-Space allocation lies in an address * environment that may not be in place. So, in that case, we must call * nxflat_addrenv_select to temporarily instantiate that address space * it can be initialized. */ #ifdef CONFIG_ADDRENV ret = nxflat_addrenv_select(loadinfo); if (ret < 0) { bdbg("ERROR: nxflat_addrenv_select() failed: %d\n", ret); return ret; } #endif /* Now, read the data into allocated DSpace at doffset into the allocated * DSpace memory. */ ret = nxflat_read(loadinfo, (char*)loadinfo->dspace->region, dreadsize, doffset); if (ret < 0) { bdbg("Failed to read .data section: %d\n", ret); goto errout; } bvdbg("TEXT: %08x Entry point offset: %08x Data offset: %08x\n", loadinfo->ispace, loadinfo->entryoffs, doffset); /* Restore the original address environment */ #ifdef CONFIG_ADDRENV ret = nxflat_addrenv_restore(loadinfo); if (ret < 0) { bdbg("ERROR: nxflat_addrenv_restore() failed: %d\n", ret); return ret; } #endif return OK; errout: #ifdef CONFIG_ADDRENV (void)nxflat_addrenv_restore(loadinfo); #endif (void)nxflat_unload(loadinfo); return ret; }