From dc67e6085851c490607625c11074b87fabe565d0 Mon Sep 17 00:00:00 2001 From: Robert Pengelly Date: Thu, 10 Apr 2025 02:29:48 +0100 Subject: [PATCH] Experimental COFF creation support --- Makefile.p32 | 2 +- Makefile.pdw | 2 +- Makefile.unix | 2 +- Makefile.w32 | 2 +- Makefile.wat | 2 +- coff.c | 451 ++++++++++++++++++++++++++++++++++++++++++++++++++ coff.h | 101 +++++++++++ ld.c | 10 +- ld.h | 3 +- lib.c | 7 + pe.c | 2 +- 11 files changed, 576 insertions(+), 8 deletions(-) create mode 100644 coff.c create mode 100644 coff.h diff --git a/Makefile.p32 b/Makefile.p32 index afcbc23..c6a6bda 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 elks.o hashtab.o ld.o lib.o link.o map.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 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 ecb3254..6768ad4 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 elks.o hashtab.o ld.o lib.o link.o map.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 pe.o report.o section.o symbol.o vector.o write7x.o all: clean slink.exe diff --git a/Makefile.unix b/Makefile.unix index a0416b7..e91df01 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 elks.c hashtab.c ld.c lib.c link.c map.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 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 7e70fd9..dea27fc 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 elks.c hashtab.c ld.c lib.c link.c map.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 pe.c report.c section.c symbol.c vector.c write7x.c all: slink.exe diff --git a/Makefile.wat b/Makefile.wat index 96156bb..9e6719d 100644 --- a/Makefile.wat +++ b/Makefile.wat @@ -4,7 +4,7 @@ SRCDIR ?= $(CURDIR) VPATH := $(SRCDIR) -SRC := aout.c elks.c hashtab.c ld.c lib.c link.c map.c pe.c report.c section.c symbol.c vector.c write7x.c +SRC := aout.c coff.c elks.c hashtab.c ld.c lib.c link.c map.c pe.c report.c section.c symbol.c vector.c write7x.c all: slink.exe diff --git a/coff.c b/coff.c new file mode 100644 index 0000000..daae198 --- /dev/null +++ b/coff.c @@ -0,0 +1,451 @@ +/****************************************************************************** + * @file coff.c + *****************************************************************************/ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "coff.h" +#include "ld.h" +#include "lib.h" +#include "report.h" +#include "section.h" +#include "write7x.h" + +struct exclude_symbol { + + char *name; + struct exclude_symbol *next; + +}; + +static struct exclude_symbol *exclude_symbols = 0; + +static int generate_reloc_section = 1; +static int can_be_relocated = 0; + +static int check_reloc_section_needed_section_part (struct section_part *part) { + + struct reloc_howto *reloc_howto; + unsigned long i; + + for (i = 0; i < part->reloc_cnt; i++) { + + reloc_howto = part->reloc_arr[i].howto; + + if (reloc_howto == &reloc_howtos[RELOC_TYPE_16] || reloc_howto == &reloc_howtos[RELOC_TYPE_32]) { + return 1; + } + + } + + return 0; + +} + +static int check_reloc_section_needed (void) { + + struct section *section; + struct section_part *part; + struct subsection *subsection; + + for (section = all_sections; section; section = section->next) { + + for (part = section->first_part; part; part = part->next) { + + if (check_reloc_section_needed_section_part (part)) { + return 1; + } + + } + + for (subsection = section->all_subsections; subsection; subsection = subsection->next) { + + for (part = subsection->first_part; part; part = part->next) { + + if (check_reloc_section_needed_section_part (part)) { + return 1; + } + + } + + } + + } + + return 0; + +} + +static void exclude_symbols_free (void) { + + struct exclude_symbol *exclude_symbol; + + for (exclude_symbol = exclude_symbols; exclude_symbol; exclude_symbol = exclude_symbols) { + + exclude_symbols = exclude_symbol->next; + + free (exclude_symbol->name); + free (exclude_symbol); + + } + +} + +static void generate_base_relocation_block (struct section *section, struct coff_relocation_entry *ibr_hdr_p, unsigned long num_relocs, struct section *saved_section, struct section_part *saved_part, unsigned long saved_i) { + + struct section_part *reloc_part; + struct reloc_entry *relocs; + + unsigned char *write_pos; + unsigned long i; + + struct section_part *part; + + reloc_part = section_part_new (section, object_file_make (FAKE_LD_FILENAME, 0)); + reloc_part->content_size = num_relocs * sizeof (*ibr_hdr_p); + + reloc_part->content = xmalloc (reloc_part->content_size); + write_pos = reloc_part->content; + + for (part = ((section = saved_section) ? saved_part : section->first_part); part; part = part->next) { + + relocs = part->reloc_arr; + + for (i = ((part == saved_part) ? saved_i : 0); i < part->reloc_cnt; i++) { + + integer_to_array (part->rva + relocs[i].offset, ibr_hdr_p->VirtualAddress, 4); + + if (relocs[i].howto == &reloc_howtos[RELOC_TYPE_16]) { + integer_to_array (IMAGE_REL_I386_DIR16, ibr_hdr_p->Type, 2); + } else if (relocs[i].howto == &reloc_howtos[RELOC_TYPE_32]) { + integer_to_array (IMAGE_REL_I386_DIR32, ibr_hdr_p->Type, 2); + } else { + continue; + } + + memcpy (write_pos, ibr_hdr_p, sizeof (*ibr_hdr_p)); + write_pos += sizeof (*ibr_hdr_p); + + if (!--num_relocs) { + goto _finish; + } + + } + + } + + report_at (program_name, 0, REPORT_INTERNAL_ERROR, "num_relocs mismatch while generating .reloc section"); + +_finish: + + section_append_section_part (section, reloc_part); + +} + +static struct section *last_section = 0; + +static unsigned long base_of_code = 0; +static unsigned long base_of_data = 0; + +static unsigned long size_of_code = 0; +static unsigned long size_of_initialized_data = 0; +static unsigned long size_of_uninitialized_data = 0; + +static unsigned long translate_section_flags_to_characteristics (int flags) { + + unsigned long characteristics = 0; + + if (!(flags & SECTION_FLAG_READONLY)) { + characteristics |= IMAGE_SCN_MEM_WRITE; + } + + if (flags & SECTION_FLAG_CODE) { + characteristics |= IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_EXECUTE; + } + + if (flags & SECTION_FLAG_DATA) { + characteristics |= IMAGE_SCN_CNT_INITIALIZED_DATA; + } + + if (flags & SECTION_FLAG_NEVER_LOAD) { + characteristics |= IMAGE_SCN_TYPE_NOLOAD; + } + + if (flags & SECTION_FLAG_DEBUGGING) { + characteristics |= IMAGE_SCN_LNK_INFO; + } + + if (flags & SECTION_FLAG_EXCLUDE) { + characteristics |= IMAGE_SCN_LNK_REMOVE; + } + + if (!(flags & SECTION_FLAG_NOREAD)) { + characteristics |= IMAGE_SCN_MEM_READ; + } + + if (flags & SECTION_FLAG_SHARED) { + characteristics |= IMAGE_SCN_MEM_SHARED; + } + + if ((flags & SECTION_FLAG_ALLOC) && !(flags & SECTION_FLAG_LOAD)) { + characteristics |= IMAGE_SCN_CNT_UNINITIALIZED_DATA; + } + + return characteristics; + +} + +static void write_sections (unsigned char *data) { + + unsigned char *pos = data + state->size_of_headers; + unsigned long characteristics = 0; + + struct coff_section_table_entry *hdr; + struct section *section; + + for (section = all_sections; section; section = section->next) { + + if (!(hdr = (struct coff_section_table_entry *) section->object_dependent_data)) { + section->object_dependent_data = (hdr = xmalloc (sizeof (*hdr))); + } + + memset (hdr->Name, 0, sizeof (hdr->Name)); + memcpy (hdr->Name, section->name, (strlen (section->name) >= sizeof (hdr->Name)) ? sizeof (hdr->Name) : strlen (section->name)); + + write741_to_byte_array (hdr->VirtualSize, section->total_size); + write741_to_byte_array (hdr->VirtualAddress, section->rva); + + if (!section->is_bss) { + + write741_to_byte_array (hdr->SizeOfRawData, section->total_size); + write741_to_byte_array (hdr->PointerToRawData, pos - data); + + if (array_to_integer (hdr->NumberOfRelocations, 2) > 0) { + write741_to_byte_array (hdr->PointerToRelocations, (pos - data) + section->total_size); + } + + section_write (section, pos); + pos += (section->total_size + (array_to_integer (hdr->NumberOfRelocations, 2) * sizeof (struct coff_relocation_entry))); + + } + + characteristics = translate_section_flags_to_characteristics (section->flags); + write741_to_byte_array (hdr->Characteristics, characteristics); + + if (characteristics & IMAGE_SCN_CNT_CODE) { + + if (!base_of_code) { + base_of_code = array_to_integer (hdr->VirtualAddress, 4); + } + + size_of_code += array_to_integer (hdr->VirtualSize, 4); + + } else if (characteristics & IMAGE_SCN_CNT_INITIALIZED_DATA) { + + if (!base_of_data) { + base_of_data = array_to_integer (hdr->VirtualAddress, 4); + } + + size_of_initialized_data += array_to_integer (hdr->VirtualSize, 4); + + } else if (characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA) { + size_of_uninitialized_data += array_to_integer (hdr->VirtualSize, 4); + } + + last_section = section; + + } + +} + +void coff_after_link (void) { + + unsigned long num_relocs = 0, saved_i = 0, i; + + struct section *saved_section = 0, *section; + struct section_part *saved_part = 0, *part; + + struct coff_relocation_entry ibr_hdr; + struct reloc_entry *relocs; + + struct coff_section_table_entry *hdr; + + if (!generate_reloc_section) { + return; + } + + memset (&ibr_hdr, 0, sizeof (ibr_hdr)); + + for (section = all_sections; section; section = section->next) { + + section->object_dependent_data = (hdr = xmalloc (sizeof (*hdr))); + + for (part = section->first_part; part; part = part->next) { + + relocs = part->reloc_arr; + + for (i = 0; i < part->reloc_cnt; i++) { + + if (relocs[i].howto != &reloc_howtos[RELOC_TYPE_16] && relocs[i].howto != &reloc_howtos[RELOC_TYPE_32]) { + continue; + } + + if (num_relocs && part->rva + relocs[i].offset < array_to_integer (ibr_hdr.VirtualAddress, 4)) { + + generate_base_relocation_block (section, &ibr_hdr, num_relocs, saved_section, saved_part, saved_i); + num_relocs = 0; + + } + + if (num_relocs == 0) { + + integer_to_array (part->rva + relocs[i].offset, ibr_hdr.VirtualAddress, 4); + + saved_section = section; + saved_part = part; + + saved_i = i; + + } + + num_relocs++; + + } + + } + + if (num_relocs) { + generate_base_relocation_block (section, &ibr_hdr, num_relocs, saved_section, saved_part, saved_i); + } + + integer_to_array (num_relocs, hdr->NumberOfRelocations, 2); + + } + +} + +void coff_before_link (void) { + + exclude_symbols_free (); + + if (!check_reloc_section_needed ()) { + + can_be_relocated = 1; + generate_reloc_section = 0; + + } + + if (generate_reloc_section) { + can_be_relocated = 1; + } + + state->size_of_headers = sizeof (struct coff_header) + sizeof (struct coff_optional_header); + state->size_of_headers += sizeof (struct coff_section_table_entry) * section_count (); + +} + +void coff_write (const char *filename) { + + FILE *fp; + + unsigned long data_size = 0; + unsigned char *data, *pos; + + struct coff_header *coffhdr; + struct coff_optional_header *opthdr; + + struct section *section; + struct coff_section_table_entry *sec_ent; + + if (!(fp = fopen (filename, "wb"))) { + + report_at (program_name, 0, REPORT_ERROR, "failed to open '%s' for writing", filename); + return; + + } + + for (section = all_sections; section; section = section->next) { + + if (!section->is_bss) { + + data_size += section->total_size; + + if ((sec_ent = section->object_dependent_data)) { + data_size += (array_to_integer (sec_ent->NumberOfRelocations, 2) * sizeof (struct coff_relocation_entry)); + } + + } + + } + + data_size += state->size_of_headers; + + data = xmalloc (data_size); + write_sections (data); + + coffhdr = (struct coff_header *) data; + opthdr = (struct coff_optional_header *) ((char *) coffhdr + sizeof (*coffhdr)); + + pos = (unsigned char *) opthdr + sizeof (*opthdr); + + write721_to_byte_array (coffhdr->Machine, IMAGE_FILE_MACHINE_I386); + write721_to_byte_array (coffhdr->NumberOfSections, section_count ()); + write741_to_byte_array (coffhdr->TimeDateStamp, time (0)); + write721_to_byte_array (coffhdr->SizeOfOptionalHeader, sizeof (*opthdr)); + + { + + unsigned short characteristics = 0; + + characteristics |= IMAGE_FILE_EXECUTABLE_IMAGE; + /*characteristics |= IMAGE_FILE_LINE_NUMS_STRIPPED;*/ + characteristics |= IMAGE_FILE_LOCAL_SYMS_STRIPPED; + characteristics |= IMAGE_FILE_LITTLE_ENDIAN; + + if (!can_be_relocated) { + characteristics |= IMAGE_FILE_RELOCS_STRIPPED; + } + + write721_to_byte_array (coffhdr->Characteristics, characteristics); + + } + + write721_to_byte_array (opthdr->Magic, IMAGE_FILE_MAGIC_I386); + + opthdr->MajorLinkerVersion = 0; + opthdr->MinorLinkerVersion = 10; + + write741_to_byte_array (opthdr->SizeOfCode, size_of_code); + write741_to_byte_array (opthdr->SizeOfInitializedData, size_of_initialized_data); + write741_to_byte_array (opthdr->SizeOfUninitializedData, size_of_uninitialized_data); + + write741_to_byte_array (opthdr->AddressOfEntryPoint, state->entry_point); + + write741_to_byte_array (opthdr->BaseOfCode, base_of_code); + write741_to_byte_array (opthdr->BaseOfData, base_of_data); + + for (section = all_sections; section; section = section->next) { + + struct coff_section_table_entry *hdr = section->object_dependent_data; + + memcpy (pos, hdr, sizeof (*hdr)); + pos += sizeof (*hdr); + + free (hdr); + + } + + if (fwrite (data, data_size, 1, fp) != 1) { + report_at (program_name, 0, REPORT_ERROR, "failed to write data to '%s'", filename); + } + + free (data); + fclose (fp); + +} diff --git a/coff.h b/coff.h new file mode 100644 index 0000000..1c826ae --- /dev/null +++ b/coff.h @@ -0,0 +1,101 @@ +/****************************************************************************** + * @file coff.h + *****************************************************************************/ +#ifndef _COFF_H +#define _COFF_H + +#define IMAGE_FILE_MACHINE_I386 0x014C + +struct coff_header { + + unsigned char Machine[2]; + unsigned char NumberOfSections[2]; + + unsigned char TimeDateStamp[4]; + unsigned char PointerToSymbolTable[4]; + unsigned char NumberOfSymbols[4]; + + unsigned char SizeOfOptionalHeader[2]; + unsigned char Characteristics[2]; + +}; + +#define IMAGE_FILE_RELOCS_STRIPPED 0x0001 +#define IMAGE_FILE_EXECUTABLE_IMAGE 0x0002 +#define IMAGE_FILE_LINE_NUMS_STRIPPED 0x0004 +#define IMAGE_FILE_LOCAL_SYMS_STRIPPED 0x0008 +#define IMAGE_FILE_LITTLE_ENDIAN 0x0100 + +struct coff_optional_header { + + unsigned char Magic[2]; + + unsigned char MajorLinkerVersion; + unsigned char MinorLinkerVersion; + + unsigned char SizeOfCode[4]; + unsigned char SizeOfInitializedData[4]; + unsigned char SizeOfUninitializedData[4]; + + unsigned char AddressOfEntryPoint[4]; + + unsigned char BaseOfCode[4]; + unsigned char BaseOfData[4]; + +}; + +#define IMAGE_FILE_MAGIC_I386 0x010B + +struct coff_section_table_entry { + + char Name[8]; + + unsigned char VirtualSize[4]; + unsigned char VirtualAddress[4]; + + unsigned char SizeOfRawData[4]; + unsigned char PointerToRawData[4]; + unsigned char PointerToRelocations[4]; + unsigned char PointerToLinenumbers[4]; + + unsigned char NumberOfRelocations[2]; + unsigned char NumberOfLinenumbers[2]; + + unsigned char Characteristics[4]; + +}; + +#define IMAGE_SCN_TYPE_NOLOAD 0x00000002 +#define IMAGE_SCN_CNT_CODE 0x00000020 +#define IMAGE_SCN_CNT_INITIALIZED_DATA 0x00000040 +#define IMAGE_SCN_CNT_UNINITIALIZED_DATA 0x00000080 +#define IMAGE_SCN_LNK_INFO 0x00000200 +#define IMAGE_SCN_LNK_REMOVE 0x00000800 +#define IMAGE_SCN_ALIGN_4BYTES 0x00300000 +#define IMAGE_SCN_MEM_SHARED 0x10000000 +#define IMAGE_SCN_MEM_EXECUTE 0x20000000 +#define IMAGE_SCN_MEM_READ 0x40000000 +#define IMAGE_SCN_MEM_WRITE 0x80000000 + +struct coff_relocation_entry { + + unsigned char VirtualAddress[4]; + unsigned char SymbolTableIndex[4]; + + unsigned char Type[2]; + +}; + +#define IMAGE_REL_I386_ABSOLUTE 0x0000 +#define IMAGE_REL_I386_DIR16 0x0001 +#define IMAGE_REL_I386_REL16 0x0002 +#define IMAGE_REL_I386_DIR32 0x0006 +#define IMAGE_REL_I386_DIR32NB 0x0007 +#define IMAGE_REL_I386_REL32 0x0014 + +void coff_after_link (void); +void coff_before_link (void); + +void coff_write (const char *filename); + +#endif /* _COFF_H */ diff --git a/ld.c b/ld.c index f7b83f0..2541ac0 100644 --- a/ld.c +++ b/ld.c @@ -7,6 +7,7 @@ #include "aout.h" #include "ar.h" +#include "coff.h" #include "elks.h" #include "ld.h" #include "lib.h" @@ -254,7 +255,9 @@ int main (int argc, char **argv) { } - if (state->format == LD_FORMAT_I386_PE) { + if (state->format == LD_FORMAT_I386_COFF) { + coff_before_link (); + } else if (state->format == LD_FORMAT_I386_PE) { pe_before_link (); } @@ -316,6 +319,11 @@ int main (int argc, char **argv) { pe_after_link (); pe_write (state->output_filename); + } else if (state->format == LD_FORMAT_I386_COFF) { + + coff_after_link (); + coff_write (state->output_filename); + } if (state->output_map_filename) { diff --git a/ld.h b/ld.h index b500a3e..2a4f36d 100644 --- a/ld.h +++ b/ld.h @@ -34,7 +34,8 @@ struct ld_state { #define LD_FORMAT_I386_ELKS 0x03 #define LD_FORMAT_I386_AOUT 0x04 -#define LD_FORMAT_I386_PE 0x05 +#define LD_FORMAT_I386_COFF 0x05 +#define LD_FORMAT_I386_PE 0x06 extern struct ld_state *state; extern const char *program_name; diff --git a/lib.c b/lib.c index a859c31..4a56ca5 100644 --- a/lib.c +++ b/lib.c @@ -187,6 +187,13 @@ static void use_option (const char *cmd_arg, int idx, const char *optarg) { } + if (xstrcasecmp (optarg, "coff-i386") == 0) { + + state->format = LD_FORMAT_I386_COFF; + break; + + } + report_at (program_name, 0, REPORT_ERROR, "unrecognised output format '%s'", optarg); exit (EXIT_FAILURE); diff --git a/pe.c b/pe.c index b49d128..16bbc1a 100644 --- a/pe.c +++ b/pe.c @@ -558,7 +558,7 @@ void pe_after_link (void) { } if (!(reloc_section = section_find (".reloc"))) { - report_at (program_name, 0, REPORT_INTERNAL_ERROR, ".reloc section count not be found"); + report_at (program_name, 0, REPORT_INTERNAL_ERROR, ".reloc section could not be found"); } integer_to_array (0, ibr_hdr.RVAOfBlock, 4); -- 2.34.1