Huge reworking to get shared libraries (and native replacements) working.
Not there yet, but the framework is pretty close to what I want.
This commit is contained in:
1 parent
1d8ad0ce35
commit
443bcf2178
14 files changed
+536
-141
No files matched your search
@@ -62,6 +62,7 @@ static void parseExe(const char *exefname, uint8 *exe, uint32 exelen)
|
||||
{
|
||||
printf("%s\n", exefname);
|
||||
|
||||
const uint8 *origexe = exe;
|
||||
if (!sanityCheckExe(&exe, &exelen))
|
||||
return;
|
||||
|
||||
@@ -444,6 +445,33 @@ static void parseExe(const char *exefname, uint8 *exe, uint32 exelen)
|
||||
printf("%u: %s\n", (unsigned int) i+1, name);
|
||||
}
|
||||
|
||||
const uint8 *name_table;
|
||||
|
||||
printf("Resident name table:\n");
|
||||
name_table = exe + lx->resident_name_table_offset;
|
||||
for (uint32 i = 0; *name_table; i++) {
|
||||
const uint8 namelen = *(name_table++);
|
||||
char name[256];
|
||||
memcpy(name, name_table, namelen);
|
||||
name[namelen] = '\0';
|
||||
name_table += namelen;
|
||||
const uint16 ordinal = *((const uint16 *) name_table); name_table += 2;
|
||||
printf("%u: '%s' (ordinal %u)\n", (unsigned int) i, name, (unsigned int) ordinal);
|
||||
} // for
|
||||
|
||||
printf("Non-resident name table:\n");
|
||||
name_table = origexe + lx->non_resident_name_table_offset;
|
||||
const uint8 *end_of_name_table = name_table + lx->non_resident_name_table_len;
|
||||
for (uint32 i = 0; (name_table < end_of_name_table) && *name_table; i++) {
|
||||
const uint8 namelen = *(name_table++);
|
||||
char name[256];
|
||||
memcpy(name, name_table, namelen);
|
||||
name[namelen] = '\0';
|
||||
name_table += namelen;
|
||||
const uint16 ordinal = *((const uint16 *) name_table); name_table += 2;
|
||||
printf("%u: '%s' (ordinal %u)\n", (unsigned int) i, name, (unsigned int) ordinal);
|
||||
} // for
|
||||
|
||||
#if 0
|
||||
const uint8 *fixup_record_table = exe + fixup_record_table_offset;
|
||||
const uint8 *import_proc_table = exe + lx->import_proc_table_offset;
|
||||
|
||||
+351
-138
@@ -8,19 +8,23 @@
|
||||
#include <errno.h>
|
||||
#include <sys/mman.h>
|
||||
#include <assert.h>
|
||||
#include <dlfcn.h>
|
||||
|
||||
#include "lx_loader.h"
|
||||
|
||||
static LxModule *GLoadedModules = NULL;
|
||||
|
||||
// !!! FIXME: move this into an lx_common.c file.
|
||||
static int sanityCheckExe(uint8 **_exe, uint32 *_exelen)
|
||||
static int sanityCheckLxModule(uint8 **_exe, uint32 *_exelen)
|
||||
{
|
||||
if (*_exelen < 196) {
|
||||
fprintf(stderr, "not an OS/2 LX EXE\n");
|
||||
fprintf(stderr, "not an OS/2 LX module\n");
|
||||
return 0;
|
||||
}
|
||||
const uint32 header_offset = *((uint32 *) (*_exe + 0x3C));
|
||||
//printf("header offset is %u\n", (unsigned int) header_offset);
|
||||
if ((header_offset + sizeof (LxHeader)) >= *_exelen) {
|
||||
fprintf(stderr, "not an OS/2 LX EXE\n");
|
||||
fprintf(stderr, "not an OS/2 LX module\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -30,7 +34,7 @@ static int sanityCheckExe(uint8 **_exe, uint32 *_exelen)
|
||||
const LxHeader *lx = (const LxHeader *) *_exe;
|
||||
|
||||
if ((lx->magic_l != 'L') || (lx->magic_x != 'X')) {
|
||||
fprintf(stderr, "not an OS/2 LX EXE\n");
|
||||
fprintf(stderr, "not an OS/2 LX module\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -40,7 +44,7 @@ static int sanityCheckExe(uint8 **_exe, uint32 *_exelen)
|
||||
}
|
||||
|
||||
if (lx->lx_version != 0) {
|
||||
fprintf(stderr, "Program is unknown LX EXE version (%u)\n", (unsigned int) lx->lx_version);
|
||||
fprintf(stderr, "Program is unknown LX module version (%u)\n", (unsigned int) lx->lx_version);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -62,7 +66,7 @@ static int sanityCheckExe(uint8 **_exe, uint32 *_exelen)
|
||||
// !!! FIXME: check if EIP and ESP are non-zero vs per-process library bits, etc.
|
||||
|
||||
return 1;
|
||||
} // sanityCheckExe
|
||||
} // sanityCheckLxModule
|
||||
|
||||
/* this algorithm is from lxlite 138u. */
|
||||
static int decompressExePack2(uint8 *dst, const uint32 dstlen, const uint8 *src, const uint32 srclen)
|
||||
@@ -167,40 +171,23 @@ static int decompressExePack2(uint8 *dst, const uint32 dstlen, const uint8 *src,
|
||||
return 1;
|
||||
} // decompressExePack2
|
||||
|
||||
void missing_ordinal_called(const char *module, const uint32 ordinal)
|
||||
static void missingEntryPointCalled(const char *module, const char *entry)
|
||||
{
|
||||
fflush(stdout);
|
||||
fflush(stderr);
|
||||
fprintf(stderr, "\n\nMissing ordinal '%u' in module '%s' called!\n", ordinal, module);
|
||||
fprintf(stderr, "\n\nMissing entry point '%s' in module '%s' called!\n", entry, module);
|
||||
fprintf(stderr, "Aborting.\n\n\n");
|
||||
//STUBBED("output backtrace");
|
||||
fflush(stderr);
|
||||
_exit(1);
|
||||
} // missing_ordinal_called
|
||||
|
||||
static uint32 DosPutMessage(uint32 handle, uint32 msglen, const char *msg)
|
||||
static uint32 generateMissingTrampoline(const char *_module, const char *_entry)
|
||||
{
|
||||
// !!! FIXME: this isn't right, but good enough for now.
|
||||
fwrite(msg, msglen, 1, stdout);
|
||||
fflush(stdout);
|
||||
return 0;
|
||||
} // DosPutMessage
|
||||
|
||||
static uint32 getModuleProcAddrByOrdinal(const LxHeader *lx, const char * const * const modules, const uint16 moduleid, const uint32 importid)
|
||||
{
|
||||
if (moduleid == 0) {
|
||||
fprintf(stderr, "uhoh, looking for module ordinal 0, which is illegal.\n");
|
||||
return 0;
|
||||
} else if (moduleid > lx->num_import_mod_entries) {
|
||||
fprintf(stderr, "uhoh, looking for module ordinal %u, but only %u available.\n", (unsigned int) moduleid, (unsigned int) lx->num_import_mod_entries);
|
||||
return 0;
|
||||
} // else if
|
||||
|
||||
//fprintf(stderr, "importid == %u\n", (unsigned int) importid);
|
||||
|
||||
static void *page = NULL;
|
||||
static uint32 pageused = 0;
|
||||
static uint32 pagesize = 0;
|
||||
|
||||
if (pagesize == 0)
|
||||
pagesize = getpagesize();
|
||||
|
||||
@@ -213,28 +200,22 @@ static uint32 getModuleProcAddrByOrdinal(const LxHeader *lx, const char * const
|
||||
pageused = 0;
|
||||
} // if
|
||||
|
||||
// !!! FIXME: obviously, this is going to have to be more robust.
|
||||
if (strcmp(modules[moduleid-1], "MSG") == 0)
|
||||
{
|
||||
if (importid == 5)
|
||||
return (uint32) (size_t) DosPutMessage;
|
||||
} // if
|
||||
|
||||
void *trampoline = page + pageused;
|
||||
char *ptr = (char *) trampoline;
|
||||
const uint32 ordinal = importid;
|
||||
char *module = strdup(_module);
|
||||
char *entry = strdup(_entry);
|
||||
|
||||
*(ptr++) = 0x55; // pushl %ebp
|
||||
*(ptr++) = 0x89; // movl %esp,%ebp
|
||||
*(ptr++) = 0xE5; // ...movl %esp,%ebp
|
||||
*(ptr++) = 0x68; // pushl immediate
|
||||
memcpy(ptr, &ordinal, sizeof (uint32));
|
||||
memcpy(ptr, &entry, sizeof (char *));
|
||||
ptr += sizeof (uint32);
|
||||
*(ptr++) = 0x68; // pushl immediate
|
||||
memcpy(ptr, &modules[moduleid-1], sizeof (uint32));
|
||||
memcpy(ptr, &module, sizeof (char *));
|
||||
ptr += sizeof (uint32);
|
||||
*(ptr++) = 0xB8; // movl immediate to %eax
|
||||
const void *fn = missing_ordinal_called;
|
||||
const void *fn = missingEntryPointCalled;
|
||||
memcpy(ptr, &fn, sizeof (void *));
|
||||
ptr += sizeof (void *);
|
||||
*(ptr++) = 0xFF; // call absolute in %eax.
|
||||
@@ -247,13 +228,164 @@ static uint32 getModuleProcAddrByOrdinal(const LxHeader *lx, const char * const
|
||||
if (pageused % 4) // keep these aligned to 32 bits.
|
||||
pageused += (4 - (pageused % 4));
|
||||
|
||||
printf("Generated trampoline %p for module '%s' export '%s'\n", trampoline, module, entry);
|
||||
|
||||
return (uint32) (size_t) trampoline;
|
||||
} // generateMissingTrampoline
|
||||
|
||||
static __attribute__((noreturn)) void runLxModule(const LxModule *lxmod, const int argc, char **argv)
|
||||
{
|
||||
// !!! FIXME: right now, we don't list any environment variables, because they probably don't make sense to drop in (even PATH uses a different separator on Unix).
|
||||
// !!! FIXME: eventually, the environment table looks like this (double-null to terminate list): var1=a\0var2=b\0var3=c\0\0
|
||||
// The command line, if I'm reading the Open Watcom __OS2Main() implementation correctly, looks like this...
|
||||
// \0programname\0argv0\0argv1\0argvN\0
|
||||
size_t len = 0;
|
||||
for (int i = 0; i < argc; i++) {
|
||||
len += strlen(argv[i]) + 1;
|
||||
}
|
||||
|
||||
const char *tmpenv = "IS_2INE=1";
|
||||
const size_t envlen = strlen(tmpenv);
|
||||
len += envlen;
|
||||
len += 4; // null terminators.
|
||||
|
||||
char *env = (char *) malloc(len);
|
||||
if (!env) {
|
||||
fprintf(stderr, "Out of memory\n");
|
||||
exit(1);
|
||||
} // if
|
||||
|
||||
strcpy(env, tmpenv);
|
||||
char *ptr = env + envlen;
|
||||
*(ptr++) = '\0';
|
||||
*(ptr++) = '\0';
|
||||
char *cmd = ptr;
|
||||
strcpy(ptr, argv[0]);
|
||||
ptr += strlen(argv[0]);
|
||||
*(ptr++) = '\0';
|
||||
for (int i = 0; i < argc; i++) {
|
||||
strcpy(ptr, argv[i]);
|
||||
ptr += strlen(argv[i]);
|
||||
*(ptr++) = '\0';
|
||||
} // for
|
||||
*(ptr++) = '\0';
|
||||
|
||||
// ...and you pass it the pointer to argv0. This is (at least as far as the docs suggest) appended to the environment table.
|
||||
printf("jumping into LX land...! eip=0x%X esp=0x%X\n", (unsigned int) lxmod->eip, (unsigned int) lxmod->esp); fflush(stdout);
|
||||
|
||||
// !!! FIXME: need to set up OS/2 TIB and put it in FS register.
|
||||
__asm__ __volatile__ (
|
||||
"movl %%esi,%%esp \n\t" // use the OS/2 process's stack.
|
||||
"pushl %%eax \n\t" // cmd
|
||||
"pushl %%ecx \n\t" // env
|
||||
"pushl $0 \n\t" // reserved
|
||||
"pushl $0 \n\t" // module handle !!! FIXME
|
||||
"leal 1f,%%eax \n\t" // address that entry point should return to.
|
||||
"pushl %%eax \n\t"
|
||||
"pushl %%edi \n\t" // the OS/2 process entry point (we'll "ret" to it instead of jmp, so stack and registers are all correct).
|
||||
"xorl %%eax,%%eax \n\t"
|
||||
"xorl %%ebx,%%ebx \n\t"
|
||||
"xorl %%ecx,%%ecx \n\t"
|
||||
"xorl %%edx,%%edx \n\t"
|
||||
"xorl %%esi,%%esi \n\t"
|
||||
"xorl %%edi,%%edi \n\t"
|
||||
"xorl %%ebp,%%ebp \n\t"
|
||||
// !!! FIXME: init other registers!
|
||||
"ret \n\t" // go to OS/2 land!
|
||||
"1: \n\t" // ...and return here.
|
||||
"pushl %%eax \n\t" // call _exit() with whatever is in %eax.
|
||||
"call _exit \n\t"
|
||||
: // no outputs
|
||||
: "a" (cmd), "c" (env), "S" (lxmod->esp), "D" (lxmod->eip)
|
||||
: "memory"
|
||||
);
|
||||
|
||||
__builtin_unreachable();
|
||||
} // runLxModule
|
||||
|
||||
static void freeLxModule(LxModule *lxmod)
|
||||
{
|
||||
if (!lxmod)
|
||||
return;
|
||||
|
||||
// !!! FIXME: mutex from here
|
||||
lxmod->refcount--;
|
||||
printf("unref'd module '%s' to %u\n", lxmod->name, (unsigned int) lxmod->refcount);
|
||||
if (lxmod->refcount > 0)
|
||||
return; // something is still using it.
|
||||
|
||||
if (lxmod->next)
|
||||
lxmod->next->prev = lxmod->prev;
|
||||
|
||||
if (lxmod->prev)
|
||||
lxmod->prev->next = lxmod->next;
|
||||
|
||||
if (lxmod == GLoadedModules)
|
||||
GLoadedModules = lxmod->next;
|
||||
// !!! FIXME: mutex to here
|
||||
|
||||
for (uint32 i = 0; i < lxmod->lx.num_import_mod_entries; i++)
|
||||
freeLxModule(lxmod->dependencies[i]);
|
||||
free(lxmod->dependencies);
|
||||
|
||||
for (uint32 i = 0; i < lxmod->lx.module_num_objects; i++) {
|
||||
if (lxmod->mmaps[i].addr)
|
||||
munmap(lxmod->mmaps[i].addr, lxmod->mmaps[i].size);
|
||||
} // for
|
||||
free(lxmod->mmaps);
|
||||
|
||||
free(lxmod->exported_names);
|
||||
free(lxmod->exported_ordinals);
|
||||
|
||||
if (lxmod->nativelib)
|
||||
dlclose(lxmod->nativelib);
|
||||
|
||||
free(lxmod);
|
||||
} // freeLxModule
|
||||
|
||||
static LxModule *loadLxModuleByModuleNameInternal(const char *modname, const int dependency_tree_depth);
|
||||
|
||||
static uint32 getModuleProcAddrByOrdinal(const LxModule *module, const uint32 ordinal)
|
||||
{
|
||||
fprintf(stderr, "lookup module == '%s', ordinal == %u\n", module->name, (unsigned int) ordinal);
|
||||
|
||||
const LxExportedOrdinal *expord = module->exported_ordinals;
|
||||
for (uint32 i = 0; i < module->num_ordinals; i++, expord++) {
|
||||
if (expord->ordinal == ordinal)
|
||||
return expord->addr;
|
||||
} // for
|
||||
|
||||
#if 1
|
||||
char entry[128];
|
||||
snprintf(entry, sizeof (entry), "ORDINAL_%u", (unsigned int) ordinal);
|
||||
return generateMissingTrampoline(module->name, entry);
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
} // getModuleProcAddrByOrdinal
|
||||
|
||||
#if 0
|
||||
static uint32 getModuleProcAddrByName(const LxModule *module, const char *name)
|
||||
{
|
||||
fprintf(stderr, "lookup module == '%s', name == '%s'\n", module->name, name);
|
||||
|
||||
const LxExportedName *expname = module->exported_names;
|
||||
for (uint32 i = 0; i < module->num_names; i++, expname++) {
|
||||
if (strcmp(expname->name, name) == 0)
|
||||
return expname->addr;
|
||||
} // for
|
||||
|
||||
#if 1
|
||||
return generateMissingTrampoline(module->name, name);
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
} // getModuleProcAddrByName
|
||||
#endif
|
||||
|
||||
static void doFixup(uint8 *page, const uint16 offset, const uint32 finalval, const uint16 finalval2, const uint32 finalsize)
|
||||
{
|
||||
#if 0
|
||||
#if 1
|
||||
if (finalsize == 6) {
|
||||
printf("fixing up %p to to 0x%X:0x%X (6 bytes)...\n", page + offset, (unsigned int) finalval2, (unsigned int) finalval);
|
||||
} else {
|
||||
@@ -279,9 +411,9 @@ static void doFixup(uint8 *page, const uint16 offset, const uint32 finalval, con
|
||||
} // switch
|
||||
} // doFixup
|
||||
|
||||
static void fixupPage(const LxHeader *lx, const char * const * const modules, const LxObjectTableEntry *obj, uint8 *page)
|
||||
static void fixupPage(const uint8 *exe, LxModule *lxmod, const LxObjectTableEntry *obj, uint8 *page)
|
||||
{
|
||||
const uint8 *exe = (const uint8 *) lx; // this is the start of the LX EXE (past the DOS stub, etc).
|
||||
const LxHeader *lx = &lxmod->lx;
|
||||
const uint32 *fixuppage = (((const uint32 *) (exe + lx->fixup_page_table_offset)) + (obj->page_table_index - 1));
|
||||
const uint32 fixupoffset = *fixuppage;
|
||||
const uint32 fixuplen = fixuppage[1] - fixuppage[0];
|
||||
@@ -372,7 +504,13 @@ static void fixupPage(const LxHeader *lx, const char * const * const modules, co
|
||||
importid = (uint32) *((uint16 *) fixup); fixup += 2;
|
||||
} // else
|
||||
|
||||
finalval = getModuleProcAddrByOrdinal(lx, modules, moduleid, importid);
|
||||
if (moduleid == 0) {
|
||||
fprintf(stderr, "uhoh, looking for module ordinal 0, which is illegal.\n");
|
||||
} else if (moduleid > lx->num_import_mod_entries) {
|
||||
fprintf(stderr, "uhoh, looking for module ordinal %u, but only %u available.\n", (unsigned int) moduleid, (unsigned int) lx->num_import_mod_entries);
|
||||
} else {
|
||||
finalval = getModuleProcAddrByOrdinal(lxmod->dependencies[moduleid-1], importid);
|
||||
} // else
|
||||
break;
|
||||
} // case
|
||||
|
||||
@@ -421,41 +559,74 @@ static void fixupPage(const LxHeader *lx, const char * const * const modules, co
|
||||
} // while
|
||||
} // fixupPage
|
||||
|
||||
static __attribute__((noreturn)) void loadExe(const char *exefname, uint8 *exe, uint32 exelen)
|
||||
static LxModule *loadLxModule(uint8 *exe, uint32 exelen, int dependency_tree_depth)
|
||||
{
|
||||
LxModule *retval = NULL;
|
||||
const uint8 *origexe = exe;
|
||||
//const uint32 origexelen = exelen;
|
||||
|
||||
if (!sanityCheckExe(&exe, &exelen))
|
||||
exit(1);
|
||||
if (!sanityCheckLxModule(&exe, &exelen))
|
||||
goto loadlx_failed;
|
||||
|
||||
const LxHeader *lx = (const LxHeader *) exe;
|
||||
|
||||
// !!! FIXME: recursively load all external dependencies from lx->import_module_table_offset, here.
|
||||
char **modules = malloc(sizeof (char *) * lx->num_import_mod_entries);
|
||||
if (!modules) {
|
||||
retval = (LxModule *) malloc(sizeof (LxModule));
|
||||
if (!retval) {
|
||||
fprintf(stderr, "Out of memory!\n");
|
||||
exit(1);
|
||||
}
|
||||
goto loadlx_failed;
|
||||
} // if
|
||||
memset(retval, '\0', sizeof (*retval));
|
||||
retval->refcount = 1;
|
||||
retval->dependencies = (LxModule **) malloc(sizeof (LxModule *) * lx->num_import_mod_entries);
|
||||
retval->mmaps = (LxMmaps *) malloc(sizeof (LxMmaps) * lx->module_num_objects);
|
||||
if (!retval->dependencies || !retval->mmaps) {
|
||||
fprintf(stderr, "Out of memory!\n");
|
||||
goto loadlx_failed;
|
||||
} // if
|
||||
memset(retval->dependencies, '\0', sizeof (LxModule *) * lx->num_import_mod_entries);
|
||||
memset(retval->mmaps, '\0', sizeof (LxMmaps) * lx->module_num_objects);
|
||||
memcpy(&retval->lx, lx, sizeof (*lx));
|
||||
|
||||
if (lx->resident_name_table_offset) {
|
||||
const uint8 *name_table = exe + lx->resident_name_table_offset;
|
||||
const uint8 namelen = *(name_table++);
|
||||
char *ptr = retval->name;
|
||||
for (uint32 i = 0; i < namelen; i++) {
|
||||
const char ch = *(name_table++);
|
||||
*(ptr++) = ((ch >= 'a') && (ch <= 'z')) ? (ch + ('A' - 'a')) : ch;
|
||||
} // for
|
||||
*ptr = '\0';
|
||||
} // if
|
||||
|
||||
const char *modname = retval->name;
|
||||
printf("ref'd new module '%s' to %u\n", modname, 1);
|
||||
|
||||
// !!! FIXME: apparently OS/2 does 1024, but they're not loading the module into RAM each time.
|
||||
if (++dependency_tree_depth > 32) {
|
||||
fprintf(stderr, "Likely circular dependency in module '%s'\n", modname);
|
||||
goto loadlx_failed;
|
||||
} // if
|
||||
|
||||
const uint8 *import_modules_table = exe + lx->import_module_table_offset;
|
||||
for (uint32 i = 0; i < lx->num_import_mod_entries; i++) {
|
||||
const uint8 namelen = *(import_modules_table++);
|
||||
// !!! FIXME: name can't be more than 127 chars, according to docs. Check this.
|
||||
char *str = (char *) malloc(namelen + 1);
|
||||
if (!str) {
|
||||
fprintf(stderr, "Out of memory!\n");
|
||||
exit(1);
|
||||
if (namelen > 127) {
|
||||
fprintf(stderr, "Import module %u name is > 127 chars (%u). Corrupt file or bug!\n", (unsigned int) (i + 1), (unsigned int) namelen);
|
||||
goto loadlx_failed;
|
||||
}
|
||||
memcpy(str, import_modules_table, namelen);
|
||||
char name[128];
|
||||
memcpy(name, import_modules_table, namelen);
|
||||
import_modules_table += namelen;
|
||||
str[namelen] = '\0';
|
||||
modules[i] = str;
|
||||
name[namelen] = '\0';
|
||||
retval->dependencies[i] = loadLxModuleByModuleNameInternal(name, dependency_tree_depth);
|
||||
if (!retval->dependencies[i]) {
|
||||
fprintf(stderr, "Failed to load dependency '%s' for module '%s'\n", name, modname);
|
||||
goto loadlx_failed;
|
||||
} // if
|
||||
} // for
|
||||
|
||||
const LxObjectTableEntry *obj = ((const LxObjectTableEntry *) (exe + lx->object_table_offset));
|
||||
for (uint32 i = 0; i < lx->module_num_objects; i++, obj++) {
|
||||
|
||||
if (obj->object_flags & 0x8) // !!! FIXME: resource object; ignore this until resource support is written, later.
|
||||
continue;
|
||||
|
||||
@@ -467,17 +638,20 @@ static __attribute__((noreturn)) void loadExe(const char *exefname, uint8 *exe,
|
||||
((obj->object_flags & 0x2) ? PROT_WRITE : 0) |
|
||||
((obj->object_flags & 0x4) ? PROT_EXEC : 0);
|
||||
|
||||
const int mmapflags = MAP_ANON | MAP_PRIVATE | MAP_FIXED;
|
||||
void *base = (void *) ((size_t) obj->reloc_base_addr);
|
||||
const int mmapflags = MAP_ANON | MAP_PRIVATE | MAP_FIXED;
|
||||
void *mmapaddr = mmap(base, vsize, PROT_READ|PROT_WRITE, mmapflags, -1, 0);
|
||||
// we'll mprotect() these pages to the proper permissions later.
|
||||
|
||||
if (mmapaddr == ((void *) MAP_FAILED)) {
|
||||
fprintf(stderr, "mmap(%p, %u, RW-, ANON|PRIVATE|FIXED, -1, 0) failed (%d): %s\n",
|
||||
base, (unsigned int) vsize, errno, strerror(errno));
|
||||
exit(1);
|
||||
goto loadlx_failed;
|
||||
} // if
|
||||
|
||||
retval->mmaps[i].addr = mmapaddr;
|
||||
retval->mmaps[i].size = vsize;
|
||||
|
||||
const LxObjectPageTableEntry *objpage = ((const LxObjectPageTableEntry *) (exe + lx->object_page_table_offset));
|
||||
objpage += obj->page_table_index - 1;
|
||||
|
||||
@@ -508,18 +682,18 @@ static __attribute__((noreturn)) void loadExe(const char *exefname, uint8 *exe,
|
||||
src = origexe + lx->data_pages_offset + (objpage->page_data_offset << lx->page_offset_shift);
|
||||
if (!decompressExePack2(dst, lx->page_size, src, objpage->data_size)) {
|
||||
fprintf(stderr, "Failed to decompress object page (corrupt file or bug).\n");
|
||||
exit(1);
|
||||
goto loadlx_failed;
|
||||
} // if
|
||||
break;
|
||||
|
||||
case 0x08: // !!! FIXME: this is listed in lxlite, presumably this is exepack3 or something. Maybe this was new to Warp4, like exepack2 was new to Warp3. Pull this algorithm in from lxlite later.
|
||||
default:
|
||||
fprintf(stderr, "Don't know how to load an object page of type %u!\n", (unsigned int) objpage->flags);
|
||||
exit(1);
|
||||
goto loadlx_failed;
|
||||
} // switch
|
||||
|
||||
// Now run any fixups for the page...
|
||||
fixupPage(lx, modules, obj, dst);
|
||||
fixupPage(exe, retval, obj, dst);
|
||||
|
||||
dst += lx->page_size;
|
||||
} // for
|
||||
@@ -538,68 +712,131 @@ static __attribute__((noreturn)) void loadExe(const char *exefname, uint8 *exe,
|
||||
(prot&PROT_WRITE) ? "W" : "-",
|
||||
(prot&PROT_EXEC) ? "X" : "-",
|
||||
errno, strerror(errno));
|
||||
exit(1);
|
||||
goto loadlx_failed;
|
||||
} // if
|
||||
|
||||
} // for
|
||||
|
||||
// All the pages we need from the EXE are loaded into the appropriate spots in memory.
|
||||
//printf("mmap()'d everything we need!\n");
|
||||
printf("mmap()'d everything we need!\n");
|
||||
|
||||
uint32 eip = lx->eip;
|
||||
retval->eip = lx->eip;
|
||||
if (lx->eip_object != 0) {
|
||||
const LxObjectTableEntry *targetobj = ((const LxObjectTableEntry *) (exe + lx->object_table_offset)) + (lx->eip_object - 1);
|
||||
eip += targetobj->reloc_base_addr;
|
||||
retval->eip += targetobj->reloc_base_addr;
|
||||
} // if
|
||||
|
||||
// !!! FIXME: esp==0 means something special for programs (and is ignored for library init).
|
||||
uint32 esp = lx->esp;
|
||||
assert(esp != 0);
|
||||
// !!! FIXME: "A zero value in this field indicates that the stack pointer is to be initialized to the highest address/offset in the object"
|
||||
retval->esp = lx->esp;
|
||||
if (lx->esp_object != 0) { // !!! FIXME: ignore for libraries
|
||||
const LxObjectTableEntry *targetobj = ((const LxObjectTableEntry *) (exe + lx->object_table_offset)) + (lx->esp_object - 1);
|
||||
esp += targetobj->reloc_base_addr;
|
||||
retval->esp += targetobj->reloc_base_addr;
|
||||
} // if
|
||||
|
||||
// !!! FIXME: right now, we don't list any environment variables, because they probably don't make sense to drop in (even PATH uses a different separator on Unix).
|
||||
// !!! FIXME: eventually, the environment table looks like this (double-null to terminate list): var1=a\0var2=b\0var3=c\0\0
|
||||
// The command line, if I'm reading the Open Watcom __OS2Main() implementation correctly, looks like this...
|
||||
// \0programname\0argv0\0argv1\0argvN\0
|
||||
static char *env = "TEST=abc\0\0hello.exe\0hello.exe\0\0";
|
||||
char *cmd = env + 10; // !!! FIXME
|
||||
// module is ready to use, put it in the loaded list.
|
||||
// !!! FIXME: mutex this
|
||||
if (GLoadedModules) {
|
||||
retval->next = GLoadedModules;
|
||||
GLoadedModules->prev = retval;
|
||||
} // if
|
||||
GLoadedModules = retval;
|
||||
|
||||
// ...and you pass it the pointer to argv0. This is (at least as far as the docs suggest) appended to the environment table.
|
||||
//printf("jumping into LX land...! eip=0x%X esp=0x%X\n", (unsigned int) eip, (unsigned int) esp); fflush(stdout);
|
||||
return retval;
|
||||
|
||||
// !!! FIXME: need to set up OS/2 TIB and put it in FS register.
|
||||
__asm__ __volatile__ (
|
||||
"movl %%esi,%%esp \n\t" // use the OS/2 process's stack.
|
||||
"pushl %%eax \n\t" // cmd
|
||||
"pushl %%ecx \n\t" // env
|
||||
"pushl $0 \n\t" // reserved
|
||||
"pushl $0 \n\t" // module handle !!! FIXME
|
||||
"leal 1f,%%eax \n\t" // address that entry point should return to.
|
||||
"pushl %%eax \n\t"
|
||||
"pushl %%edi \n\t" // the OS/2 process entry point (we'll "ret" to it instead of jmp, so stack and registers are all correct).
|
||||
"xorl %%eax,%%eax \n\t"
|
||||
"xorl %%ebx,%%ebx \n\t"
|
||||
"xorl %%ecx,%%ecx \n\t"
|
||||
"xorl %%edx,%%edx \n\t"
|
||||
"xorl %%esi,%%esi \n\t"
|
||||
"xorl %%edi,%%edi \n\t"
|
||||
"xorl %%ebp,%%ebp \n\t"
|
||||
// !!! FIXME: init other registers!
|
||||
"ret \n\t" // go to OS/2 land!
|
||||
"1: \n\t" // ...and return here.
|
||||
"pushl %%eax \n\t" // call _exit() with whatever is in %eax.
|
||||
"call _exit \n\t"
|
||||
: // no outputs
|
||||
: "a" (cmd), "c" (env), "S" (esp), "D" (eip)
|
||||
: "memory"
|
||||
);
|
||||
loadlx_failed:
|
||||
freeLxModule(retval);
|
||||
return NULL;
|
||||
} // loadLxModule
|
||||
|
||||
__builtin_unreachable();
|
||||
} // loadExe
|
||||
static LxModule *loadLxModuleByPathInternal(const char *fname, const int dependency_tree_depth)
|
||||
{
|
||||
const char *what = NULL;
|
||||
uint8 *module = NULL;
|
||||
uint32 modulelen = 0;
|
||||
FILE *io = NULL;
|
||||
|
||||
// !!! FIXME: locate correct file (case-insensitive checking, convert '\\' to '/' etc)...
|
||||
|
||||
#define LOADFAIL(x) { what = x; goto loadmod_failed; }
|
||||
|
||||
if ((io = fopen(fname, "rb")) == NULL) LOADFAIL("open");
|
||||
if (fseek(io, 0, SEEK_END) < 0) LOADFAIL("seek");
|
||||
modulelen = ftell(io);
|
||||
if ((module = (uint8 *) malloc(modulelen)) == NULL) LOADFAIL("malloc");
|
||||
rewind(io);
|
||||
if (fread(module, modulelen, 1, io) != 1) LOADFAIL("read");
|
||||
fclose(io);
|
||||
|
||||
#undef LOADFAIL
|
||||
|
||||
LxModule *retval = loadLxModule(module, modulelen, dependency_tree_depth);
|
||||
free(module);
|
||||
return retval;
|
||||
|
||||
loadmod_failed:
|
||||
fprintf(stderr, "%s failure on '%s: %s'\n", what, fname, strerror(errno));
|
||||
if (io)
|
||||
fclose(io);
|
||||
free(module);
|
||||
return NULL;
|
||||
} // loadLxModuleByPathInternal
|
||||
|
||||
static inline LxModule *loadLxModuleByPath(const char *fname)
|
||||
{
|
||||
return loadLxModuleByPathInternal(fname, 0);
|
||||
} // loadLxModuleByPath
|
||||
|
||||
static LxModule *loadNativeReplacement(const char *fname)
|
||||
{
|
||||
// !!! FIXME: mutex this.
|
||||
void *lib = dlopen(fname, RTLD_LOCAL | RTLD_NOW);
|
||||
if (lib == NULL)
|
||||
return NULL;
|
||||
|
||||
LxNativeReplacementEntryPoint fn = (LxNativeReplacementEntryPoint) dlsym(lib, "loadNativeLxModule");
|
||||
if (!fn) {
|
||||
dlclose(lib);
|
||||
return NULL;
|
||||
} // if
|
||||
|
||||
LxModule *retval = fn();
|
||||
if (retval) {
|
||||
printf("Loaded native replacement library '%s' (%u exports).\n", fname, (unsigned int) retval->num_ordinals);
|
||||
retval->nativelib = lib;
|
||||
} // if
|
||||
|
||||
return retval;
|
||||
} // loadNativeReplacement
|
||||
|
||||
static LxModule *loadLxModuleByModuleNameInternal(const char *modname, const int dependency_tree_depth)
|
||||
{
|
||||
// !!! FIXME: mutex this
|
||||
for (LxModule *i = GLoadedModules; i != NULL; i = i->next) {
|
||||
if (strcasecmp(i->name, modname) == 0) {
|
||||
i->refcount++;
|
||||
printf("ref'd module '%s' to %u\n", i->name, (unsigned int) i->refcount);
|
||||
return i;
|
||||
} // if
|
||||
} // for
|
||||
|
||||
// !!! FIXME: decide the right path to the file, or if it's a native replacement library.
|
||||
char fname[256];
|
||||
snprintf(fname, sizeof (fname), "%s.dll", modname);
|
||||
LxModule *retval = loadLxModuleByPathInternal(fname, dependency_tree_depth);
|
||||
if (!retval) {
|
||||
snprintf(fname, sizeof (fname), "native/%s.so", modname);
|
||||
for (char *ptr = fname; *ptr; ptr++) {
|
||||
*ptr = (((*ptr >= 'A') && (*ptr <= 'Z')) ? (*ptr - ('A' - 'a')) : *ptr);
|
||||
} // for
|
||||
retval = loadNativeReplacement(fname);
|
||||
} // if
|
||||
return retval;
|
||||
} // loadLxModuleByModuleNameInternal
|
||||
|
||||
static inline LxModule *loadLxModuleByModuleName(const char *modname)
|
||||
{
|
||||
return loadLxModuleByModuleNameInternal(modname, 0);
|
||||
} // loadLxModuleByModuleName
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
@@ -608,36 +845,12 @@ int main(int argc, char **argv)
|
||||
return 1;
|
||||
}
|
||||
|
||||
const char *exefname = argv[1];
|
||||
FILE *io = fopen(exefname, "rb");
|
||||
if (!io) {
|
||||
fprintf(stderr, "can't open '%s: %s'\n", exefname, strerror(errno));
|
||||
return 2;
|
||||
}
|
||||
|
||||
if (fseek(io, 0, SEEK_END) < 0) {
|
||||
fprintf(stderr, "can't seek in '%s': %s\n", exefname, strerror(errno));
|
||||
return 3;
|
||||
}
|
||||
|
||||
const uint32 exelen = ftell(io);
|
||||
uint8 *exe = (uint8 *) malloc(exelen);
|
||||
if (!exe) {
|
||||
fprintf(stderr, "Out of memory\n");
|
||||
return 4;
|
||||
}
|
||||
|
||||
rewind(io);
|
||||
if (fread(exe, exelen, 1, io) != 1) {
|
||||
fprintf(stderr, "read failure on '%s': %s\n", exefname, strerror(errno));
|
||||
return 5;
|
||||
}
|
||||
|
||||
fclose(io);
|
||||
|
||||
loadExe(exefname, exe, exelen);
|
||||
|
||||
free(exe);
|
||||
const LxModule *lxmod = loadLxModuleByPath(argv[1]);
|
||||
if (lxmod) {
|
||||
argc--;
|
||||
argv++;
|
||||
runLxModule(lxmod, argc, argv);
|
||||
} // if
|
||||
|
||||
return 0;
|
||||
} // main
|
||||
|
||||
+80
@@ -3,7 +3,10 @@
|
||||
|
||||
#define _GNU_SOURCE 1
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
typedef uint8_t uint8;
|
||||
typedef uint16_t uint16;
|
||||
@@ -89,6 +92,83 @@ typedef struct LxResourceTableEntry
|
||||
|
||||
#pragma pack(pop)
|
||||
|
||||
|
||||
typedef struct LxMmaps
|
||||
{
|
||||
void *addr;
|
||||
size_t size;
|
||||
} LxMmaps;
|
||||
|
||||
typedef struct LxExportedOrdinal
|
||||
{
|
||||
uint32 ordinal;
|
||||
uint32 addr;
|
||||
} LxExportedOrdinal;
|
||||
|
||||
typedef struct LxExportedName
|
||||
{
|
||||
char name[128]; // !!! FIXME: allocate this.
|
||||
uint32 addr;
|
||||
} LxExportedName;
|
||||
|
||||
struct LxModule;
|
||||
typedef struct LxModule LxModule;
|
||||
struct LxModule
|
||||
{
|
||||
LxHeader lx;
|
||||
uint32 refcount;
|
||||
char name[256]; // !!! FIXME: allocate this.
|
||||
LxModule **dependencies;
|
||||
LxMmaps *mmaps;
|
||||
uint32 num_ordinals;
|
||||
LxExportedOrdinal *exported_ordinals;
|
||||
uint32 num_names;
|
||||
LxExportedName *exported_names;
|
||||
void *nativelib;
|
||||
uint32 eip;
|
||||
uint32 esp;
|
||||
LxModule *prev; // all loaded modules are in a doubly-linked list.
|
||||
LxModule *next; // all loaded modules are in a doubly-linked list.
|
||||
};
|
||||
|
||||
typedef LxModule *(*LxNativeReplacementEntryPoint)(void);
|
||||
|
||||
// !!! FIXME: this is nasty for several reasons.
|
||||
#define NATIVE_REPLACEMENT_TABLE(modname) \
|
||||
LxModule *loadNativeLxModule(void) { \
|
||||
LxModule *retval = (LxModule *) malloc(sizeof (LxModule)); \
|
||||
if (!retval) goto loadnative_failed; \
|
||||
memset(retval, '\0', sizeof (LxModule)); \
|
||||
retval->refcount = 1; \
|
||||
strcpy(retval->name, modname); \
|
||||
|
||||
#define NATIVE_REPLACEMENT(fn, ord) { \
|
||||
void *ptr = realloc(retval->exported_names, (retval->num_names+1) * sizeof (LxExportedName)); \
|
||||
if (!ptr) { goto loadnative_failed; } \
|
||||
retval->exported_names = (LxExportedName *) ptr; \
|
||||
strcpy(retval->exported_names[retval->num_names].name, #fn); \
|
||||
retval->exported_names[retval->num_names].addr = (uint32) ((size_t) fn); \
|
||||
retval->num_names++; \
|
||||
ptr = realloc(retval->exported_ordinals, (retval->num_ordinals+1) * sizeof (LxExportedOrdinal)); \
|
||||
if (!ptr) { goto loadnative_failed; } \
|
||||
retval->exported_ordinals = (LxExportedOrdinal *) ptr; \
|
||||
retval->exported_ordinals[retval->num_ordinals].ordinal = ord; \
|
||||
retval->exported_ordinals[retval->num_ordinals].addr = (uint32) ((size_t) fn); \
|
||||
retval->num_ordinals++; \
|
||||
}
|
||||
|
||||
#define END_NATIVE_REPLACEMENT_TABLE() \
|
||||
return retval; \
|
||||
\
|
||||
loadnative_failed: \
|
||||
if (retval) { \
|
||||
free(retval->exported_ordinals); \
|
||||
free(retval->exported_names); \
|
||||
free(retval); \
|
||||
} \
|
||||
return NULL; \
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// end of lx_loader.h ...
|
||||
|
||||
@@ -10,7 +10,18 @@
|
||||
# 32-bit Linux totally lets me have that space, though. :)
|
||||
#clang -pagezero_size 0x1000 -image_base 0x100000000 -Wall -O0 -ggdb3 -o lx_loader lx_loader.c && ./lx_loader ./cmd.exe
|
||||
|
||||
gcc -std=c99 -Wall -O0 -ggdb3 -o lx_dump lx_dump.c &&
|
||||
gcc -m32 -std=c99 -Wall -O0 -ggdb3 -o lx_loader lx_loader.c &&
|
||||
./lx_dump tests/hello.exe &&
|
||||
set -x
|
||||
set -e
|
||||
|
||||
gcc -m32 -std=c99 -Wall -O0 -ggdb3 -fPIC -shared -Wl,-soname,msg.so -o native/msg.so native/msg.c
|
||||
gcc -m32 -std=c99 -Wall -O0 -ggdb3 -fPIC -shared -Wl,-soname,doscalls.so.so -o native/doscalls.so native/doscalls.c
|
||||
gcc -m32 -std=c99 -Wall -O0 -ggdb3 -fPIC -shared -Wl,-soname,nls.so -o native/nls.so native/nls.c
|
||||
gcc -m32 -std=c99 -Wall -O0 -ggdb3 -fPIC -shared -Wl,-soname,quecalls.so -o native/queecalls.so native/quecalls.c
|
||||
gcc -m32 -std=c99 -Wall -O0 -ggdb3 -fPIC -shared -Wl,-soname,viocalls.so -o native/viocalls.so native/viocalls.c
|
||||
gcc -m32 -std=c99 -Wall -O0 -ggdb3 -fPIC -shared -Wl,-soname,kdbcalls.so -o native/kbdcalls.so native/kbdcalls.c
|
||||
gcc -m32 -std=c99 -Wall -O0 -ggdb3 -fPIC -shared -Wl,-soname,sesmgr.so -o native/sesmgr.so native/sesmgr.c
|
||||
|
||||
gcc -std=c99 -Wall -O0 -ggdb3 -o lx_dump lx_dump.c
|
||||
gcc -m32 -std=c99 -Wall -O0 -ggdb3 -o lx_loader lx_loader.c -ldl
|
||||
./lx_dump tests/hello.exe
|
||||
./lx_loader tests/hello.exe
|
||||
@@ -0,0 +1,7 @@
|
||||
#include "../lx_loader.h"
|
||||
|
||||
NATIVE_REPLACEMENT_TABLE("doscalls")
|
||||
END_NATIVE_REPLACEMENT_TABLE()
|
||||
|
||||
// end of doscalls.c ...
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
#include "../lx_loader.h"
|
||||
|
||||
NATIVE_REPLACEMENT_TABLE("kbdcalls")
|
||||
END_NATIVE_REPLACEMENT_TABLE()
|
||||
|
||||
// end of kbdcalls.c ...
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
#include "../lx_loader.h"
|
||||
|
||||
static uint32 DosPutMessage(uint32 handle, uint32 msglen, const char *msg)
|
||||
{
|
||||
// !!! FIXME: this isn't right, but good enough for now.
|
||||
fwrite(msg, msglen, 1, stdout);
|
||||
fflush(stdout);
|
||||
return 0;
|
||||
} // DosPutMessage
|
||||
|
||||
NATIVE_REPLACEMENT_TABLE("msg")
|
||||
NATIVE_REPLACEMENT(DosPutMessage, 5)
|
||||
END_NATIVE_REPLACEMENT_TABLE()
|
||||
|
||||
// end of msg.c ...
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
#include "../lx_loader.h"
|
||||
|
||||
NATIVE_REPLACEMENT_TABLE("nls")
|
||||
END_NATIVE_REPLACEMENT_TABLE()
|
||||
|
||||
// end of nls.c ...
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
#include "../lx_loader.h"
|
||||
|
||||
NATIVE_REPLACEMENT_TABLE("quecalls")
|
||||
END_NATIVE_REPLACEMENT_TABLE()
|
||||
|
||||
// end of quecalls.c ...
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
#include "../lx_loader.h"
|
||||
|
||||
NATIVE_REPLACEMENT_TABLE("sesmgr")
|
||||
END_NATIVE_REPLACEMENT_TABLE()
|
||||
|
||||
// end of sesmgr.c ...
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
#include "../lx_loader.h"
|
||||
|
||||
NATIVE_REPLACEMENT_TABLE("viocalls")
|
||||
END_NATIVE_REPLACEMENT_TABLE()
|
||||
|
||||
// end of viocalls.c ...
|
||||
|
||||
Executable
BIN
Binary file not shown.
Executable
+5
@@ -0,0 +1,5 @@
|
||||
#include <stdio.h>
|
||||
int main(void) {
|
||||
printf("hello world\n");
|
||||
return 0;
|
||||
}
|
||||
Executable
BIN
Binary file not shown.
Reference in new issue
Block a user