From: Robert Pengelly Date: Tue, 29 Apr 2025 22:27:35 +0000 (+0100) Subject: Initial OMF object support X-Git-Url: https://git.candlhat.org/?a=commitdiff_plain;h=f2a5e29f77357a5a87413dc20fe85583b106bfc4;p=slink.git Initial OMF object support --- diff --git a/Makefile.p32 b/Makefile.p32 index cadf210..91479e4 100644 --- a/Makefile.p32 +++ b/Makefile.p32 @@ -6,7 +6,7 @@ CC=gcc386 LD=ld386 COPTS=-S -O2 -fno-common -ansi -I. -I./include -I../pdos/pdpclib -I../pdos/src -D__PDOS386__ -D__32BIT__ -D__NOBIVA__ -D__PDOS__ -Wall -Werror -ansi -m32 -pedantic -COBJ=aout.o coff.o elks.o hashtab.o ld.o lib.o link.o map.o mz.o pe.o report.o section.o symbol.o vector.o write7x.o +COBJ=aout.o coff.o elks.o hashtab.o ld.o lib.o link.o map.o mz.o omf.o pe.o report.o section.o symbol.o vector.o write7x.o all: clean slink.exe diff --git a/Makefile.pdw b/Makefile.pdw index 41cf2fe..0799d40 100644 --- a/Makefile.pdw +++ b/Makefile.pdw @@ -6,7 +6,7 @@ CC=gccwin LD=ldwin COPTS=-S -O2 -fno-common -ansi -I. -I./include -I../pdos/pdpclib -I../pdos/src -D__WIN32__ -D__NOBIVA__ -D__PDOS__ -Wall -Werror -ansi -m32 -pedantic -COBJ=aout.o coff.o elks.o hashtab.o ld.o lib.o link.o map.o mz.o pe.o report.o section.o symbol.o vector.o write7x.o +COBJ=aout.o coff.o elks.o hashtab.o ld.o lib.o link.o map.o mz.o omf.o pe.o report.o section.o symbol.o vector.o write7x.o all: clean slink.exe diff --git a/Makefile.std b/Makefile.std index 6a670cc..0d0f811 100644 --- a/Makefile.std +++ b/Makefile.std @@ -4,7 +4,7 @@ LD=pdld COPTS=-S -O2 -fno-common -ansi -I. -I../pdos/pdpclib -D__WIN32__ -D__NOBIVA__ -D__PDOS__ COBJ=aout.obj coff.obj elks.obj hashtab.obj ld.obj lib.obj link.obj map.obj \ - mz.obj pe.obj report.obj section.obj symbol.obj vector.obj write7x.obj + mz.obj omf.obj pe.obj report.obj section.obj symbol.obj vector.obj write7x.obj all: clean slink.exe diff --git a/Makefile.unix b/Makefile.unix index 48f3b0a..17b1945 100644 --- a/Makefile.unix +++ b/Makefile.unix @@ -7,7 +7,7 @@ VPATH := $(SRCDIR) CC := gcc CFLAGS := -D_FILE_OFFSET_BITS=64 -O2 -Wall -Werror -Wextra -ansi -pedantic -std=c90 -CSRC := aout.c coff.c elks.c hashtab.c ld.c lib.c link.c map.c mz.c pe.c report.c section.c symbol.c vector.c write7x.c +CSRC := aout.c coff.c elks.c hashtab.c ld.c lib.c link.c map.c mz.c omf.c pe.c report.c section.c symbol.c vector.c write7x.c ifeq ($(OS), Windows_NT) all: slink.exe diff --git a/Makefile.w32 b/Makefile.w32 index 27c237d..a34c7e2 100644 --- a/Makefile.w32 +++ b/Makefile.w32 @@ -7,7 +7,7 @@ VPATH := $(SRCDIR) CC := gcc CFLAGS := -D_FILE_OFFSET_BITS=64 -O2 -Wall -Werror -Wextra -ansi -pedantic -std=c90 -CSRC := aout.c coff.c elks.c hashtab.c ld.c lib.c link.c map.c mz.c pe.c report.c section.c symbol.c vector.c write7x.c +CSRC := aout.c coff.c elks.c hashtab.c ld.c lib.c link.c map.c mz.c omf.c pe.c report.c section.c symbol.c vector.c write7x.c all: slink.exe diff --git a/elks.h b/elks.h index 6f9b136..668dc23 100644 --- a/elks.h +++ b/elks.h @@ -1,5 +1,5 @@ /****************************************************************************** - * @file aout.h + * @file elks.h *****************************************************************************/ #ifndef _ELKS_H #define _ELKS_H diff --git a/ld.c b/ld.c index 2b1798b..9c3d68e 100644 --- a/ld.c +++ b/ld.c @@ -12,6 +12,7 @@ #include "ld.h" #include "lib.h" #include "mz.h" +#include "omf.h" #include "pe.h" #include "report.h" #include "section.h" @@ -74,6 +75,8 @@ static void read_object_file (const char *filename, unsigned char *data, unsigne read_elks_object (filename, data, data_size); } else if (data[0] == 0x07 && data[1] == 0x01) { read_aout_object (filename, data, data_size); + } else if (data[0] == RECORD_TYPE_THEADR) { + read_omf_object (filename, data, data_size); } else { report_at (program_name, 0, REPORT_ERROR, "%s: unrecognised file format", filename); } diff --git a/omf.c b/omf.c new file mode 100644 index 0000000..cb3cdf1 --- /dev/null +++ b/omf.c @@ -0,0 +1,600 @@ +/****************************************************************************** + * @file omf.c + *****************************************************************************/ +#include +#include +#include + +#include "ld.h" +#include "lib.h" +#include "omf.h" +#include "reloc.h" +#include "report.h" +#include "section.h" +#include "symbol.h" + +static void estimate (void *data, unsigned long data_size, const char *filename, unsigned long *num_segments_p, unsigned long *num_extdefs_p, unsigned long *num_pubdefs_p, unsigned long *num_lnames_p) { + + unsigned char *pos = (unsigned char *) data; + + unsigned char record_type; + unsigned short record_len; + + unsigned char *pubdef_name, *pubdef_name_end; + unsigned char pubdef_name_len; + + unsigned char *extdef_name, *extdef_name_end; + unsigned char extdef_name_len; + + unsigned char *lname, *lname_end; + unsigned char lname_len; + + unsigned long num_segments = 0, num_extdefs = 0, num_pubdefs = 0, num_lnames = 0; + int big_fields; + + while (pos < (unsigned char *) data + data_size) { + + record_type = pos[0]; + + big_fields = record_type & 1; + record_type &= ~1; + + record_len = array_to_integer (pos + 1, 2); + + { + + unsigned char checksum = 0; + unsigned long i; + + for (i = 0; i < (unsigned long) record_len + 3; i++) { + checksum += pos[i]; + } + + if (checksum != 0) { + report_at (program_name, 0, REPORT_WARNING, "%s: invalid checksum", filename); + } + + } + + pos += 3; + + if (record_type == RECORD_TYPE_EXTDEF) { + + extdef_name_end = (extdef_name = pos) + record_len - 1; + + while (extdef_name != extdef_name_end) { + + extdef_name_len = extdef_name[0]; + + if (extdef_name + 1 + extdef_name_len + 1 > extdef_name_end) { + + report_at (program_name, 0, REPORT_FATAL_ERROR, "%s: incorrect extdef string length", filename); + exit (EXIT_FAILURE); + + } + + num_extdefs++; + extdef_name = extdef_name + 1 + extdef_name_len + 1; + + } + + } else if (record_type == RECORD_TYPE_PUBDEF) { + + pubdef_name_end = (pubdef_name = pos) + record_len - 3; + + if (big_fields) { + + report_at (program_name, 0, REPORT_INTERNAL_ERROR, "%s: big fields not supported for record %#x", filename, record_type); + exit (EXIT_FAILURE); + + } + + while (pubdef_name != pubdef_name_end) { + + pubdef_name_len = pubdef_name[2]; + + if (pubdef_name + 2 + 1 + pubdef_name_len + 1 > pubdef_name_end) { + + report_at (program_name, 0, REPORT_FATAL_ERROR, "%s: incorrect pubdef string length", filename); + exit (EXIT_FAILURE); + + } + + num_pubdefs++; + pubdef_name = pubdef_name + 2 + 1 + pubdef_name_len + 1; + + } + + } else if (record_type == RECORD_TYPE_LNAMES) { + + lname_end = (lname = pos) + record_len - 1; + + while (lname != lname_end) { + + lname_len = lname[0]; + + if (lname + 1 + lname_len > lname_end) { + + report_at (program_name, 0, REPORT_FATAL_ERROR, "%s: incorrect lname string length", filename); + exit (EXIT_FAILURE); + + } + + num_lnames++; + lname = lname + 1 + lname_len; + + } + + } else if (record_type == RECORD_TYPE_SEGDEF) { + num_segments++; + } + + pos += record_len; + + } + + *num_segments_p = num_segments; + *num_extdefs_p = num_extdefs; + *num_pubdefs_p = num_pubdefs; + *num_lnames_p = num_lnames; + +} + + +void read_omf_object (const char *filename, unsigned char *data, unsigned long data_size) { + + unsigned char *pos = (unsigned char *) data; + + unsigned char base_segment_index; + unsigned short public_offset; + + struct { + + struct section_part *part; + unsigned long offset, size; + + } prev_ledata = { 0 }; + + char **lnames; + int big_fields; + + unsigned char record_type; + unsigned short record_len; + + unsigned char *pubdef_name, *pubdef_name_end; + unsigned char pubdef_name_len; + + unsigned char *extdef_name, *extdef_name_end; + unsigned char extdef_name_len; + + unsigned char *lname, *lname_end; + unsigned char lname_len; + + unsigned long num_segments, num_extdefs, num_pubdefs, num_lnames; + unsigned long i_segments, i_extdefs, i_pubdefs, i_lnames; + + struct section_part **part_p_array, *part; + struct section *section; + + struct object_file *of; + struct symbol *symbol; + + estimate (data, data_size, filename, &num_segments, &num_extdefs, &num_pubdefs, &num_lnames); + + num_lnames++; + num_segments++; + + i_lnames = 1; + i_segments = 1; + i_pubdefs = num_segments; + i_extdefs = i_pubdefs + num_pubdefs; + + lnames = xmalloc (num_lnames * sizeof (*lnames)); + + part_p_array = xmalloc (sizeof (*part_p_array) * num_segments); + of = object_file_make (filename, num_segments + num_pubdefs + num_extdefs); + + while (pos < (unsigned char *) data + data_size) { + + record_type = pos[0]; + + big_fields = record_type & 1; + record_type &= ~1; + + record_len = array_to_integer (pos + 1, 2); + pos += 3; + + if (record_type == RECORD_TYPE_THEADR) { + + unsigned char string_length = pos[0]; + + if (string_length > record_len - 2) { + + report_at (program_name, 0, REPORT_FATAL_ERROR, "%s: incorrect string length", filename); + exit (EXIT_FAILURE); + + } + + } else if (record_type == RECORD_TYPE_COMENT) { + /* Nothing needs to be done. */ + } else if (record_type == RECORD_TYPE_MODEND) { + + if (pos + record_len != data + data_size) { + + report_at (program_name, 0, REPORT_FATAL_ERROR, "%s: MODEND record is not the last record", filename); + exit (EXIT_FAILURE); + + } + + } else if (record_type == RECORD_TYPE_EXTDEF) { + + extdef_name_end = (extdef_name = pos) + record_len - 1; + + while (extdef_name != extdef_name_end) { + + extdef_name_len = extdef_name[0]; + + symbol = of->symbol_arr + i_extdefs; + i_extdefs++; + + symbol->name = xstrndup ((char *) extdef_name + 1, extdef_name_len); + symbol->value = 0; + symbol->part = 0; + symbol->section_number = UNDEFINED_SECTION_NUMBER; + + symbol_record_external_symbol (symbol); + extdef_name = extdef_name + 1 + extdef_name_len + 1; + + } + + } else if (record_type == RECORD_TYPE_PUBDEF) { + + pubdef_name_end = (pubdef_name = pos) + record_len - 3; + base_segment_index = pubdef_name[1]; + + while (pubdef_name != pubdef_name_end) { + + pubdef_name_len = pubdef_name[2]; + + public_offset = array_to_integer (pubdef_name + 3 + pubdef_name_len, 2); + symbol = of->symbol_arr + i_extdefs++; + + if (base_segment_index >= i_segments || !base_segment_index) { + + report_at (program_name, 0, REPORT_FATAL_ERROR, "%s: invalid base segment index %d", filename, base_segment_index); + exit (EXIT_FAILURE); + + } + + symbol->name = xstrndup ((char *) pubdef_name + 3, pubdef_name_len); + symbol->value = public_offset; + symbol->part = part_p_array[base_segment_index]; + symbol->section_number = base_segment_index; + + symbol_record_external_symbol (symbol); + pubdef_name = pubdef_name + 2 + 1 + pubdef_name_len + 1; + + } + + } else if (record_type == RECORD_TYPE_LNAMES) { + + lname_end = (lname = pos) + record_len - 1; + + while (lname != lname_end) { + + lname_len = lname[0]; + + lnames[i_lnames++] = xstrndup ((char *) lname + 1, lname_len); + lname = lname + 1 + lname_len; + + } + + } else if (record_type == RECORD_TYPE_SEGDEF) { + + unsigned char attributes = pos[0]; + + unsigned short segment_name_index; + unsigned short class_name_index; + + unsigned long segment_len; + + if (!(attributes & SEGMENT_ATTR_P)) { + + report_at (program_name, 0, REPORT_FATAL_ERROR, "%s: OMF USE16 segments are not supported", filename); + exit (EXIT_FAILURE); + + } + + if ((attributes >> 5) == 0) { + + report_at (program_name, 0, REPORT_FATAL_ERROR, "%s: OMF absolute segments are not supported", filename); + exit (EXIT_FAILURE); + + } + + if (big_fields) { + + segment_len = array_to_integer (pos + 1, 4); + + segment_name_index = pos[5]; + class_name_index = pos[6]; + + } else { + + report_at (__FILE__, __LINE__, REPORT_INTERNAL_ERROR, "%s: non big fields not supported for record %#x", filename, record_type); + exit (EXIT_FAILURE); + + } + + if (segment_name_index >= i_lnames || !segment_name_index) { + + report_at (program_name, 0, REPORT_FATAL_ERROR, "%s: invalid segment name index", filename); + exit (EXIT_FAILURE); + + } + + if (class_name_index >= i_lnames || !class_name_index) { + + report_at (program_name, 0, REPORT_FATAL_ERROR, "%s: invalid class name index", filename); + exit (EXIT_FAILURE); + + } + + section = section_find_or_make (lnames[segment_name_index]); + + if (strcmp (lnames[class_name_index], "CODE") == 0) { + section->flags = SECTION_FLAG_ALLOC | SECTION_FLAG_LOAD | SECTION_FLAG_READONLY | SECTION_FLAG_CODE; + } else if (strcmp (lnames[class_name_index], "DATA") == 0) { + section->flags = SECTION_FLAG_ALLOC | SECTION_FLAG_LOAD | SECTION_FLAG_DATA; + } else { + section->flags = SECTION_FLAG_ALLOC | SECTION_FLAG_LOAD | SECTION_FLAG_DATA; + } + + part = section_part_new (section, of); + + switch (attributes >> 5) { + + case 1: + + part->alignment = 1; + break; + + case 2: + + part->alignment = 2; + break; + + case 3: + + part->alignment = 16; + break; + + case 4: + + part->alignment = 4096; + break; + + case 5: + + part->alignment = 4; + break; + + default: + + report_at (program_name, 0, REPORT_FATAL_ERROR, "%s: invalid segment alignment", filename); + exit (EXIT_FAILURE); + + } + + part->content = xmalloc (part->content_size = segment_len); + symbol = of->symbol_arr + i_segments; + + symbol->name = xstrdup (section->name); + symbol->flags = SYMBOL_FLAG_SECTION_SYMBOL; + symbol->value = 0; + symbol->size = 0; + symbol->part = part; + symbol->section_number = i_segments; + + section_append_section_part (section, part); + part_p_array[i_segments++] = part; + + } else if (record_type == RECORD_TYPE_GRPDEF) { + /* Nothing needs to be done. */ + } else if (record_type == RECORD_TYPE_FIXUPP) { + + struct section_part *part = prev_ledata.part; + + unsigned char *subrec = pos; + unsigned char *end = pos + record_len - 1; + + if (!part) { + + report_at (program_name, 0, REPORT_FATAL_ERROR, "%s: FIXUPP record has no preceding LEDATA record", filename); + exit (EXIT_FAILURE); + + } + + if (!big_fields) { + + report_at (__FILE__, __LINE__, REPORT_INTERNAL_ERROR, "%s: non big field not supported for record %#x", filename, record_type); + exit (EXIT_FAILURE); + + } + + while (subrec != end) { + + if (subrec[0] & 0x80) { + + struct reloc_entry *reloc; + unsigned long old_reloc_count; + + unsigned char fixdat, frame_method, target_method; + unsigned short data_record_offset, target_datum; + + if (subrec[0] & 0x40) { + + report_at (__FILE__, __LINE__, REPORT_INTERNAL_ERROR, "%s: only self-relative OMF fixups are supported", filename); + exit (EXIT_FAILURE); + + } + + if (((subrec[0] >> 2) & 0x0f) != 9) { + + report_at (__FILE__, __LINE__, REPORT_INTERNAL_ERROR, "%s: only 32-bit offset OMF fixups are supported", filename); + exit (EXIT_FAILURE); + + } + + data_record_offset = ((unsigned short) (subrec[0] & 0x03)) << 8; + data_record_offset |= subrec[1]; + + if (data_record_offset >= prev_ledata.size) { + + report_at (program_name, 0, REPORT_FATAL_ERROR, "%s: invalid data record", filename); + exit (EXIT_FAILURE); + + } + + fixdat = subrec[2]; + + if (fixdat & 0x80) { + + report_at (__FILE__, __LINE__, REPORT_INTERNAL_ERROR, "%s: only explicit frame methods in OMF fixups are supported", filename); + exit (EXIT_FAILURE); + + } + + if (fixdat & 0x08) { + + report_at (__FILE__, __LINE__, REPORT_INTERNAL_ERROR, "%s: only explicit targets methods in OMF fixups are supported", filename); + exit (EXIT_FAILURE); + + } + + if (!(fixdat & 0x04)) { + + report_at (__FILE__, __LINE__, REPORT_INTERNAL_ERROR, "%s: displacement fields in OMF fixups are supported", filename); + exit (EXIT_FAILURE); + + } + + frame_method = (fixdat & 0x70) >> 4; + + target_method = fixdat & 0x03; + target_datum = subrec[3]; + + if (frame_method != METHOD_F5) { + + report_at (__FILE__, __LINE__, REPORT_INTERNAL_ERROR, "%s: only F5 frame methods in OMF fixups are supported", filename); + exit (EXIT_FAILURE); + + } + + if (target_method != METHOD_T2_EXTDEF) { + + report_at (__FILE__, __LINE__, REPORT_INTERNAL_ERROR, "%s: only T2 target methods in OMF fixups are supported", filename); + exit (EXIT_FAILURE); + + } + + old_reloc_count = part->reloc_cnt; + part->reloc_cnt += 1; + + part->reloc_arr = xrealloc (part->reloc_arr, part->reloc_cnt * sizeof (*part->reloc_arr)); + memset (part->reloc_arr + old_reloc_count, 0, (part->reloc_cnt - old_reloc_count) * sizeof *part->reloc_arr); + + reloc = part->reloc_arr + old_reloc_count; + + if (target_method == METHOD_T2_EXTDEF) { + + if (target_datum > num_extdefs || !target_datum) { + + report_at (program_name, 0, REPORT_FATAL_ERROR, "%s: invalid target datum", filename); + exit (EXIT_FAILURE); + + } + + reloc->symbol = of->symbol_arr + num_segments + num_pubdefs + target_datum - 1; + + } + + reloc->offset = data_record_offset; + reloc->addend = 0; + reloc->howto = &reloc_howtos[RELOC_TYPE_PC32]; + + subrec += 4; + + } else { + + report_at (__FILE__, __LINE__, REPORT_INTERNAL_ERROR, "%s: THREAD subrecords are not yet supported", filename); + exit (EXIT_FAILURE); + + } + + } + + } else if (record_type == RECORD_TYPE_LEDATA) { + + unsigned char seg_index = pos[0]; + + unsigned char *data_bytes; + unsigned short size; + + unsigned long offset; + + if (big_fields) { + + offset = array_to_integer (pos + 1, 4); + size = record_len - 6; + data_bytes = pos + 5; + + } else { + + offset = array_to_integer (pos + 1, 2); + size = record_len - 4; + data_bytes = pos + 3; + + } + + if (seg_index >= i_segments || !seg_index) { + + report_at (program_name, 0, REPORT_FATAL_ERROR, "%s: invalid seg index", filename); + exit (EXIT_FAILURE); + + } + + part = part_p_array[seg_index]; + + if (offset + size > part->content_size) { + + report_at (program_name, 0, REPORT_FATAL_ERROR, "%s: invalid LEDATA offset and size combination for segment", filename); + exit (EXIT_FAILURE); + + } + + memcpy (part->content + offset, data_bytes, size); + + prev_ledata.part = part; + prev_ledata.offset = offset; + prev_ledata.size = size; + + } else { + + report_at (__FILE__, __LINE__, REPORT_INTERNAL_ERROR, "%s: not yet supported record type %#x", filename, record_type | big_fields); + exit (EXIT_FAILURE); + + } + + pos += record_len; + + } + + for (i_lnames = 0; i_lnames < num_lnames; i_lnames++) { + free (lnames[i_lnames]); + } + + free (lnames); + free (part_p_array); + +} diff --git a/omf.h b/omf.h new file mode 100644 index 0000000..b8bfef3 --- /dev/null +++ b/omf.h @@ -0,0 +1,25 @@ +/****************************************************************************** + * @file omf.h + *****************************************************************************/ +#ifndef _OMF_H +#define _OMF_H + +#define RECORD_TYPE_THEADR 0x80 +#define RECORD_TYPE_COMENT 0x88 +#define RECORD_TYPE_MODEND 0x8A +#define RECORD_TYPE_EXTDEF 0x8C +#define RECORD_TYPE_PUBDEF 0x90 +#define RECORD_TYPE_LNAMES 0x96 +#define RECORD_TYPE_SEGDEF 0x98 +#define RECORD_TYPE_GRPDEF 0x9A +#define RECORD_TYPE_FIXUPP 0x9C +#define RECORD_TYPE_LEDATA 0xA0 + +#define SEGMENT_ATTR_P 0x1 + +#define METHOD_T2_EXTDEF 2 +#define METHOD_F5 5 + +void read_omf_object (const char *filename, unsigned char *data, unsigned long data_size); + +#endif /* _OMF_H */