ref: 751095831f3fdd1004805a31342c6da6c6976eca
dir: /ld/coff32.c/
static char sccsid[] = "@(#) ./ld/coff32.c"; #include <assert.h> #include <ctype.h> #include <errno.h> #include <limits.h> #include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include "../inc/coff32/filehdr.h" #include "../inc/coff32/scnhdr.h" #include "../inc/coff32/syms.h" #include "../inc/scc.h" #include "ld.h" static FILHDR * getfhdr(Obj *obj, unsigned char *buff, FILHDR *hdr) { int n; n = (*obj->unpack)(buff, "sslllss", &hdr->f_magic, &hdr->f_nscns, &hdr->f_timdat, &hdr->f_symptr, &hdr->f_nsyms, &hdr->f_opthdr, &hdr->f_flags); assert(n == FILHSZ); return hdr; } static int readstr(Obj *obj, long off) { unsigned char buff[4]; char *str; size_t siz; if (fseek(obj->fp, off, SEEK_SET) == EOF) return -1; if (fread(buff, 4, 1, obj->fp) != 1) return -1; (*obj->unpack)(buff, "l", &siz); siz -= 4; if (siz == 0) { obj->strtbl = NULL; obj->strsiz = 0; return 0; } if (siz > SIZE_MAX || (str = malloc(siz)) == NULL) outmem(); if (fread(str, siz, 1, obj->fp) != 1) return -1; obj->strtbl = str; obj->strsiz = siz; return 0; } static SCNHDR * getscn(Obj *obj, unsigned char *buff, SCNHDR *scn) { int n; n = (*obj->unpack)(buff, "'8llllllssl", scn->s_name, &scn->s_paddr, &scn->s_vaddr, &scn->s_size, &scn->s_scnptr, &scn->s_relptr, &scn->s_lnnoptr, &scn->s_nrelloc, &scn->s_nlnno, &scn->s_flags); assert(n == SCNHSZ); return scn; } static int readsects(Obj *obj, long off) { unsigned a, nsec, i; unsigned char buff[SCNHSZ]; SCNHDR *scn, *p; FILHDR *hdr; Symbol *sym, **sec; hdr = obj->filhdr; nsec = hdr->f_nscns; if (nsec > SIZE_MAX / sizeof(*scn)) return -1; if (nsec > SIZE_MAX / sizeof(Symbol *)) return -1; scn = malloc(nsec * sizeof(*scn)); sec = malloc(nsec * sizeof(Symbol *)); if (!scn || !sec) outmem(); obj->sections = sec; obj->scnhdr = scn; if (fseek(obj->fp, off, SEEK_SET) == EOF) return -1; a = obj->align - 1; for (i = 0; i < nsec; ++i) { p = &scn[i]; if (fread(buff, SCNHSZ, 1, obj->fp) != 1) return -1; getscn(obj, buff, p); sym = lookup(p->s_name); sym->size = (sym->size + a) & a; if (sym->size > ULLONG_MAX - p->s_size) { fprintf(stderr, "ld: %s: overflow in section '%s'\n", obj->fname, p->s_name); exit(EXIT_FAILURE); } sym->size += p->s_size; obj->sections[i] = sym; newsect(sym); } return 0; } static void getsym(Obj *obj, unsigned char *buff, SYMENT *ent) { int n; long off, zero; char *name; n = (*obj->unpack)(buff, "'8lsscc", &ent->n_name, &ent->n_value, &ent->n_scnum, &ent->n_type, &ent->n_sclass, &ent->n_numaux); assert(n == SYMESZ); name = ent->n_name; if (!name[0] && !name[1] && !name[2] && !name[3]) (*obj->unpack)(buff, "ll", &ent->n_zeroes, &ent->n_offset); } static char * symname(Obj *obj, SYMENT *ent) { long off; if (ent->n_zeroes != 0) return ent->n_name; off = ent->n_offset; if (off >= obj->strsiz) { fprintf(stderr, "ld: invalid offset in symbol table: %zd\n", off); return ""; } return &obj->strtbl[off]; } static char typeof(Obj *obj, SYMENT *ent) { SCNHDR *sec; FILHDR *hdr; int c, n; long flags; switch (ent->n_scnum) { case N_DEBUG: c = 'n'; break; case N_ABS: c = 'a'; break; case N_UNDEF: c = (ent->n_value != 0) ? 'C' : 'U'; break; default: sec = obj->scnhdr; hdr = obj->filhdr; n = ent->n_scnum; if (n > hdr->f_nscns) return '?'; sec = &sec[n-1]; flags = sec->s_flags; if (flags & STYP_TEXT) c = 't'; else if (flags & STYP_DATA) c = 'd'; else if (flags & STYP_BSS) c = 'b'; else c = '?'; break; } if (ent->n_sclass == C_EXT) c = toupper(c); return c; } static TUINT getval(Obj *obj, SYMENT *ent) { FILHDR *hdr = obj->filhdr;; SCNHDR *scn; Symbol *sym; unsigned n; if (ent->n_scnum > hdr->f_nscns) { fprintf(stderr, "ld: %s: incorrect section number\n", obj->fname, ent->n_scnum); exit(EXIT_FAILURE); } n = ent->n_scnum-1; scn = obj->scnhdr; scn = &scn[n]; sym = obj->sections[n]; return ent->n_value + (sym->size - scn->s_size); } static int readsyms(Obj *obj, long off) { int type; unsigned i, n, aux; FILHDR *hdr = obj->filhdr;; if (fseek(obj->fp, off, SEEK_SET) == EOF) return -1; if (hdr->f_nsyms > SIZE_MAX / sizeof(Symbol *)) { fprintf(stderr, "ld: %s: overflow in size of symbol redirection\n", obj->fname); exit(EXIT_FAILURE); } obj->symbols = malloc(sizeof(Symbol *) * sizeof(Symbol *)); if (!obj->symbols) outmem(); hdr = obj->filhdr; aux = n = 0; for (i = 0; i < hdr->f_nsyms; i++) { Symbol *sym; TUINT value; SYMENT ent; unsigned char buff[SYMESZ]; char *name; if (fread(buff, SYMESZ, 1, obj->fp) != 1) return -1; if (aux > 0) { aux--; continue; } getsym(obj, buff, &ent); aux = ent.n_numaux; name = symname(obj, &ent); type = typeof(obj, &ent); sym = lookup(name); if (ent.n_scnum <= 0) continue; switch (sym->type) { case 'U': sym->type = type; sym->value = getval(obj, &ent); if (type != 'U') { obj->define = 1; sym->where = obj; } if (type == 'C') sym->size = ent.n_value; break; case 'C': switch (type) { case 'U': case 'C': if (ent.n_value > sym->size) sym->size = ent.n_value; break; default: obj->define = 1; sym->where = obj; sym->type = type; sym->value = getval(obj, &ent); break; } break; default: if (type != 'U') { fprintf(stderr, "ld: %s: redifinition of symbol '%s'\n", obj->fname, sym->name); } break; } obj->symbols[i] = sym; } return 0; } static void load(Obj *obj) { unsigned char buff[FILHSZ]; FILHDR *hdr; char *strtbl; long symoff, secoff, stroff, pos; pos = ftell(obj->fp); if (fread(buff, FILHSZ, 1, obj->fp) != 1) goto bad_file; if ((hdr = malloc(sizeof(*hdr))) == NULL) outmem(); getfhdr(obj, buff, hdr); obj->filhdr = hdr; stroff = pos + hdr->f_symptr + hdr->f_nsyms*SYMESZ; symoff = pos + hdr->f_symptr; secoff = pos + FILHSZ + hdr->f_opthdr; if (readstr(obj, stroff) < 0) goto bad_file; if (readsects(obj, secoff) < 0) goto bad_file; if (readsyms(obj, symoff) < 0) goto bad_file; return; bad_file: if (ferror(obj->fp)) die("ld: %s: %s", obj->fname, strerror(errno)); die("ld: %s: corrupted file", obj->fname); } static void unload(Obj *obj) { /* TODO */ pop(obj); delobj(obj); } static void pass1(Obj *obj) { load(obj); if (obj->member && !obj->define) unload(obj); } static void pass2(Obj *obj) { } Fmt coff32; static Obj * probe(char *fname, char *member, FILE *fp) { int c; int c1, c2; fpos_t pos; unsigned short magic; unsigned align; int (*unpack)(unsigned char *, char *, ...); Obj *obj; fgetpos(fp, &pos); c1 = getc(fp); c2 = getc(fp); fsetpos(fp, &pos); if (ferror(fp)) die("ld: %s: %s", fname, strerror(errno)); if (c1 == EOF || c2 == EOF) return 0; magic = c1 | c2 << 8; switch (magic) { case COFF_I386MAGIC: case COFF_Z80MAGIC: unpack = lunpack; align = 2; break; default: return NULL; } obj = newobj(fname, member, fp); obj->unpack = unpack; obj->align = align; obj->fmt = &coff32; return obj; } Fmt coff32 = { .probe = probe, .pass1 = pass1, .pass2 = pass2, };