#include #include #include #include "stone.h" #if stone_omf typedef unsigned char stone_uint8; typedef unsigned short stone_uint16; typedef unsigned int stone_uint32; typedef unsigned long stone_ulong; struct omf_segment { stone_ulong off; stone_ulong len; }; static stone_uint16 fgetw (FILE *file) { stone_uint8 a, b; a = fgetc (file); b = fgetc (file); return a | (b << 8); } int stone_load_omf (FILE *file, char *filename) { #define defy(CALL) do { if ((CALL)) { stone_report ("%s", #CALL); return defeat (); } } while (0) int nseg = 0; struct omf_segment *seg = NULL; stone_object obj; stone_uint8 type; stone_uint16 length; stone_ulong end; int cseg = -1; int c; int defeat (void) { stone_free_obj (&obj); if (seg != NULL) free (seg); seg = NULL; nseg = 0; return -1; } stone_clear_obj (&obj); defy (fseek (file, 0, SEEK_SET) != 0); type = fgetc (file); length = fgetw (file); if (type != 0x80) return -1; defy ((obj.src = strdup (filename)) == NULL); while (1) { end = ftell (file) + length; switch (type) { case 0x8B: /* End */ goto finish; case 0x8C: /* Extern */ { int namelength; stone_object_sym *sym; while (1) { if ((stone_ulong) ftell (file) >= end - 1) break; namelength = fgetc (file); defy (stone_resize (obj.sym, obj.nsym + 1) == NULL); sym = &obj.sym [obj.nsym ++]; defy ((sym->name = malloc (namelength + 1)) == NULL); defy ((int) fread (sym->name, 1, namelength, file) != namelength); sym->name [namelength] = '\0'; sym->obj = -1; sym->sym = -1; sym->type = csst_import; sym->off = 0; fgetc (file); } break; } case 0x90: /* Public */ { stone_uint8 groupindex; stone_uint8 segmentindex; stone_object_sym *sym; groupindex = fgetc (file); segmentindex = fgetc (file) - 1; defy (segmentindex >= nseg); while (1) { stone_uint8 namelength; stone_uint16 offset; stone_uint8 type; if ((stone_ulong) ftell (file) >= end - 1) break; namelength = fgetc (file); defy (stone_resize (obj.sym, obj.nsym + 1) == NULL); sym = &obj.sym [obj.nsym ++]; defy ((sym->name = malloc (namelength + 1)) == NULL); defy (fread (sym->name, 1, namelength, file) != namelength); sym->name [namelength] = '\0'; offset = fgetw (file); type = fgetc (file); defy (offset >= seg [segmentindex].len); sym->obj = -1; sym->sym = -1; sym->type = csst_export; sym->off = offset + seg [segmentindex].off; } break; } case 0x98: /* Segment */ { stone_uint8 attrib = fgetc (file); stone_uint16 length = fgetw (file); stone_uint8 nameindex = fgetc (file); stone_uint8 classindex = fgetc (file); stone_uint8 overlayindex = fgetc (file); (void) attrib, (void) nameindex, (void) classindex, (void) overlayindex; defy (stone_resize (seg, nseg + 1) == NULL); seg [nseg].off = nseg > 0 ? seg [nseg - 1].off + seg [nseg - 1].len : 0; seg [nseg].len = length; stone_resize (obj.code, obj.len + length); memset (obj.code + obj.len, 0, length); obj.len = seg [nseg].off + seg [nseg].len; nseg ++; break; } case 0x9D: /* Relocations */ defy (cseg == -1); while (ftell (file) < (int) end - 1) { stone_object_rel *rel; stone_uint8 type; stone_uint16 locat; stone_uint8 attrib; stone_uint8 symindex; type = fgetc (file); defy (!(type & 128)); locat = (type << 8) | fgetc (file); attrib = fgetc (file); symindex = fgetc (file) - 1; stone_resize (obj.rel, obj.nrel + 1); rel = &obj.rel [obj.nrel ++]; rel->off = (locat & 1023) + seg [cseg].off; defy (rel->off + 4 >= obj.len); rel->sym = symindex; defy (symindex >= obj.nsym); rel->type = csr_relative2; rel->base = *(int *) &obj.code [rel->off] - 4; } break; case 0xA0: /* Segment data */ { stone_uint8 segmentindex; stone_uint16 offset; segmentindex = fgetc (file) - 1; offset = fgetw (file); defy (segmentindex >= nseg); defy (offset + length - 4 > (int) seg [segmentindex].len); defy ((int) fread (obj.code + seg [segmentindex].off + offset, 1, length - 4, file) != length - 4); cseg = segmentindex; break; } case 0xA2: /* Iterated data */ { stone_uint8 segmentindex; stone_uint16 offset; stone_uint16 length; segmentindex = fgetc (file) - 1; offset = fgetw (file); length = fgetw (file); if (fgetc (file) != 0x01 || fgetc (file) != 0x00 || fgetc (file) != 0x01 || fgetc (file) != 0x00 || fgetc (file) != 0x00 || fgetc (file) != 0x00 || fgetc (file) != 0x01 || fgetc (file) != 0x00) { stone_report ("iterated data must be zero type"); return defeat (); } cseg = segmentindex; defy (cseg >= nseg); defy (offset + length > (int) seg [cseg].len); memset (obj.code + seg [cseg].off + offset, 0, length); break; } case 0x80: /* Source filename (Translator's header) */ case 0x88: /* Comment */ case 0x96: /* Names */ case 0x9A: /* Group */ break; default: stone_report ("%02Xh %d - unknown record type", type, length); return defeat (); } defy (fseek (file, end, SEEK_SET) != 0); type = fgetc (file); length = fgetw (file); if (feof (file)) break; } finish: for (c = 1; c < stone_nobj; c ++) if (stone_obj [c].user <= 0) break; if (c >= stone_nobj) { stone_nobj ++; defy (stone_resize (stone_obj, stone_nobj) == NULL); } stone_obj [c] = obj; return c; } #endif /* stone_omf */