Initial add. Runs exactly one program.
Run make.sh on a Linux box to see it run tests/hello.exe!
This commit is contained in:
commit
161cde892e
10 files changed
+3236
No files matched your search
+23
@@ -0,0 +1,23 @@
|
||||
|
||||
Copyright (c) 2016 Ryan C. Gordon.
|
||||
|
||||
This software is provided 'as-is', without any express or implied warranty.
|
||||
In no event will the authors be held liable for any damages arising from
|
||||
the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software in a
|
||||
product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
|
||||
Ryan C. Gordon <icculus@icculus.org>
|
||||
|
||||
@@ -0,0 +1,503 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include "lx_loader.h"
|
||||
|
||||
// !!! FIXME: some cut-and-paste with lx_loader.c ...
|
||||
|
||||
static int sanityCheckExe(uint8 **_exe, uint32 *_exelen)
|
||||
{
|
||||
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", (unsigned int) 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;
|
||||
}
|
||||
|
||||
if ((lx->byte_order != 0) || (lx->word_order != 0)) {
|
||||
fprintf(stderr, "Program is not little-endian!\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (lx->lx_version != 0) {
|
||||
fprintf(stderr, "Program is unknown LX EXE version (%u)\n", (unsigned int) lx->lx_version);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (lx->cpu_type > 3) { // 1==286, 2==386, 3==486
|
||||
fprintf(stderr, "Program needs unknown CPU type (%u)\n", (unsigned int) lx->cpu_type);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (lx->os_type != 1) { // 1==OS/2, others: dos4, windows, win386, unknown.
|
||||
fprintf(stderr, "Program needs unknown OS type (%u)\n", (unsigned int) lx->os_type);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (lx->page_size != 4096) {
|
||||
fprintf(stderr, "Program page size isn't 4096 (%u)\n", (unsigned int) lx->page_size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
} // sanityCheckExe
|
||||
|
||||
|
||||
static void parseExe(const char *exefname, uint8 *exe, uint32 exelen)
|
||||
{
|
||||
printf("%s\n", exefname);
|
||||
|
||||
if (!sanityCheckExe(&exe, &exelen))
|
||||
return;
|
||||
|
||||
const LxHeader *lx = (const LxHeader *) exe;
|
||||
printf("module version: %u\n", (unsigned int) lx->module_version);
|
||||
|
||||
printf("module flags:");
|
||||
if (lx->module_flags & 0x4) printf(" LIBINIT");
|
||||
if (lx->module_flags & 0x10) printf(" INTERNALFIXUPS");
|
||||
if (lx->module_flags & 0x20) printf(" EXTERNALFIXUPS");
|
||||
if (lx->module_flags & 0x100) printf(" PMINCOMPAT");
|
||||
if (lx->module_flags & 0x200) printf(" PMCOMPAT");
|
||||
if (lx->module_flags & 0x300) printf(" USESPM");
|
||||
if (lx->module_flags & 0x2000) printf(" NOTLOADABLE");
|
||||
if (lx->module_flags & 0x8000) printf(" LIBRARYMODULE");
|
||||
if (lx->module_flags & 0x18000) printf(" PROTMEMLIBRARYMODULE");
|
||||
if (lx->module_flags & 0x20000) printf(" PHYSDRIVERMODULE");
|
||||
if (lx->module_flags & 0x28000) printf(" VIRTDRIVERMODULE");
|
||||
if (lx->module_flags & 0x40000000) printf(" LIBTERM");
|
||||
printf("\n");
|
||||
|
||||
printf("Number of pages in module: %u\n", (unsigned int) lx->module_num_pages);
|
||||
printf("EIP Object number: %u\n", (unsigned int) lx->eip_object);
|
||||
printf("EIP: 0x%X\n", (unsigned int) lx->eip);
|
||||
printf("ESP Object number: %u\n", (unsigned int) lx->esp_object);
|
||||
printf("ESP: 0x%X\n", (unsigned int) lx->esp);
|
||||
printf("Page size: %u\n", (unsigned int) lx->page_size);
|
||||
printf("Page offset shift: %u\n", (unsigned int) lx->page_offset_shift);
|
||||
printf("Fixup section size: %u\n", (unsigned int) lx->fixup_section_size);
|
||||
printf("Fixup section checksum: 0x%X\n", (unsigned int) lx->fixup_section_checksum);
|
||||
printf("Loader section size: %u\n", (unsigned int) lx->loader_section_size);
|
||||
printf("Loader section checksum: 0x%X\n", (unsigned int) lx->loader_section_checksum);
|
||||
printf("Object table offset: %u\n", (unsigned int) lx->object_table_offset);
|
||||
printf("Number of objects in module: %u\n", (unsigned int) lx->module_num_objects);
|
||||
printf("Object page table offset: %u\n", (unsigned int) lx->object_page_table_offset);
|
||||
printf("Object iterated pages offset: %u\n", (unsigned int) lx->object_iter_pages_offset);
|
||||
printf("Resource table offset: %u\n", (unsigned int) lx->resource_table_offset);
|
||||
printf("Number of resource table entries: %u\n", (unsigned int) lx->num_resource_table_entries);
|
||||
printf("Resident name table offset: %u\n", (unsigned int) lx->resident_name_table_offset);
|
||||
printf("Entry table offset: %u\n", (unsigned int) lx->entry_table_offset);
|
||||
printf("Module directives offset: %u\n", (unsigned int) lx->module_directives_offset);
|
||||
printf("Number of module directives: %u\n", (unsigned int) lx->num_module_directives);
|
||||
printf("Fixup page table offset: %u\n", (unsigned int) lx->fixup_page_table_offset);
|
||||
printf("Fixup record table offset: %u\n", (unsigned int) lx->fixup_record_table_offset);
|
||||
printf("Import module table offset: %u\n", (unsigned int) lx->import_module_table_offset);
|
||||
printf("Number of inport module entries: %u\n", (unsigned int) lx->num_import_mod_entries);
|
||||
printf("Import procedure name table offset: %u\n", (unsigned int) lx->import_proc_table_offset);
|
||||
printf("Per-page checksum offset: %u\n", (unsigned int) lx->per_page_checksum_offset);
|
||||
printf("Data pages offset: %u\n", (unsigned int) lx->data_pages_offset);
|
||||
printf("Number of preload pages: %u\n", (unsigned int) lx->num_preload_pages);
|
||||
printf("Non-resident name table offset: %u\n", (unsigned int) lx->non_resident_name_table_offset);
|
||||
printf("Non-resident name table length: %u\n", (unsigned int) lx->non_resident_name_table_len);
|
||||
printf("Non-resident name table checksum: 0x%X\n", (unsigned int) lx->non_resident_name_table_checksum);
|
||||
printf("Auto data segment object number: %u\n", (unsigned int) lx->auto_ds_object_num);
|
||||
printf("Debug info offset: %u\n", (unsigned int) lx->debug_info_offset);
|
||||
printf("Debug info length: %u\n", (unsigned int) lx->debug_info_len);
|
||||
printf("Number of instance pages in preload section: %u\n", (unsigned int) lx->num_instance_preload);
|
||||
printf("Number of instance pages in demand section: %u\n", (unsigned int) lx->num_instance_demand);
|
||||
printf("Heap size: %u\n", (unsigned int) lx->heapsize);
|
||||
|
||||
/* This is apparently a requirement as of OS/2 2.0, according to lxexe.txt. */
|
||||
if ((lx->object_iter_pages_offset != 0) && (lx->object_iter_pages_offset != lx->data_pages_offset)) {
|
||||
fprintf(stderr, "Object iterator pages offset must be 0 or equal to Data pages offset\n");
|
||||
}
|
||||
|
||||
// when an LX file says "object" it's probably more like "section" or "segment" ...?
|
||||
printf("\n");
|
||||
printf("Object table (%u entries):\n", (unsigned int) lx->module_num_objects);
|
||||
for (uint32 i = 0; i < lx->module_num_objects; i++) {
|
||||
const LxObjectTableEntry *obj = ((const LxObjectTableEntry *) (exe + lx->object_table_offset)) + i;
|
||||
printf("Object #%u:\n", (unsigned int) i+1);
|
||||
printf("Virtual size: %u\n", (unsigned int) obj->virtual_size);
|
||||
printf("Relocation base address: 0x%X\n", (unsigned int) obj->reloc_base_addr);
|
||||
printf("Object flags:");
|
||||
if (obj->object_flags & 0x1) printf(" READ");
|
||||
if (obj->object_flags & 0x2) printf(" WRITE");
|
||||
if (obj->object_flags & 0x4) printf(" EXEC");
|
||||
if (obj->object_flags & 0x8) printf(" RESOURCE");
|
||||
if (obj->object_flags & 0x10) printf(" DISCARD");
|
||||
if (obj->object_flags & 0x20) printf(" SHARED");
|
||||
if (obj->object_flags & 0x40) printf(" PRELOAD");
|
||||
if (obj->object_flags & 0x80) printf(" INVALID");
|
||||
if (obj->object_flags & 0x100) printf(" ZEROFILL");
|
||||
if (obj->object_flags & 0x200) printf(" RESIDENT");
|
||||
if (obj->object_flags & 0x300) printf(" RESIDENT+CONTIG");
|
||||
if (obj->object_flags & 0x400) printf(" RESIDENT+LONGLOCK");
|
||||
if (obj->object_flags & 0x800) printf(" SYSRESERVED");
|
||||
if (obj->object_flags & 0x1000) printf(" 16:16");
|
||||
if (obj->object_flags & 0x2000) printf(" BIG");
|
||||
if (obj->object_flags & 0x4000) printf(" CONFORM");
|
||||
if (obj->object_flags & 0x8000) printf(" IOPL");
|
||||
printf("\n");
|
||||
printf("Page table index: %u\n", (unsigned int) obj->page_table_index);
|
||||
printf("Number of page table entries: %u\n", (unsigned int) obj->num_page_table_entries);
|
||||
printf("System-reserved field: %u\n", (unsigned int) obj->reserved);
|
||||
|
||||
printf("Object pages:\n");
|
||||
const LxObjectPageTableEntry *objpage = ((const LxObjectPageTableEntry *) (exe + lx->object_page_table_offset)) + (obj->page_table_index - 1);
|
||||
const uint32 *fixuppage = (((const uint32 *) (exe + lx->fixup_page_table_offset)) + (obj->page_table_index - 1));
|
||||
for (uint32 i = 0; i < obj->num_page_table_entries; i++, objpage++) {
|
||||
printf("Object Page #%u:\n", (unsigned int) (i + obj->page_table_index));
|
||||
printf("Page data offset: 0x%X\n", (unsigned int) objpage->page_data_offset);
|
||||
printf("Page data size: %u\n", (unsigned int) objpage->data_size);
|
||||
printf("Page flags: (%u)", (unsigned int) objpage->flags);
|
||||
if (objpage->flags == 0x0) printf(" PHYSICAL");
|
||||
else if (objpage->flags == 0x1) printf(" ITERATED");
|
||||
else if (objpage->flags == 0x2) printf(" INVALID");
|
||||
else if (objpage->flags == 0x3) printf(" ZEROFILL");
|
||||
else if (objpage->flags == 0x4) printf(" RANGE");
|
||||
else if (objpage->flags == 0x5) printf(" COMPRESSED");
|
||||
else printf(" UNKNOWN");
|
||||
printf("\n");
|
||||
const uint32 fixupoffset = *fixuppage;
|
||||
const uint32 fixuplen = fixuppage[1] - fixuppage[0];
|
||||
printf("Page's fixup record offset: %u\n", (unsigned int) fixupoffset);
|
||||
printf("Page's fixup record size: %u\n", (unsigned int) fixuplen);
|
||||
printf("Fixup records:\n");
|
||||
const uint8 *fixup = (exe + lx->fixup_record_table_offset) + fixupoffset;
|
||||
const uint8 *fixupend = fixup + fixuplen;
|
||||
for (uint32 i = 0; fixup < fixupend; i++) {
|
||||
printf("Fixup Record #%u:\n", (unsigned int) (i + 1));
|
||||
printf("Source type: ");
|
||||
const uint8 srctype = *(fixup++);
|
||||
if (srctype & 0x10) printf("[FIXUPTOALIAS] ");
|
||||
if (srctype & 0x20) printf("[SOURCELIST] ");
|
||||
switch (srctype & 0xF) {
|
||||
case 0x00: printf("Byte fixup"); break;
|
||||
case 0x02: printf("16-bit selector fixup"); break;
|
||||
case 0x03: printf("16:16 pointer fixup"); break;
|
||||
case 0x05: printf("16-bit offset fixup"); break;
|
||||
case 0x06: printf("16:32 pointer fixup"); break;
|
||||
case 0x07: printf("32-bit offset fixup"); break;
|
||||
case 0x08: printf("32-bit self-relative offset fixup"); break;
|
||||
default: printf("(undefined fixup)"); break;
|
||||
} // switch
|
||||
printf("\n");
|
||||
|
||||
const uint8 fixupflags = *(fixup++);
|
||||
printf("Target flags:");
|
||||
switch (fixupflags & 0x3) {
|
||||
case 0x0: printf(" INTERNAL"); break;
|
||||
case 0x1: printf(" IMPORTBYORDINAL"); break;
|
||||
case 0x2: printf(" IMPORTBYNAME"); break;
|
||||
case 0x3: printf(" INTERNALVIAENTRY"); break;
|
||||
} // switch
|
||||
|
||||
if (fixupflags & 0x4) printf(" ADDITIVE");
|
||||
if (fixupflags & 0x8) printf(" INTERNALCHAINING");
|
||||
if (fixupflags & 0x10) printf(" 32BITTARGETOFFSET");
|
||||
if (fixupflags & 0x20) printf(" 32BITADDITIVE");
|
||||
if (fixupflags & 0x40) printf(" 16BITORDINAL");
|
||||
if (fixupflags & 0x80) printf(" 8BITORDINAL");
|
||||
printf("\n");
|
||||
|
||||
uint8 srclist_count = 0;
|
||||
if (srctype & 0x20) { // source list
|
||||
srclist_count = *(fixup++);
|
||||
printf("Source offset list count: %u\n", (unsigned int) srclist_count);
|
||||
} else {
|
||||
const uint16 srcoffset = *((uint16 *) fixup); fixup += 2;
|
||||
printf("Source offset: %u\n", (unsigned int) srcoffset);
|
||||
} // else
|
||||
printf("\n");
|
||||
|
||||
switch (fixupflags & 0x3) {
|
||||
case 0x0:
|
||||
printf("Internal fixup record:\n");
|
||||
if (fixupflags & 0x40) { // 16 bit value
|
||||
const uint16 val = *((uint16 *) fixup); fixup += 2;
|
||||
printf("Object: %u\n", (unsigned int) val);
|
||||
} else {
|
||||
const uint8 val = *(fixup++);
|
||||
printf("Object: %u\n", (unsigned int) val);
|
||||
} // else
|
||||
|
||||
printf("Target offset: ");
|
||||
if ((srctype & 0xF) == 0x2) { // 16-bit selector fixup
|
||||
printf("[not used for 16-bit selector fixups]\n");
|
||||
} else if (fixupflags & 0x10) { // 32-bit target offset
|
||||
const uint32 val = *((uint32 *) fixup); fixup += 4;
|
||||
printf("%u\n", (unsigned int) val);
|
||||
} else { // 16-bit target offset
|
||||
const uint16 val = *((uint16 *) fixup); fixup += 2;
|
||||
printf("%u\n", (unsigned int) val);
|
||||
} // else
|
||||
break;
|
||||
|
||||
case 0x1:
|
||||
printf("Import by ordinal fixup record:\n");
|
||||
if (fixupflags & 0x40) { // 16 bit value
|
||||
const uint16 val = *((uint16 *) fixup); fixup += 2;
|
||||
printf("Module ordinal: %u\n", (unsigned int) val);
|
||||
} else {
|
||||
const uint8 val = *(fixup++);
|
||||
printf("Module ordinal: %u\n", (unsigned int) val);
|
||||
} // else
|
||||
|
||||
if (fixupflags & 0x80) { // 8 bit value
|
||||
const uint8 val = *(fixup++);
|
||||
printf("Import ordinal: %u\n", (unsigned int) val);
|
||||
} else if (fixupflags & 0x10) { // 32-bit value
|
||||
const uint32 val = *((uint32 *) fixup); fixup += 4;
|
||||
printf("Import ordinal: %u\n", (unsigned int) val);
|
||||
} else { // 16-bit value
|
||||
const uint16 val = *((uint16 *) fixup); fixup += 2;
|
||||
printf("Import ordinal: %u\n", (unsigned int) val);
|
||||
} // else
|
||||
|
||||
uint32 additive = 0;
|
||||
if (fixupflags & 0x4) { // Has additive.
|
||||
if (fixupflags & 0x20) { // 32-bit value
|
||||
additive = *((uint32 *) fixup);
|
||||
fixup += 4;
|
||||
} else { // 16-bit value
|
||||
additive = *((uint16 *) fixup);
|
||||
fixup += 2;
|
||||
} // else
|
||||
} // if
|
||||
printf("Additive: %u\n", (unsigned int) additive);
|
||||
break;
|
||||
|
||||
case 0x2:
|
||||
printf("Import by name fixup record:\n");
|
||||
printf("WRITE ME\n"); exit(1);
|
||||
break;
|
||||
|
||||
case 0x3:
|
||||
printf("Internal entry table fixup record:\n");
|
||||
printf("WRITE ME\n"); exit(1);
|
||||
break;
|
||||
} // switch
|
||||
|
||||
if (srctype & 0x20) { // source list
|
||||
printf("Source offset list:");
|
||||
for (uint8 i = 0; i < srclist_count; i++) {
|
||||
const uint16 val = *((uint16 *) fixup); fixup += 2;
|
||||
printf(" %u", (unsigned int) val);
|
||||
} // for
|
||||
} // if
|
||||
|
||||
printf("\n\n");
|
||||
} // while
|
||||
|
||||
printf("\n");
|
||||
} // for
|
||||
} // for
|
||||
|
||||
printf("\n");
|
||||
printf("Resource table (%u entries):\n", (unsigned int) lx->num_resource_table_entries);
|
||||
for (uint32 i = 0; i < lx->num_resource_table_entries; i++) {
|
||||
const LxResourceTableEntry *rsrc = ((const LxResourceTableEntry *) (exe + lx->resource_table_offset)) + i;
|
||||
printf("%u:\n", (unsigned int) i);
|
||||
printf("Type ID: %u\n", (unsigned int) rsrc->type_id);
|
||||
printf("Name ID: %u\n", (unsigned int) rsrc->name_id);
|
||||
printf("Resource size: %u\n", (unsigned int) rsrc->resource_size);
|
||||
printf("Object: %u\n", (unsigned int) rsrc->object);
|
||||
printf("Offset: 0x%X\n", (unsigned int) rsrc->offset);
|
||||
printf("\n");
|
||||
} // for
|
||||
|
||||
printf("\n");
|
||||
|
||||
printf("Entry table:\n");
|
||||
int bundleid = 1;
|
||||
const uint8 *entryptr = exe + lx->entry_table_offset;
|
||||
while (*entryptr) { /* end field has a value of zero. */
|
||||
const uint8 numentries = *(entryptr++); /* number of entries in this bundle */
|
||||
const uint8 bundletype = (*entryptr) & ~0x80;
|
||||
const uint8 paramtypes = (*(entryptr++) & 0x80) ? 1 : 0;
|
||||
|
||||
printf("Bundle %d (%u entries): ", bundleid, (unsigned int) numentries);
|
||||
bundleid++;
|
||||
|
||||
if (paramtypes)
|
||||
printf("[PARAMTYPES] ");
|
||||
|
||||
switch (bundletype) {
|
||||
case 0x00:
|
||||
printf("UNUSED\n");
|
||||
printf(" %u unused entries.\n\n", (unsigned int) numentries);
|
||||
break;
|
||||
|
||||
case 0x01:
|
||||
printf("16BIT\n");
|
||||
printf(" Object number: %u\n", (unsigned int) *((const uint16 *) entryptr)); entryptr += 2;
|
||||
for (uint8 i = 0; i < numentries; i++) {
|
||||
printf(" %u:\n", (unsigned int) i);
|
||||
printf(" Flags:");
|
||||
if (*entryptr & 0x1) printf(" EXPORTED");
|
||||
printf("\n");
|
||||
printf(" Parameter word count: %u\n", (unsigned int) ((*entryptr & 0xF8) >> 3)); entryptr++;
|
||||
printf(" Offset: %u\n", (unsigned int) *((const uint16 *) entryptr)); entryptr += 2;
|
||||
printf("\n");
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x02:
|
||||
printf("286CALLGATE\n");
|
||||
printf(" Object number: %u\n", (unsigned int) *((const uint16 *) entryptr)); entryptr += 2;
|
||||
for (uint8 i = 0; i < numentries; i++) {
|
||||
printf(" %u:\n", (unsigned int) i);
|
||||
printf(" Flags:");
|
||||
if (*entryptr & 0x1) printf(" EXPORTED");
|
||||
printf("\n");
|
||||
printf(" Parameter word count: %u\n", (unsigned int) ((*entryptr & 0xF8) >> 3)); entryptr++;
|
||||
printf(" Offset: %u\n", (unsigned int) *((const uint16 *) entryptr)); entryptr += 2;
|
||||
printf(" Callgate selector: %u\n", (unsigned int) *((const uint16 *) entryptr)); entryptr += 2;
|
||||
printf("\n");
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x03: printf("32BIT\n");
|
||||
printf(" Object number: %u\n", (unsigned int) *((const uint16 *) entryptr)); entryptr += 2;
|
||||
for (uint8 i = 0; i < numentries; i++) {
|
||||
printf(" %u:\n", (unsigned int) i);
|
||||
printf(" Flags:");
|
||||
if (*entryptr & 0x1) printf(" EXPORTED");
|
||||
printf("\n");
|
||||
printf(" Parameter word count: %u\n", (unsigned int) ((*entryptr & 0xF8) >> 3)); entryptr++;
|
||||
printf(" Offset: %u\n", (unsigned int) *((const uint32 *) entryptr)); entryptr += 4;
|
||||
printf("\n");
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x04: printf("FORWARDER\n"); break;
|
||||
printf(" Reserved field: %u\n", (unsigned int) *((const uint16 *) entryptr)); entryptr += 2;
|
||||
for (uint8 i = 0; i < numentries; i++) {
|
||||
printf(" %u:\n", (unsigned int) i);
|
||||
printf(" Flags:");
|
||||
const int isordinal = (*entryptr & 0x1);
|
||||
if (isordinal) printf(" IMPORTBYORDINAL");
|
||||
printf("\n");
|
||||
printf(" Reserved for future use: %u\n", (unsigned int) ((*entryptr & 0xF8) >> 3)); entryptr++;
|
||||
printf(" Module ordinal number: %u\n", (unsigned int) *((const uint16 *) entryptr)); entryptr += 2;
|
||||
if (isordinal) {
|
||||
printf(" Import ordinal number: %u\n", (unsigned int) *((const uint32 *) entryptr)); entryptr += 4;
|
||||
} else {
|
||||
printf(" Import name offset: %u\n", (unsigned int) *((const uint32 *) entryptr)); entryptr += 4;
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
printf("UNKNOWN (%u)\n\n", (unsigned int) bundletype);
|
||||
break; // !!! FIXME: what to do?
|
||||
} // switch
|
||||
} // while
|
||||
printf("\n");
|
||||
|
||||
printf("Module directives (%u entries):\n", (unsigned int) lx->num_module_directives);
|
||||
const uint8 *dirptr = exe + lx->module_directives_offset;
|
||||
for (uint32 i = 0; i < lx->num_module_directives; i++) {
|
||||
printf("%u:\n", (unsigned int) i);
|
||||
printf("Directive ID: %u\n", (unsigned int) *((const uint16 *) dirptr)); dirptr += 2;
|
||||
printf("Data size: %u\n", (unsigned int) *((const uint16 *) dirptr)); dirptr += 2;
|
||||
printf("Data offset: %u\n", (unsigned int) *((const uint32 *) dirptr)); dirptr += 4;
|
||||
printf("\n");
|
||||
// !!! FIXME: verify record directive table, etc, based on Directive ID
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
if (lx->per_page_checksum_offset == 0) {
|
||||
printf("No per-page checksums available.\n");
|
||||
} else {
|
||||
printf("!!! FIXME: look at per-page checksums!\n");
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
|
||||
printf("Import modules (%u entries):\n", (unsigned int) lx->num_import_mod_entries);
|
||||
const uint8 *import_modules_table = exe + lx->import_module_table_offset;
|
||||
for (uint32 i = 0; i < lx->num_import_mod_entries; i++) {
|
||||
char name[128];
|
||||
const uint8 namelen = *(import_modules_table++);
|
||||
// !!! FIXME: name can't be more than 127 chars, according to docs. Check this.
|
||||
memcpy(name, import_modules_table, namelen);
|
||||
import_modules_table += namelen;
|
||||
name[namelen] = '\0';
|
||||
printf("%u: %s\n", (unsigned int) i, name);
|
||||
}
|
||||
|
||||
#if 0
|
||||
const uint8 *fixup_record_table = exe + fixup_record_table_offset;
|
||||
const uint8 *import_proc_table = exe + lx->import_proc_table_offset;
|
||||
for (uint32 i = 0; i < 1000; i++) {
|
||||
char name[128];
|
||||
const uint8 namelen = *(import_proc_table++) & 0x7F;
|
||||
// !!! FIXME: name can't be more than 127 chars, according to docs. Check this.
|
||||
memcpy(name, import_proc_table, namelen);
|
||||
import_proc_table += namelen;
|
||||
name[namelen] = '\0';
|
||||
printf("PROC: %s\n", name);
|
||||
}
|
||||
#endif
|
||||
} // parseExe
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
if (argc != 2) {
|
||||
fprintf(stderr, "USAGE: %s <program.exe>\n", argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
const char *exefname = argv[1];
|
||||
FILE *io = fopen(exefname, "rb");
|
||||
if (!io) {
|
||||
fprintf(stderr, "can't open '%s: %s'\n", exefname, strerror(errno));
|
||||
return 2;
|
||||
}
|
||||
|
||||
if (fseek(io, 0, SEEK_END) < 0) {
|
||||
fprintf(stderr, "can't seek in '%s': %s\n", exefname, strerror(errno));
|
||||
return 3;
|
||||
}
|
||||
|
||||
const uint32 exelen = ftell(io);
|
||||
uint8 *exe = (uint8 *) malloc(exelen);
|
||||
if (!exe) {
|
||||
fprintf(stderr, "Out of memory\n");
|
||||
return 4;
|
||||
}
|
||||
|
||||
rewind(io);
|
||||
if (fread(exe, exelen, 1, io) != 1) {
|
||||
fprintf(stderr, "read failure on '%s': %s\n", exefname, strerror(errno));
|
||||
return 5;
|
||||
}
|
||||
|
||||
fclose(io);
|
||||
|
||||
parseExe(exefname, exe, exelen);
|
||||
|
||||
free(exe);
|
||||
|
||||
return 0;
|
||||
} // main
|
||||
|
||||
// end of lx_dump.c ...
|
||||
+611
@@ -0,0 +1,611 @@
|
||||
#define _GNU_SOURCE 1
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <stdint.h>
|
||||
#include <errno.h>
|
||||
#include <sys/mman.h>
|
||||
#include <assert.h>
|
||||
#include "lx_loader.h"
|
||||
|
||||
// !!! FIXME: move this into an lx_common.c file.
|
||||
static int sanityCheckExe(uint8 **_exe, uint32 *_exelen)
|
||||
{
|
||||
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", (unsigned int) 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;
|
||||
}
|
||||
|
||||
if ((lx->byte_order != 0) || (lx->word_order != 0)) {
|
||||
fprintf(stderr, "Program is not little-endian!\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (lx->lx_version != 0) {
|
||||
fprintf(stderr, "Program is unknown LX EXE version (%u)\n", (unsigned int) lx->lx_version);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (lx->cpu_type > 3) { // 1==286, 2==386, 3==486
|
||||
fprintf(stderr, "Program needs unknown CPU type (%u)\n", (unsigned int) lx->cpu_type);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (lx->os_type != 1) { // 1==OS/2, others: dos4, windows, win386, unknown.
|
||||
fprintf(stderr, "Program needs unknown OS type (%u)\n", (unsigned int) lx->os_type);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (lx->page_size != 4096) {
|
||||
fprintf(stderr, "Program page size isn't 4096 (%u)\n", (unsigned int) lx->page_size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// !!! FIXME: check if EIP and ESP are non-zero vs per-process library bits, etc.
|
||||
|
||||
return 1;
|
||||
} // sanityCheckExe
|
||||
|
||||
/* this algorithm is from lxlite 138u. */
|
||||
static int decompressExePack2(uint8 *dst, const uint32 dstlen, const uint8 *src, const uint32 srclen)
|
||||
{
|
||||
uint32 sOf = 0;
|
||||
uint32 dOf = 0;
|
||||
uint32 bOf = 0;
|
||||
uint32 b1 = 0;
|
||||
uint32 b2 = 0;
|
||||
|
||||
#define SRCAVAIL(n) ((sOf + (n)) <= srclen)
|
||||
#define DSTAVAIL(n) ((dOf + (n)) <= dstlen)
|
||||
|
||||
do {
|
||||
if (!SRCAVAIL(1))
|
||||
break;
|
||||
|
||||
b1 = src[sOf];
|
||||
switch (b1 & 3) {
|
||||
case 0:
|
||||
if (b1 == 0) {
|
||||
if (SRCAVAIL(2)) {
|
||||
if (src[sOf + 1] == 0) {
|
||||
sOf += 2;
|
||||
break;
|
||||
} else if (SRCAVAIL(3) && DSTAVAIL(src[sOf + 1])) {
|
||||
memset(dst + dOf, src[sOf + 2], src[sOf + 1]);
|
||||
sOf += 3;
|
||||
dOf += src[sOf - 2];
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
} else if (SRCAVAIL((b1 >> 2) + 1) && DSTAVAIL(b1 >> 2)) {
|
||||
memcpy(dst + dOf, src + (sOf + 1), b1 >> 2);
|
||||
dOf += b1 >> 2;
|
||||
sOf += (b1 >> 2) + 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case 1:
|
||||
if (!SRCAVAIL(2))
|
||||
return 0;
|
||||
bOf = (*((const uint16 *) (src + sOf))) >> 7;
|
||||
b2 = ((b1 >> 4) & 7) + 3;
|
||||
b1 = ((b1 >> 2) & 3);
|
||||
sOf += 2;
|
||||
if (SRCAVAIL(b1) && DSTAVAIL(b1 + b2) && (dOf + b1 - bOf >= 0)) {
|
||||
memcpy(dst + dOf, src + sOf, b1);
|
||||
dOf += b1;
|
||||
sOf += b1;
|
||||
memmove(dst + dOf, dst + (dOf - bOf), b2);
|
||||
dOf += b2;
|
||||
} else {
|
||||
return 0;
|
||||
} // else
|
||||
break;
|
||||
|
||||
case 2:
|
||||
if (!SRCAVAIL(2))
|
||||
return 0;
|
||||
bOf = (*((const uint16 *) (src + sOf))) >> 4;
|
||||
b1 = ((b1 >> 2) & 3) + 3;
|
||||
if (DSTAVAIL(b1) && (dOf - bOf >= 0)) {
|
||||
memmove(dst + dOf, dst + (dOf - bOf), b1);
|
||||
dOf += b1;
|
||||
sOf += 2;
|
||||
} else {
|
||||
return 0;
|
||||
} // else
|
||||
break;
|
||||
|
||||
case 3:
|
||||
if (!SRCAVAIL(3))
|
||||
return 0;
|
||||
b2 = ((*((const uint16 *) (src + sOf))) >> 6) & 0x3F;
|
||||
b1 = (src[sOf] >> 2) & 0x0F;
|
||||
bOf = (*((const uint16 *) (src + (sOf + 1)))) >> 4;
|
||||
sOf += 3;
|
||||
if (SRCAVAIL(b1) && DSTAVAIL(b1 + b2) && (dOf + b1 - bOf >= 0))
|
||||
{
|
||||
memcpy(dst + dOf, src + sOf, b1);
|
||||
dOf += b1;
|
||||
sOf += b1;
|
||||
memmove(dst + dOf, dst + (dOf - bOf), b2);
|
||||
dOf += b2;
|
||||
} else {
|
||||
return 0;
|
||||
} // else
|
||||
break;
|
||||
} // switch
|
||||
} while (dOf < dstlen);
|
||||
#undef SRCAVAIL
|
||||
#undef DSTAVAIL
|
||||
|
||||
// pad out the rest of the page with zeroes.
|
||||
memset(dst + dOf, '\0', dstlen - dOf);
|
||||
return 1;
|
||||
} // decompressExePack2
|
||||
|
||||
void missing_ordinal_called(const uint32 ordinal)
|
||||
{
|
||||
fflush(stdout);
|
||||
fflush(stderr);
|
||||
fprintf(stderr, "\n\nMissing ordinal '%u' called!\n", ordinal);
|
||||
fprintf(stderr, "Aborting.\n\n\n");
|
||||
//STUBBED("output backtrace");
|
||||
fflush(stderr);
|
||||
_exit(1);
|
||||
} // missing_ordinal_called
|
||||
|
||||
static uint32 DosPutMessage(uint32 handle, uint32 msglen, const char *msg)
|
||||
{
|
||||
// !!! FIXME: this isn't right, but good enough for now.
|
||||
fwrite(msg, msglen, 1, stdout);
|
||||
fflush(stdout);
|
||||
return 0;
|
||||
} // DosPutMessage
|
||||
|
||||
static uint32 getModuleProcAddrByOrdinal(const uint16 moduleid, const uint32 importid)
|
||||
{
|
||||
// !!! FIXME: write me for real.
|
||||
if (moduleid != 1) { // this is MSG in hello.exe, for testing purposes.
|
||||
fprintf(stderr, "uhoh, looking for module ordinal that isn't #1.\n");
|
||||
return 0;
|
||||
} // if
|
||||
|
||||
//fprintf(stderr, "importid == %u\n", (unsigned int) importid);
|
||||
|
||||
static void *page = NULL;
|
||||
static uint32 pageused = 0;
|
||||
static uint32 pagesize = 0;
|
||||
if (pagesize == 0)
|
||||
pagesize = getpagesize();
|
||||
|
||||
if ((!page) || ((pagesize - pageused) < 32))
|
||||
{
|
||||
if (page)
|
||||
mprotect(page, pagesize, PROT_READ | PROT_EXEC);
|
||||
page = valloc(pagesize);
|
||||
mprotect(page, pagesize, PROT_READ | PROT_WRITE | PROT_EXEC);
|
||||
pageused = 0;
|
||||
} // if
|
||||
|
||||
if (importid == 5) // !!! FIXME: this is DosPutMessage in the MSG module.
|
||||
return (uint32) (size_t) DosPutMessage;
|
||||
|
||||
void *trampoline = page + pageused;
|
||||
char *ptr = (char *) trampoline;
|
||||
const uint32 ordinal = importid;
|
||||
|
||||
*(ptr++) = 0x55; // pushl %ebp
|
||||
*(ptr++) = 0x89; // movl %esp,%ebp
|
||||
*(ptr++) = 0xE5; // ...movl %esp,%ebp
|
||||
*(ptr++) = 0x68; // pushl immediate
|
||||
memcpy(ptr, &ordinal, sizeof (uint32));
|
||||
ptr += sizeof (uint32);
|
||||
*(ptr++) = 0xB8; // movl immediate to %eax
|
||||
const void *fn = missing_ordinal_called;
|
||||
memcpy(ptr, &fn, sizeof (void *));
|
||||
ptr += sizeof (void *);
|
||||
*(ptr++) = 0xFF; // call absolute in %eax.
|
||||
*(ptr++) = 0xD0; // ...call absolute in %eax.
|
||||
|
||||
const uint32 trampoline_len = (uint32) (ptr - ((char *) trampoline));
|
||||
assert(trampoline_len <= 32);
|
||||
pageused += trampoline_len;
|
||||
|
||||
if (pageused % 4) // keep these aligned to 32 bits.
|
||||
pageused += (4 - (pageused % 4));
|
||||
|
||||
return (uint32) (size_t) trampoline;
|
||||
} // getModuleProcAddrByOrdinal
|
||||
|
||||
|
||||
static void doFixup(uint8 *page, const uint16 offset, const uint32 finalval, const uint16 finalval2, const uint32 finalsize)
|
||||
{
|
||||
#if 0
|
||||
if (finalsize == 6) {
|
||||
printf("fixing up %p to to 0x%X:0x%X (6 bytes)...\n", page + offset, (unsigned int) finalval2, (unsigned int) finalval);
|
||||
} else {
|
||||
printf("fixing up %p to to 0x%X (%u bytes)...\n", page + offset, (unsigned int) finalval, (unsigned int) finalsize);
|
||||
} // else
|
||||
fflush(stdout);
|
||||
#endif
|
||||
|
||||
switch (finalsize) {
|
||||
case 1: { uint8 *dst = (uint8 *) (page + offset); *dst = (uint8) finalval; } break;
|
||||
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;
|
||||
break;
|
||||
} // case
|
||||
default:
|
||||
fprintf(stderr, "BUG! Unexpected fixup final size of %u\n", (unsigned int) finalsize);
|
||||
exit(1);
|
||||
} // switch
|
||||
} // doFixup
|
||||
|
||||
static void fixupPage(const LxHeader *lx, const LxObjectTableEntry *obj, uint8 *page)
|
||||
{
|
||||
const uint8 *exe = (const uint8 *) lx; // this is the start of the LX EXE (past the DOS stub, etc).
|
||||
const uint32 *fixuppage = (((const uint32 *) (exe + lx->fixup_page_table_offset)) + (obj->page_table_index - 1));
|
||||
const uint32 fixupoffset = *fixuppage;
|
||||
const uint32 fixuplen = fixuppage[1] - fixuppage[0];
|
||||
const uint8 *fixup = (exe + lx->fixup_record_table_offset) + fixupoffset;
|
||||
const uint8 *fixupend = fixup + fixuplen;
|
||||
|
||||
while (fixup < fixupend) {
|
||||
const uint8 srctype = *(fixup++);
|
||||
const uint8 fixupflags = *(fixup++);
|
||||
uint8 srclist_count = 0;
|
||||
uint16 srcoffset = 0;
|
||||
|
||||
uint32 finalval = 0;
|
||||
uint16 finalval2 = 0;
|
||||
uint32 finalsize = 0;
|
||||
|
||||
switch (srctype & 0xF) {
|
||||
case 0x0: // byte fixup
|
||||
finalsize = 1;
|
||||
break;
|
||||
case 0x2: // 16-bit selector fixup
|
||||
case 0x5: // 16-bit offset fixup
|
||||
finalsize = 2;
|
||||
break;
|
||||
case 0x3: // 16:16 pointer fixup
|
||||
case 0x7: // 32-bit offset fixup
|
||||
case 0x8: // 32-bit self-relative offset fixup
|
||||
finalsize = 4;
|
||||
break;
|
||||
case 0x6: // 16:32 pointer fixup
|
||||
finalsize = 6;
|
||||
break;
|
||||
} // switch
|
||||
|
||||
if (srctype & 0x20) { // contains a source list
|
||||
srclist_count = *(fixup++);
|
||||
} else {
|
||||
srcoffset = *((uint16 *) fixup);
|
||||
fixup += 2;
|
||||
} // else
|
||||
|
||||
switch (fixupflags & 0x3) {
|
||||
case 0x0: { // Internal fixup record
|
||||
uint16 objectid = 0;
|
||||
if (fixupflags & 0x40) { // 16 bit value
|
||||
objectid = *((uint16 *) fixup); fixup += 2;
|
||||
} else {
|
||||
objectid = (uint16) *(fixup++);
|
||||
} // 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 (fixupflags & 0x10) { // 32-bit target offset
|
||||
targetoffset = *((uint32 *) fixup); fixup += 4;
|
||||
} else { // 16-bit target offset
|
||||
targetoffset = (uint32) (*((uint16 *) fixup)); fixup += 2;
|
||||
} // else
|
||||
} // if
|
||||
|
||||
// !!! FIXME: I have no idea if this is right at all.
|
||||
// !!! FIXME: Is this guaranteed to not references object pages I haven't loaded/fixed up yet?
|
||||
const LxObjectTableEntry *targetobj = ((const LxObjectTableEntry *) (exe + lx->object_table_offset)) + (objectid - 1);
|
||||
const uint8 *ptr = (uint8 *) ((size_t) (targetobj->reloc_base_addr + targetoffset));
|
||||
switch (finalsize) {
|
||||
case 1: finalval = (uint32) *((uint8 *) ptr); break;
|
||||
case 2: finalval = (uint32) *((uint16 *) ptr); break;
|
||||
case 4: finalval = (uint32) *((uint32 *) ptr); break;
|
||||
case 6: finalval2 = (uint32) *((uint16 *) ptr); finalval = (uint32) *((uint32 *) (ptr + 2)); break;
|
||||
} // switch
|
||||
break;
|
||||
} // case
|
||||
|
||||
case 0x1: { // Import by ordinal fixup record
|
||||
uint16 moduleid = 0; // module ordinal
|
||||
if (fixupflags & 0x40) { // 16 bit value
|
||||
moduleid = *((uint16 *) fixup); fixup += 2;
|
||||
} else {
|
||||
moduleid = (uint16) *(fixup++);
|
||||
} // else
|
||||
|
||||
uint32 importid = 0; // import ordinal
|
||||
if (fixupflags & 0x80) { // 8 bit value
|
||||
importid = (uint32) *(fixup++);
|
||||
} else if (fixupflags & 0x10) { // 32-bit value
|
||||
importid = *((uint32 *) fixup); fixup += 4;
|
||||
} else { // 16-bit value
|
||||
importid = (uint32) *((uint16 *) fixup); fixup += 2;
|
||||
} // else
|
||||
|
||||
finalval = getModuleProcAddrByOrdinal(moduleid, importid);
|
||||
break;
|
||||
} // case
|
||||
|
||||
case 0x2: { // Import by name fixup record
|
||||
fprintf(stderr, "FIXUP 0x2 WRITE ME\n");
|
||||
exit(1);
|
||||
break;
|
||||
} // case
|
||||
|
||||
case 0x3: { // Internal entry table fixup record
|
||||
fprintf(stderr, "FIXUP 0x3 WRITE ME\n");
|
||||
exit(1);
|
||||
break;
|
||||
} // case
|
||||
} // switch
|
||||
|
||||
uint32 additive = 0;
|
||||
if (fixupflags & 0x4) { // Has additive.
|
||||
if (fixupflags & 0x20) { // 32-bit value
|
||||
additive = *((uint32 *) fixup); fixup += 4;
|
||||
} else { // 16-bit value
|
||||
additive = (uint32) *((uint16 *) fixup);
|
||||
fixup += 2;
|
||||
} // else
|
||||
} // if
|
||||
finalval += additive;
|
||||
|
||||
if (srctype & 0x20) { // source list
|
||||
for (uint8 i = 0; i < srclist_count; i++) {
|
||||
const uint16 offset = *((uint16 *) fixup); fixup += 2;
|
||||
if ((srctype & 0xF) == 0x08) { // self-relative fixup?
|
||||
assert(finalval2 == 0);
|
||||
doFixup(page, offset, finalval - (((uint32)page)+offset+4), 0, finalsize);
|
||||
} else {
|
||||
doFixup(page, offset, finalval, finalval2, finalsize);
|
||||
}
|
||||
} // for
|
||||
} else {
|
||||
if ((srctype & 0xF) == 0x08) { // self-relative fixup?
|
||||
assert(finalval2 == 0);
|
||||
doFixup(page, srcoffset, finalval - (((uint32)page)+srcoffset+4), 0, finalsize);
|
||||
} else {
|
||||
doFixup(page, srcoffset, finalval, finalval2, finalsize);
|
||||
}
|
||||
} // else
|
||||
} // while
|
||||
} // fixupPage
|
||||
|
||||
static __attribute__((noreturn)) void loadExe(const char *exefname, uint8 *exe, uint32 exelen)
|
||||
{
|
||||
const uint8 *origexe = exe;
|
||||
//const uint32 origexelen = exelen;
|
||||
|
||||
if (!sanityCheckExe(&exe, &exelen))
|
||||
exit(1);
|
||||
|
||||
const LxHeader *lx = (const LxHeader *) exe;
|
||||
|
||||
const LxObjectTableEntry *obj = ((const LxObjectTableEntry *) (exe + lx->object_table_offset));
|
||||
for (uint32 i = 0; i < lx->module_num_objects; i++, obj++) {
|
||||
uint32 vsize = obj->virtual_size;
|
||||
if ((vsize % lx->page_size) != 0)
|
||||
vsize += lx->page_size - (vsize % lx->page_size);
|
||||
|
||||
const int prot = ((obj->object_flags & 0x1) ? PROT_READ : 0) |
|
||||
((obj->object_flags & 0x2) ? PROT_WRITE : 0) |
|
||||
((obj->object_flags & 0x1) ? PROT_EXEC : 0);
|
||||
|
||||
const int mmapflags = MAP_ANON | MAP_PRIVATE | MAP_FIXED;
|
||||
void *base = (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.
|
||||
|
||||
if (mmapaddr == ((void *) MAP_FAILED)) {
|
||||
fprintf(stderr, "mmap(%p, %u, RW-, ANON|PRIVATE|FIXED, -1, 0) failed (%d): %s\n",
|
||||
base, (unsigned int) vsize, errno, strerror(errno));
|
||||
exit(1);
|
||||
} // if
|
||||
|
||||
const LxObjectPageTableEntry *objpage = ((const LxObjectPageTableEntry *) (exe + lx->object_page_table_offset));
|
||||
objpage += obj->page_table_index - 1;
|
||||
|
||||
uint8 *dst = (uint8 *) mmapaddr;
|
||||
const uint32 numPageTableEntries = obj->num_page_table_entries;
|
||||
for (uint32 pagenum = 0; pagenum < numPageTableEntries; pagenum++, objpage++) {
|
||||
const uint8 *src;
|
||||
switch (objpage->flags) {
|
||||
case 0x00: // preload
|
||||
src = origexe + lx->data_pages_offset + (objpage->page_data_offset << lx->page_offset_shift);
|
||||
memcpy(dst, src, objpage->data_size);
|
||||
if (objpage->data_size < lx->page_size) {
|
||||
memset(dst + objpage->data_size, '\0', lx->page_size - objpage->data_size);
|
||||
}
|
||||
break;
|
||||
|
||||
//case 0x01: // FIXME: write me ... iterated, this is exepack1, I think.
|
||||
// src = origexe + lx->object_iter_pages_offset + (objpage->page_data_offset << lx->page_offset_shift);
|
||||
// memcpy(dst, src, objpage->data_size);
|
||||
// break;
|
||||
|
||||
case 0x02: // INVALID, just zero it out.
|
||||
case 0x03: // ZEROFILL, just zero it out.
|
||||
memset(dst, '\0', lx->page_size);
|
||||
break;
|
||||
|
||||
case 0x05: // exepack2
|
||||
src = origexe + lx->data_pages_offset + (objpage->page_data_offset << lx->page_offset_shift);
|
||||
if (!decompressExePack2(dst, lx->page_size, src, objpage->data_size)) {
|
||||
fprintf(stderr, "Failed to decompress object page (corrupt file or bug).\n");
|
||||
exit(1);
|
||||
} // if
|
||||
break;
|
||||
|
||||
case 0x08: // !!! FIXME: this is listed in lxlite, presumably this is exepack3 or something. Maybe this was new to Warp4, like exepack2 was new to Warp3. Pull this algorithm in from lxlite later.
|
||||
default:
|
||||
fprintf(stderr, "Don't know how to load an object page of type %u!\n", (unsigned int) objpage->flags);
|
||||
exit(1);
|
||||
} // switch
|
||||
|
||||
// Now run any fixups for the page...
|
||||
fixupPage(lx, obj, dst);
|
||||
|
||||
dst += lx->page_size;
|
||||
} // for
|
||||
|
||||
// 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) {
|
||||
memset(dst, '\0', remain);
|
||||
} // if
|
||||
|
||||
// Now set all the pages of this object to the proper final permissions...
|
||||
if (mprotect(mmapaddr, vsize, prot) == -1) {
|
||||
fprintf(stderr, "mprotect(%p, %u, %s%s%s, ANON|PRIVATE|FIXED, -1, 0) failed (%d): %s\n",
|
||||
base, (unsigned int) vsize,
|
||||
(prot&PROT_READ) ? "R" : "-",
|
||||
(prot&PROT_WRITE) ? "W" : "-",
|
||||
(prot&PROT_EXEC) ? "X" : "-",
|
||||
errno, strerror(errno));
|
||||
exit(1);
|
||||
} // if
|
||||
|
||||
} // for
|
||||
|
||||
// All the pages we need from the EXE are loaded into the appropriate spots in memory.
|
||||
//printf("mmap()'d everything we need!\n");
|
||||
|
||||
uint32 eip = lx->eip;
|
||||
if (lx->eip_object != 0) {
|
||||
const LxObjectTableEntry *targetobj = ((const LxObjectTableEntry *) (exe + lx->object_table_offset)) + (lx->eip_object - 1);
|
||||
eip += targetobj->reloc_base_addr;
|
||||
} // if
|
||||
|
||||
// !!! FIXME: esp==0 means something special for programs (and is ignored for library init).
|
||||
uint32 esp = lx->esp;
|
||||
assert(esp != 0);
|
||||
if (lx->esp_object != 0) { // !!! FIXME: ignore for libraries
|
||||
const LxObjectTableEntry *targetobj = ((const LxObjectTableEntry *) (exe + lx->object_table_offset)) + (lx->esp_object - 1);
|
||||
esp += targetobj->reloc_base_addr;
|
||||
} // if
|
||||
|
||||
// !!! FIXME: right now, we don't list any environment variables, because they probably don't make sense to drop in (even PATH uses a different separator on Unix).
|
||||
// !!! FIXME: eventually, the environment table looks like this (double-null to terminate list): var1=a\0var2=b\0var3=c\0\0
|
||||
// The command line, if I'm reading the Open Watcom __OS2Main() implementation correctly, looks like this...
|
||||
// \0programname\0argv0\0argv1\0argvN\0
|
||||
static char *env = "TEST=abc\0\0hello.exe\0hello.exe\0\0";
|
||||
char *cmd = env + 10; // !!! FIXME
|
||||
|
||||
// ...and you pass it the pointer to argv0. This is (at least as far as the docs suggest) appended to the environment table.
|
||||
//printf("jumping into LX land...! eip=0x%X esp=0x%X\n", (unsigned int) eip, (unsigned int) esp); fflush(stdout);
|
||||
|
||||
// !!! FIXME: need to set up OS/2 TIB and put it in FS register.
|
||||
__asm__ __volatile__ (
|
||||
"movl %%esi,%%esp \n\t" // use the OS/2 process's stack.
|
||||
"pushl %%eax \n\t" // cmd
|
||||
"pushl %%ecx \n\t" // env
|
||||
"pushl $0 \n\t" // reserved
|
||||
"pushl $0 \n\t" // module handle !!! FIXME
|
||||
"leal 1f,%%eax \n\t" // address that entry point should return to.
|
||||
"pushl %%eax \n\t"
|
||||
"pushl %%edi \n\t" // the OS/2 process entry point (we'll "ret" to it instead of jmp, so stack and registers are all correct).
|
||||
"xorl %%eax,%%eax \n\t"
|
||||
"xorl %%ebx,%%ebx \n\t"
|
||||
"xorl %%ecx,%%ecx \n\t"
|
||||
"xorl %%edx,%%edx \n\t"
|
||||
"xorl %%esi,%%esi \n\t"
|
||||
"xorl %%edi,%%edi \n\t"
|
||||
"xorl %%ebp,%%ebp \n\t"
|
||||
// !!! FIXME: init other registers!
|
||||
"ret \n\t" // go to OS/2 land!
|
||||
"1: \n\t" // ...and return here.
|
||||
"pushl %%eax \n\t" // call _exit() with whatever is in %eax.
|
||||
"call _exit \n\t"
|
||||
: // no outputs
|
||||
: "a" (cmd), "c" (env), "S" (esp), "D" (eip)
|
||||
: "memory"
|
||||
);
|
||||
|
||||
__builtin_unreachable();
|
||||
} // loadExe
|
||||
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
if (argc != 2) {
|
||||
fprintf(stderr, "USAGE: %s <program.exe>\n", argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
const char *exefname = argv[1];
|
||||
FILE *io = fopen(exefname, "rb");
|
||||
if (!io) {
|
||||
fprintf(stderr, "can't open '%s: %s'\n", exefname, strerror(errno));
|
||||
return 2;
|
||||
}
|
||||
|
||||
if (fseek(io, 0, SEEK_END) < 0) {
|
||||
fprintf(stderr, "can't seek in '%s': %s\n", exefname, strerror(errno));
|
||||
return 3;
|
||||
}
|
||||
|
||||
const uint32 exelen = ftell(io);
|
||||
uint8 *exe = (uint8 *) malloc(exelen);
|
||||
if (!exe) {
|
||||
fprintf(stderr, "Out of memory\n");
|
||||
return 4;
|
||||
}
|
||||
|
||||
rewind(io);
|
||||
if (fread(exe, exelen, 1, io) != 1) {
|
||||
fprintf(stderr, "read failure on '%s': %s\n", exefname, strerror(errno));
|
||||
return 5;
|
||||
}
|
||||
|
||||
fclose(io);
|
||||
|
||||
loadExe(exefname, exe, exelen);
|
||||
|
||||
free(exe);
|
||||
|
||||
return 0;
|
||||
} // main
|
||||
|
||||
// end of lx_loader.c ...
|
||||
|
||||
+95
@@ -0,0 +1,95 @@
|
||||
#ifndef _INCL_LX_LOADER_H_
|
||||
#define _INCL_LX_LOADER_H_ 1
|
||||
|
||||
#define _GNU_SOURCE 1
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
typedef uint8_t uint8;
|
||||
typedef uint16_t uint16;
|
||||
typedef uint32_t uint32;
|
||||
|
||||
#pragma pack(push, 1)
|
||||
typedef struct LxHeader
|
||||
{
|
||||
uint8 magic_l;
|
||||
uint8 magic_x;
|
||||
uint8 byte_order;
|
||||
uint8 word_order;
|
||||
uint32 lx_version;
|
||||
uint16 cpu_type;
|
||||
uint16 os_type;
|
||||
uint32 module_version;
|
||||
uint32 module_flags;
|
||||
uint32 module_num_pages;
|
||||
uint32 eip_object;
|
||||
uint32 eip;
|
||||
uint32 esp_object;
|
||||
uint32 esp;
|
||||
uint32 page_size;
|
||||
uint32 page_offset_shift;
|
||||
uint32 fixup_section_size;
|
||||
uint32 fixup_section_checksum;
|
||||
uint32 loader_section_size;
|
||||
uint32 loader_section_checksum;
|
||||
uint32 object_table_offset;
|
||||
uint32 module_num_objects;
|
||||
uint32 object_page_table_offset;
|
||||
uint32 object_iter_pages_offset;
|
||||
uint32 resource_table_offset;
|
||||
uint32 num_resource_table_entries;
|
||||
uint32 resident_name_table_offset;
|
||||
uint32 entry_table_offset;
|
||||
uint32 module_directives_offset;
|
||||
uint32 num_module_directives;
|
||||
uint32 fixup_page_table_offset;
|
||||
uint32 fixup_record_table_offset;
|
||||
uint32 import_module_table_offset;
|
||||
uint32 num_import_mod_entries;
|
||||
uint32 import_proc_table_offset;
|
||||
uint32 per_page_checksum_offset;
|
||||
uint32 data_pages_offset;
|
||||
uint32 num_preload_pages;
|
||||
uint32 non_resident_name_table_offset;
|
||||
uint32 non_resident_name_table_len;
|
||||
uint32 non_resident_name_table_checksum;
|
||||
uint32 auto_ds_object_num;
|
||||
uint32 debug_info_offset;
|
||||
uint32 debug_info_len;
|
||||
uint32 num_instance_preload;
|
||||
uint32 num_instance_demand;
|
||||
uint32 heapsize;
|
||||
} LxHeader;
|
||||
|
||||
typedef struct LxObjectTableEntry
|
||||
{
|
||||
uint32 virtual_size;
|
||||
uint32 reloc_base_addr;
|
||||
uint32 object_flags;
|
||||
uint32 page_table_index;
|
||||
uint32 num_page_table_entries;
|
||||
uint32 reserved;
|
||||
} LxObjectTableEntry;
|
||||
|
||||
typedef struct LxObjectPageTableEntry
|
||||
{
|
||||
uint32 page_data_offset;
|
||||
uint16 data_size;
|
||||
uint16 flags;
|
||||
} LxObjectPageTableEntry;
|
||||
|
||||
typedef struct LxResourceTableEntry
|
||||
{
|
||||
uint16 type_id;
|
||||
uint16 name_id;
|
||||
uint32 resource_size;
|
||||
uint16 object;
|
||||
uint32 offset;
|
||||
} LxResourceTableEntry;
|
||||
|
||||
#pragma pack(pop)
|
||||
|
||||
#endif
|
||||
|
||||
// end of lx_loader.h ...
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Note that this doesn't work on Mac OS X because I can't mmap()
|
||||
# address 0x10000, which is the usual base address for OS/2 binaries.
|
||||
# It appears the address is already accessible when main() begins, but no
|
||||
# objects seem to be loaded there. I don't know what it is...maybe some
|
||||
# thing allocated by the C runtime at startup? Have to solve that somehow or
|
||||
# give up on Mac support.
|
||||
#
|
||||
# 32-bit Linux totally lets me have that space, though. :)
|
||||
#clang -pagezero_size 0x1000 -image_base 0x100000000 -Wall -O0 -ggdb3 -o lx_loader lx_loader.c && ./lx_loader ./cmd.exe
|
||||
|
||||
gcc -std=c99 -Wall -O0 -ggdb3 -o lx_dump lx_dump.c &&
|
||||
gcc -m32 -std=c99 -Wall -O0 -ggdb3 -o lx_loader lx_loader.c &&
|
||||
./lx_dump tests/hello.exe &&
|
||||
./lx_loader tests/hello.exe
|
||||
BIN
Binary file not shown.
Binary file not shown.
+1955
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,33 @@
|
||||
; This code is roughly from an example at:
|
||||
; http://www.os2world.com/wiki/index.php/OS/2_Miniaturization_Contest
|
||||
;
|
||||
; Assembled/linked with OpenWatcom:
|
||||
;
|
||||
; wasm hello.asm
|
||||
; wlink file hello lib os2386 option st=32k
|
||||
;
|
||||
; --ryan.
|
||||
|
||||
.386p
|
||||
|
||||
EXTRN DosPutMessage:BYTE
|
||||
|
||||
_DATA SEGMENT BYTE PUBLIC USE32 'STACK'
|
||||
_msg:
|
||||
DB "Hello world! I'm a 32-bit OS/2 binary!", 0aH
|
||||
_DATA ENDS
|
||||
|
||||
_TEXT SEGMENT BYTE PUBLIC USE32 'CODE'
|
||||
ASSUME CS:_TEXT, DS:_TEXT, SS:_TEXT
|
||||
|
||||
startup:
|
||||
push offset flat:_msg
|
||||
push 40
|
||||
push 1
|
||||
call near ptr flat:DosPutMessage
|
||||
add esp,0CH
|
||||
ret
|
||||
_TEXT ENDS
|
||||
|
||||
END startup
|
||||
|
||||
Binary file not shown.
Reference in new issue
Block a user