/* * COFF loader. * Copyright (C) 2023, stsp2@yandex.ru * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include #include #include #include #include #include #include "dos.h" #include "util.h" #include "coff.h" #define STUB_DEBUG 0 #if STUB_DEBUG #define stub_debug(...) printf(__VA_ARGS__) #else #define stub_debug(...) #endif struct coff_header { unsigned short f_magic; /* Magic number */ unsigned short f_nscns; /* Number of Sections */ int32_t f_timdat; /* Time & date stamp */ int32_t f_symptr; /* File pointer to Symbol Table */ int32_t f_nsyms; /* Number of Symbols */ unsigned short f_opthdr; /* sizeof(Optional Header) */ unsigned short f_flags; /* Flags */ }; struct opt_header { unsigned short magic; /* Magic Number */ unsigned short vstamp; /* Version stamp */ uint32_t tsize; /* Text size in bytes */ uint32_t dsize; /* Initialised data size */ uint32_t bsize; /* Uninitialised data size */ uint32_t entry; /* Entry point */ uint32_t text_start; /* Base of Text used for this file */ uint32_t data_start; /* Base of Data used for this file */ }; struct scn_header { char s_name[8]; /* Section Name */ int32_t s_paddr; /* Physical Address */ int32_t s_vaddr; /* Virtual Address */ int32_t s_size; /* Section Size in Bytes */ int32_t s_scnptr; /* File offset to the Section data */ int32_t s_relptr; /* File offset to the Relocation table for this Section */ int32_t s_lnnoptr; /* File offset to the Line Number table for this Section */ unsigned short s_nreloc; /* Number of Relocation table entries */ unsigned short s_nlnno; /* Number of Line Number table entries */ int32_t s_flags; /* Flags for this section */ }; enum { SCT_TEXT, SCT_DATA, SCT_BSS, SCT_MAX }; static struct scn_header scns[SCT_MAX]; struct coff_h { uint32_t length; uint32_t entry; }; static void read_section(char *buf, int ifile, long coffset, int sc) { long bytes; _dos_seek(ifile, coffset + scns[sc].s_scnptr, SEEK_SET); bytes = _long_read(ifile, buf, scns[sc].s_vaddr, scns[sc].s_size); stub_debug("read returned %li\n", bytes); if (bytes != scns[sc].s_size) { fprintf(stderr, "err reading %i bytes, got %li\n", scns[sc].s_size, bytes); // _exit(EXIT_FAILURE); } } static void *read_coff_headers(int ifile) { struct coff_header chdr; struct opt_header ohdr; struct coff_h *h; int rc; unsigned rd; rc = _dos_read(ifile, &chdr, sizeof(chdr), &rd); /* get the COFF header */ if (rc || rd != sizeof(chdr)) { fprintf(stderr, "bad COFF header\n"); return NULL; } if (chdr.f_opthdr < sizeof(ohdr)) { fprintf(stderr, "opt header size mismatch: %i %zi\n", chdr.f_opthdr, sizeof(ohdr)); return NULL; } rc = _dos_read(ifile, &ohdr, sizeof(ohdr), &rd); /* get the COFF opt header */ if (rc || rd != sizeof(ohdr)) { fprintf(stderr, "bad COFF opt header\n"); return NULL; } if (chdr.f_opthdr > sizeof(ohdr)) _dos_seek(ifile, chdr.f_opthdr - sizeof(ohdr), SEEK_CUR); rc = _dos_read(ifile, scns, sizeof(scns[0]) * SCT_MAX, &rd); if (rc || rd != sizeof(scns[0]) * SCT_MAX) { fprintf(stderr, "failed to read section headers\n"); return NULL; } #if STUB_DEBUG for (int i = 0; i < SCT_MAX; i++) { struct scn_header *h = &scns[i]; stub_debug("Section %s pa 0x%lx va 0x%lx size 0x%lx foffs 0x%lx\n", h->s_name, h->s_paddr, h->s_vaddr, h->s_size, h->s_scnptr); } #endif h = malloc(sizeof(*h)); assert(h); h->entry = ohdr.entry; h->length = scns[SCT_BSS].s_vaddr + scns[SCT_BSS].s_size; return h; } static uint32_t get_coff_va(void *handle) { return 0; // ??? } static uint32_t get_coff_length(void *handle) { struct coff_h *h = handle; return h->length; } static uint32_t get_coff_entry(void *handle) { struct coff_h *h = handle; return h->entry; } static void read_coff_sections(void *handle, char *ptr, int ifile, uint32_t offset) { struct coff_h *h = handle; read_section(ptr, ifile, offset, SCT_TEXT); read_section(ptr, ifile, offset, SCT_DATA); memset(ptr + scns[SCT_BSS].s_vaddr, 0, scns[SCT_BSS].s_size + (scns[SCT_BSS].s_size & 1)); free(h); } static void coff_close(void *handle) { } struct ldops coff_ops = { read_coff_headers, get_coff_va, get_coff_length, get_coff_entry, read_coff_sections, coff_close, };