int nxflat_init(const char *filename, struct nxflat_loadinfo_s *loadinfo) { uint32_t datastart; uint32_t dataend; uint32_t bssstart; uint32_t bssend; int ret; bvdbg("filename: %s loadinfo: %p\n", filename, loadinfo); /* Clear the load info structure */ memset(loadinfo, 0, sizeof(struct nxflat_loadinfo_s)); /* Open the binary file */ loadinfo->filfd = open(filename, O_RDONLY); if (loadinfo->filfd < 0) { bdbg("Failed to open NXFLAT binary %s: %d\n", filename, ret); return -errno; } /* Read the NXFLAT header from offset 0 */ ret = nxflat_read(loadinfo, (char*)&loadinfo->header, sizeof(struct nxflat_hdr_s), 0); if (ret < 0) { bdbg("Failed to read NXFLAT header: %d\n", ret); return ret; } nxflat_dumpbuffer("NXFLAT header", (FAR const uint8_t*)&loadinfo->header, sizeof(struct nxflat_hdr_s)); /* Verify the NXFLAT header */ if (nxflat_verifyheader(&loadinfo->header) != 0) { /* This is not an error because we will be called to attempt loading * EVERY binary. Returning -ENOEXEC simply informs the system that * the file is not an NXFLAT file. Besides, if there is something worth * complaining about, nnxflat_verifyheader() has already * done so. */ bdbg("Bad NXFLAT header\n"); return -ENOEXEC; } /* Save all of the input values in the loadinfo structure * and extract some additional information from the xflat * header. Note that the information in the xflat header is in * network order. */ datastart = ntohl(loadinfo->header.h_datastart); dataend = ntohl(loadinfo->header.h_dataend); bssstart = dataend; bssend = ntohl(loadinfo->header.h_bssend); /* And put this information into the loadinfo structure as well. * * Note that: * * isize = the address range from 0 up to datastart. * datasize = the address range from datastart up to dataend * bsssize = the address range from dataend up to bssend. */ loadinfo->entryoffs = ntohl(loadinfo->header.h_entry); loadinfo->isize = datastart; loadinfo->datasize = dataend - datastart; loadinfo->bsssize = bssend - dataend; loadinfo->stacksize = ntohl(loadinfo->header.h_stacksize); /* This is the initial dspace size. We'll re-calculate this later * after the memory has been allocated. */ loadinfo->dsize = bssend - datastart; /* Get the offset to the start of the relocations (we'll relocate * this later). */ loadinfo->relocstart = ntohl(loadinfo->header.h_relocstart); loadinfo->reloccount = ntohs(loadinfo->header.h_reloccount); return 0; }
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; }