#include #include #include #include #include "fungelf.h" #define ELFCLASS32 1 #define ELFDATA2LSB 1 #define ELFDATA2MSB 2 #define ET_EXEC 2 #define PT_LOAD 1 static cbns_t CBnewsection = NULL; static cbgc_t CBgetcell = NULL; static cbsc_t CBsetcell = NULL; static cbse_t CBsetentry = NULL; static int read_ehdr(struct ELFHeader *eh, FILE *fp); static int read_phdr(struct PHdr *eh, FILE *fp); static void read_int(const unsigned char *bp, void *pval, int len); static void load_BSS_section(struct PHdr *phdr, unsigned int gfill); static int load_text_section(struct PHdr *phdr, FILE *fp, unsigned int gfill); /* Load either an ELF file or a plain ASCII file. */ int FungELF_load(FILE *fp, cbns_t cbns, cbgc_t cbgc, cbsc_t cbsc, cbse_t cbse) { int rc = FungELF_loadelf(fp, cbns, cbgc, cbsc, cbse); if (rc < 0) { rewind(fp); rc = FungELF_loadascii(fp, cbns, cbgc, cbsc, cbse); } return rc; } const char *FungELF_strerror(int rc) { switch (rc) { case -1: return "File too short"; case -2: return "Wrong file layout"; case -3: return "Error reading ELF headers"; case -4: return "Error reading text section"; } if (rc >= 0) return "No error"; if (rc < 0) return "Unknown error"; } /* Get ASCII data from a text file, and write it into the 'memory' array * starting at (0,0). Do not treat tabs specially; do not pad with spaces. * Treat \n, \r, and \r\n as line separators; never write \r or \n into * memory. Do not treat \f specially; this is not Trefunge. */ int FungELF_loadascii(FILE *fp, cbns_t cbns, cbgc_t cbgc, cbsc_t cbsc, cbse_t cbse) { int x=0, y=0; CBnewsection = cbns; CBgetcell = cbgc; CBsetcell = cbsc; CBsetentry = cbse; while (1) { int k = getc(fp); if (k == EOF) break; if (k == '\r') { y += 1; if (y == 512) break; x = 0; k = getc(fp); if (k == '\n') continue; } else if (k == '\n') { y += 1; if (y == 512) break; x = 0; continue; } assert(0 <= x); assert(0 <= y && y < 512); if (x < 512) { CBsetcell(x, y, k); ++x; } } return 1; } /* Get the data from a FungELF file, and write it into the 'memory' array. */ int FungELF_loadelf(FILE *fp, cbns_t cbns, cbgc_t cbgc, cbsc_t cbsc, cbse_t cbse) { struct ELFHeader ehdr; unsigned int gfill = -1u; int i; CBnewsection = cbns; CBgetcell = cbgc; CBsetcell = cbsc; CBsetentry = cbse; if (read_ehdr(&ehdr, fp) != sizeof ehdr) return -1; if (ehdr.e_ident[0] != 0x7F) return -2; if (ehdr.e_ident[1] != 'E') return -2; if (ehdr.e_ident[2] != 'L') return -2; if (ehdr.e_ident[3] != 'F') return -2; if (ehdr.e_ident[4] != ELFCLASS32) return -2; if (ehdr.e_ident[5] != ELFDATA2MSB && ehdr.e_ident[5] != ELFDATA2LSB) return -2; if (ehdr.e_type != ET_EXEC) return -2; if (ehdr.e_ehsize < 42) return -2; if (ehdr.e_phentsize < 28) return -2; /* Deal with a global fill value. */ if (ehdr.e_flags & PF_FUNG_FILLVALUE) { int i, j; gfill = ehdr.e_flags & 0777777; if (CBsetcell != NULL) for (i=0; i <= 0777; ++i) for (j=0; j <= 0777; ++j) CBsetcell(i, j, gfill); } /* Read the sections. */ for (i=0; i < ehdr.e_phnum; ++i) { struct PHdr phdr; if (fseek(fp, ehdr.e_phoff + i*ehdr.e_phentsize, SEEK_SET) != 0) return -3; if (read_phdr(&phdr, fp) != ehdr.e_phentsize) return -3; if (phdr.p_type != PT_LOAD) return -3; if (phdr.p_filesz == 0) { /* it's a BSS section */ load_BSS_section(&phdr, gfill); } else { if (load_text_section(&phdr, fp, gfill) != 0) return -4; } } /* Set PC to the entry point, and DeltaPC to zero. * The first instruction of the program had better * be something that affects PC or DeltaPC, or the * program will immediately go into an infinite loop * and get killed by the simulator. */ if (CBsetentry != NULL) CBsetentry(ehdr.e_entry & 0777, (ehdr.e_entry >> 9) & 0777); return ehdr.e_phnum; } static void load_BSS_section(struct PHdr *phdr, unsigned int gfill) { int fillvalue = phdr->p_flags & 0777777; int nevermyfill = ((phdr->p_flags & PF_FUNG_NEVERMYFILL) != 0); int i, j; int startx = phdr->p_vaddr & 0777; int starty = (phdr->p_vaddr >> 9) & 0777; int w = phdr->p_memsz & 0777; int h = (phdr->p_memsz >> 9) & 0777; if (CBnewsection != NULL) CBnewsection(startx, starty, w, h, 0); if (nevermyfill || !(phdr->p_flags & PF_FUNG_FILLVALUE)) { /* This BSS section is a no-op; it never fills any cells. */ return; } if (CBsetcell != NULL) { for (j=0; j < h; ++j) { for (i=0; i < w; ++i) { int cx = (startx+i) & 0777; int cy = (starty+j) & 0777; CBsetcell(cx, cy, fillvalue); } } } } static int load_text_section(struct PHdr *phdr, FILE *fp, unsigned int gfill) { unsigned int myfill = (phdr->p_flags & PF_FUNG_FILLVALUE) ? (phdr->p_flags & 0777777) : -1u; int nevermyfill = ((phdr->p_flags & PF_FUNG_NEVERMYFILL) != 0); int i, j; int startx = phdr->p_vaddr & 0777; int starty = (phdr->p_vaddr >> 9) & 0777; int w = phdr->p_memsz & 0777; int h = (phdr->p_memsz >> 9) & 0777; static unsigned char buffer[512*3]; if (CBnewsection != NULL) CBnewsection(startx, starty, w, h, 1); if (fseek(fp, phdr->p_offset, SEEK_SET) != 0) return -1; for (j=0; j < h; ++j) { if ((int)fread(buffer, 1, 3*w, fp) != 3*w) return -1; if (CBsetcell != NULL) { for (i=0; i < w; ++i) { unsigned int cx = (startx+i) & 0777; unsigned int cy = (starty+j) & 0777; unsigned int WoRd = buffer[3*i] & 3; WoRd = (WoRd << 8) | buffer[3*i+1]; WoRd = (WoRd << 8) | buffer[3*i+2]; if (!(nevermyfill && WoRd == myfill)) CBsetcell(cx, cy, WoRd); } } } return 0; } static int endianness; /* We need our own struct-writing (and struct-reading) routines * to make sure that the endianness of the host isn't a problem. */ static int read_ehdr(struct ELFHeader *eh, FILE *fp) { unsigned char buffer[sizeof *eh]; int rc = fread(buffer, 1, sizeof buffer, fp); if (rc != sizeof buffer) return rc; memcpy(eh->e_ident, buffer, 16); endianness = eh->e_ident[5]; assert(sizeof buffer == 52); read_int(buffer+16, &eh->e_type, 2); read_int(buffer+18, &eh->e_machine, 2); read_int(buffer+20, &eh->e_version, 4); read_int(buffer+24, &eh->e_entry, 4); read_int(buffer+28, &eh->e_phoff, 4); read_int(buffer+32, &eh->e_shoff, 4); read_int(buffer+36, &eh->e_flags, 4); read_int(buffer+40, &eh->e_ehsize, 2); read_int(buffer+42, &eh->e_phentsize, 2); read_int(buffer+44, &eh->e_phnum, 2); read_int(buffer+46, &eh->e_shentsize, 2); read_int(buffer+48, &eh->e_shnum, 2); read_int(buffer+50, &eh->e_shstrndx, 2); return rc; } static int read_phdr(struct PHdr *ph, FILE *fp) { unsigned char buffer[sizeof *ph]; int rc = fread(buffer, 1, sizeof buffer, fp); if (rc != sizeof buffer) return rc; assert(sizeof buffer == 32); read_int(buffer+0, &ph->p_type, 4); read_int(buffer+4, &ph->p_offset, 4); read_int(buffer+8, &ph->p_vaddr, 4); read_int(buffer+12, &ph->p_paddr, 4); read_int(buffer+16, &ph->p_filesz, 4); read_int(buffer+20, &ph->p_memsz, 4); read_int(buffer+24, &ph->p_flags, 4); read_int(buffer+28, &ph->p_align, 4); return rc; } /* Reads big-endian or little-endian, depending on 'endianness'. */ static void read_int(const unsigned char *bp, void *pval, int len) { unsigned int val = 0; int oldlen = len; if (endianness == ELFDATA2MSB) { while (len > 0) { val <<= 8; val |= *bp++; --len; } } else { while (len > 0) { val <<= 8; val |= bp[--len]; } } if (oldlen == 2) *(unsigned short *)pval = val; else *(unsigned int *)pval = val; }