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.
This commit is contained in:
1 parent
c4ba0aaa12
commit
8e2c1449ab
2 files changed
+378
-30
No files matched your search
@@ -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)
|
||||
|
||||
|
||||
|
||||
@@ -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;
|
||||
} // 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;
|
||||
}
|
||||
|
||||
return 1;
|
||||
} // sanityCheckNeExe
|
||||
|
||||
static int sanityCheckExe(uint8 **_exe, uint32 *_exelen, int *_is_lx)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
*_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 void parseExe(const char *exefname, uint8 *exe, uint32 exelen)
|
||||
static int parseLxExe(const uint8 *origexe, const uint8 *exe)
|
||||
{
|
||||
printf("%s\n", exefname);
|
||||
|
||||
const uint8 *origexe = exe;
|
||||
if (!sanityCheckExe(&exe, &exelen))
|
||||
return;
|
||||
|
||||
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)
|
||||
|
||||
Reference in new issue
Block a user