From 26a52988a6213749a451ccd592b34ffe7dc29359 Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Thu, 27 Oct 2016 01:44:26 -0400 Subject: [PATCH] 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. --- cvtasm.pl | 25 +++ lx_loader.c | 357 ++++++++++++++++++++++++++++++++++++++----- lx_loader.h | 13 ++ native/doscalls.c | 43 ++++-- native/doscalls.h | 2 +- native/os2native.h | 13 +- native/os2native16.h | 215 ++++++++++++++++++++++++++ native/os2types.h | 15 ++ native/viocalls.c | 39 ++++- native/viocalls.h | 29 ++++ 10 files changed, 694 insertions(+), 57 deletions(-) create mode 100755 cvtasm.pl create mode 100644 native/os2native16.h diff --git a/cvtasm.pl b/cvtasm.pl new file mode 100755 index 0000000..9468270 --- /dev/null +++ b/cvtasm.pl @@ -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; + } +} + diff --git a/lx_loader.c b/lx_loader.c index e2eaee6..0c89a31 100644 --- a/lx_loader.c +++ b/lx_loader.c @@ -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; diff --git a/lx_loader.h b/lx_loader.h index 2f9dff5..d6b3026 100644 --- a/lx_loader.h +++ b/lx_loader.h @@ -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); diff --git a/native/doscalls.c b/native/doscalls.c index 338d18b..7ca85eb 100644 --- a/native/doscalls.c +++ b/native/doscalls.c @@ -1,12 +1,11 @@ -#define _POSIX_C_SOURCE 199309 -#define _BSD_SOURCE -#define _GNU_SOURCE +#include "os2native.h" +#include "doscalls.h" + #include #include #include #include #include -#include #include #include #include @@ -14,10 +13,6 @@ #include #include #include -#include - -#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) { diff --git a/native/doscalls.h b/native/doscalls.h index 0836eda..ca6339e 100644 --- a/native/doscalls.h +++ b/native/doscalls.h @@ -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); diff --git a/native/os2native.h b/native/os2native.h index 6c0a904..ff5591b 100644 --- a/native/os2native.h +++ b/native/os2native.h @@ -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 #include +#include #include #include #include +#include #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 ... diff --git a/native/os2native16.h b/native/os2native16.h new file mode 100644 index 0000000..3dfbe56 --- /dev/null +++ b/native/os2native16.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 ... + diff --git a/native/os2types.h b/native/os2types.h index bc122fa..62b631e 100644 --- a/native/os2types.h +++ b/native/os2types.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); diff --git a/native/viocalls.c b/native/viocalls.c index 7d074cc..8ab3012 100644 --- a/native/viocalls.c +++ b/native/viocalls.c @@ -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 ... diff --git a/native/viocalls.h b/native/viocalls.h index 61e8f0f..9b72aaa 100644 --- a/native/viocalls.h +++ b/native/viocalls.h @@ -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