mirror of
https://github.com/fincs/FeOS.git
synced 2025-06-19 03:25:33 -04:00
583 lines
12 KiB
C
583 lines
12 KiB
C
#include "loader.h"
|
|
#include "feosfifo.h"
|
|
#include <fcntl.h>
|
|
#include <unistd.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <limits.h>
|
|
|
|
static module_t _LdrLoadModule(const char* aFilename, const char* aModuleName);
|
|
|
|
static const char* moduleSearchPaths[] =
|
|
{
|
|
"/data/FeOS/lib",
|
|
"/data/FeOS/bin",
|
|
NULL
|
|
};
|
|
|
|
module_t LdrLoadModule(const char* aFilename)
|
|
{
|
|
sassert(aFilename, ERRSTR_INVALIDPARAM);
|
|
if (*aFilename == '/')
|
|
{
|
|
// Path was specified, we have to extract the module name.
|
|
|
|
int name_len = strlen(aFilename);
|
|
int ext_pos = name_len;
|
|
int i;
|
|
for (i = name_len-1; i >= 0; i --)
|
|
{
|
|
if (aFilename[i] == '.' && ext_pos == name_len)
|
|
{
|
|
ext_pos = i;
|
|
continue;
|
|
}
|
|
if (aFilename[i] == '/')
|
|
{
|
|
i ++;
|
|
break;
|
|
}
|
|
}
|
|
if (!i) return NULL;
|
|
|
|
name_len = ext_pos - i;
|
|
|
|
char* aModuleName = (char*) malloc(name_len+1);
|
|
memcpy(aModuleName, aFilename + i, name_len);
|
|
aModuleName[name_len] = '\0';
|
|
|
|
// Check if the module is already loaded
|
|
{
|
|
fxe_runtime_header* header = LdrModuleListFind(aModuleName);
|
|
if (header)
|
|
{
|
|
header->refcount ++;
|
|
free(aModuleName);
|
|
return header->hThis;
|
|
}
|
|
}
|
|
|
|
module_t ret = _LdrLoadModule(aFilename, aModuleName);
|
|
free(aModuleName);
|
|
return ret;
|
|
}else
|
|
{
|
|
// Check if the module is already loaded
|
|
{
|
|
fxe_runtime_header* header = LdrModuleListFind(aFilename);
|
|
if (header)
|
|
{
|
|
header->refcount ++;
|
|
return header->hThis;
|
|
}
|
|
}
|
|
|
|
// The module name was specified, we have to find the path to the module itself
|
|
{
|
|
char* buf = (char*) malloc(PATH_MAX+1);
|
|
if (!buf) return NULL;
|
|
|
|
struct stat st;
|
|
|
|
// Iterate through the search paths in order to find the module
|
|
const char** aBuf;
|
|
for (aBuf = moduleSearchPaths; *aBuf; aBuf ++)
|
|
{
|
|
sniprintf(buf, PATH_MAX+1, "%s/%s.fx2", *aBuf, aFilename);
|
|
if (stat(buf, &st) == 0)
|
|
break;
|
|
}
|
|
|
|
// Load the module
|
|
module_t ret = *aBuf ? _LdrLoadModule(buf, aFilename) : NULL;
|
|
free(buf);
|
|
return ret;
|
|
}
|
|
}
|
|
}
|
|
|
|
static module_t _LdrLoadModule(const char* aFilename, const char* aModuleName)
|
|
{
|
|
int aModuleNameLen = strlen(aModuleName)+1;
|
|
|
|
int fd = open(aFilename, O_RDONLY);
|
|
if(fd == -1)
|
|
return NULL;
|
|
|
|
fxe2_header_t head;
|
|
size_t totalsize;
|
|
if(read(fd, &head, sizeof(fxe2_header_t)) != sizeof(fxe2_header_t)
|
|
|| head.magic != 0x31305A46 /* FX01 */
|
|
|| FX2_SUBSYSTEM(head.flags) != FX2_SUBSYSTEM_STANDARD
|
|
|| FEOS_VPACK_MAKE(head.osmajorver, head.osminorver) > FEOS_VERSION_PACK)
|
|
{
|
|
close(fd);
|
|
return NULL;
|
|
}
|
|
|
|
totalsize = head.loadsize + head.bsssize + sizeof(fxe_runtime_header) + aModuleNameLen;
|
|
|
|
void* pMem = memalign(32, (totalsize+31) & ~31);
|
|
if(pMem == NULL)
|
|
{
|
|
close(fd);
|
|
return NULL;
|
|
}
|
|
|
|
if(read(fd, pMem, head.loadsize) != head.loadsize)
|
|
{
|
|
free(pMem);
|
|
close(fd);
|
|
return NULL;
|
|
}
|
|
|
|
// Clear the BSS
|
|
//dmaFillWords(0, pMem + head.loadsize, head.bsssize);
|
|
memset(pMem + head.loadsize, 0, head.bsssize);
|
|
|
|
if(head.nrelocs)
|
|
{
|
|
word_t relocsize = head.nrelocs * sizeof(fxe2_reloc_t);
|
|
fxe2_reloc_t* pRelocs = (fxe2_reloc_t*) malloc(relocsize);
|
|
if(!pRelocs)
|
|
{
|
|
free(pMem);
|
|
close(fd);
|
|
return NULL;
|
|
}
|
|
|
|
if(read(fd, pRelocs, relocsize) != relocsize)
|
|
{
|
|
free(pRelocs);
|
|
free(pMem);
|
|
close(fd);
|
|
return NULL;
|
|
}
|
|
|
|
// TODO: range checks
|
|
register int i, j;
|
|
word_t* patch = (word_t*) pMem;
|
|
for(i = 0; i < head.nrelocs; i ++)
|
|
{
|
|
patch += pRelocs[i].skip;
|
|
for(j = 0; j < pRelocs[i].patch; j ++)
|
|
*patch++ += (word_t) pMem;
|
|
}
|
|
|
|
free(pRelocs);
|
|
}
|
|
|
|
// Build the runtime header
|
|
char* namebuf = (char*)(pMem + totalsize - aModuleNameLen);
|
|
fxe_runtime_header* rh = (fxe_runtime_header*)(namebuf - sizeof(fxe_runtime_header));
|
|
rh->hThis = pMem;
|
|
rh->name = namebuf;
|
|
rh->refcount = 1;
|
|
rh->file = fd;
|
|
rh->size = head.loadsize + head.bsssize;
|
|
rh->entrypoint = (FeOSMain) (pMem + head.entrypoint);
|
|
rh->exp.count = head.nexports;
|
|
rh->imp.count = head.nimports;
|
|
|
|
strcpy(namebuf, aModuleName);
|
|
|
|
// Read exports
|
|
if (head.nexports && head.sexports)
|
|
{
|
|
word_t sexports = head.sexports;
|
|
word_t nexports = head.nexports;
|
|
|
|
fxe2_export_t* exptbl = (fxe2_export_t*) malloc(sexports);
|
|
if (!exptbl)
|
|
{
|
|
free(pMem), close(fd);
|
|
return NULL;
|
|
}
|
|
|
|
if (read(fd, exptbl, sexports) != sexports)
|
|
{
|
|
free(exptbl), free(pMem), close(fd);
|
|
return NULL;
|
|
}
|
|
|
|
register int i;
|
|
for(i = 0; i < nexports; i ++)
|
|
{
|
|
// TODO: range checks
|
|
exptbl[i].nameoffset += (word_t) exptbl;
|
|
exptbl[i].address += (word_t) pMem;
|
|
}
|
|
|
|
rh->exp.table = exptbl;
|
|
}
|
|
|
|
// Read imports
|
|
if (head.nimports && head.simports)
|
|
{
|
|
/* It's... basically the same code!!!111 */
|
|
word_t simports = head.simports;
|
|
word_t nimports = head.nimports;
|
|
|
|
fxe2_import_t* imptbl = (fxe2_import_t*) malloc(simports);
|
|
if (!imptbl)
|
|
{
|
|
free(pMem), close(fd);
|
|
return NULL;
|
|
}
|
|
|
|
if (read(fd, imptbl, simports) != simports)
|
|
{
|
|
free(imptbl), free(pMem), close(fd);
|
|
return NULL;
|
|
}
|
|
|
|
register int i;
|
|
for(i = 0; i < nimports; i ++)
|
|
{
|
|
// TODO: range checks
|
|
imptbl[i].nameoffset += (word_t) imptbl;
|
|
if (imptbl[i].address != FX2_IMP_SELECT_MODULE)
|
|
imptbl[i].address += (word_t) pMem;
|
|
}
|
|
|
|
if (!LdrResolveImports(imptbl, nimports))
|
|
{
|
|
free(imptbl), free(pMem), close(fd);
|
|
return NULL;
|
|
}
|
|
|
|
rh->imp.table = imptbl;
|
|
}
|
|
|
|
if(head.flags & FX2_LDRFLAGS_HASIMPCOPY)
|
|
{
|
|
fxe2_impcopy_head_t ich;
|
|
fxe2_impcopy_t impcpy;
|
|
|
|
if(read(fd, &ich, sizeof(ich)) != sizeof(ich))
|
|
{
|
|
_impcopy_err:
|
|
free(pMem);
|
|
close(fd);
|
|
return NULL;
|
|
}
|
|
|
|
if(ich.type != FX2_IMPCOPY_NORMAL)
|
|
goto _impcopy_err;
|
|
|
|
// TODO: range checks
|
|
register int i;
|
|
for(i = 0; i < ich.count; i ++)
|
|
{
|
|
if(read(fd, &impcpy, sizeof(impcpy)) != sizeof(impcpy))
|
|
goto _impcopy_err;
|
|
|
|
impcpy.from += (word_t) pMem;
|
|
impcpy.to += (word_t) pMem;
|
|
|
|
word_t impcpy_data = *impcpy.pTo;
|
|
word_t impcpy_off = (word_t)((int)impcpy_data >> 4);
|
|
|
|
switch (impcpy_data & 0xF)
|
|
{
|
|
case 0: // simple copy
|
|
*impcpy.pTo = *impcpy.pFrom + impcpy_off;
|
|
break;
|
|
|
|
case 1: // relative offset
|
|
*impcpy.pTo = *impcpy.pFrom - impcpy.to + impcpy_off;
|
|
break;
|
|
|
|
default: goto _impcopy_err;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Write the pointer to the runtime header
|
|
*(volatile word_t*)pMem = (word_t) rh;
|
|
|
|
// Update caches
|
|
DC_FlushRange(pMem, totalsize);
|
|
IC_InvalidateRange(pMem, totalsize);
|
|
|
|
// Add this module to the list of loaded modules
|
|
LdrModuleListAdd(rh);
|
|
|
|
// Get the start and size of the extra data section
|
|
rh->extrapos = tell(fd);
|
|
lseek(fd, 0, SEEK_END);
|
|
rh->extrasize = tell(fd) - rh->extrapos;
|
|
lseek(fd, rh->extrapos, SEEK_SET);
|
|
|
|
// Run the constructors
|
|
rh->entrypoint(FEOS_EP_INIT, 0, 0, 0);
|
|
|
|
// Get the exception index table
|
|
if (rh->entrypoint(FEOS_EP_GETEXIDXTBL, (word_t) &rh->exidx, 0, 0) != FEOS_RC_OK)
|
|
{
|
|
rh->exidx.table = NULL;
|
|
rh->exidx.nentries = 0;
|
|
}
|
|
|
|
return pMem;
|
|
}
|
|
|
|
int LdrResGetSize(module_t hMod)
|
|
{
|
|
CHK_HMOD(hMod);
|
|
return GetRuntimeData(hMod)->extrasize;
|
|
}
|
|
|
|
int LdrResRead(module_t hMod, void* buf, size_t size)
|
|
{
|
|
CHK_HMOD(hMod);
|
|
return read(GetRuntimeData(hMod)->file, buf, size);
|
|
}
|
|
|
|
int LdrResSeek(module_t hMod, int pos, int mode)
|
|
{
|
|
CHK_HMOD(hMod);
|
|
fxe_runtime_header* rh = GetRuntimeData(hMod);
|
|
int fd = rh->file;
|
|
int base = rh->extrapos;
|
|
switch (mode)
|
|
{
|
|
case SEEK_SET:
|
|
return lseek(fd, base+pos, SEEK_SET);
|
|
case SEEK_CUR:
|
|
{
|
|
int target = tell(fd) + pos;
|
|
if (target < base) target = base;
|
|
return lseek(fd, target, SEEK_SET);
|
|
}
|
|
case SEEK_END:
|
|
{
|
|
int target = base + rh->extrasize + pos;
|
|
if (target < base) target = base;
|
|
return lseek(fd, target, SEEK_SET);
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
int LdrResTell(module_t hMod)
|
|
{
|
|
CHK_HMOD(hMod);
|
|
fxe_runtime_header* rh = GetRuntimeData(hMod);
|
|
return tell(rh->file) - rh->extrapos;
|
|
}
|
|
|
|
static FeOSLoadStruct __ldSt;
|
|
#define GET_LOADST() (&__ldSt)
|
|
|
|
module_t DSLoadARM7(const char* aFilename, int* pFifoCh)
|
|
{
|
|
sassert(aFilename, ERRSTR_INVALIDPARAM);
|
|
sassert(pFifoCh, ERRSTR_INVALIDPARAM);
|
|
|
|
FeOSLoadStruct* ldSt = GET_LOADST();
|
|
FeOSFifoMsg msg;
|
|
msg.type = FEOS_ARM7_LOAD_MODULE;
|
|
msg.loadStruct = ldSt;
|
|
|
|
int fd = open(aFilename, O_RDONLY);
|
|
if(fd == -1)
|
|
return NULL;
|
|
|
|
fxe2_header_t head;
|
|
if(read(fd, &head, sizeof(fxe2_header_t)) != sizeof(fxe2_header_t)
|
|
|| head.magic != 0x31305A46 /* FX01 */
|
|
|| FX2_SUBSYSTEM(head.flags) != FX2_SUBSYSTEM_STANDARD
|
|
|| FEOS_VPACK_MAKE(head.osmajorver, head.osminorver) > FEOS_VERSION_PACK)
|
|
{
|
|
_shorterr:
|
|
close(fd);
|
|
return NULL;
|
|
}
|
|
|
|
size_t readsize;
|
|
void* pMem = malloc((readsize = head.loadsize + head.nrelocs*sizeof(fxe2_reloc_t)) + head.simports);
|
|
if(pMem == NULL) goto _shorterr;
|
|
|
|
// Read loadable section + relocations
|
|
if(read(fd, pMem, readsize) != readsize)
|
|
{
|
|
_fullerr:
|
|
free(pMem);
|
|
goto _shorterr;
|
|
}
|
|
|
|
// Skip over exports
|
|
lseek(fd, head.sexports, SEEK_CUR);
|
|
|
|
// Read imports
|
|
if(read(fd, (u8*)pMem + readsize, head.simports) != head.simports)
|
|
goto _fullerr;
|
|
|
|
close(fd);
|
|
|
|
// Set the entrypoint
|
|
*(volatile word_t*)pMem = head.entrypoint;
|
|
|
|
// Fill in loadStruct structure
|
|
ldSt->data = pMem;
|
|
ldSt->size = head.loadsize;
|
|
ldSt->bsssize = head.bsssize;
|
|
ldSt->imps.count = head.nimports;
|
|
ldSt->imps.table = (fxe2_import_t*)((u8*)pMem + readsize);
|
|
ldSt->nrelocs = head.nrelocs;
|
|
ldSt->relocs = (fxe2_reloc_t*)((u8*)pMem + head.loadsize);
|
|
|
|
DC_FlushRange(pMem, readsize + head.simports);
|
|
DC_FlushRange(ldSt, sizeof(FeOSLoadStruct));
|
|
|
|
// Tell the ARM7 to load the module
|
|
fifoSendDatamsg(FIFO_FEOS, sizeof(FeOSFifoMsg), (void*) &msg);
|
|
|
|
// Wait for it to load
|
|
while(!fifoCheckDatamsg(FIFO_FEOS));
|
|
|
|
// Free the temporary memory
|
|
free(pMem);
|
|
|
|
// Return
|
|
fifoGetDatamsg(FIFO_FEOS, sizeof(FeOSFifoMsg), (void*) &msg);
|
|
*pFifoCh = msg.fifoCh;
|
|
return msg.hModule;
|
|
}
|
|
|
|
void DSFreeARM7(module_t hMod, int fifoCh)
|
|
{
|
|
sassert(hMod, ERRSTR_INVALIDPARAM);
|
|
|
|
FeOSFifoMsg msg;
|
|
msg.type = FEOS_ARM7_UNLOAD_MODULE;
|
|
msg.hModule = hMod;
|
|
msg.fifoCh = fifoCh;
|
|
fifoSendDatamsg(FIFO_FEOS, sizeof(FeOSFifoMsg), (void*) &msg);
|
|
while(!fifoCheckValue32(FIFO_FEOS));
|
|
fifoGetValue32(FIFO_FEOS);
|
|
}
|
|
|
|
void LdrFreeModule(module_t hMod)
|
|
{
|
|
CHK_HMOD(hMod);
|
|
fxe_runtime_header* rh = GetRuntimeData(hMod);
|
|
|
|
rh->refcount --;
|
|
if (rh->refcount) return;
|
|
|
|
// Run the destructors
|
|
rh->entrypoint(FEOS_EP_FINI, 0, 0, 0);
|
|
|
|
// Remove the module from the list of loaded modules
|
|
LdrModuleListRemove(rh);
|
|
|
|
// Free the export table
|
|
if (rh->exp.count)
|
|
free(rh->exp.table);
|
|
|
|
// Free the import table
|
|
if (rh->imp.count)
|
|
{
|
|
LdrFreeImports(rh->imp.table, rh->imp.count);
|
|
free(rh->imp.table);
|
|
}
|
|
|
|
// Close the file
|
|
close(rh->file);
|
|
|
|
// Free the module memory
|
|
free(hMod);
|
|
}
|
|
|
|
void LdrLockModule(module_t hMod)
|
|
{
|
|
CHK_HMOD(hMod);
|
|
GetRuntimeData(hMod)->refcount ++;
|
|
}
|
|
|
|
void LdrUnlockModule(module_t hMod)
|
|
{
|
|
CHK_HMOD(hMod);
|
|
GetRuntimeData(hMod)->refcount --;
|
|
}
|
|
|
|
void* LdrFindInTbl(const fxe_inmem_exports* exphdr, const char* name)
|
|
{
|
|
/*
|
|
register int i;
|
|
for(i = 0; i < exphdr->count; i ++)
|
|
if (strcmp(exphdr->table[i].name, name) == 0)
|
|
return exphdr->table[i].addr;
|
|
*/
|
|
int min = 0, max = exphdr->count;
|
|
|
|
while (max >= min)
|
|
{
|
|
int mid = (min + max) / 2;
|
|
int rc = strcmp(exphdr->table[mid].name, name);
|
|
if (rc < 0)
|
|
min = mid + 1;
|
|
else if (rc > 0)
|
|
{
|
|
if (mid == 0) break;
|
|
max = mid - 1;
|
|
} else
|
|
return exphdr->table[mid].addr;
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
iprintf("{DBG} Cannot resolve %s\n", name);
|
|
#endif
|
|
|
|
return NULL;
|
|
}
|
|
|
|
int LdrResolveImports(fxe2_import_t* imptbl, int count)
|
|
{
|
|
register int i;
|
|
fxe_inmem_exports* exptable = NULL;
|
|
|
|
for(i = 0; i < count; i ++)
|
|
{
|
|
fxe2_import_t* imp = imptbl + i;
|
|
void* sym = NULL;
|
|
|
|
if(imp->address == FX2_IMP_SELECT_MODULE)
|
|
{
|
|
module_t hMod = LdrLoadModule(imp->name);
|
|
if (!hMod) return 0;
|
|
|
|
exptable = &GetRuntimeData(hMod)->exp;
|
|
continue;
|
|
}
|
|
|
|
if (!exptable) return 0;
|
|
|
|
sym = LdrFindInTbl(exptable, imp->name);
|
|
|
|
if (!sym) return 0;
|
|
|
|
*(imp->addr) = sym;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
void LdrFreeImports(fxe2_import_t* imptbl, int count)
|
|
{
|
|
register int i;
|
|
|
|
for(i = 0; i < count; i ++)
|
|
{
|
|
fxe2_import_t* imp = imptbl + i;
|
|
|
|
if(imp->address != FX2_IMP_SELECT_MODULE) continue;
|
|
|
|
fxe_runtime_header* header = LdrModuleListFind(imp->name);
|
|
if (!header) continue; // oops, memory corruption? (shouldn't happen)
|
|
|
|
LdrFreeModule(header->hThis);
|
|
}
|
|
}
|