Initial add. Runs exactly one program.

Run make.sh on a Linux box to see it run tests/hello.exe!
This commit is contained in:
Ryan C. Gordon committed 2016-09-19 19:41:11 -04:00
commit 161cde892e
10 files changed
+3236

No files matched your search

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