From 8e2c1449abcb321d1e9606a941b13f7ffd675bcc Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Wed, 7 Mar 2018 15:54:01 -0500 Subject: [PATCH] Initial work on parsing NE (16-bit) executables. This is just lx_dump for now, but if some work proves out, we can load these with lx_loader, too, and bridge the 16-bit APIs to 32-bits the same as we do anyhow, giving us access to 16-bit OS/2 binaries. --- lib2ine.h | 41 ++++++ lx_dump.c | 363 +++++++++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 376 insertions(+), 28 deletions(-) diff --git a/lib2ine.h b/lib2ine.h index 61f561b..4266353 100644 --- a/lib2ine.h +++ b/lib2ine.h @@ -137,6 +137,47 @@ typedef struct LxResourceTableEntry uint32 offset; } LxResourceTableEntry; +typedef struct NeHeader +{ + uint8 magic_n; + uint8 magic_e; + uint8 linker_version; + uint8 linker_revision; + uint16 entry_table_offset; + uint16 entry_table_size; + uint32 crc32; + uint16 module_flags; + uint16 auto_data_segment; + uint16 dynamic_heap_size; + uint16 stack_size; + uint16 reg_ip; + uint16 reg_cs; + uint16 reg_sp; + uint16 reg_ss; + uint16 num_segment_table_entries; + uint16 num_module_ref_table_entries; + uint16 non_resident_name_table_size; + uint16 segment_table_offset; + uint16 resource_table_offset; + uint16 resident_name_table_offset; + uint16 module_reference_table_offset; + uint16 imported_names_table_offset; + uint32 non_resident_name_table_offset; + uint16 num_movable_entries; + uint16 sector_alignment_shift_count; + uint16 num_resource_entries; + uint8 exe_type; + uint8 reserved[9]; +} NeHeader; + +typedef struct NeSegmentTableEntry +{ + uint16 offset; + uint16 size; + uint16 segment_flags; + uint16 minimum_allocation; +} NeSegmentTableEntry; + #pragma pack(pop) diff --git a/lx_dump.c b/lx_dump.c index 09e65ea..8d8cc99 100644 --- a/lx_dump.c +++ b/lx_dump.c @@ -15,29 +15,9 @@ // !!! FIXME: some cut-and-paste with lx_loader.c ... -static int sanityCheckExe(uint8 **_exe, uint32 *_exelen) +static int sanityCheckLxExe(const uint8 *exe) { - if (*_exelen < 196) { - fprintf(stderr, "not an OS/2 LX EXE\n"); - return 0; - } - const uint32 header_offset = *((uint32 *) (*_exe + 0x3C)); - //printf("header offset is %u\n", (uint) header_offset); - if ((header_offset + sizeof (LxHeader)) >= *_exelen) { - fprintf(stderr, "not an OS/2 LX EXE\n"); - return 0; - } - - *_exe += header_offset; // skip the DOS stub, etc. - *_exelen -= header_offset; - - const LxHeader *lx = (const LxHeader *) *_exe; - - if ((lx->magic_l != 'L') || (lx->magic_x != 'X')) { - fprintf(stderr, "not an OS/2 LX EXE\n"); - return 0; - } - + const LxHeader *lx = (const LxHeader *) exe; if ((lx->byte_order != 0) || (lx->word_order != 0)) { fprintf(stderr, "Program is not little-endian!\n"); return 0; @@ -64,18 +44,53 @@ static int sanityCheckExe(uint8 **_exe, uint32 *_exelen) } return 1; -} // sanityCheckExe +} // sanityCheckLxExe +static int sanityCheckNeExe(const uint8 *exe) +{ + const NeHeader *ne = (const NeHeader *) exe; + if (ne->exe_type != 1) { + fprintf(stderr, "Not an OS/2 NE EXE file (exe_type is %d, not 1)\n", (int) ne->exe_type); + return 0; + } -static void parseExe(const char *exefname, uint8 *exe, uint32 exelen) + return 1; +} // sanityCheckNeExe + +static int sanityCheckExe(uint8 **_exe, uint32 *_exelen, int *_is_lx) { - printf("%s\n", exefname); + if (*_exelen < 196) { + fprintf(stderr, "not an OS/2 EXE\n"); + return 0; + } + const uint32 header_offset = *((uint32 *) (*_exe + 0x3C)); + //printf("header offset is %u\n", (uint) header_offset); + if ((header_offset + sizeof (LxHeader)) >= *_exelen) { + fprintf(stderr, "not an OS/2 EXE\n"); + return 0; + } - const uint8 *origexe = exe; - if (!sanityCheckExe(&exe, &exelen)) - return; + *_exe += header_offset; // skip the DOS stub, etc. + *_exelen -= header_offset; + + const uint8 *magic = *_exe; + if ((magic[0] == 'L') && (magic[1] == 'X')) { + *_is_lx = 1; + return sanityCheckLxExe(*_exe); + } else if ((magic[0] == 'N') && (magic[1] == 'E')) { + *_is_lx = 0; + return sanityCheckNeExe(*_exe); + } + + fprintf(stderr, "not an OS/2 EXE\n"); + return 0; +} // sanityCheckExe + +static int parseLxExe(const uint8 *origexe, const uint8 *exe) +{ const LxHeader *lx = (const LxHeader *) exe; + printf("LX (32-bit) executable.\n"); printf("module version: %u\n", (uint) lx->module_version); printf("module flags:"); @@ -516,6 +531,298 @@ static void parseExe(const char *exefname, uint8 *exe, uint32 exelen) const uint16 ordinal = *((const uint16 *) name_table); name_table += 2; printf("%u: '%s' (ordinal %u)\n", (uint) i, name, (uint) ordinal); } // for + + return 1; +} // parseLxExe + +static int parseNeExe(const uint8 *origexe, const uint8 *exe) +{ + const NeHeader *ne = (const NeHeader *) exe; + printf("NE (16-bit) executable.\n"); + printf("Linker version: %u\n", (uint) ne->linker_version); + printf("Linker revision: %u\n", (uint) ne->linker_revision); + printf("Entry table offset: %u\n", (uint) ne->entry_table_offset); + printf("Entry table size: %u\n", (uint) ne->entry_table_size); + printf("CRC32: 0x%X\n", (uint) ne->crc32); + printf("Module flags:"); + if (ne->module_flags == 0) printf(" NOAUTODATA"); + if (ne->module_flags & 0x1) printf(" SINGLEDATA"); + if (ne->module_flags & 0x2) printf(" MULTIPLEDATA"); + if (ne->module_flags & 0x2000) printf(" NOTLOADABLE"); + if (ne->module_flags & 0x8000) printf(" LIBRARYMODULE"); + printf("\n"); + printf("Automatic data segment: %u\n", (uint) ne->auto_data_segment); + printf("Dynamic heap size: %u\n", (uint) ne->dynamic_heap_size); + printf("Stack size: %u\n", (uint) ne->stack_size); + printf("Initial code address: %X:%X\n", (uint) ne->reg_cs, (uint) ne->reg_ip); + printf("Initial stack address: %X:%X\n", (uint) ne->reg_ss, (uint) ne->reg_sp); + printf("Number of segment table entries: %u\n", (uint) ne->num_segment_table_entries); + printf("Number of module reference table entries: %u\n", (uint) ne->num_module_ref_table_entries); + printf("Non-resident name table size: %u\n", (uint) ne->non_resident_name_table_size); + printf("Segment table offset: %u\n", (uint) ne->segment_table_offset); + printf("Resource table offset: %u\n", (uint) ne->resource_table_offset); + printf("Resident name table offset: %u\n", (uint) ne->resident_name_table_offset); + printf("Module reference table offset: %u\n", (uint) ne->module_reference_table_offset); + printf("Imported names table offset: %u\n", (uint) ne->imported_names_table_offset); + printf("Non-resident name table offset: %u\n", (uint) ne->non_resident_name_table_offset); + printf("Number of movable entries: %u\n", (uint) ne->num_movable_entries); + printf("Sector alignment shift count: %u (%u bytes)\n", (uint) ne->sector_alignment_shift_count, (uint) (1 << ne->sector_alignment_shift_count)); + printf("Number of resource entries: %u\n", (uint) ne->num_resource_entries); + printf("Executable type: %u\n", (uint) ne->exe_type); + printf("\n"); + + const uint32 sector_size = 1 << ne->sector_alignment_shift_count; + + if (ne->num_segment_table_entries > 0) { + const uint32 total = (uint32) ne->num_segment_table_entries; + printf("Segment table (%u entries):\n", (uint) total); + const NeSegmentTableEntry *seg = (const NeSegmentTableEntry *) (exe + ne->segment_table_offset); + for (uint32 i = 0; i < total; i++, seg++) { + printf(" %u:\n", (uint) (i+1)); + printf(" Logical-sector offset: %u", (uint) seg->offset); + if (seg->offset == 0) { + printf(" (no file data)\n"); + } else { + printf(" (byte position %u)\n", (uint) (seg->offset * sector_size)); + } + printf(" Size: %u\n", (seg->size == 0) ? (uint) 0x10000 : (uint) seg->size); + printf(" Segment flags:"); + switch (seg->segment_flags & 0x7) { + case 0: printf(" CODE"); break; + case 1: printf(" DATA"); break; + default: printf(" [UNKNOWN SEGMENT TYPE]"); break; + } + if (seg->segment_flags & 0x10) printf(" MOVABLE"); + if (seg->segment_flags & 0x40) printf(" PRELOAD"); + if (seg->segment_flags & 0x100) printf(" RELOCINFO"); + if (seg->segment_flags & 0xF000) printf(" DISCARD"); + printf("\n"); + printf(" Minimum allocation: %u\n", (seg->minimum_allocation == 0) ? (uint) 0x10000 : (uint) seg->minimum_allocation); + + if (seg->segment_flags & 0x100) { // has relocations (fixups) + const uint8 *segptr = origexe + (seg->offset * sector_size); + const uint8 *fixupptr = segptr + (seg->size ? seg->size : 0xFFFF); + const uint32 num_fixups = (uint32) *((const uint16 *) fixupptr); fixupptr += 2; + printf(" Fixup records (%u entries):\n", (uint) num_fixups); + for (uint32 j = 0; j < num_fixups; j++) { + const uint8 srctype = *(fixupptr++); + const uint8 flags = *(fixupptr++); + uint32 srcchain_offset = (uint32) *((const uint16 *) fixupptr); fixupptr += 2; + const int additive = (flags & 0x4); + fixupptr += 4; // !!! FIXME + printf(" %u:\n", (uint) j); + printf(" Source type: "); + switch (srctype & 0xF) { + case 0: printf("LOBYTE"); break; + case 2: printf("SEGMENT"); break; + case 3: printf("FAR_ADDR"); break; + case 5: printf("OFFSET"); break; + default: printf("[unknown type]"); break; + } + printf("\n"); + printf(" Flags:"); + switch (flags & 0x3) { + case 0: printf(" INTERNALREF"); break; + case 1: printf(" IMPORTORDINAL"); break; + case 2: printf(" IMPORTNAME"); break; + case 3: printf(" OSFIXUP"); break; + } + if (additive) printf(" ADDITIVE"); + printf("\n"); + + if (additive) { + printf(" Additive source: %u", (uint) srcchain_offset); + } else { + printf(" Source chain:"); + do { + printf(" %u", (uint) srcchain_offset); + srcchain_offset = *((const uint16 *) (segptr + srcchain_offset)); + } while (srcchain_offset < seg->size); + } + printf("\n"); + } + } + } + printf("\n"); + } + + if (ne->num_resource_entries > 0) { + const uint8 *ptr = exe + ne->resource_table_offset; + const uint32 total = (uint32) ne->num_resource_entries; + printf("Resources (%u entries):\n", (uint) total); + int idx = 0; + while (*((const uint16 *) ptr)) { + const uint16 typeid = *((const uint16 *) ptr); ptr += 2; + const uint32 num_resources = (uint32) *((const uint16 *) ptr); ptr += 2; + ptr += 4; // reserved. + for (uint32 j = 0; j < num_resources; j++) { + idx++; + printf(" %d:", idx); + printf(" Type ID: "); + if (typeid & 0x8000) { + printf("%u\n", (uint) (typeid & ~0x8000)); + } else { + const uint8 *str = (exe + ne->resource_table_offset) + typeid; + const uint32 len = (uint32) *(str++); + printf("\""); + for (uint32 k = 0; k < len; k++, str++) { + printf("%c", (int) *str); + } + printf("\"\n"); + } + + const uint16 offset = *((const uint16 *) ptr); ptr += 2; + const uint16 size = *((const uint16 *) ptr); ptr += 2; + const uint16 flags = *((const uint16 *) ptr); ptr += 2; + const uint16 resourceid = *((const uint16 *) ptr); ptr += 2; + ptr += 4; // reserved. + printf(" Offset: %u\n", (uint) offset); + printf(" Size: %u\n", (uint) size); + printf(" Flags:"); + if (flags & 0x10) printf(" MOVABLE"); + if (flags & 0x20) printf(" PURE"); + if (flags & 0x40) printf(" PRELOAD"); + printf("\n"); + printf(" Resource ID: "); + if (resourceid & 0x8000) { + printf("%u\n", (uint) (resourceid & ~0x8000)); + } else { + const uint8 *str = (exe + ne->resource_table_offset) + resourceid; + const uint32 len = (uint32) *(str++); + printf("\""); + for (uint32 k = 0; k < len; k++, str++) { + printf("%c", (int) *str); + } + printf("\"\n"); + } + } + } + printf("\n"); + } + + if (ne->resident_name_table_offset > 0) { + const uint8 *name_table = exe + ne->resident_name_table_offset; + if (*name_table) { + printf("Resident name table:\n"); + for (uint32 i = 0; *name_table; i++) { + const uint8 namelen = *(name_table++); + char name[256]; + memcpy(name, name_table, namelen); + name[namelen] = '\0'; + name_table += namelen; + const uint16 ordinal = *((const uint16 *) name_table); name_table += 2; + printf(" %u: '%s' (ordinal %u)\n", (uint) i, name, (uint) ordinal); + } + printf("\n"); + } + } + + if (ne->non_resident_name_table_offset > 0) { + const uint8 *name_table = origexe + ne->non_resident_name_table_offset; + const uint8 *end_of_name_table = name_table + ne->non_resident_name_table_size; + if ((name_table < end_of_name_table) && *name_table) { + printf("Non-resident name table:\n"); + for (uint32 i = 0; (name_table < end_of_name_table) && *name_table; i++) { + const uint8 namelen = *(name_table++); + char name[256]; + memcpy(name, name_table, namelen); + name[namelen] = '\0'; + name_table += namelen; + const uint16 ordinal = *((const uint16 *) name_table); name_table += 2; + printf(" %u: '%s' (ordinal %u)\n", (uint) i, name, (uint) ordinal); + } + printf("\n"); + } + } + + if (ne->num_module_ref_table_entries > 0) { + const uint32 total = (uint32) ne->num_module_ref_table_entries; + printf("Module reference table (%u entries):\n", (uint) total); + const uint16 *ptr = (const uint16 *) (exe + ne->module_reference_table_offset); + for (uint32 i = 0; i < total; i++, ptr++) { + const uint8 *name = (exe + ne->imported_names_table_offset) + *ptr; + const uint32 name_string_len = (uint32) *name; + name++; + printf(" %u: ", (uint) i); + for (uint32 j = 0; j < name_string_len; j++, name++) { + printf("%c", (int) *name); + } + printf("\n"); + } + printf("\n"); + } + + if (ne->entry_table_size > 0) { + const uint8 *ptr = exe + ne->entry_table_offset; + if (*ptr) { + printf("Entry table:\n"); + uint32 ordinal = 1; + while (1) { + const uint8 bundled = *(ptr++); + if (!bundled) { + break; + } + + const uint8 bundletype = *(ptr++); + switch (bundletype) { + case 0x00: // unused entries, to skip ordinals. + ordinal += bundled; + break; + + case 0xFF: // Moveable segment entries. + for (uint32 i = 0; i < bundled; i++, ordinal++) { + const uint8 flags = *(ptr++); + const uint16 int3f = *((const uint16 *) ptr); ptr += 2; + const uint8 segment = *(ptr++); + const uint16 offset = *((const uint16 *) ptr); ptr += 2; + printf(" %u:\n", (uint) ordinal); + printf(" Flags: MOVABLE"); + if (flags & 0x1) printf(" EXPORTED"); + if (flags & 0x2) printf(" GLOBAL"); + printf("\n"); + printf(" Int3f: %X\n", (uint) int3f); + printf(" Segment: %u\n", (uint) segment); + printf(" Offset: %u\n", (uint) offset); + } + break; + + default: + for (uint32 i = 0; i < bundled; i++, ordinal++) { + const uint8 flags = *(ptr++); + const uint16 offset = *((const uint16 *) ptr); ptr += 2; + printf(" %u:\n", (uint) ordinal); + printf(" Flags: FIXED"); + if (flags & 0x1) printf(" EXPORTED"); + if (flags & 0x2) printf(" GLOBAL"); + printf("\n"); + printf(" Segment: %u\n", (uint) bundletype); + printf(" Offset: %u\n", (uint) offset); + } + break; + } + } + printf("\n"); + } + } + + return 1; +} // parseNeExe + +static void parseExe(const char *exefname, uint8 *exe, uint32 exelen) +{ + int is_lx = 0; + + printf("%s\n", exefname); + + const uint8 *origexe = exe; + if (!sanityCheckExe(&exe, &exelen, &is_lx)) + return; + + if (is_lx) { + parseLxExe(origexe, exe); + } else { + parseNeExe(origexe, exe); + } } // parseExe int main(int argc, char **argv)