[PATCH] krnl386.exe16: Improved support for early NE format executables.

Martin Payne development at martinpayne.me.uk
Fri Dec 15 03:02:23 CST 2017


  * If OS type in NE header is 0x00 (unknown) or the linker version is
    4.x, check for imports from DOSCALLS and KERNEL to distinguish between
    Windows and OS/2 or Multitasking DOS 4 executables.
  * Prevents DISCARDABLE segments from link4 4.x executables being loaded
    as 32 bit segments.
  * Assumes target Windows version is 1.01 if linker version is 4 or there
    is no expected version set in NE header.
  * Don't try to load NE files with OS type of Windows/386. Even
    Windows/386 doesn't recognise these as Windows executables.

Tested on Fedora 26 x86

Signed-off-by: Martin Payne <development at martinpayne.me.uk>
---
 dlls/krnl386.exe16/ne_module.c | 70 +++++++++++++++++++++++++++++++++++++++---
 1 file changed, 66 insertions(+), 4 deletions(-)

diff --git a/dlls/krnl386.exe16/ne_module.c b/dlls/krnl386.exe16/ne_module.c
index 14f8075166..72043a90ff 100644
--- a/dlls/krnl386.exe16/ne_module.c
+++ b/dlls/krnl386.exe16/ne_module.c
@@ -597,11 +597,10 @@ static HMODULE16 build_module( const void *mapping, SIZE_T mapping_size, LPCSTR
 
     /* We now have a valid NE header */
 
-    /* check to be able to fall back to loading OS/2 programs as DOS
-     * FIXME: should this check be reversed in order to be less strict?
-     * (only fail for OS/2 ne_exetyp 0x01 here?) */
+    /* check to be able to fall back to loading OS/2 programs as DOS */
     if ((ne_header->ne_exetyp != 0x02 /* Windows */)
-        && (ne_header->ne_exetyp != 0x04) /* Windows 386 */)
+        && (ne_header->ne_exetyp != 0x00 /* Unknown - possible early 1.x or 2.x app */)
+        && (ne_header->ne_ver == 0x04 /* link4 version 4.x has no ne_exetyp field - possible early 1.x app */))
         return ERROR_BAD_FORMAT;
 
     size = sizeof(NE_MODULE) +
@@ -649,6 +648,11 @@ static HMODULE16 build_module( const void *mapping, SIZE_T mapping_size, LPCSTR
 
     pModule->ne_flags &= ~(NE_FFLAGS_BUILTIN | NE_FFLAGS_WIN32);
 
+    /* The original NE spec (which link4 version 4.x follows) doesn't have an ne_expver field and reserves it for
+       future use. Other early executables have the version set to 0.0. Assume Windows 1.01 in either case. */
+    if ((ne_header->ne_ver == 0x04) || (ne_header->ne_expver == 0x0000))
+        pModule->ne_expver = 0x0101;
+
     /* Get the segment table */
 
     pModule->ne_segtab = pData - (BYTE *)pModule;
@@ -658,6 +662,24 @@ static HMODULE16 build_module( const void *mapping, SIZE_T mapping_size, LPCSTR
     for (i = ne_header->ne_cseg; i > 0; i--, pSeg++)
     {
         memcpy( pData, pSeg, sizeof(*pSeg) );
+
+        /* The original NE spec (which link4 version 4.x follows) uses the top four bits of the segment flags to set a
+           discard priority (0 = not discardable, 15 = highest priority). Later specs repurpose the top three bits and
+           use a single bit for the discardable flag. The top three bits must be masked off and discardable flag set
+           appropriately, otherwise a discard priority could get misinterpreted as a 32 bit segment flag which results
+           in an application crash. */
+        if (ne_header->ne_ver == 0x04)
+        {
+            WORD* seg_flags;
+            BOOL isDiscardable;
+
+            seg_flags = (WORD*)(pData + FIELD_OFFSET(SEGTABLEENTRY, flags));
+            isDiscardable = ((*seg_flags & 0xF000) > 0);
+
+            if (isDiscardable)
+                *seg_flags = ((*seg_flags & 0x0FFF) | NE_SEGFLAGS_DISCARDABLE);
+        }
+
         pData += sizeof(SEGTABLEENTRY);
     }
 
@@ -697,6 +719,46 @@ static HMODULE16 build_module( const void *mapping, SIZE_T mapping_size, LPCSTR
                        ne_header->ne_enttab - ne_header->ne_imptab )) goto failed;
     pData += ne_header->ne_enttab - ne_header->ne_imptab;
 
+    /* If the linker version is 4.x or the executable type is unknown, it is necessary to check the import table for
+       known Windows and OS/2 libraries to determine whether it's a Windows executable. Executables with no module table
+       or which don't import known Windows or OS/2 libraries are also assumed to be Windows. */
+    if (((ne_header->ne_ver == 0x04) || (ne_header->ne_exetyp == 0x00)) && pModule->ne_modtab)
+    {
+        /* Array of offsets into the imported names table where module names are located */
+        WORD* module_table;
+
+        /* Series of Pascal strings containing the module names */
+        LPCSTR imported_names;
+
+        int i;
+        BOOL importsDosCalls = FALSE;
+        BOOL importsKernel = FALSE;
+
+        /* Get address of module table and imported names table from their offsets */
+        module_table = (WORD*)(((BYTE*)pModule) + pModule->ne_modtab);
+        imported_names = (LPCSTR)(((BYTE*)pModule) + pModule->ne_imptab);
+
+        /* Look for imports from DOSCALLS and KERNEL */
+        for (i = 0; i < pModule->ne_cmod; i++)
+        {
+            /* Module name is a Pascal string with byte length prefix */
+            LPCSTR module_name = imported_names + module_table[i];
+
+            if (!importsDosCalls && !strncmp("DOSCALLS", &module_name[1], module_name[0]))
+                importsDosCalls = TRUE;
+
+            else if (!importsKernel && !strncmp("KERNEL", &module_name[1], module_name[0]))
+                importsKernel = TRUE;
+
+            if (importsDosCalls && importsKernel)
+                break;
+        }
+
+        /* If the module has imports from DOSCALLS but not KERNEL, assume it's for OS/2 or Multitasking DOS 4. */
+        if (importsDosCalls && !importsKernel)
+            goto failed;
+    }
+
     /* Load entry table, convert it to the optimized version used by Windows */
 
     pModule->ne_enttab = pData - (BYTE *)pModule;
-- 
2.15.1.windows.2




More information about the wine-devel mailing list