16-bit support!
This gets us everything we need (I hope!) to support 16-bit code in LX binaries and call into 16-bit OS/2 APIs. This is just enough to call into VioGetMode() (which isn't otherwise implemented yet) as an example. If this is ever used Winelib-style to provide an OS/2 API to native Linux binaries, the 16-bit system APIs are 32-bit clean and C callable, as one might expect them to be. All the tapdancing is to support existing LX binaries in the loader and bridge between the 32-bit clean APIs and existing 16-bit code that expects to call 16-bit APIs. This was a staggering amount of effort, but yay! Other fixes and improvements probably leaked into this commit, as a lot of code got touched and a lot of research and debugging took place.
This commit is contained in:
1 parent
29696db8b3
commit
26a52988a6
10 files changed
+694
-57
No files matched your search
@@ -0,0 +1,25 @@
|
||||
#!/usr/bin/perl -w
|
||||
|
||||
# run this on the output from ndisasm.
|
||||
|
||||
use warnings;
|
||||
use strict;
|
||||
|
||||
while (<>) {
|
||||
chomp;
|
||||
my ($bytes, $asm) = /\A.*?\s+(.*?)\s+(.*)\Z/;
|
||||
my $first = 1;
|
||||
while ($bytes ne '') {
|
||||
$bytes =~ s/\A(..)//;
|
||||
my $hex = $1;
|
||||
if ($first and ($bytes eq '')) {
|
||||
print(" *(ptr++) = 0x$hex; /* $asm */\n");
|
||||
} elsif ($first) {
|
||||
print(" *(ptr++) = 0x$hex; /* $asm... */\n");
|
||||
} else {
|
||||
print(" *(ptr++) = 0x$hex; /* ...$asm */\n");
|
||||
}
|
||||
$first = 0;
|
||||
}
|
||||
}
|
||||
|
||||
+318
-39
@@ -23,7 +23,6 @@
|
||||
|
||||
static LxLoaderState _GLoaderState;
|
||||
static LxLoaderState *GLoaderState = &_GLoaderState;
|
||||
static uint8 GMainTibSpace[LXTIBSIZE];
|
||||
|
||||
// !!! FIXME: move this into an lx_common.c file.
|
||||
static int sanityCheckLxModule(uint8 **_exe, uint32 *_exelen)
|
||||
@@ -410,6 +409,149 @@ static void *generateMissingTrampoline(const char *_module, const char *_entry)
|
||||
} // generateMissingTrampoline
|
||||
|
||||
|
||||
// !!! FIXME: mutex this
|
||||
static int allocateSelector(const uint16 selector, const uint32 addr, const unsigned int contents, const int is32bit)
|
||||
{
|
||||
assert(selector < (sizeof (GLoaderState->ldt) / sizeof (GLoaderState->ldt[0])));
|
||||
|
||||
if (GLoaderState->ldt[selector])
|
||||
return 0; // already in use.
|
||||
|
||||
//const int expand_down = (contents == MODIFY_LDT_CONTENTS_STACK);
|
||||
|
||||
struct user_desc entry;
|
||||
entry.entry_number = (unsigned int) selector;
|
||||
entry.base_addr = (unsigned int) addr; //(expand_down ? (addr + 0x10000) : addr);
|
||||
entry.limit = 16; //expand_down ? 0 : 16;
|
||||
entry.seg_32bit = is32bit;
|
||||
entry.contents = contents;
|
||||
entry.read_exec_only = 0;
|
||||
entry.limit_in_pages = 1;
|
||||
entry.seg_not_present = 0;
|
||||
entry.useable = 1;
|
||||
if (syscall(SYS_modify_ldt, 1, &entry, sizeof (entry)) != 0)
|
||||
return 0;
|
||||
|
||||
GLoaderState->ldt[selector] = addr;
|
||||
return 1;
|
||||
} // allocateSelector
|
||||
|
||||
// !!! FIXME: mutex this
|
||||
static int findSelector(const uint32 _addr, uint16 *outselector, uint16 *outoffset)
|
||||
{
|
||||
uint32 addr = _addr;
|
||||
if (addr < 4096)
|
||||
return 0; // we won't map the NULL page.
|
||||
|
||||
const uint32 *ldt = GLoaderState->ldt;
|
||||
|
||||
int available = -1;
|
||||
int preferred = -1;
|
||||
|
||||
// optimize for the case where we need a selector that happenes to be in tiled memory,
|
||||
// since it's fast to look up.
|
||||
if (addr < (1024 * 1024 * 512)) {
|
||||
const uint16 idx = (uint16) (addr >> 16);
|
||||
const uint32 tile = ldt[idx];
|
||||
if (tile == 0) {
|
||||
preferred = available = idx; // we can use this piece.
|
||||
} else if ((tile <= addr) && ((tile + 0x10000) > addr)) {
|
||||
*outselector = idx;
|
||||
*outoffset = (uint16) (addr - tile);
|
||||
//printf("SELECTOR: found tiled selector 0x%X for address %p\n", (uint) idx, (void *) addr);
|
||||
return 1; // already allocated to this address.
|
||||
} // if
|
||||
} // if
|
||||
|
||||
for (int i = 0; i < (sizeof (GLoaderState->ldt) / sizeof (GLoaderState->ldt[0])); i++) {
|
||||
const uint32 tile = ldt[i];
|
||||
if (tile == 0)
|
||||
available = i;
|
||||
else if ((tile <= addr) && ((tile + 0x10000) > addr)) {
|
||||
*outselector = (uint16) i;
|
||||
*outoffset = (uint16) (addr - tile);
|
||||
//printf("SELECTOR: found existing selector 0x%X for address %p\n", (uint) i, (void *) addr);
|
||||
return 1; // already allocated to this address.
|
||||
} // else if
|
||||
} // for
|
||||
|
||||
// nothing allocated to this address so far. Try to allocate something.
|
||||
if (available == -1) {
|
||||
fprintf(stderr, "Uhoh, we've run out of LDT selectors! Probably about to crash...\n"); fflush(stderr);
|
||||
return 0; // uh oh, out of selectors!
|
||||
} // if
|
||||
|
||||
const uint32 diff = addr % 0x10000;
|
||||
addr -= diff; // make sure we start on a 64k border.
|
||||
|
||||
const uint16 selector = (uint16) (preferred != -1) ? preferred : available;
|
||||
if (!allocateSelector(selector, addr, MODIFY_LDT_CONTENTS_CODE, 0)) {
|
||||
fprintf(stderr, "Uhoh, we've failed to allocate LDT selector %u! Probably about to crash...\n", (uint) selector); fflush(stderr);
|
||||
return 0;
|
||||
} // if
|
||||
|
||||
//printf("SELECTOR: allocated selector 0x%X for address %p\n", (uint) selector, (void *) addr);
|
||||
*outselector = selector;
|
||||
*outoffset = (uint16) diff;
|
||||
return 1;
|
||||
} // findSelector
|
||||
|
||||
// !!! FIXME: mutex this
|
||||
static void freeSelector(const uint16 selector)
|
||||
{
|
||||
assert(selector < (sizeof (GLoaderState->ldt) / sizeof (GLoaderState->ldt[0])));
|
||||
|
||||
if (!GLoaderState->ldt[selector])
|
||||
return; // already free.
|
||||
|
||||
struct user_desc entry;
|
||||
memset(&entry, '\0', sizeof (entry));
|
||||
entry.entry_number = (unsigned int) selector;
|
||||
entry.read_exec_only = 1;
|
||||
entry.seg_not_present = 1;
|
||||
if (syscall(SYS_modify_ldt, 1, &entry, sizeof (entry)) != 0)
|
||||
return; // oh well.
|
||||
|
||||
GLoaderState->ldt[selector] = 0;
|
||||
} // freeSelector
|
||||
|
||||
static void *convert1616to32(const uint32 addr1616)
|
||||
{
|
||||
const uint16 selector = (uint16) (addr1616 >> 19); // slide segment down, and shift out control bits.
|
||||
const uint16 offset = (uint16) (addr1616 % 0x10000); // all our LDT segments start at 64k boundaries (at the moment!).
|
||||
assert(GLoaderState->ldt[selector] != 0);
|
||||
return (void *) (size_t) (GLoaderState->ldt[selector] + offset);
|
||||
} // convert1616to32
|
||||
|
||||
// EMX (and probably many other things) occasionally has to call a 16-bit
|
||||
// system API, and assumes its stack is tiled in the LDT; it'll just shift
|
||||
// the stack pointer and use it as a stack segment for the 16-bit call
|
||||
// without calling DosFlatToSeg(). So we tile the main thread's stack and
|
||||
// pray that covers it. If we have to tile _every_ thread's stack, we can do
|
||||
// that later.
|
||||
// !!! FIXME: if we do this for secondary thread stacks, we'll need to mutex this.
|
||||
static void initOs2StackSegments(uint32 addr, uint32 stacklen)
|
||||
{
|
||||
//printf("base == %p, stacklen == %u\n", (void*)addr, (uint) stacklen);
|
||||
const uint32 diff = addr % 0x10000;
|
||||
addr -= diff; // make sure we start on a 64k border.
|
||||
stacklen += diff; // make sure we start on a 64k border.
|
||||
|
||||
// We fill in LDT tiles for the entire stack (EMX, etc, assume this will work).
|
||||
if (stacklen % 0x10000) // pad this out to 64k
|
||||
stacklen += 0x10000 - (stacklen % 0x10000);
|
||||
|
||||
// !!! FIXME: do we have to allocate these backwards? (stack grows down).
|
||||
while (stacklen) {
|
||||
//printf("Allocating selector 0x%X for stack %p ... \n", (uint) (addr >> 16), (void *) addr);
|
||||
if (!allocateSelector((uint16) (addr >> 16), addr, MODIFY_LDT_CONTENTS_DATA/*STACK*/, 0)) {
|
||||
FIXME("uhoh, couldn't set up an LDT entry for a stack segment! Might crash later!");
|
||||
} // if
|
||||
stacklen -= 0x10000;
|
||||
addr += 0x10000;
|
||||
} // while
|
||||
} // initOs2StackSegments
|
||||
|
||||
// OS/2 threads keep their Thread Information Block at FS:0x0000, so we have
|
||||
// to ask the Linux kernel to screw around with 16-bit selectors on our
|
||||
// behalf so we don't crash out when apps try to access it directly.
|
||||
@@ -474,7 +616,6 @@ static void deinitOs2Tib(const uint16 selector)
|
||||
assert(rc == 0); FIXME("this can legit fail, though!");
|
||||
} // deinitOs2Tib
|
||||
|
||||
|
||||
static __attribute__((noreturn)) void runLxModule(LxModule *lxmod, const int argc, char **argv, char **envp)
|
||||
{
|
||||
uint8 *stack = (uint8 *) ((size_t) lxmod->esp);
|
||||
@@ -503,7 +644,7 @@ static __attribute__((noreturn)) void runLxModule(LxModule *lxmod, const int arg
|
||||
"ret \n\t" // go to OS/2 land!
|
||||
"1: \n\t" // ...and return here.
|
||||
"andl $-16, %%esp \n\t" // align the stack for macOS.
|
||||
"subl $-4, %%esp \n\t" // align the stack for macOS.
|
||||
"subl $-4, %%esp \n\t" // align the stack for macOS.
|
||||
"pushl %%eax \n\t" // call _exit() with whatever is in %eax.
|
||||
"call _exit \n\t"
|
||||
: // no outputs
|
||||
@@ -628,8 +769,10 @@ static void freeLxModule(LxModule *lxmod)
|
||||
free(lxmod->dependencies);
|
||||
|
||||
for (uint32 i = 0; i < lxmod->lx.module_num_objects; i++) {
|
||||
if (lxmod->mmaps[i].alias != 0xFFFF)
|
||||
freeSelector(lxmod->mmaps[i].alias);
|
||||
if (lxmod->mmaps[i].addr)
|
||||
munmap(lxmod->mmaps[i].addr, lxmod->mmaps[i].size);
|
||||
munmap(lxmod->mmaps[i].mapped, lxmod->mmaps[i].size);
|
||||
} // for
|
||||
free(lxmod->mmaps);
|
||||
|
||||
@@ -646,16 +789,25 @@ static void freeLxModule(LxModule *lxmod)
|
||||
|
||||
static LxModule *loadLxModuleByModuleNameInternal(const char *modname, const int dependency_tree_depth);
|
||||
|
||||
static void *getModuleProcAddrByOrdinal(const LxModule *module, const uint32 ordinal)
|
||||
static void *getModuleProcAddrByOrdinal(const LxModule *module, const uint32 ordinal, const LxExport **_lxexp)
|
||||
{
|
||||
//printf("lookup module == '%s', ordinal == %u\n", module->name, (uint) ordinal);
|
||||
|
||||
const LxExport *lxexp = module->exports;
|
||||
for (uint32 i = 0; i < module->num_exports; i++, lxexp++) {
|
||||
if (lxexp->ordinal == ordinal)
|
||||
return lxexp->addr;
|
||||
if (lxexp->ordinal == ordinal) {
|
||||
if (_lxexp)
|
||||
*_lxexp = lxexp;
|
||||
void *retval = lxexp->addr;
|
||||
if (lxexp->object && lxexp->object->alias != 0xFFFF) // 16-bit bridge? this is actually a void**, due to some macro salsa. :/
|
||||
retval = *((void**) retval);
|
||||
return retval;
|
||||
} // if
|
||||
} // for
|
||||
|
||||
if (_lxexp)
|
||||
*_lxexp = NULL;
|
||||
|
||||
#if 1
|
||||
char entry[128];
|
||||
snprintf(entry, sizeof (entry), "ordinal #%u", (uint) ordinal);
|
||||
@@ -665,16 +817,25 @@ static void *getModuleProcAddrByOrdinal(const LxModule *module, const uint32 ord
|
||||
#endif
|
||||
} // getModuleProcAddrByOrdinal
|
||||
|
||||
static void *getModuleProcAddrByName(const LxModule *module, const char *name)
|
||||
static void *getModuleProcAddrByName(const LxModule *module, const char *name, const LxExport **_lxexp)
|
||||
{
|
||||
//printf("lookup module == '%s', name == '%s'\n", module->name, name);
|
||||
|
||||
const LxExport *lxexp = module->exports;
|
||||
for (uint32 i = 0; i < module->num_exports; i++, lxexp++) {
|
||||
if (strcmp(lxexp->name, name) == 0)
|
||||
return lxexp->addr;
|
||||
if (strcmp(lxexp->name, name) == 0) {
|
||||
if (_lxexp)
|
||||
*_lxexp = lxexp;
|
||||
void *retval = lxexp->addr;
|
||||
if (lxexp->object && lxexp->object->alias != 0xFFFF) // 16-bit bridge? this is actually a void**, due to some macro salsa. :/
|
||||
retval = *((void**) retval);
|
||||
return retval;
|
||||
} // if
|
||||
} // for
|
||||
|
||||
if (_lxexp)
|
||||
*_lxexp = NULL;
|
||||
|
||||
#if 1
|
||||
return generateMissingTrampoline(module->name, name);
|
||||
#else
|
||||
@@ -698,10 +859,10 @@ static void doFixup(uint8 *page, const sint16 offset, const uint32 finalval, con
|
||||
case 2: { uint16 *dst = (uint16 *) (page + offset); *dst = (uint16) finalval; } break;
|
||||
case 4: { uint32 *dst = (uint32 *) (page + offset); *dst = (uint32) finalval; } break;
|
||||
case 6: {
|
||||
uint16 *dst1 = (uint16 *) (page + offset);
|
||||
*dst1 = (uint16) finalval2;
|
||||
uint32 *dst2 = (uint32 *) (page + offset + 2);
|
||||
*dst2 = (uint32) finalval;
|
||||
uint32 *dst1 = (uint32 *) (page + offset);
|
||||
*dst1 = (uint32) finalval;
|
||||
uint16 *dst2 = (uint16 *) (page + offset + 4);
|
||||
*dst2 = (uint16) finalval2;
|
||||
break;
|
||||
} // case
|
||||
default:
|
||||
@@ -719,7 +880,7 @@ static void fixupPage(const uint8 *exe, LxModule *lxmod, const LxObjectTableEntr
|
||||
const uint32 fixuplen = fixuppage[1] - fixuppage[0];
|
||||
const uint8 *fixup = (exe + lx->fixup_record_table_offset) + fixupoffset;
|
||||
const uint8 *fixupend = fixup + fixuplen;
|
||||
// int record = 0;
|
||||
//int record = 0;
|
||||
while (fixup < fixupend) {
|
||||
//record++; printf("FIXUP obj #%u page #%u record #: %d\n", (uint) (obj - origobj), (uint) pagenum, record);
|
||||
const uint8 srctype = *(fixup++);
|
||||
@@ -730,25 +891,40 @@ static void fixupPage(const uint8 *exe, LxModule *lxmod, const LxObjectTableEntr
|
||||
uint32 finalval = 0;
|
||||
uint16 finalval2 = 0;
|
||||
uint32 finalsize = 0;
|
||||
int allow_fixup_to_alias = 0;
|
||||
|
||||
switch (srctype & 0xF) {
|
||||
case 0x0: // byte fixup
|
||||
finalsize = 1;
|
||||
break;
|
||||
case 0x2: // 16-bit selector fixup
|
||||
finalsize = 2;
|
||||
allow_fixup_to_alias = 1;
|
||||
break;
|
||||
case 0x5: // 16-bit offset fixup
|
||||
finalsize = 2;
|
||||
break;
|
||||
case 0x3: // 16:16 pointer fixup
|
||||
finalsize = 4;
|
||||
allow_fixup_to_alias = 1;
|
||||
break;
|
||||
case 0x7: // 32-bit offset fixup
|
||||
case 0x8: // 32-bit self-relative offset fixup
|
||||
finalsize = 4;
|
||||
break;
|
||||
case 0x6: // 16:32 pointer fixup
|
||||
finalval2 = GLoaderState->original_cs;
|
||||
finalsize = 6;
|
||||
allow_fixup_to_alias = 1;
|
||||
break;
|
||||
} // switch
|
||||
|
||||
const int fixup_to_alias = ((srctype & 0x10) != 0);
|
||||
if (fixup_to_alias) { // fixup to alias flag.
|
||||
if (!allow_fixup_to_alias)
|
||||
fprintf(stderr, "WARNING: Bogus fixup srctype (%u) with fixup-to-alias flag!\n", (uint) (srctype & 0xF));
|
||||
} // if
|
||||
|
||||
if (srctype & 0x20) { // contains a source list
|
||||
srclist_count = *(fixup++);
|
||||
} else {
|
||||
@@ -766,8 +942,8 @@ static void fixupPage(const uint8 *exe, LxModule *lxmod, const LxObjectTableEntr
|
||||
} // else
|
||||
|
||||
uint32 targetoffset = 0;
|
||||
// !!! FIXME: where do we use 16-bit selectors? What does this mean in a 32-bit app?
|
||||
if ((srctype & 0xF) != 0x2) { // not a 16-bit selector fixup?
|
||||
|
||||
if ((srctype & 0xF) != 2) { // not used for 16-bit selector fixups.
|
||||
if (fixupflags & 0x10) { // 32-bit target offset
|
||||
targetoffset = *((uint32 *) fixup); fixup += 4;
|
||||
} else { // 16-bit target offset
|
||||
@@ -775,8 +951,22 @@ static void fixupPage(const uint8 *exe, LxModule *lxmod, const LxObjectTableEntr
|
||||
} // else
|
||||
} // if
|
||||
|
||||
const uint32 base = (uint32) (size_t) lxmod->mmaps[objectid - 1].addr;
|
||||
finalval = base + targetoffset;
|
||||
if (!fixup_to_alias) {
|
||||
const uint32 base = (uint32) (size_t) lxmod->mmaps[objectid - 1].addr;
|
||||
finalval = base + targetoffset;
|
||||
} else {
|
||||
if (lxmod->mmaps[objectid - 1].alias == 0xFFFF) {
|
||||
fprintf(stderr, "uhoh, need a 16-bit alias fixup, but object has no 16:16 alias!\n");
|
||||
} else {
|
||||
const uint16 selector = (lxmod->mmaps[objectid - 1].alias << 3) | 7;
|
||||
switch (srctype & 0xF) {
|
||||
case 0x2: finalval = selector; break; // 16-bit selector fixup?
|
||||
case 0x3: finalval = (((uint32) selector) << 16) | targetoffset; break; // 16:16 pointer fixup
|
||||
case 0x6: finalval = targetoffset; finalval2 = selector; break; // 16:32 pointer fixup
|
||||
default: assert(!"shouldn't hit this code"); break;
|
||||
} // switch
|
||||
} // else
|
||||
} // else
|
||||
break;
|
||||
} // case
|
||||
|
||||
@@ -797,12 +987,28 @@ static void fixupPage(const uint8 *exe, LxModule *lxmod, const LxObjectTableEntr
|
||||
importid = (uint32) *((uint16 *) fixup); fixup += 2;
|
||||
} // else
|
||||
|
||||
const LxExport *lxexp = NULL;
|
||||
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", (uint) moduleid, (uint) lx->num_import_mod_entries);
|
||||
} else {
|
||||
finalval = (uint32) (size_t) getModuleProcAddrByOrdinal(lxmod->dependencies[moduleid-1], importid);
|
||||
//printf("import-by-ordinal fixup: module=%u import=%u\n", (uint) moduleid, (uint) importid);
|
||||
finalval = (uint32) (size_t) getModuleProcAddrByOrdinal(lxmod->dependencies[moduleid-1], importid, &lxexp);
|
||||
} // else
|
||||
|
||||
if (fixup_to_alias) {
|
||||
if (!lxexp || !lxexp->object || (lxexp->object->alias == 0xFFFF)) {
|
||||
fprintf(stderr, "uhoh, couldn't find a selector for a fixup-to-alias address!\n");
|
||||
} else {
|
||||
const uint16 selector = (lxexp->object->alias << 3) | 7;
|
||||
switch (srctype & 0xF) {
|
||||
case 0x2: finalval = selector; break; // 16-bit selector fixup? !!! FIXME:
|
||||
case 0x3: finalval = (((uint32) selector) << 16) | (finalval - ((uint32)lxexp->object->addr)); break; // 16:16 pointer fixup
|
||||
case 0x6: finalval -= ((uint32)lxexp->object->addr); finalval2 = selector; break; // 16:32 pointer fixup
|
||||
default: assert(!"shouldn't hit this code"); break;
|
||||
} // switch
|
||||
} // else
|
||||
} // else
|
||||
break;
|
||||
} // case
|
||||
@@ -822,6 +1028,7 @@ static void fixupPage(const uint8 *exe, LxModule *lxmod, const LxObjectTableEntr
|
||||
name_offset = *((uint16 *) fixup); fixup += 2;
|
||||
} // else
|
||||
|
||||
const LxExport *lxexp = NULL;
|
||||
if (moduleid == 0) {
|
||||
fprintf(stderr, "uhoh, looking for module ordinal 0, which is illegal.\n");
|
||||
} else if (moduleid > lx->num_import_mod_entries) {
|
||||
@@ -832,14 +1039,30 @@ static void fixupPage(const uint8 *exe, LxModule *lxmod, const LxObjectTableEntr
|
||||
const uint8 namelen = *(import_name++) & 0x7F; // the top bit is reserved.
|
||||
memcpy(name, import_name, namelen);
|
||||
name[namelen] = '\0';
|
||||
finalval = (uint32) (size_t) getModuleProcAddrByName(lxmod->dependencies[moduleid-1], name);
|
||||
//printf("import-by-name fixup: module=%u import='%s'\n", (uint) moduleid, name);
|
||||
finalval = (uint32) (size_t) getModuleProcAddrByName(lxmod->dependencies[moduleid-1], name, &lxexp);
|
||||
} // else
|
||||
|
||||
if (fixup_to_alias) {
|
||||
if (!lxexp || !lxexp->object || (lxexp->object->alias == 0xFFFF)) {
|
||||
fprintf(stderr, "uhoh, couldn't find a selector for a fixup-to-alias address!\n");
|
||||
} else {
|
||||
const uint16 selector = (lxexp->object->alias << 3) | 7;
|
||||
switch (srctype & 0xF) {
|
||||
case 0x2: finalval = selector; break; // 16-bit selector fixup? !!! FIXME:
|
||||
case 0x3: finalval = (((uint32) selector) << 16) | (finalval - ((uint32)lxexp->object->addr)); break; // 16:16 pointer fixup
|
||||
case 0x6: finalval -= ((uint32)lxexp->object->addr); finalval2 = selector; break; // 16:32 pointer fixup
|
||||
default: assert(!"shouldn't hit this code"); break;
|
||||
} // switch
|
||||
} // else
|
||||
} // else
|
||||
break;
|
||||
} // case
|
||||
|
||||
case 0x3: { // Internal entry table fixup record
|
||||
fprintf(stderr, "FIXUP 0x3 WRITE ME\n");
|
||||
exit(1);
|
||||
FIXME("FIXUP 0x3 WRITE ME");
|
||||
assert(!"write me");
|
||||
_exit(1);
|
||||
break;
|
||||
} // case
|
||||
} // switch
|
||||
@@ -863,7 +1086,7 @@ static void fixupPage(const uint8 *exe, LxModule *lxmod, const LxObjectTableEntr
|
||||
doFixup(page, offset, finalval - (((uint32)page)+offset+4), 0, finalsize);
|
||||
} else {
|
||||
doFixup(page, offset, finalval, finalval2, finalsize);
|
||||
}
|
||||
} // else
|
||||
} // for
|
||||
} else {
|
||||
if ((srctype & 0xF) == 0x08) { // self-relative fixup?
|
||||
@@ -871,7 +1094,7 @@ static void fixupPage(const uint8 *exe, LxModule *lxmod, const LxObjectTableEntr
|
||||
doFixup(page, srcoffset, finalval - (((uint32)page)+srcoffset+4), 0, finalsize);
|
||||
} else {
|
||||
doFixup(page, srcoffset, finalval, finalval2, finalsize);
|
||||
}
|
||||
} // else
|
||||
} // else
|
||||
} // while
|
||||
} // fixupPage
|
||||
@@ -950,6 +1173,9 @@ static LxModule *loadLxModule(const char *fname, uint8 *exe, uint32 exelen, int
|
||||
} // if
|
||||
memset(retval->dependencies, '\0', sizeof (LxModule *) * lx->num_import_mod_entries);
|
||||
memset(retval->mmaps, '\0', sizeof (LxMmaps) * lx->module_num_objects);
|
||||
for (int i = 0; i < lx->module_num_objects; i++)
|
||||
retval->mmaps[i].alias = 0xFFFF;
|
||||
|
||||
memcpy(&retval->lx, lx, sizeof (*lx));
|
||||
|
||||
if (lx->resident_name_table_offset) {
|
||||
@@ -980,6 +1206,7 @@ static LxModule *loadLxModule(const char *fname, uint8 *exe, uint32 exelen, int
|
||||
goto loadlx_failed;
|
||||
} // if
|
||||
|
||||
int uses_aliases = 0;
|
||||
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.
|
||||
@@ -989,12 +1216,24 @@ static LxModule *loadLxModule(const char *fname, uint8 *exe, uint32 exelen, int
|
||||
if ((vsize % lx->page_size) != 0)
|
||||
vsize += lx->page_size - (vsize % lx->page_size);
|
||||
|
||||
const int mmapflags = MAP_POPULATE | MAP_ANON | MAP_PRIVATE | (isDLL ? 0 : MAP_FIXED);
|
||||
const int needs_1616_alias = ((obj->object_flags & 0x1000) != 0); // Object needs a 16:16 alias.
|
||||
if (needs_1616_alias) {
|
||||
if (vsize > 0x10000) {
|
||||
fprintf(stderr, "Module '%s' object #%u needs a 16:16 alias, but is > 64k\n", modname, (uint) (i+1));
|
||||
goto loadlx_failed; // I guess this is an error...?
|
||||
} // if
|
||||
uses_aliases = 1;
|
||||
|
||||
if (isDLL) // (specifically: if !MAP_FIXED)
|
||||
vsize += 0x10000; // make sure we can align to a 64k boundary.
|
||||
} // if
|
||||
|
||||
const int mmapflags = MAP_ANON | MAP_PRIVATE | (isDLL ? 0 : MAP_FIXED);
|
||||
void *base = isDLL ? NULL : (void *) ((size_t) obj->reloc_base_addr);
|
||||
void *mmapaddr = mmap(base, vsize, PROT_READ|PROT_WRITE, mmapflags, -1, 0);
|
||||
// we'll mprotect() these pages to the proper permissions later.
|
||||
|
||||
//fprintf(stderr, "mmap(%p, %u, RW-, ANON|PRIVATE%s, -1, 0) == %p\n", base, (uint) vsize, isDLL ? "" : "|FIXED", mmapaddr);
|
||||
//fprintf(stderr, "%s lxobj #%u mmap(%p, %u, %c%c%c, ANON|PRIVATE%s, -1, 0) == %p\n", retval->name, (uint) i, base, (uint) vsize, (obj->object_flags & 0x1) ? 'R' : '-', (obj->object_flags & 0x2) ? 'W' : '-', (obj->object_flags & 0x4) ? 'X' : '-', isDLL ? "" : "|FIXED", mmapaddr);
|
||||
|
||||
if (mmapaddr == ((void *) MAP_FAILED)) {
|
||||
fprintf(stderr, "mmap(%p, %u, RW-, ANON|PRIVATE%s, -1, 0) failed (%d): %s\n",
|
||||
@@ -1002,9 +1241,21 @@ static LxModule *loadLxModule(const char *fname, uint8 *exe, uint32 exelen, int
|
||||
goto loadlx_failed;
|
||||
} // if
|
||||
|
||||
retval->mmaps[i].addr = mmapaddr;
|
||||
retval->mmaps[i].mapped = mmapaddr;
|
||||
retval->mmaps[i].size = vsize;
|
||||
|
||||
// force objects with a 16:16 alias to a 64k boundary.
|
||||
size_t adjust = (size_t) mmapaddr;
|
||||
if (needs_1616_alias && ((adjust % 0x10000) != 0)) {
|
||||
assert(isDLL); // this must be fixed to a specific address, hopefully handled this...?
|
||||
const size_t diff = 0x10000 - (adjust % 0x10000);
|
||||
vsize -= diff;
|
||||
adjust += diff;
|
||||
mmapaddr = (void *) adjust;
|
||||
} // if
|
||||
|
||||
retval->mmaps[i].addr = mmapaddr;
|
||||
|
||||
const LxObjectPageTableEntry *objpage = ((const LxObjectPageTableEntry *) (exe + lx->object_page_table_offset));
|
||||
objpage += obj->page_table_index - 1;
|
||||
|
||||
@@ -1055,9 +1306,8 @@ static LxModule *loadLxModule(const char *fname, uint8 *exe, uint32 exelen, int
|
||||
|
||||
// any bytes at the end of this object that we didn't initialize? Zero them out.
|
||||
const uint32 remain = vsize - ((uint32) (dst - ((uint8 *) mmapaddr)));
|
||||
if (remain) {
|
||||
if (remain)
|
||||
memset(dst, '\0', remain);
|
||||
} // if
|
||||
} // for
|
||||
|
||||
// All the pages we need from the EXE are loaded into the appropriate spots in memory.
|
||||
@@ -1072,17 +1322,30 @@ static LxModule *loadLxModule(const char *fname, uint8 *exe, uint32 exelen, int
|
||||
// !!! FIXME: esp==0 means something special for programs (and is ignored for library init).
|
||||
// !!! FIXME: "A zero value in this field indicates that the stack pointer is to be initialized to the highest address/offset in the object"
|
||||
if (!isDLL) {
|
||||
retval->esp = lx->esp;
|
||||
if (lx->esp_object != 0) {
|
||||
const uint32 base = (uint32) ((size_t)retval->mmaps[lx->esp_object - 1].addr);
|
||||
retval->esp += base;
|
||||
} // if
|
||||
} // if
|
||||
assert(lx->esp_object != 0);
|
||||
const uint32 stackbase = (uint32) ((size_t)retval->mmaps[lx->esp_object - 1].addr);
|
||||
retval->esp = lx->esp + stackbase;
|
||||
|
||||
if (!isDLL) {
|
||||
// This needs to be set up now, so it's available to any library
|
||||
// init code that runs in LX land.
|
||||
initOs2Tib(GMainTibSpace, (void *) ((size_t) retval->esp), retval->mmaps[retval->lx.esp_object - 1].size, 0);
|
||||
initOs2Tib(GLoaderState->main_tibspace, (void *) ((size_t) retval->esp), retval->mmaps[retval->lx.esp_object - 1].size, 0);
|
||||
initOs2StackSegments(stackbase, retval->mmaps[retval->lx.esp_object - 1].size);
|
||||
} // if
|
||||
|
||||
// now we have the stack tiled--and it got the specific selectors it
|
||||
// needed--let's start making 16:16 aliases for the appropriate objects.
|
||||
if (uses_aliases) {
|
||||
obj = ((const LxObjectTableEntry *) (exe + lx->object_table_offset));
|
||||
for (uint32 i = 0; i < lx->module_num_objects; i++, obj++) {
|
||||
if ((obj->object_flags & 0x1000) == 0)
|
||||
continue; // Object doesn't need a 16:16 alias, skip it.
|
||||
uint16 offset = 0;
|
||||
if (!findSelector((uint32) (size_t) retval->mmaps[i].addr, &retval->mmaps[i].alias, &offset)) {
|
||||
fprintf(stderr, "Ran out of LDT entries getting a 16:16 alias for '%s' object #%u!\n", modname, (uint) (i + 1));
|
||||
goto loadlx_failed;
|
||||
} // if
|
||||
assert(offset == 0);
|
||||
} // for
|
||||
} // if
|
||||
|
||||
// Set up our exports...
|
||||
@@ -1124,6 +1387,7 @@ static LxModule *loadLxModule(const char *fname, uint8 *exe, uint32 exelen, int
|
||||
entryptr++;
|
||||
lxexp->ordinal = ordinal++;
|
||||
lxexp->addr = ((uint8 *) retval->mmaps[objidx].addr) + *((const uint16 *) entryptr);
|
||||
lxexp->object = &retval->mmaps[objidx];
|
||||
lxexp++;
|
||||
entryptr += 2;
|
||||
} // for
|
||||
@@ -1136,6 +1400,7 @@ static LxModule *loadLxModule(const char *fname, uint8 *exe, uint32 exelen, int
|
||||
entryptr++;
|
||||
lxexp->ordinal = ordinal++;
|
||||
lxexp->addr = ((uint8 *) retval->mmaps[objidx].addr) + *((const uint32 *) entryptr);
|
||||
lxexp->object = &retval->mmaps[objidx];
|
||||
lxexp++;
|
||||
entryptr += 4;
|
||||
}
|
||||
@@ -1210,7 +1475,7 @@ static LxModule *loadLxModule(const char *fname, uint8 *exe, uint32 exelen, int
|
||||
((obj->object_flags & 0x2) ? PROT_WRITE : 0) |
|
||||
((obj->object_flags & 0x4) ? PROT_EXEC : 0);
|
||||
|
||||
if (mprotect(retval->mmaps[i].addr, retval->mmaps[i].size, prot) == -1) {
|
||||
if (mprotect(retval->mmaps[i].mapped, retval->mmaps[i].size, prot) == -1) {
|
||||
fprintf(stderr, "mprotect(%p, %u, %s%s%s, ANON|PRIVATE|FIXED, -1, 0) failed (%d): %s\n",
|
||||
retval->mmaps[i].addr, (uint) retval->mmaps[i].size,
|
||||
(prot&PROT_READ) ? "R" : "-",
|
||||
@@ -1607,10 +1872,24 @@ int main(int argc, char **argv, char **envp)
|
||||
if (!installSignalHandlers())
|
||||
return 1;
|
||||
|
||||
if (getenv("TRACE_NATIVE"))
|
||||
GLoaderState->trace_native = 1;
|
||||
|
||||
unsigned int segment = 0;
|
||||
__asm__ __volatile__ ( "movw %%cs, %%ax \n\t" : "=a" (segment) );
|
||||
GLoaderState->original_cs = segment;
|
||||
__asm__ __volatile__ ( "movw %%ss, %%ax \n\t" : "=a" (segment) );
|
||||
GLoaderState->original_ss = segment;
|
||||
__asm__ __volatile__ ( "movw %%ds, %%ax \n\t" : "=a" (segment) );
|
||||
GLoaderState->original_ds = segment;
|
||||
|
||||
const char *envr = getenv("IS_2INE");
|
||||
GLoaderState->subprocess = (envr != NULL);
|
||||
GLoaderState->initOs2Tib = initOs2Tib;
|
||||
GLoaderState->deinitOs2Tib = deinitOs2Tib;
|
||||
GLoaderState->findSelector = findSelector;
|
||||
GLoaderState->freeSelector = freeSelector;
|
||||
GLoaderState->convert1616to32 = convert1616to32;
|
||||
GLoaderState->loadModule = loadLxModuleByModuleName;
|
||||
GLoaderState->locatePathCaseInsensitive = locatePathCaseInsensitive;
|
||||
GLoaderState->makeUnixPath = makeUnixPath;
|
||||
|
||||
+13
@@ -129,8 +129,10 @@ typedef struct LxResourceTableEntry
|
||||
|
||||
typedef struct LxMmaps
|
||||
{
|
||||
void *mapped;
|
||||
void *addr;
|
||||
size_t size;
|
||||
uint16 alias; // 16:16 alias, if one.
|
||||
} LxMmaps;
|
||||
|
||||
typedef struct LxExport
|
||||
@@ -138,6 +140,7 @@ typedef struct LxExport
|
||||
uint32 ordinal;
|
||||
const char *name; // can be NULL
|
||||
void *addr;
|
||||
const LxMmaps *object;
|
||||
} LxExport;
|
||||
|
||||
struct LxModule;
|
||||
@@ -149,6 +152,7 @@ struct LxModule
|
||||
char name[256]; // !!! FIXME: allocate this.
|
||||
LxModule **dependencies;
|
||||
LxMmaps *mmaps;
|
||||
|
||||
const LxExport *exports;
|
||||
uint32 num_exports;
|
||||
void *nativelib;
|
||||
@@ -202,14 +206,23 @@ typedef struct LxLoaderState
|
||||
{
|
||||
LxModule *loaded_modules;
|
||||
LxModule *main_module;
|
||||
uint8 main_tibspace[LXTIBSIZE];
|
||||
LxPIB pib;
|
||||
int subprocess;
|
||||
int running;
|
||||
int trace_native;
|
||||
uint16 original_cs;
|
||||
uint16 original_ss;
|
||||
uint16 original_ds;
|
||||
uint32 ldt[8192];
|
||||
uint32 *tlspage;
|
||||
uint32 tlsmask; // one bit for each TLS slot in use.
|
||||
uint8 tlsallocs[32]; // number of TLS slots allocated in one block, indexed by starting block (zero if not the starting block).
|
||||
uint16 (*initOs2Tib)(uint8 *tibspace, void *_topOfStack, const size_t stacklen, const uint32 tid);
|
||||
void (*deinitOs2Tib)(const uint16 selector);
|
||||
int (*findSelector)(const uint32 addr, uint16 *outselector, uint16 *outoffset);
|
||||
void (*freeSelector)(const uint16 selector);
|
||||
void *(*convert1616to32)(const uint32 addr1616);
|
||||
LxModule *(*loadModule)(const char *modname);
|
||||
int (*locatePathCaseInsensitive)(char *buf);
|
||||
char *(*makeUnixPath)(const char *os2path, uint32 *err);
|
||||
|
||||
+32
-11
@@ -1,12 +1,11 @@
|
||||
#define _POSIX_C_SOURCE 199309
|
||||
#define _BSD_SOURCE
|
||||
#define _GNU_SOURCE
|
||||
#include "os2native.h"
|
||||
#include "doscalls.h"
|
||||
|
||||
#include <unistd.h>
|
||||
#include <limits.h>
|
||||
#include <time.h>
|
||||
#include <dirent.h>
|
||||
#include <fcntl.h>
|
||||
#include <pthread.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/stat.h>
|
||||
@@ -14,10 +13,6 @@
|
||||
#include <sys/time.h>
|
||||
#include <sys/resource.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include "os2native.h"
|
||||
#include "doscalls.h"
|
||||
|
||||
static LxLoaderState *GLoaderState = NULL;
|
||||
|
||||
@@ -100,6 +95,11 @@ typedef struct
|
||||
static DirFinder GHDir1;
|
||||
|
||||
|
||||
// !!! FIXME:
|
||||
#undef TRACE_NATIVE
|
||||
#define TRACE_NATIVE(...) do { if (GLoaderState->trace_native) { fprintf(stderr, "2INE TRACE [%lu]: ", (unsigned long) pthread_self()); fprintf(stderr, __VA_ARGS__); fprintf(stderr, ";\n"); } } while (0)
|
||||
|
||||
|
||||
LX_NATIVE_MODULE_DEINIT({
|
||||
ExitListItem *next = GExitList;
|
||||
GExitList = NULL;
|
||||
@@ -599,11 +599,32 @@ APIRET DosSetExceptionHandler(PEXCEPTIONREGISTRATIONRECORD rec)
|
||||
return NO_ERROR;
|
||||
} // DosSetExceptionHandler
|
||||
|
||||
ULONG DosFlatToSel(PVOID ptr)
|
||||
ULONG _DosFlatToSel(PVOID ptr)
|
||||
{
|
||||
TRACE_NATIVE("DosFlatToSel(%p)", ptr);
|
||||
return ((ULONG) ((size_t)ptr)) >> 16; // !!! FIXME
|
||||
} // DosFlatToSel
|
||||
|
||||
uint16 selector = 0;
|
||||
uint16 offset = 0;
|
||||
if (!GLoaderState->findSelector((uint32) ptr, &selector, &offset)) {
|
||||
fprintf(stderr, "Uhoh, ran out of LDT entries?!\n");
|
||||
return 0; // oh well, crash.
|
||||
} // if
|
||||
|
||||
selector = (selector << 3) | 7;
|
||||
return (((uint32)selector) << 16) | ((uint32) offset);
|
||||
} // _DosFlatToSel
|
||||
|
||||
// DosFlatToSel() passes its argument in %eax, so a little asm to bridge that...
|
||||
__asm__ (
|
||||
".globl DosFlatToSel \n\t"
|
||||
".type DosFlatToSel, @function \n\t"
|
||||
"DosFlatToSel: \n\t"
|
||||
" pushl %eax \n\t"
|
||||
" call _DosFlatToSel \n\t"
|
||||
" addl $4, %esp \n\t"
|
||||
" ret \n\t"
|
||||
".size _DosFlatToSel, .-_DosFlatToSel \n\t"
|
||||
);
|
||||
|
||||
APIRET DosSetSignalExceptionFocus(BOOL32 flag, PULONG pulTimes)
|
||||
{
|
||||
|
||||
+1
-1
@@ -386,7 +386,7 @@ APIRET OS2API DosExitList(ULONG ordercode, PFNEXITLIST pfn);
|
||||
APIRET OS2API DosCreateEventSem(PSZ pszName, PHEV phev, ULONG flAttr, BOOL32 fState);
|
||||
APIRET OS2API DosCreateMutexSem(PSZ pszName, PHMTX phmtx, ULONG flAttr, BOOL32 fState);
|
||||
APIRET OS2API DosSetExceptionHandler(PEXCEPTIONREGISTRATIONRECORD pERegRec);
|
||||
ULONG OS2API DosFlatToSel(PVOID ptr);
|
||||
ULONG OS2API DosFlatToSel(VOID);
|
||||
APIRET OS2API DosSetSignalExceptionFocus(BOOL32 flag, PULONG pulTimes);
|
||||
APIRET OS2API DosSetRelMaxFH(PLONG pcbReqCount, PULONG pcbCurMaxFH);
|
||||
APIRET OS2API DosAllocMem(PPVOID ppb, ULONG cb, ULONG flag);
|
||||
|
||||
+9
-4
@@ -1,11 +1,16 @@
|
||||
#ifndef _INCL_NATIVE_H_
|
||||
#define _INCL_NATIVE_H_
|
||||
#ifndef _INCL_OS2NATIVE_H_
|
||||
#define _INCL_OS2NATIVE_H_
|
||||
|
||||
#define _POSIX_C_SOURCE 199309
|
||||
#define _BSD_SOURCE
|
||||
#define _GNU_SOURCE
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <pthread.h>
|
||||
#include <errno.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include "os2types.h"
|
||||
#include "os2errors.h"
|
||||
@@ -28,7 +33,7 @@ void OS2EXPORT lxNativeModuleDeinit(void);
|
||||
initcode; \
|
||||
static const LxExport lx_native_exports[] = {
|
||||
|
||||
#define LX_NATIVE_EXPORT(fn, ord) { ord, #fn, fn }
|
||||
#define LX_NATIVE_EXPORT(fn, ord) { ord, #fn, fn, NULL }
|
||||
|
||||
#define LX_NATIVE_MODULE_INIT_END() \
|
||||
}; \
|
||||
@@ -38,5 +43,5 @@ void OS2EXPORT lxNativeModuleDeinit(void);
|
||||
|
||||
#endif
|
||||
|
||||
// end of native.h ...
|
||||
// end of os2native.h ...
|
||||
|
||||
@@ -0,0 +1,215 @@
|
||||
#ifndef _INCL_OS2NATIVE16_H_
|
||||
#define _INCL_OS2NATIVE16_H_
|
||||
|
||||
#include "os2native.h"
|
||||
|
||||
// !!! FIXME: _lots_ of macro salsa in here.
|
||||
|
||||
#define LX_NATIVE_MODULE_16BIT_SUPPORT() \
|
||||
static LxLoaderState *GLoaderState = NULL; \
|
||||
static LxMmaps obj16;
|
||||
|
||||
// These are the 16-bit entry points, which are exported to the LX loader
|
||||
// (not when linking directly to this shared library).
|
||||
#define LX_NATIVE_MODULE_16BIT_API(fn) \
|
||||
static void *fn##16 = NULL;
|
||||
|
||||
#define LX_NATIVE_MODULE_16BIT_SUPPORT_END()
|
||||
|
||||
#define LX_NATIVE_MODULE_DEINIT_16BIT_SUPPORT() \
|
||||
if ((obj16.alias != 0xFFFF) && (GLoaderState)) { \
|
||||
GLoaderState->freeSelector(obj16.alias); \
|
||||
} \
|
||||
if (obj16.mapped != NULL) { \
|
||||
munmap(obj16.mapped, obj16.size); \
|
||||
} \
|
||||
obj16.mapped = obj16.addr = NULL; \
|
||||
obj16.size = 0; \
|
||||
obj16.alias = 0xFFFF; \
|
||||
GLoaderState = NULL;
|
||||
|
||||
#define LX_NATIVE_MODULE_INIT_16BIT_SUPPORT() \
|
||||
GLoaderState = lx_state; \
|
||||
\
|
||||
obj16.mapped = obj16.addr = NULL; \
|
||||
obj16.size = 0; \
|
||||
obj16.alias = 0xFFFF; \
|
||||
\
|
||||
const size_t vsize = 0x10000 * 2; \
|
||||
void *mmapaddr = mmap(NULL, vsize, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); \
|
||||
if (mmapaddr == ((void *) MAP_FAILED)) { \
|
||||
fprintf(stderr, "mmap(NULL, 0x20000, RW-, ANON|PRIVATE, -1, 0) failed (%d): %s\n", errno, strerror(errno)); \
|
||||
GLoaderState = NULL; \
|
||||
return 0; \
|
||||
} \
|
||||
\
|
||||
obj16.mapped = mmapaddr; \
|
||||
obj16.size = vsize; \
|
||||
\
|
||||
/* force objects with a 16:16 alias to a 64k boundary. */ \
|
||||
size_t adjust = (size_t) mmapaddr; \
|
||||
if ((adjust % 0x10000) != 0) { \
|
||||
const size_t diff = 0x10000 - (adjust % 0x10000); \
|
||||
adjust += diff; \
|
||||
mmapaddr = (void *) adjust; \
|
||||
} \
|
||||
obj16.addr = mmapaddr; \
|
||||
\
|
||||
uint16 offset = 0; \
|
||||
if (!GLoaderState->findSelector((uint32) obj16.addr, &obj16.alias, &offset)) { \
|
||||
fprintf(stderr, "couldn't find a selector for 16-bit entry points!\n"); \
|
||||
munmap(obj16.mapped, vsize); \
|
||||
obj16.mapped = obj16.addr = NULL; \
|
||||
obj16.size = 0; \
|
||||
obj16.alias = 0xFFFF; \
|
||||
GLoaderState = NULL; \
|
||||
return 0; \
|
||||
} \
|
||||
assert(offset == 0); \
|
||||
\
|
||||
uint8 *ptr = (uint8 *) mmapaddr;
|
||||
|
||||
|
||||
/* This is the original assembly code, for use with NASM.
|
||||
USE16 ; start in 16-bit code where our 16-bit caller lands.
|
||||
MOV BX, SS ; save off ss:sp
|
||||
SHL EBX, 16 ; scootch over!
|
||||
MOV BX, SP ; save off ss:sp
|
||||
|
||||
; our stack is tiled, so we can easily find the linear address of it.
|
||||
MOV AX, SS ; move the stack segment into the low word.
|
||||
SHR AX, 3 ; shift out the control bits from the segment.
|
||||
SHL EAX, 16 ; move the remaining selector bits to the high word of %eax
|
||||
MOV AX, SP ; move the stack offset into the low word. Now %eax has the linear stack pointer.
|
||||
|
||||
JMP DWORD 0x7788:0x33332222 ; jmp into the 32-bit code segment (the next instruction!).
|
||||
|
||||
USE32
|
||||
MOV CX, 0xABCD ; original linear stack segment from lx_loader's main().
|
||||
MOV SS, CX
|
||||
MOV ESP, EAX ; and the same stack pointer, but linear.
|
||||
ADD EAX, 4 ; %eax now points to original function arguments on the stack.
|
||||
|
||||
PUSH EBX ; save original ss:sp to stack.
|
||||
PUSH DS ; save off the caller's data segment.
|
||||
MOV CX, 0x8888 ; restore our linear data segment.
|
||||
MOV DS, CX
|
||||
|
||||
PUSH EAX ; make this the sole argument to the bridge function.
|
||||
MOV EAX, 0x55555555 ; absolute address of our 32-bit bridging function in C.
|
||||
CALL [EAX] ; call our 32-bit bridging function in C.
|
||||
; don't touch EAX anymore, it has the return value now!
|
||||
ADD ESP, 4 ; dump our function argument.
|
||||
|
||||
POP DS ; get back our 16-bit data segment.
|
||||
|
||||
; Restore 16:16 stack. !!! FIXME: can use LSS if we figure out prefix and DS politics.
|
||||
POP BX ; Get our original ss back.
|
||||
POP CX ; Get our original sp back.
|
||||
|
||||
JMP WORD 0xAAAA:0xBBBB ; back into 16-bit land (the next instruction!).
|
||||
|
||||
USE16
|
||||
MOV SS, CX
|
||||
MOV SP, BX
|
||||
RETF 0x22 ; ...and back to the (far) caller, clearing the args (Pascal calling convention!) with retval in AX.
|
||||
*/
|
||||
|
||||
#define LX_NATIVE_INIT_16BIT_BRIDGE(fn, argbytes) \
|
||||
fn##16 = ptr; \
|
||||
\
|
||||
/* instructions are in Intel syntax here, not AT&T. */ \
|
||||
/* USE16 */ \
|
||||
*(ptr++) = 0x8C; /* mov bx,ss... */ \
|
||||
*(ptr++) = 0xD3; /* ...mov bx,ss */ \
|
||||
*(ptr++) = 0x66; /* shl ebx,byte 0x10... */ \
|
||||
*(ptr++) = 0xC1; /* ...shl ebx,byte 0x10 */ \
|
||||
*(ptr++) = 0xE3; /* ...shl ebx,byte 0x10 */ \
|
||||
*(ptr++) = 0x10; /* ...shl ebx,byte 0x10 */ \
|
||||
*(ptr++) = 0x89; /* mov bx,sp... */ \
|
||||
*(ptr++) = 0xE3; /* ...mov bx,sp */ \
|
||||
*(ptr++) = 0x8C; /* mov ax,ss... */ \
|
||||
*(ptr++) = 0xD0; /* ...mov ax,ss */ \
|
||||
*(ptr++) = 0xC1; /* shr ax,byte 0x3... */ \
|
||||
*(ptr++) = 0xE8; /* ...shr ax,byte 0x3 */ \
|
||||
*(ptr++) = 0x03; /* ...shr ax,byte 0x3 */ \
|
||||
*(ptr++) = 0x66; /* shl eax,byte 0x10... */ \
|
||||
*(ptr++) = 0xC1; /* ...shl eax,byte 0x10 */ \
|
||||
*(ptr++) = 0xE0; /* ...shl eax,byte 0x10 */ \
|
||||
*(ptr++) = 0x10; /* ...shl eax,byte 0x10 */ \
|
||||
*(ptr++) = 0x89; /* mov ax,sp... */ \
|
||||
*(ptr++) = 0xE0; /* ...mov ax,sp */ \
|
||||
*(ptr++) = 0x66; /* jmp dword 0x7788:0x33332222... */ \
|
||||
*(ptr++) = 0xEA; /* ...jmp dword 0x7788:0x33332222 */ \
|
||||
const uint32 jmp32addr = (uint32) (ptr + 6); \
|
||||
memcpy(ptr, &jmp32addr, 4); ptr += 4; \
|
||||
memcpy(ptr, &GLoaderState->original_cs, 2); ptr += 2; \
|
||||
\
|
||||
/* USE32 */ \
|
||||
*(ptr++) = 0x66; /* mov cx,0xabcd... */ \
|
||||
*(ptr++) = 0xB9; /* ...mov cx,0xabcd */ \
|
||||
memcpy(ptr, &GLoaderState->original_ss, 2); ptr += 2; \
|
||||
*(ptr++) = 0x8E; /* mov ss,ecx... */ \
|
||||
*(ptr++) = 0xD1; /* ...mov ss,ecx */ \
|
||||
*(ptr++) = 0x89; /* mov esp,eax... */ \
|
||||
*(ptr++) = 0xC4; /* ...mov esp,eax */ \
|
||||
*(ptr++) = 0x83; /* add eax,byte +0x4... */ \
|
||||
*(ptr++) = 0xC0; /* ...add eax,byte +0x4 */ \
|
||||
*(ptr++) = 0x04; /* ...add eax,byte +0x4 */ \
|
||||
*(ptr++) = 0x53; /* push ebx */ \
|
||||
*(ptr++) = 0x1E; /* push ds */ \
|
||||
*(ptr++) = 0x66; /* mov cx,0x8888... */ \
|
||||
*(ptr++) = 0xB9; /* ...mov cx,0x8888 */ \
|
||||
memcpy(ptr, &GLoaderState->original_ds, 2); ptr += 2; \
|
||||
*(ptr++) = 0x8E; /* mov ds,ecx... */ \
|
||||
*(ptr++) = 0xD9; /* ...mov ds,ecx */ \
|
||||
*(ptr++) = 0x50; /* push eax */ \
|
||||
*(ptr++) = 0xB8; /* mov eax,0x55555555... */ \
|
||||
const uint32 callbridgeaddr = (uint32) bridge16to32_##fn; \
|
||||
memcpy(ptr, &callbridgeaddr, 4); ptr += 4; \
|
||||
*(ptr++) = 0xFF; /* call dword [eax]... */ \
|
||||
*(ptr++) = 0xD0; /* ...call dword [eax] */ \
|
||||
*(ptr++) = 0x83; /* add esp,byte +0x4... */ \
|
||||
*(ptr++) = 0xC4; /* ...add esp,byte +0x4 */ \
|
||||
*(ptr++) = 0x04; /* ...add esp,byte +0x4 */ \
|
||||
*(ptr++) = 0x1F; /* pop ds */ \
|
||||
*(ptr++) = 0x66; /* pop bx... */ \
|
||||
*(ptr++) = 0x5B; /* ...pop bx */ \
|
||||
*(ptr++) = 0x66; /* pop cx... */ \
|
||||
*(ptr++) = 0x59; /* ...pop cx */ \
|
||||
*(ptr++) = 0x66; /* jmp word 0xaaaa:0xbbbb... */ \
|
||||
*(ptr++) = 0xEA; /* ...jmp word 0xaaaa:0xbbbb */ \
|
||||
const uint16 jmp16offset = (uint16) ((((uint32)ptr) - ((uint32)mmapaddr))+4); \
|
||||
memcpy(ptr, &jmp16offset, 2); ptr += 2; \
|
||||
const uint16 jmp16segment = (obj16.alias << 3) | 7; \
|
||||
memcpy(ptr, &jmp16segment, 2); ptr += 2; \
|
||||
\
|
||||
/* USE16 */ \
|
||||
*(ptr++) = 0x8E; /* mov ss,cx... */ \
|
||||
*(ptr++) = 0xD1; /* ...mov ss,cx */ \
|
||||
*(ptr++) = 0x89; /* mov sp,bx... */ \
|
||||
*(ptr++) = 0xDC; /* ...mov sp,bx */ \
|
||||
*(ptr++) = 0xCA; /* retf 0x22... */ \
|
||||
const uint16 argbytecount = argbytes; \
|
||||
memcpy(ptr, &argbytecount, 2); ptr += 2; \
|
||||
|
||||
|
||||
#define LX_NATIVE_MODULE_INIT_16BIT_SUPPORT_END() \
|
||||
assert((((uint32)ptr) - ((uint32)mmapaddr)) < 0x10000); /* don't be more than 64k. */ \
|
||||
if (mprotect(obj16.mapped, vsize, PROT_READ | PROT_EXEC) == -1) { \
|
||||
fprintf(stderr, "mprotect() failed for 16-bit bridge code!\n"); \
|
||||
munmap(obj16.mapped, vsize); \
|
||||
GLoaderState->freeSelector(obj16.alias); \
|
||||
obj16.mapped = obj16.addr = NULL; \
|
||||
obj16.size = 0; \
|
||||
obj16.alias = 0xFFFF; \
|
||||
GLoaderState = NULL; \
|
||||
return 0; \
|
||||
} \
|
||||
|
||||
#define LX_NATIVE_EXPORT16(fn, ord) { ord, #fn, &fn##16, &obj16 }
|
||||
|
||||
#endif
|
||||
|
||||
// end of os2native16.h ...
|
||||
|
||||
@@ -19,6 +19,20 @@ extern "C" {
|
||||
#define OS2API APIENTRY OS2EXPORT
|
||||
#endif
|
||||
|
||||
// Note that we never export actual 16-bit APIs from these headers, even if
|
||||
// we have them marked as OS2API16. This is just for documentation's sake at
|
||||
// the moment. If you compile against these headers and link against an
|
||||
// OS2API16 function, they will be exist and operate in a linear address
|
||||
// space. If you call them from an LX executable, though, the system assumes
|
||||
// you're calling in from 16-bit code and exports something that cleans up
|
||||
// the details behind the scenes.
|
||||
// For developing against this API directly on Linux, you should assume this
|
||||
// works like OS/2's PowerPC port, and the same 16-bit APIs you used on x86
|
||||
// were made 32-bit clean by default for the new platform.
|
||||
#ifndef OS2API16
|
||||
#define OS2API16 OS2API
|
||||
#endif
|
||||
|
||||
typedef uint32_t APIRET;
|
||||
typedef uint16_t APIRET16;
|
||||
typedef uint32_t APIRET32;
|
||||
@@ -51,6 +65,7 @@ typedef HANDLE HEV, *PHEV;
|
||||
typedef HANDLE HMTX, *PHMTX;
|
||||
typedef HANDLE PID, *PPID;
|
||||
typedef HANDLE TID, *PTID;
|
||||
typedef SHANDLE HVIO, *PHVIO;
|
||||
typedef PCHAR PSZ;
|
||||
|
||||
typedef int (APIENTRY *PFN)(void);
|
||||
|
||||
+37
-2
@@ -1,7 +1,42 @@
|
||||
#include "os2native.h"
|
||||
#include "os2native16.h"
|
||||
#include "viocalls.h"
|
||||
|
||||
LX_NATIVE_MODULE_INIT()
|
||||
LX_NATIVE_MODULE_16BIT_SUPPORT()
|
||||
LX_NATIVE_MODULE_16BIT_API(VioGetMode)
|
||||
LX_NATIVE_MODULE_16BIT_SUPPORT_END()
|
||||
|
||||
// !!! FIXME:
|
||||
#undef TRACE_NATIVE
|
||||
#define TRACE_NATIVE(...) do { if (GLoaderState->trace_native) { fprintf(stderr, "2INE TRACE [%lu]: ", (unsigned long) pthread_self()); fprintf(stderr, __VA_ARGS__); fprintf(stderr, ";\n"); } } while (0)
|
||||
|
||||
LX_NATIVE_MODULE_DEINIT({
|
||||
LX_NATIVE_MODULE_DEINIT_16BIT_SUPPORT();
|
||||
})
|
||||
|
||||
APIRET16 VioGetMode(PVIOMODEINFO pvioModeInfo, HVIO hvio)
|
||||
{
|
||||
TRACE_NATIVE("VioGetMode(%p, %u)", pvioModeInfo, (uint) hvio);
|
||||
FIXME("write me");
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
} // VioGetMode
|
||||
|
||||
static APIRET16 bridge16to32_VioGetMode(uint8 *args)
|
||||
{
|
||||
const HVIO hvio = *((HVIO *) args); args += 2;
|
||||
PVIOMODEINFO pvmi = (PVIOMODEINFO) GLoaderState->convert1616to32(*((uint32*) args)); //args += 4;
|
||||
return VioGetMode(pvmi, hvio);
|
||||
} // bridge16to32_VioGetMode
|
||||
|
||||
static int initViocalls(LxLoaderState *lx_state)
|
||||
{
|
||||
LX_NATIVE_MODULE_INIT_16BIT_SUPPORT()
|
||||
LX_NATIVE_INIT_16BIT_BRIDGE(VioGetMode, 6)
|
||||
LX_NATIVE_MODULE_INIT_16BIT_SUPPORT_END()
|
||||
return 1;
|
||||
} // initViocalls
|
||||
|
||||
LX_NATIVE_MODULE_INIT({ if (!initViocalls(lx_state)) return NULL; })
|
||||
LX_NATIVE_EXPORT16(VioGetMode, 21)
|
||||
LX_NATIVE_MODULE_INIT_END()
|
||||
|
||||
// end of viocalls.c ...
|
||||
|
||||
@@ -7,6 +7,35 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#pragma pack(push, 1)
|
||||
typedef struct
|
||||
{
|
||||
USHORT cb;
|
||||
UCHAR fbType;
|
||||
UCHAR color;
|
||||
USHORT col;
|
||||
USHORT row;
|
||||
USHORT hres;
|
||||
USHORT vres;
|
||||
UCHAR fmt_ID;
|
||||
UCHAR attrib;
|
||||
ULONG buf_addr;
|
||||
ULONG buf_length;
|
||||
ULONG full_length;
|
||||
ULONG partial_length;
|
||||
PCHAR ext_data_addr;
|
||||
} VIOMODEINFO, *PVIOMODEINFO;
|
||||
#pragma pack(pop)
|
||||
|
||||
enum
|
||||
{
|
||||
VGMT_OTHER = 0x1,
|
||||
VGMT_GRAPHICS = 0x02,
|
||||
VGMT_DISABLEBURST = 0x04
|
||||
};
|
||||
|
||||
APIRET16 OS2API16 VioGetMode(PVIOMODEINFO pvioModeInfo, HVIO hvio);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
Reference in new issue
Block a user