commit 161cde892edc68e07964830775b7ed7642ff5e91 Author: Ryan C. Gordon Date: Mon Sep 19 19:41:11 2016 -0400 Initial add. Runs exactly one program. Run make.sh on a Linux box to see it run tests/hello.exe! diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..f4d2027 --- /dev/null +++ b/LICENSE.txt @@ -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 + diff --git a/lx_dump.c b/lx_dump.c new file mode 100644 index 0000000..9074b2b --- /dev/null +++ b/lx_dump.c @@ -0,0 +1,503 @@ +#include +#include +#include +#include +#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 \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 ... diff --git a/lx_loader.c b/lx_loader.c new file mode 100644 index 0000000..da81a1e --- /dev/null +++ b/lx_loader.c @@ -0,0 +1,611 @@ +#define _GNU_SOURCE 1 + +#include +#include +#include +#include +#include +#include +#include +#include +#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 \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 ... + diff --git a/lx_loader.h b/lx_loader.h new file mode 100644 index 0000000..7396b67 --- /dev/null +++ b/lx_loader.h @@ -0,0 +1,95 @@ +#ifndef _INCL_LX_LOADER_H_ +#define _INCL_LX_LOADER_H_ 1 + +#define _GNU_SOURCE 1 + +#include + +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 ... + diff --git a/make.sh b/make.sh new file mode 100755 index 0000000..17c9130 --- /dev/null +++ b/make.sh @@ -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 diff --git a/research/1996-10-09_Rel_10_IBM_OS2_16-32-BIT_OBJECT_MODULE_FORMAT_(OMF)_AND_LINEAR_EXECUTABLE_MODULE_FORMAT_(LX)-lx386.pdf b/research/1996-10-09_Rel_10_IBM_OS2_16-32-BIT_OBJECT_MODULE_FORMAT_(OMF)_AND_LINEAR_EXECUTABLE_MODULE_FORMAT_(LX)-lx386.pdf new file mode 100644 index 0000000..eafc022 Binary files /dev/null and b/research/1996-10-09_Rel_10_IBM_OS2_16-32-BIT_OBJECT_MODULE_FORMAT_(OMF)_AND_LINEAR_EXECUTABLE_MODULE_FORMAT_(LX)-lx386.pdf differ diff --git a/research/The Effect of Compression on Performance in a Demand Paging System.pdf b/research/The Effect of Compression on Performance in a Demand Paging System.pdf new file mode 100644 index 0000000..645a7c6 Binary files /dev/null and b/research/The Effect of Compression on Performance in a Demand Paging System.pdf differ diff --git a/research/lxexe.txt b/research/lxexe.txt new file mode 100644 index 0000000..77cc5ec --- /dev/null +++ b/research/lxexe.txt @@ -0,0 +1,1955 @@ +#: 142 S0/CompuServe Mail + 30-Jul-92 05:58 PDT +Sb: Linear eXecutable Module Format Documentation +Fm: INTERNET:jwh@citi.umich.edu +Sender: jwh@citi.umich.edu +Received: from citi.umich.edu by iha.compuserve.com (5.65/5.910516) + id AA03573; Thu, 30 Jul 92 08:51:20 -0400 +Message-Id: <9207301251.AA03573@iha.compuserve.com> +Received: from citi.umich.edu [141.211.128.175] by citi.umich.edu with SMTP; +Thu, 30 Jul 92 08:50:12 -0400 +From: jwh@citi.umich.edu +To: 72251.750@compuserve.com +Date: Thu, 30 Jul 92 08:50:10 -0400 +Subject: Linear eXecutable Module Format Documentation + + +A couple of months ago you were kind enough to send me a copy of the +LX EXE header documentation that you had. I thought I would return +the favor. I recently received a later copy of the document via e-mail +from a person at IBM. I was told that I could distribute this +document to whoever I chose. + +------- Forwarded Message + +Received: from vnet.ibm.com [192.124.113.4] by +citi.umich.edu with SMTP; Fri, 17 Jul 92 18:20:28 -0400 +Received: from AUSTIN by vnet.ibm.com (IBM VM SMTP V2R2) with BSMTP id 2715; + Fri, 17 Jul 92 18:19:51 EDT +Received: by AUSTIN (XAGENTA 3.0) id 0659; Fri, 17 Jul 1992 14:04:19 -0500 +Date: Fri, 17 Jul 92 14:02:20 CDT +From: "Alan Stephens (512) 838-0071 (t/l 678)" +To: jwh@citi.umich.edu +Subject: Linear eXecutable Module Format + +James, + +I've found the owner of the document, and he sent a current copy to me. I've +attached it to the bottom of my note (following my signature). Please feel +free to distribute this to whomever you choose. I hope I've been able to help. + +Regards, + Alan + +- +------------------------------------------------------------------------------ +Alan Stephens Surface Mail: IBM Austin +IBM Personal Systems Programming Ctr. Bldg. 901, ZIP 9151 +VNet: STEPHENS at AUSTIN 11400 Burnet Rd. +IBMMail: USIB37RD at IBMMAIL Austin, TX 78758 +Internet: stephens@vnet.ibm.com, or phone: (512) 838-0071 (t/l 678) + apsteph@cs.utexas.edu +- -----8<---8<---Cut +Here---8<---8<--------------------------------------------- + + LX - Linear eXecutable Module Format Description + + June 3, 1992 + + + + Figure 1. 32-bit Linear EXE File Layout + + 00h +------------------+ <--+ + | DOS 2 Compatible | | + | EXE Header | | + 1Ch +------------------+ | + | unused | | + +------------------+ | + 24h | OEM Identifier | | + 26h | OEM Info | | + | | |-- DOS 2.0 Section + 3Ch | Offset to | | (Discarded) + | Linear EXE | | + | Header | | + 40h +------------------+ | + | DOS 2.0 Stub | | + | Program & | | + | Reloc. Table | | + +------------------+ <--+ + | | + xxh +------------------+ <--+ + | Executable | | + | Info | | + +------------------+ | + | Module | | + | Info | | + +------------------+ |-- Linear Executable + | Loader Section | | Module Header + | Info | | (Resident) + +------------------+ | + | Table Offset | | + | Info | | + +------------------+ <--+ + | Object Table | | + +------------------+ | + | Object Page Table| | + +------------------+ | + | Resource Table | | + +------------------+ | + | Resident Name | | + | Table | | + +------------------+ |-- Loader Section + | Entry Table | | (Resident) + +------------------+ | + | Module Format | | + | Directives Table | | + | (Optional) | | + +------------------+ | + | Resident | | + | Directives Data | | + | (Optional) | | + | | | + | (Verify Record) | | + +------------------+ | + | Per-Page | | + | Checksum | | + +------------------+ <--+ + | Fixup Page Table | | + +------------------+ | + | Fixup Record | | + | Table | | + +------------------+ |-- Fixup Section + | Import Module | | (Optionally Resident) + | Name Table | | + +------------------+ | + | Import Procedure | | + | Name Table | | + +------------------+ <--+ + | Preload Pages | | + +------------------+ | + | Demand Load | | + | Pages | | + +------------------+ | + | Iterated Pages | | + +------------------+ | + | Non-Resident | |-- (Non-Resident) + | Name Table | | + +------------------+ | + | Non-Resident | | + | Directives Data | | + | (Optional) | | + | | | + | (To be Defined) | | + +------------------+ <--+ + | Debug Info | |-- (Not used by Loader) + +------------------+ <--+ + + + + Figure 2. 32-bit Linear EXE Header + + +-----+-----+-----+-----+-----+-----+-----+-----+ + 00h | "L" "X" |B-ORD|W-ORD| FORMAT LEVEL | + +-----+-----+-----+-----+-----+-----+-----+-----+ + 08h | CPU TYPE | OS TYPE | MODULE VERSION | + +-----+-----+-----+-----+-----+-----+-----+-----+ + 10h | MODULE FLAGS | MODULE # OF PAGES | + +-----+-----+-----+-----+-----+-----+-----+-----+ + 18h | EIP OBJECT # | EIP | + +-----+-----+-----+-----+-----+-----+-----+-----+ + 20h | ESP OBJECT # | ESP | + +-----+-----+-----+-----+-----+-----+-----+-----+ + 28h | PAGE SIZE | PAGE OFFSET SHIFT | + +-----+-----+-----+-----+-----+-----+-----+-----+ + 30h | FIXUP SECTION SIZE | FIXUP SECTION CHECKSUM| + +-----+-----+-----+-----+-----+-----+-----+-----+ + 38h | LOADER SECTION SIZE |LOADER SECTION CHECKSUM| + +-----+-----+-----+-----+-----+-----+-----+-----+ + 40h | OBJECT TABLE OFF | # OBJECTS IN MODULE | + +-----+-----+-----+-----+-----+-----+-----+-----+ + 48h | OBJECT PAGE TABLE OFF | OBJECT ITER PAGES OFF | + +-----+-----+-----+-----+-----+-----+-----+-----+ + 50h | RESOURCE TABLE OFFSET |#RESOURCE TABLE ENTRIES| + +-----+-----+-----+-----+-----+-----+-----+-----+ + 58h | RESIDENT NAME TBL OFF | ENTRY TABLE OFFSET | + +-----+-----+-----+-----+-----+-----+-----+-----+ + 60h | MODULE DIRECTIVES OFF | # MODULE DIRECTIVES | + +-----+-----+-----+-----+-----+-----+-----+-----+ + 68h | FIXUP PAGE TABLE OFF |FIXUP RECORD TABLE OFF | + +-----+-----+-----+-----+-----+-----+-----+-----+ + 70h | IMPORT MODULE TBL OFF | # IMPORT MOD ENTRIES | + +-----+-----+-----+-----+-----+-----+-----+-----+ + 78h | IMPORT PROC TBL OFF | PER-PAGE CHECKSUM OFF | + +-----+-----+-----+-----+-----+-----+-----+-----+ + 80h | DATA PAGES OFFSET | #PRELOAD PAGES | + +-----+-----+-----+-----+-----+-----+-----+-----+ + 88h | NON-RES NAME TBL OFF | NON-RES NAME TBL LEN | + +-----+-----+-----+-----+-----+-----+-----+-----+ + 90h | NON-RES NAME TBL CKSM | AUTO DS OBJECT # | + +-----+-----+-----+-----+-----+-----+-----+-----+ + 98h | DEBUG INFO OFF | DEBUG INFO LEN | + +-----+-----+-----+-----+-----+-----+-----+-----+ + A0h | #INSTANCE PRELOAD | #INSTANCE DEMAND | + +-----+-----+-----+-----+-----+-----+-----+-----+ + A8h | HEAPSIZE | + +-----+-----+-----+-----+ + + Note: The OBJECT ITER PAGES OFF must either be 0 or set to the + same value as DATA PAGES OFFSET in OS/2 2.0. Ie., iterated pages are + required to be in the same section of the file as regular pages. + + Note: Table offsets in the Linear EXE Header may be set to + zero to indicate that the table does not exist in the EXE + file and it's size is zero. + + "L" "X" = DW Signature word. + The signature word is used by the loader to identify + the EXE file as a valid 32-bit Linear Executable + Module Format. "L" is low order byte. "X" is high + order byte. + + B-ORD = DB Byte Ordering. + This byte specifies the byte ordering for the linear + EXE format. The values are: + + 00H - Little Endian Byte Ordering. + 01H - Big Endian Byte Ordering. + + W-ORD = DB Word Ordering. + This byte specifies the Word ordering for the linear + EXE format. The values are: + + 00H - Little Endian Word Ordering. + 01H - Big Endian Word Ordering. + + Format Level = DD Linear EXE Format Level. + The Linear EXE Format Level is set to 0 for the + initial version of the 32-bit linear EXE format. + Each incompatible change to the linear EXE format + must increment this value. This allows the system + to recognized future EXE file versions so that an + appropriate error message may be displayed if an + attempt is made to load them. + + CPU Type = DW Module CPU Type. + This field specifies the type of CPU required by + this module to run. The values are: + + 01H - 80286 or upwardly compatible CPU is + required to execute this module. + 02H - 80386 or upwardly compatible CPU is + required to execute this module. + 03H - 80486 or upwardly compatible CPU is + required to execute this module. + + OS Type = DW Module OS Type. + This field specifies the type of Operating system + required to run this module. The currently defined + values are: + + 00H - Unknown (any "new-format" OS) + 01H - OS/2 (default) + 02H - Windows + 03H - DOS 4.x + 04H - Windows 386 + + MODULE VERSION = DD Version of the linear EXE module. + This is useful for differentiating between revisions + of dynamic linked modules. This value is specified + at link time by the user. + + MODULE FLAGS = DD Flag bits for the module. + The module flag bits have the following definitions. + + 00000001h = Reserved for system use. + 00000002h = Reserved for system use. + 00000004h = Per-Process Library Initialization. + The setting of this bit requires the EIP + Object # and EIP fields to have valid + values. If the EIP Object # and EIP fields + are valid and this bit is NOT set, then + Global Library Initialization is assumed. + Setting this bit for an EXE file is invalid. + + 00000008h = Reserved for system use. + 00000010h = Internal fixups for the module have + been applied. + The setting of this bit in a Linear + Executable Module indicates that each object + of the module has a preferred load address + specified in the Object Table Reloc Base + Addr. If the module's objects can not be + loaded at these preferred addresses, then + the relocation records that have been + retained in the file data will be applied. + + 00000020h = External fixups for the module have + been applied. + 00000040h = Reserved for system use. + 00000080h = Reserved for system use. + 00000100h = Incompatible with PM windowing. + 00000200h = Compatible with PM windowing. + 00000300h = Uses PM windowing API. + 00000400h = Reserved for system use. + 00000800h = Reserved for system use. + 00001000h = Reserved for system use. + 00002000h = Module is not loadable. + When the 'Module is not loadable' flag is + set, it indicates that either errors were + detected at link time or that the module is + being incrementally linked and therefore + can't be loaded. + + 00004000h = Reserved for system use. + 00038000h = Module type mask. + 00000000h = Program module. + A module can not contain dynamic links to + other modules that have the 'program module' + type. + + 00008000h = Library module. + 00018000h = Protected Memory Library module. + 00020000h = Physical Device Driver module. + 00028000h = Virtual Device Driver module. + 40000000h = Per-process Library Termination. + The setting of this bit requires the EIP + Object # and EIP fields to have valid + values. If the EIP Object # and EIP fields + are valid and this bit is NOT set, then + Global Library Termination is assumed. + Setting this bit for an EXE file is invalid. + + MODULE # PAGES = DD Number of pages in module. + + This field specifies the number of pages physically + contained in this module. In other words, pages + containing either enumerated or iterated data, or + zero-fill pages that have relocations, not invalid + or zero-fill pages implied by the Virtual Size in + the Object Table being larger than the number of + pages actually in the linear EXE file. These pages + are contained in the 'preload pages', 'demand load + pages' and 'iterated data pages' sections of the + linear EXE module. This is used to determine the + size of the page information tables in the linear + EXE module. + + EIP OBJECT # = DD The Object number to which the Entry + Address is relative. + This specifies the object to which the Entry Address + is relative. This must be a nonzero value for a + program module to be correctly loaded. A zero value + for a library module indicates that no library entry + routine exists. If this value is zero, then both + the Per-process Library Initialization bit and the + Per-process Library Termination bit must be clear in + the module flags, or else the loader will fail to + load the module. Further, if the Per-process + Library Termination bit is set, then the object to + which this field refers must be a 32-bit object + (i.e., the Big/Default bit must be set in the object + flags; see below). + + EIP = DD Entry Address of module. + The Entry Address is the starting address for + program modules and the library initialization and + Library termination address for library modules. + + ESP OBJECT # = DD The Object number to which the ESP is + relative. + This specifies the object to which the starting ESP + is relative. This must be a nonzero value for a + program module to be correctly loaded. This field + is ignored for a library module. + + ESP = DD Starting stack address of module. + The ESP defines the starting stack pointer address + for program modules. A zero value in this field + indicates that the stack pointer is to be + initialized to the highest address/offset in the + object. This field is ignored for a library module. + + PAGE SIZE = DD The size of one page for this system. + This field specifies the page size used by the + linear EXE format and the system. For the initial + version of this linear EXE format the page size is + 4Kbytes. (The 4K page size is specified by a value + of 4096 in this field.) + + PAGE OFFSET SHIFT = DD The shift left bits for page + offsets. + This field gives the number of bit positions to + shift left when interpreting the Object Page Table + entries' page offset field. This determines the + alignment of the page information in the file. For + example, a value of 4 in this field would align all + pages in the Data Pages and Iterated Pages sections + on 16 byte (paragraph) boundaries. A Page Offset + Shift of 9 would align all pages on a 512 byte (disk + sector) basis. The default value for this field is + 12 (decimal), which give a 4096 byte alignment. All + other offsets are byte aligned. + + FIXUP SECTION SIZE = DD Total size of the fixup + information in bytes. + This includes the following 4 tables: + + - Fixup Page Table + - Fixup Record Table + - Import Module name Table + - Import Procedure Name Table + + FIXUP SECTION CHECKSUM = DD Checksum for fixup + information. + This is a cryptographic checksum covering all of the + fixup information. The checksum for the fixup + information is kept separate because the fixup data + is not always loaded into main memory with the + 'loader section'. If the checksum feature is not + implemented, then the linker will set these fields + to zero. + + LOADER SECTION SIZE = DD Size of memory resident + tables. + This is the total size in bytes of the tables + required to be memory resident for the module, while + the module is in use. This total size includes all + tables from the Object Table down to and including + the Per-Page Checksum Table. + + LOADER SECTION CHECKSUM = DD Checksum for loader + section. + This is a cryptographic checksum covering all of the + loader section information. If the checksum feature + is not implemented, then the linker will set these + fields to zero. + + OBJECT TABLE OFF = DD Object Table offset. + This offset is relative to the beginning of the + linear EXE header. + + # OBJECTS IN MODULE = DD Object Table Count. + This defines the number of entries in Object Table. + + OBJECT PAGE TABLE OFFSET = DD Object Page Table offset + This offset is relative to the beginning of the + linear EXE header. + + OBJECT ITER PAGES OFF = DD Object Iterated Pages + offset. + This offset is relative to the beginning of the EXE + file. + + RESOURCE TABLE OFF = DD Resource Table offset. + This offset is relative to the beginning of the + linear EXE header. + + # RESOURCE TABLE ENTRIES = DD Number of entries in + Resource Table. + + RESIDENT NAME TBL OFF = DD Resident Name Table offset. + This offset is relative to the beginning of the + linear EXE header. + + ENTRY TBL OFF = DD Entry Table offset. + This offset is relative to the beginning of the + linear EXE header. + + MODULE DIRECTIVES OFF = DD Module Format Directives + Table offset. + This offset is relative to the beginning of the + linear EXE header. + + # MODULE DIRECTIVES = DD Number of Module Format + Directives in the Table. + This field specifies the number of entries in the + Module Format Directives Table. + + FIXUP PAGE TABLE OFF = DD Fixup Page Table offset. + This offset is relative to the beginning of the + linear EXE header. + + FIXUP RECORD TABLE OFF = DD Fixup Record Table Offset + This offset is relative to the beginning of the + linear EXE header. + + IMPORT MODULE TBL OFF = DD Import Module Name Table + offset. + This offset is relative to the beginning of the + linear EXE header. + + # IMPORT MOD ENTRIES = DD The number of entries in the + Import Module Name Table. + + IMPORT PROC TBL OFF = DD Import Procedure Name Table + offset. + This offset is relative to the beginning of the + linear EXE header. + + PER-PAGE CHECKSUM OFF = DD Per-Page Checksum Table + offset. + This offset is relative to the beginning of the + linear EXE header. + + DATA PAGES OFFSET = DD Data Pages Offset. + This offset is relative to the beginning of the EXE + file. + + # PRELOAD PAGES = DD Number of Preload pages for this + module. Note that OS/2 2.0 does not respect the preload + of pages as specified in the executable file for performance + reasons. + + NON-RES NAME TBL OFF = DD Non-Resident Name Table + offset. + This offset is relative to the beginning of the EXE + file. + + NON-RES NAME TBL LEN = DD Number of bytes in the + Non-resident name table. + + NON-RES NAME TBL CKSM = DD Non-Resident Name Table + Checksum. + This is a cryptographic checksum of the Non-Resident + Name Table. + + AUTO DS OBJECT # = DD The Auto Data Segment Object + number. + This is the object number for the Auto Data Segment + used by 16-bit modules. This field is supported for + 16-bit compatibility only and is not used by 32-bit + modules. + + DEBUG INFO OFF = DD Debug Information offset. + This offset is relative to the beginning of the + linear EXE header. + + DEBUG INFO LEN = DD Debug Information length. + The length of the debug information in bytes. + + # INSTANCE PRELOAD = DD Instance pages in preload + section. + The number of instance data pages found in the + preload section. + + # INSTANCE DEMAND = DD Instance pages in demand + section. + The number of instance data pages found in the + demand section. + + HEAPSIZE = DD Heap size added to the Auto DS Object. + The heap size is the number of bytes added to the + Auto Data Segment by the loader. This field is + supported for 16-bit compatibility only and is not + used by 32-bit modules. + + + + + + Program (EXE) startup registers and Library entry registers + + + Program startup registers are defined as follows. + + EIP = Starting program entry address. + + ESP = Top of stack address. + + CS = Code selector for base of linear address space. + + DS = ES = SS = Data selector for base of linear + address space. + + FS = Data selector of base of Thread Information + Block (TIB). + + GS = 0. + + EAX = EBX = 0. + + ECX = EDX = 0. + + ESI = EDI = 0. + + EBP = 0. + + [ESP+0] = Return address to routine which calls + DosExit(1,EAX). + + [ESP+4] = Module handle for program module. + + [ESP+8] = Reserved. + + [ESP+12] = Environment data object address. + + [ESP+16] = Command line linear address in + environment data object. + + + Library initialization registers are defined as follows. + + EIP = Library entry address. + + ESP = User program stack. + + CS = Code selector for base of linear address space. + + DS = ES = SS = Data selector for base of linear + address space. + + Note that a 32-bit Protected Memory Library module + will be given a GDT selector in the DS and ES + registers (PROTDS) that addresses the full linear + address space available to a application. This + selector should be saved by the initialization + routine. Non-Protected Memory Library modules will + receive a selector (FLATDS) that addresses the same + amount of linear address space as an application's + .EXE can. + + FS = Data selector of base of Thread Information + Block (TIB). + + GS = 0. + + EAX = EBX = 0. + + ECX = EDX = 0. + + ESI = EDI = 0. + + EBP = 0. + + [ESP+0] = Return address to system, (EAX) = return + code. + + [ESP+4] = Module handle for library module. + + [ESP+8] = 0 (Initialization) + + Note that a 32-bit library may specify that its entry + address is in a 16-bit code object. In this case, the + entry registers are the same as for entry to a library + using the Segmented EXE format. These are documented + elsewhere. This means that a 16-bit library may be + relinked to take advantage of the benefits of the Linear + EXE format (notably, efficient paging). + + + Library termination registers are defined as follows. + + EIP = Library entry address. + + ESP = User program stack. + + CS = Code selector for base of linear address space. + + DS = ES = SS = Data selector for base of linear + address space. + + FS = Data selector of base of Thread Information + Block (TIB). + + GS = 0. + + EAX = EBX = 0. + + ECX = EDX = 0. + + ESI = EDI = 0. + + EBP = 0. + + [ESP+0] = Return address to system. + + [ESP+4] = Module handle for library module. + + [ESP+8] = 1 (Termination) + + Note that Library termination is not allowed for + libraries with 16-bit entries. + + + + + + Object Table + + + The number of entries in the Object Table is given by the # + Objects in Module field in the linear EXE header. Entries + in the Object Table are numbered starting from one. + + Each Object Table entry has the following format: + + +-----+-----+-----+-----+-----+-----+-----+-----+ + 00h | VIRTUAL SIZE | RELOC BASE ADDR | + +-----+-----+-----+-----+-----+-----+-----+-----+ + 08h | OBJECT FLAGS | PAGE TABLE INDEX | + +-----+-----+-----+-----+-----+-----+-----+-----+ + 10h | # PAGE TABLE ENTRIES | RESERVED | + +-----+-----+-----+-----+-----+-----+-----+-----+ + + VIRTUAL SIZE = DD Virtual memory size. + This is the size of the object that will be + allocated when the object is loaded. The object's + virtual size (rounded up to the page size value) + must be greater than or equal to the total size of + the pages in the EXE file for the object. This + memory size must also be large enough to contain all + of the iterated data and uninitialized data in the + EXE file. + + RELOC BASE ADDR = DD Relocation Base Address. + The relocation base address the object is currently + relocated to. If the internal relocation fixups for + the module have been removed, this is the address + the object will be allocated at by the loader. + + OBJECT FLAGS = DW Flag bits for the object. + The object flag bits have the following definitions. + + 0001h = Readable Object. + 0002h = Writable Object. + 0004h = Executable Object. + The readable, writable and executable flags + provide support for all possible + protections. In systems where all of these + protections are not supported, the loader + will be responsible for making the + appropriate protection match for the system. + + 0008h = Resource Object. + 0010h = Discardable Object. + 0020h = Object is Shared. + 0040h = Object has Preload Pages. + 0080h = Object has Invalid Pages. + 0100h = Object has Zero Filled Pages. + 0200h = Object is Resident (valid for VDDs, PDDs + only). + 0300h = Object is Resident & Contiguous (VDDs, + PDDs only). + 0400h = Object is Resident & 'long-lockable' + (VDDs, PDDs only). + 0800h = Reserved for system use. + 1000h = 16:16 Alias Required (80x86 Specific). + 2000h = Big/Default Bit Setting (80x86 + Specific). + The 'big/default' bit , for data segments, + controls the setting of the Big bit in the + segment descriptor. (The Big bit, or B-bit, + determines whether ESP or SP is used as the + stack pointer.) For code segments, this bit + controls the setting of the Default bit in + the segment descriptor. (The Default bit, + or D-bit, determines whether the default + word size is 32-bits or 16-bits. It also + affects the interpretation of the + instruction stream.) + + 4000h = Object is conforming for code (80x86 + Specific). + 8000h = Object I/O privilege level (80x86 + Specific). + Only used for 16:16 Alias Objects. + + PAGE TABLE INDEX = DD Object Page Table Index. + This specifies the number of the first object page + table entry for this object. The object page table + specifies where in the EXE file a page can be found + for a given object and specifies per-page + attributes. + + The object table entries are ordered by logical page + in the object table. In other words the object + table entries are sorted based on the object page + table index value. + + # PAGE TABLE ENTRIES = DD # of object page table + entries for this object. + Any logical pages at the end of an object that do + not have an entry in the object page table + associated with them are handled as zero filled or + invalid pages by the loader. + + When the last logical pages of an object are not + specified with an object page table entry, they are + treated as either zero filled pages or invalid pages + based on the last entry in the object page table for + that object. If the last entry was neither a zero + filled or invalid page, then the additional pages + are treated as zero filled pages. + + RESERVED = DD Reserved for future use. Must be set to + zero. + + + + Object Page Table + + + The Object page table provides information about a logical + page in an object. A logical page may be an enumerated + page, a pseudo page or an iterated page. The structure of + the object page table in conjunction with the structure of + the object table allows for efficient access of a page when + a page fault occurs, while still allowing the physical page + data to be located in the preload page, demand load page or + iterated data page sections in the linear EXE module. The + logical page entries in the Object Page Table are numbered + starting from one. The Object Page Table is parallel to the + Fixup Page Table as they are both indexed by the logical + page number. + + Each Object Page Table entry has the following format: + + 63 32 31 16 15 0 + +-----+-----+-----+-----+-----+-----+-----+-----+ + 00h | PAGE DATA OFFSET | DATA SIZE | FLAGS | + +-----+-----+-----+-----+-----+-----+-----+-----+ + + PAGE DATA OFFSET = DD Offset to the page data in the + EXE file. + This field, when bit shifted left by the PAGE OFFSET + SHIFT from the module header, specifies the offset + from the beginning of the Preload Page section of + the physical page data in the EXE file that + corresponds to this logical page entry. The page + data may reside in the Preload Pages, Demand Load + Pages or the Iterated Data Pages sections. + + If the FLAGS field specifies that this is a + zero-Filled page then the PAGE DATA OFFSET field + will contain a 0. + + If the logical page is specified as an iterated data + page, as indicated by the FLAGS field, then this + field specifies the offset into the Iterated Data + Pages section. + + The logical page number (Object Page Table index), + is used to index the Fixup Page Table to find any + fixups associated with the logical page. + + + DATA SIZE = DW Number of bytes of data for this page. + This field specifies the actual number of bytes that + represent the page in the file. If the PAGE SIZE + field from the module header is greater than the + value of this field and the FLAGS field indicates a + Legal Physical Page, the remaining bytes are to be + filled with zeros. If the FLAGS field indicates an + Iterated Data Page, the iterated data records will + completely fill out the remainder. + + + FLAGS = DW Attributes specifying characteristics of + this logical page. + The bit definitions for this word field follow, + + 00h = Legal Physical Page in the module (Offset + from Preload Page Section). + 01h = Iterated Data Page (Offset from Iterated + Data Pages Section). + 02h = Invalid Page (zero). + 03h = Zero Filled Page (zero). + 04h = Range of Pages. + + + + + Resource Table + + + The resource table is an array of resource table entries. + Each resource table entry contains a type ID and name ID. + These entries are used to locate resource objects contained + in the Object table. The number of entries in the resource + table is defined by the Resource Table Count located in the + linear EXE header. More than one resource may be contained + within a single object. Resource table entries are in a + sorted order, (ascending, by Resource Name ID within the + Resource Type ID). This allows the DosGetResource API + function to use a binary search when looking up a resource + in a 32-bit module instead of the linear search being used + in the current 16-bit module. + + Each resource entry has the following format: + + +-----+-----+-----+-----+ + 00h | TYPE ID | NAME ID | + +-----+-----+-----+-----+ + 04h | RESOURCE SIZE | + +-----+-----+-----+-----+-----+-----+ + 08h | OBJECT | OFFSET | + +-----+-----+-----+-----+-----+-----+ + + + TYPE ID = DW Resource type ID. + The type of resources are: + + BTMP = Bitmap + EMSG = Error message string + FONT = Fonts + + NAME ID = DW An ID used as a name for the resource when + referred to. + + RESOURCE SIZE = DD The number of bytes the resource + consists of. + + OBJECT = DW The number of the object which contains the + resource. + + OFFSET = DD The offset within the specified object + where the resource begins. + + + + + + Resident or Non-resident Name Table Entry + + + The resident and non-resident name tables define the ASCII + names and ordinal numbers for exported entries in the + module. In addition the first entry in the resident name + table contains the module name. These tables are used to + translate a procedure name string into an ordinal number by + searching for a matching name string. The ordinal number is + used to locate the entry point information in the entry + table. + + The resident name table is kept resident in system memory + while the module is loaded. It is intended to contain the + exported entry point names that are frequently dynamicaly + linked to by name. Non-resident names are not kept in + memory and are read from the EXE file when a dynamic link + reference is made. Exported entry point names that are + infrequently dynamicaly linked to by name or are commonly + referenced by ordinal number should be placed in the + non-resident name table. The trade off made for references + by name is performance vs memory usage. + + Import references by name require these tables to be + searched to obtain the entry point ordinal number. Import + references by ordinal number provide the fastest lookup + since the search of these tables is not required. + + The strings are CASE SENSITIVE and are NOT NULL TERMINATED. + + Each name table entry has the following format: + + +-----+-----+-----+-----+ +-----+-----+-----+ + 00h | LEN | ASCII STRING . . . | ORDINAL # | + +-----+-----+-----+-----+ +-----+-----+-----+ + + + LEN = DB String Length. + This defines the length of the string in bytes. A + zero length indicates there are no more entries in + table. The length of each ascii name string is + limited to 127 characters. + + The high bit in the LEN field (bit 7) is defined as + an Overload bit. This bit signifies that additional + information is contained in the linear EXE module + and will be used in the future for parameter type + checking. + + ASCII STRING = DB ASCII String. + This is a variable length string with it's length + defined in bytes by the LEN field. The string is + case case sensitive and is not null terminated. + + ORDINAL # = DW Ordinal number. + The ordinal number in an ordered index into the + entry table for this entry point. + + + + + + Entry Table + + + The entry table contains object and offset information that + is used to resolve fixup references to the entry points + within this module. Not all entry points in the entry table + will be exported, some entry points will only be used within + the module. An ordinal number is used to index into the + entry table. The entry table entries are numbered starting + from one. + + The list of entries are compressed into 'bundles', where + possible. The entries within each bundle are all the same + size. A bundle starts with a count field which indicates + the number of entries in the bundle. The count is followed + by a type field which identifies the bundle format. This + provides both a means for saving space as well as a + mechanism for extending the bundle types. + + The type field allows the definition of 256 bundle types. + The following bundle types will initially be defined: + + Unused Entry. + 16-bit Entry. + 286 Call Gate Entry. + 32-bit Entry. + Forwarder Entry. + + The bundled entry table has the following format: + + +-----+-----+-----+-----+-----+ + 00h | CNT |TYPE | BUNDLE INFO . . . + +-----+-----+-----+-----+-----+ + + + CNT = DB Number of entries. + This is the number of entries in this bundle. + + A zero value for the number of entries identifies + the end of the entry table. There is no further + bundle information when the number of entries is + zero. In other words the entry table is terminated + by a single zero byte. + + TYPE = DB Bundle type. + This defines the bundle type which determines the + contents of the BUNDLE INFO. + + The follow types are defined: + + 00h = Unused Entry. + 01h = 16-bit Entry. + 02h = 286 Call Gate Entry. + 03h = 32-bit Entry. + 04h = Forwarder Entry. + 80h = Parameter Typing Information Present. + This bit signifies that additional + information is contained in the linear + EXE module and will be used in the + future for parameter type checking. + + + The following is the format for each bundle type: + + +-----+-----+ + 00h | CNT |TYPE | + +-----+-----+ + + CNT = DB Number of entries. + This is the number of unused entries to skip. + + TYPE = DB 0 (Unused Entry) + + +-----+-----+-----+-----+ + 00h | CNT |TYPE | OBJECT | + +-----+-----+-----+-----+ + 04h |FLAGS| OFFSET | + +-----+-----+-----+ + 07h | ... | . . . | + + + + + + + + CNT = DB Number of entries. + This is the number of 16-bit entries in this + bundle. The flags and offset value are repeated + this number of times. + + TYPE = DB 1 (16-bit Entry) + + OBJECT = DW Object number. + This is the object number for the entries in + this bundle. + + FLAGS = DB Entry flags. + These are the flags for this entry point. + They have the following definition. + + 01h = Exported entry flag. + F8h = Parameter word count mask. + + OFFSET = DW Offset in object. + This is the offset in the object for the + entry point defined at this ordinal number. + + + +-----+-----+-----+-----+ + 00h | CNT |TYPE | OBJECT | + +-----+-----+-----+-----+-----+ + 04h |FLAGS| OFFSET | CALLGATE | + +-----+-----+-----+-----+-----+ + 09h | ... | . . . | . . . | + + + + + + + + + + CNT = DB Number of entries. + This is the number of 286 call gate entries in + this bundle. The flags, callgate, and offset + value are repeated this number of times. + + TYPE = DB 2 (286 Call Gate Entry) + The 286 Call Gate Entry Point type is needed by + the loader only if ring 2 segments are to be + supported. 286 Call Gate entries contain 2 + extra bytes which are used by the loader to + store an LDT callgate selector value. + + OBJECT = DW Object number. + This is the object number for the entries in + this bundle. + + FLAGS = DB Entry flags. + These are the flags for this entry point. They + have the following definition. + + 01h = Exported entry flag. + F8h = Parameter word count mask. + + OFFSET = DW Offset in object. + This is the offset in the object for the entry + point defined at this ordinal number. + + CALLGATE = DW Callgate selector. + The callgate selector is a reserved field used + by the loader to store a call gate selector + value for references to ring 2 entry points. + When a ring 3 reference to a ring 2 entry point + is made, the callgate selector with a zero + offset is place in the relocation fixup address. + The segment number and offset in segment is + placed in the LDT callgate. + + + +-----+-----+-----+-----+ + 00h | CNT |TYPE | OBJECT | + +-----+-----+-----+-----+-----+ + 04h |FLAGS| OFFSET | + +-----+-----+-----+-----+-----+ + 09h | ... | . . . | + + + + + + + + + CNT = DB Number of entries. + This is the number of 32-bit entries in this + bundle. The flags and offset value are repeated + this number of times. + + TYPE = DB 3 (32-bit Entry) + The 32-bit Entry type will only be defined by + the linker when the offset in the object can not + be specified by a 16-bit offset. + + OBJECT = DW Object number. + This is the object number for the entries in + this bundle. + + FLAGS = DB Entry flags. + These are the flags for this entry point. They + have the following definition. + + 01h = Exported entry flag. + F8h = Parameter dword count mask. + + OFFSET = DD Offset in object. + This is the offset in the object for the entry + point defined at this ordinal number. + + +-----+-----+-----+-----+ + 00h | CNT |TYPE | RESERVED | + +-----+-----+-----+-----+-----+-----+-----+ + 04h |FLAGS| MOD ORD# | OFFSET / ORDNUM | + +-----+-----+-----+-----+-----+-----+-----+ + 09h | ... | ... | ... | + + + + + + + + + + + CNT = DB Number of entries. + This is the number of forwarder entries in this + bundle. The FLAGS, MOD ORD#, and OFFSET/ORDNUM + values are repeated this number of times. + + TYPE = DB 4 (Forwarder Entry) + + RESERVED = DW 0 + This field is reserved for future use. + + FLAGS = DB Forwarder flags. + These are the flags for this entry point. They + have the following definition. + + 01h = Import by ordinal. + F7h = Reserved for future use; should be zero. + + MOD ORD# = DW Module Ordinal Number + This is the index into the Import Module Name Table + for this forwarder. + + OFFSET / ORDNUM = DD Procedure Name Offset or Import + Ordinal Number + If the FLAGS field indicates import by ordinal, then + this field is the ordinal number into the Entry + Table of the target module, otherwise this field is + the offset into the Procedure Names Table of the + target module. + + A Forwarder entry (type = 4) is an entry point whose value + is an imported reference. When a load time fixup occurs + whose target is a forwarder, the loader obtains the address + imported by the forwarder and uses that imported address to + resolve the fixup. + + A forwarder may refer to an entry point in another module + which is itself a forwarder, so there can be a chain of + forwarders. The loader will traverse the chain until it + finds a non-forwarded entry point which terminates the chain + , and use this to resolve the original fixup. Circular + chains are detected by the loader and result in a load time + error. A maximum of 1024 forwarders is allowed in a chain; + more than this results in a load time error. + + Forwarders are useful for merging and recombining API calls + into different sets of libraries, while maintaining + compatibility with applications. For example, if one wanted + to combine MONCALLS, MOUCALLS, and VIOCALLS into a single + libraries, one could provide entry points for the three + libraries that are forwarders pointing to the common + implementation. + + + + Module Format Directives Table + + + The Module Format Directives Table is an optional table that + allows additional options to be specified. It also allows + for the extension of the linear EXE format by allowing + additional tables of information to be added to the linear + EXE module without affecting the format of the linear EXE + header. Likewise, module format directives provide a place + in the linear EXE module for 'temporary tables' of + information, such as incremental linking information and + statistic information gathered on the module. When there + are no module format directives for a linear EXE module, the + fields in the linear EXE header referencing the module + format directives table are zero. + + Each Module Format Directive Table entry has the following + format: + + +-----+-----+-----+-----+-----+-----+----+----+ + 00h | DIRECT # | DATA LEN | DATA OFFSET | + +-----+-----+-----+-----+-----+-----+----+----+ + + DIRECT # = DW Directive number. + The directive number specifies the type of directive + defined. This can be used to determine the format + of the information in the directive data. The + following directive numbers have been defined: + + 8000h = Resident Flag Mask. + Directive numbers with this bit set indicate + that the directive data is in the resident + area and will be kept resident in memory + when the module is loaded. + + 8001h = Verify Record Directive. (Verify record + is a resident table.) + 0002h = Language Information Directive. (This is + a non-resident table.) + 0003h = Co-Processor Required Support Table. + 0004h = Thread State Initialization Directive. + + Additional directives can be added as needed in the + future, as long as they do not overlap previously + defined directive numbers. + + DATA LEN = DW Directive data length. + This specifies the length in byte of the directive + data for this directive number. + + DIRECTIVE OFFSET = DD Directive data offset. + This is the offset to the directive data for this + directive number. It is relative to beginning of + linear EXE header for a resident table, and relative + to the beginning of the EXE file for non-resident + tables. + + + + + + Verify Record Directive Table + + + The Verify Record Directive Table is an optional table. It + maintains a record of the pages in the EXE file that have + been fixed up and written back to the original linear EXE + module, along with the module dependencies used to perform + these fixups. This table provides an efficient means for + verifying the virtual addresses required for the fixed up + pages when the module is loaded. + + Each Verify Record entry has the following format: + + +-----+-----+ + 00h |# OF ENTRY | + +-----+-----+-----+-----+-----+-----+ + 02h | MOD ORD # | VERSION | MOD # OBJ | + +-----+-----+-----+-----+-----+-----+ + 08h | OBJECT # | BASE ADDR | VIRTUAL | + +-----+-----+-----+-----+-----+-----+ + 0Eh | . . . | . . . | . . . | + + + + + + + + + + # OF ENTRY = DW Number of module dependencies. + This field specifies how many entries there are in + the verify record directive table. This is equal to + the number of modules referenced by this module. + + MOD ORD # = DW Ordinal index into the Import Module + Name Table. + This value is an ordered index in to the Import + Module Name Table for the referenced module. + + VERSION = DW Module Version. + + This is the version of the referenced module that + the fixups were originally performed. This is used + to insure the same version of the referenced module + is loaded that was fixed up in this module and + therefore the fixups are still correct. This + requires the version number in a module to be + incremented anytime the entry point offsets change. + + MOD # OBJ = DW Module # of Object Entries. + This field is used to identify the number of object + verify entries that follow for the referenced + module. + + OBJECT # = DW Object # in Module. + This field specifies the object number in the + referenced module that is being verified. + + BASE ADDR = DW Object load base address. + This is the address that the object was loaded at + when the fixups were performed. + + VIRTUAL = DW Object virtual address size. + This field specifies the total amount of virtual + memory required for this object. + + + + + + Per-Page Checksum + + + The Per-Page Checksum table provides space for a + cryptographic checksum for each physical page in the EXE + file. + + The checksum table is arranged such that the first entry in + the table corresponds to the first logical page of code/data + in the EXE file (usually a preload page) and the last entry + corresponds to the last logical page in the EXE file + (usually a iterated data page). + + +-----+-----+-----+-----+ + Logical Page #1 | CHECKSUM | + +-----+-----+-----+-----+ + Logical Page #2 | CHECKSUM | + +-----+-----+-----+-----+ + . . . + + +-----+-----+-----+-----+ + Logical Page #n | CHECKSUM | + +-----+-----+-----+-----+ + + + CHECKSUM = DD Cryptographic checksum. + + + + + + Fixup Page Table + + + The Fixup Page Table provides a simple mapping of a logical + page number to an offset into the Fixup Record Table for + that page. + + This table is parallel to the Object Page Table, except that + there is one additional entry in this table to indicate the + end of the Fixup Record Table. + + The format of each entry is: + + +-----+-----+-----+-----+ + Logical Page #1 | OFFSET FOR PAGE #1 | + +-----+-----+-----+-----+ + Logical Page #2 | OFFSET FOR PAGE #2 | + +-----+-----+-----+-----+ + . . . + +-----+-----+-----+-----+ + Logical Page #n | OFFSET FOR PAGE #n | + +-----+-----+-----+-----+ + |OFF TO END OF FIXUP REC| This is equal to: + +-----+-----+-----+-----+ Offset for page #n + Size + of fixups for page #n + + + OFFSET FOR PAGE # = DD Offset for fixup record for this + page. + This field specifies the offset, from the beginning + of the fixup record table, to the first fixup record + for this page. + + OFF TO END OF FIXUP REC = DD Offset to the end of the + fixup records. + This field specifies the offset following the last + fixup record in the fixup record table. This is the + last entry in the fixup page table. + + The fixup records are kept in order by logical page + in the fixup record table. This allows the end of + each page's fixup records is defined by the offset + for the next logical page's fixup records. This + last entry provides support of this mechanism for + the last page in the fixup page table. + + + + + + Fixup Record Table + + + The Fixup Record Table contains entries for all fixups in + the linear EXE module. The fixup records for a logical page + are grouped together and kept in sorted order by logical + page number. The fixups for each page are further sorted + such that all external fixups and internal selector/pointer + fixups come before internal non-selector/non-pointer fixups. + This allows the loader to ignore internal fixups if the + loader is able to load all objects at the addresses + specified in the object table. + + Each relocation record has the following format: + + +-----+-----+-----+-----+ + 00h | SRC |FLAGS|SRCOFF/CNT*| + +-----+-----+-----+-----+-----+-----+ + 03h/04h | TARGET DATA * | + +-----+-----+-----+-----+-----+-----+ + | SRCOFF1 @ | . . . | SRCOFFn @ | + +-----+-----+---- ----+-----+-----+ + + * These fields are variable size. + @ These fields are optional. + + + SRC = DB Source type. + The source type specifies the size and type of the + fixup to be performed on the fixup source. The + source type is defined as follows: + + 0Fh = Source mask. + 00h = Byte fixup (8-bits). + 01h = (undefined). + 02h = 16-bit Selector fixup (16-bits). + 03h = 16:16 Pointer fixup (32-bits). + 04h = (undefined). + 05h = 16-bit Offset fixup (16-bits). + 06h = 16:32 Pointer fixup (48-bits). + 07h = 32-bit Offset fixup (32-bits). + 08h = 32-bit Self-relative offset fixup + (32-bits). + 10h = Fixup to Alias Flag. + When the 'Fixup to Alias' Flag is set, the + source fixup refers to the 16:16 alias for + the object. This is only valid for source + types of 2, 3, and 6. For fixups such as + this, the linker and loader will be required + to perform additional checks such as + ensuring that the target offset for this + fixup is less than 64K. + + 20h = Source List Flag. + + When the 'Source List' Flag is set, the + SRCOFF field is compressed to a byte and + contains the number of source offsets, and a + list of source offsets follows the end of + fixup record (after the optional additive + value). + + FLAGS = DB Target Flags. + The target flags specify how the target information + is interpreted. The target flags are defined as + follows: + + 03h = Fixup target type mask. + 00h = Internal reference. + 01h = Imported reference by ordinal. + 02h = Imported reference by name. + 03h = Internal reference via entry table. + 04h = Additive Fixup Flag. + When set, an additive value trails the fixup + record (before the optional source offset + list). + + 08h = Reserved. Must be zero. + 10h = 32-bit Target Offset Flag. + When set, the target offset is 32-bits, + otherwise it is 16-bits. + + 20h = 32-bit Additive Fixup Flag. + When set, the additive value is 32-bits, + otherwise it is 16-bits. + + 40h = 16-bit Object Number/Module Ordinal Flag. + When set, the object number or module + ordinal number is 16-bits, otherwise it is + 8-bits. + + 80h = 8-bit Ordinal Flag. + When set, the ordinal number is 8-bits, + otherwise it is 16-bits. + + SRCOFF = DW/CNT = DB Source offset or source offset + list count. + This field contains either an offset or a count + depending on the Source List Flag. If the Source + List Flag is set, a list of source offsets follows + the additive field and this field contains the count + of the entries in the source offset list. + Otherwise, this is the single source offset for the + fixup. Source offsets are relative to the beginning + of the page where the fixup is to be made. + + Note that for fixups that cross page boundaries, a + separate fixup record is specified for each page. + An offset is still used for the 2nd page but it now + becomes a negative offset since the fixup originated + on the preceding page. (For example, if only the + last one byte of a 32-bit address is on the page to + be fixed up, then the offset would have a value of + -3.) + + TARGET DATA = Target data for fixup. + The format of the TARGET DATA is dependent upon + target flags. + + SRCOFF1 - SRCOFFn = DW[] Source offset list. + This list is present if the Source List Flag is set + in the Target Flags field. The number of entries in + the source offset list is defined in the SRCOFF/CNT + field. The source offsets are relative to the + beginning of the page where the fixups are to be + made. + + +-----+-----+-----+-----+ + 00h | SRC |FLAGS|SRCOFF/CNT*| + +-----+-----+-----+-----+-----+-----+ + 03h/04h | OBJECT * | TRGOFF * @ | + +-----+-----+-----+-----+-----+-----+ + | SRCOFF1 @ | . . . | SRCOFFn @ | + +-----+-----+---- ----+-----+-----+ + + * These fields are variable size. + @ These fields are optional. + + + OBJECT = D[B|W] Target object number. + This field is an index into the current module's + Object Table to specify the target Object. It + is a Byte value when the '16-bit Object + Number/Module Ordinal Flag' bit in the target + flags field is clear and a Word value when the + bit is set. + + + TRGOFF = D[W|D] Target offset. + This field is an offset into the specified + target Object. It is not present when the + Source Type specifies a 16-bit Selector fixup. + It is a Word value when the '32-bit Target + Offset Flag' bit in the target flags field is + clear and a Dword value when the bit is set. + + +-----+-----+-----+-----+ + 00h | SRC |FLAGS|SRCOFF/CNT*| + +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+ + 03h/04h | MOD ORD# *| PROCEDURE NAME OFFSET*| ADDITIVE * @ | + +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+ + | SRCOFF1 @ | . . . | SRCOFFn @ | + +-----+-----+---- ----+-----+-----+ + + * These fields are variable size. + @ These fields are optional. + + + MOD ORD # = D[B|W] Ordinal index into the Import + Module Name Table. + This value is an ordered index in to the Import + Module Name Table for the module containing the + procedure entry point. It is a Byte value when + the '16-bit Object Number/Module Ordinal' Flag + bit in the target flags field is clear and a + Word value when the bit is set. The loader + creates a table of pointers with each pointer in + the table corresponds to the modules named in + the Import Module Name Table. This value is + used by the loader to index into this table + created by the loader to locate the referenced + module. + + PROCEDURE NAME OFFSET = D[W|D] Offset into the + Import Procedure Name Table. + This field is an offset into the Import + Procedure Name Table. It is a Word value when + the '32-bit Target Offset Flag' bit in the + target flags field is clear and a Dword value + when the bit is set. + + ADDITIVE = D[W|D] Additive fixup value. + This field exists in the fixup record only when + the 'Additive Fixup Flag' bit in the target + flags field is set. When the 'Additive Fixup + Flag' is clear the fixup record does not contain + this field and is immediately followed by the + next fixup record (or by the source offset list + for this fixup record). + + This value is added to the address derived from + the target entry point. This field is a Word + value when the '32-bit Additive Flag' bit in the + target flags field is clear and a Dword value + when the bit is set. + + +-----+-----+-----+-----+ + 00h | SRC |FLAGS|SRCOFF/CNT*| + +-----+-----+-----+-----+-----+-----+-----+-----+ + 03h/04h | MOD ORD# *|IMPORT ORD*| ADDITIVE * @ | + +-----+-----+-----+-----+-----+-----+-----+-----+ + | SRCOFF1 @ | . . . | SRCOFFn @ | + +-----+-----+---- ----+-----+-----+ + + * These fields are variable size. + @ These fields are optional. + + + MOD ORD # = D[B|W] Ordinal index into the Import + Module Name Table. + This value is an ordered index in to the Import + Module Name Table for the module containing the + procedure entry point. It is a Byte value when + the '16-bit Object Number/Module Ordinal' Flag + bit in the target flags field is clear and a + Word value when the bit is set. The loader + creates a table of pointers with each pointer in + the table corresponds to the modules named in + the Import Module Name Table. This value is + used by the loader to index into this table + created by the loader to locate the referenced + module. + + IMPORT ORD = D[B|W|D] Imported ordinal number. + This is the imported procedure's ordinal number. + It is a Byte value when the '8-bit Ordinal' bit + in the target flags field is set. Otherwise it + is a Word value when the '32-bit Target Offset + Flag' bit in the target flags field is clear and + a Dword value when the bit is set. + + ADDITIVE = D[W|D] Additive fixup value. + This field exists in the fixup record only when + the 'Additive Fixup Flag' bit in the target + flags field is set. When the 'Additive Fixup + Flag' is clear the fixup record does not contain + this field and is immediately followed by the + next fixup record (or by the source offset list + for this fixup record). + + This value is added to the address derived from + the target entry point. This field is a Word + value when the '32-bit Additive Flag' bit in the + target flags field is clear and a Dword value + when the bit is set. + + +-----+-----+-----+-----+ + 00h | SRC |FLAGS|SRCOFF/CNT*| + +-----+-----+-----+-----+-----+-----+ + 03h/04h | ORD # * | ADDITIVE * @ | + +-----+-----+-----+-----+-----+-----+ + | SRCOFF1 @ | . . . | SRCOFFn @ | + +-----+-----+---- ----+-----+-----+ + + * These fields are variable size. + @ These fields are optional. + + ENTRY # = D[B|W] Ordinal index into the Entry + Table. + This field is an index into the current module's + Entry Table to specify the target Object and + offset. It is a Byte value when the '16-bit + Object Number/Module Ordinal' Flag bit in the + target flags field is clear and a Word value + when the bit is set. + + ADDITIVE = D[W|D] Additive fixup value. + This field exists in the fixup record only when + the 'Additive Fixup Flag' bit in the target + flags field is set. When the 'Additive Fixup + Flag' is clear the fixup record does not contain + this field and is immediately followed by the + next fixup record (or by the source offset list + for this fixup record). + + This value is added to the address derived from + the target entry point. This field is a Word + value when the '32-bit Additive Flag' bit in the + target flags field is clear and a Dword value + when the bit is set. + + + + + + Import Module Name Table + + + The import module name table defines the module name strings + imported through dynamic link references. These strings are + referenced through the imported relocation fixups. + + To determine the length of the import module name table + subtract the import module name table offset from the import + procedure name table offset. These values are located in + the linear EXE header. The end of the import module name + table is not terminated by a special character, it is + followed directly by the import procedure name table. + + The strings are CASE SENSITIVE and NOT NULL TERMINATED. + + Each name table entry has the following format: + + +-----+-----+-----+-----+ +-----+ + 00h | LEN | ASCII STRING . . . | + +-----+-----+-----+-----+ +-----+ + + LEN = DB String Length. + This defines the length of the string in bytes. The + length of each ascii name string is limited to 127 + characters. + + ASCII STRING = DB ASCII String. + This is a variable length string with it's length + defined in bytes by the LEN field. The string is + case sensitive and is not null terminated. + + + + + + Import Procedure Name Table + + + The import procedure name table defines the procedure name + strings imported by this module through dynamic link + references. These strings are referenced through the + imported relocation fixups. + + To determine the length of the import procedure name table + add the fixup section size to the fixup page table offset, + this computes the offset to the end of the fixup section, + then subtract the import procedure name table offset. These + values are located in the linear EXE header. The import + procedure name table is followed by the data pages section. + Since the data pages section is aligned on a 'page size' + boundary, padded space may exist between the last import + name string and the first page in the data pages section. + If this padded space exists it will be zero filled. + + The strings are CASE SENSITIVE and NOT NULL TERMINATED. + + Each name table entry has the following format: + + +-----+-----+-----+-----+ +-----+ + 00h | LEN | ASCII STRING . . . | + +-----+-----+-----+-----+ +-----+ + + LEN = DB String Length. + This defines the length of the string in bytes. The + length of each ascii name string is limited to 127 + characters. + + The high bit in the LEN field (bit 7) is defined as + an Overload bit. This bit signifies that additional + information is contained in the linear EXE module + and will be used in the future for parameter type + checking. + + ASCII STRING = DB ASCII String. + This is a variable length string with it's length + defined in bytes by the LEN field. The string is + case sensitive and is not null terminated. + + + + + + Preload Pages + + + The Preload Pages section is an optional section in the + linear EXE module that coalesces a 'preload page set' into a + contiguous section. The preload page set can be defined as + the set of first used pages in the module. The preload page + set can be specified by the application developer or can be + derived by a tool that analyzes the programs memory usage + while it is running. By grouping the preload page set + together, the preload pages can be read from the linear EXE + module with one disk read. + + The structure of the preload pages is no different than if + they were demand loaded. They are non-iterated pages. + Their sizes are determined by the Object Page Table entries + that correspond. If the specified size is less than the + PAGE SIZE field given in the linear EXE module header the + remainder of the page is filled with zeros when loaded. + + All pages begin on a PAGE OFFSET SHIFT boundary from the + base of the preload page section, as specified in the linear + EXE header. The pages are ordered by logical page number + within this section. + +Note: OS/2 2.0 does not respect preload pages. Performance tests +showed that better system performance was obtained by not +respecting the preload request in the executable file. + + + + + Demand Load Pages + + + The Demand Loaded Pages section contains all the + non-iterated pages for a linear EXE module that are not + preloaded. When required, the whole page is loaded into + memory from the module. The characteristics of each of + these pages is specified in the Object Page Table. Every + page begins on a PAGE OFFSET SHIFT boundary aligned offset + from the demand loaded pages base specified in the linear + EXE header. Their sizes are determined by the Object Page + Table entries that correspond. If the specified size is + less than the PAGE SIZE field given in the linear EXE module + header the remainder of the page is filled with zeros when + loaded. The pages are ordered by logical page number within + this section. + + + + + Iterated Data Pages + + + The Iterated Data Pages section contains all the pages for a + linear EXE module that are iterated. When required, the set + of iteration records are loaded into memory from the module + and expanded to reconstitute the page. Every set of + iteration records begins on a PAGE OFFSET SHIFT offset from + the OBJECT ITER PAGES OFF specified in the linear EXE + header. Their sizes are determined by the Object Page Table + entries that correspond. The pages are ordered by logical + page number within this section. + + This record structure is used to describe the iterated data + for an object on a per-page basis. + + +-----+-----+-----+-----+ + 00h |#ITERATIONS|DATA LENGTH| + +-----+-----+-----+-----+-----+ + 04h |DATA BYTES | . . . | ... | + +-----+-----+-----+-----+-----+ + + Figure 19. Object Iterated Data Record (Iteration Record) + + #ITERATIONS = DW Number of iterations. + This specifies the number of times that the data is + replicated. + + DATA LENGTH = DW The size of the data pattern in bytes. + This specifies the number of bytes of data of which + the pattern consists. The maximum size is one half + of the PAGE SIZE (given in the module header). If a + pattern exceeds this value then the data page will + not be condensed into iterated data. + + DATA = DB * DATA LENGTH The Data pattern to be + replicated. + The next iteration record will immediately follow + the last byte of the pattern. The offset of the + next iteration record is easily calculated from the + offset of this record by adding the DATA LENGTH + field and the sizes of the #ITERATIONS and DATA + LENGTH fields. + + + + + + Debug Information + + + The debug information is defined by the debugger and is + not controlled by the linear EXE format or linker. The only + data defined by the linear EXE format relative to the debug + information is it's offset in the EXE file and length in + bytes as defined in the linear EXE header. + + To support multiple debuggers the first word of the debug + information is a type field which determines the format of + the debug information. + + 00h 01h 02h 03h 04h + +-----+-----+-----+-----+-----+-----+-----+-----+ + | 'N' | 'B' | '0' | n | DEBUGGER DATA . . . . + +-----+-----+-----+-----+-----+-----+-----+-----+ + + + TYPE = DB DUP 4 Format type. + This defines the type of debugger data that exists + in the remainder of the debug information. The + signature consists of a string of four (4) ASCII + characters: "NB0" followed by the ASCII + representation for 'n'. The values for 'n' are + defined as follows. + + These format types are defined. + + 00h = 32-bit CodeView debugger format. + 01h = AIX debugger format. + 02h = 16-bit CodeView debugger format. + 04h = 32-bit OS/2 PM debugger (IBM) format. + + DEBUGGER DATA = Debugger specific data. + The format of the debugger data is defined by the + debugger that is being used. + + The values defined for the type field are not + enforced by the system. It is the responsibility + of the linker or debugging tools to follow the + convention for the type field that is defined here. + + + + + +------- End of Forwarded Message + + diff --git a/tests/hello.asm b/tests/hello.asm new file mode 100644 index 0000000..622768b --- /dev/null +++ b/tests/hello.asm @@ -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 + diff --git a/tests/hello.exe b/tests/hello.exe new file mode 100644 index 0000000..143309b Binary files /dev/null and b/tests/hello.exe differ