ref: ebf23256dfed83fa0018d4bf36b710351053af22
dir: /src/libmach/coff32.c/
#include <assert.h>
#include <ctype.h>
#include <limits.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <scc/coff32/filehdr.h>
#include <scc/coff32/aouthdr.h>
#include <scc/coff32/scnhdr.h>
#include <scc/coff32/syms.h>
#include <scc/coff32/reloc.h>
#include <scc/coff32/linenum.h>
#include <scc/mach.h>
#include "libmach.h"
typedef struct coff32 Coff32;
struct arch {
char *name;
unsigned char magic[2];
int type;
};
struct coff32 {
FILHDR hdr;
AOUTHDR *aout;
SCNHDR *scns;
SYMENT *ents;
RELOC **rels;
LINENO **lines;
char *strtbl;
unsigned long strsiz;
};
static struct arch archs[] = {
"coff32-i386", "\x4c\x01", OBJ(COFF32, ARCH386, LITTLE_ENDIAN),
"coff32-z80", "\x5a\x80", OBJ(COFF32, ARCHZ80, LITTLE_ENDIAN),
NULL,
};
static void
pack_hdr(int order, unsigned char *buf, FILHDR *hdr)
{
int n;
n = pack(order,
buf,
"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);
}
static void
unpack_hdr(int order, unsigned char *buf, FILHDR *hdr)
{
int n;
n = unpack(order,
buf,
"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);
}
static void
pack_scn(int order, unsigned char *buf, SCNHDR *scn)
{
int n;
n = pack(order,
buf,
"'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);
}
static void
unpack_scn(int order, unsigned char *buf, SCNHDR *scn)
{
int n;
n = unpack(order,
buf,
"'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);
}
static void
pack_ent(int order, unsigned char *buf, SYMENT *ent)
{
int n;
char *s;
/* TODO: What happens with the union? */
n = pack(order,
buf,
"'8lsscc",
ent->n_name,
&ent->n_value,
&ent->n_scnum,
&ent->n_type,
&ent->n_sclass,
&ent->n_numaux);
assert(n == SYMESZ);
}
static void
unpack_ent(int order, unsigned char *buf, SYMENT *ent)
{
int n;
char *s;
n = unpack(order,
buf,
"'8lsscc",
ent->n_name,
&ent->n_value,
&ent->n_scnum,
&ent->n_type,
&ent->n_sclass,
&ent->n_numaux);
assert(n == SYMESZ);
s = ent->n_name;
if (!s[0] && !s[1] && !s[2] && !s[3])
unpack(order, "ll", buf, &ent->n_zeroes, &ent->n_offset);
}
static void
pack_aout(int order, unsigned char *buf, AOUTHDR *aout)
{
int n;
n = unpack(order,
buf,
"ssllllll",
aout->magic,
aout->vstamp,
aout->tsize,
aout->dsize,
aout->bsize,
aout->entry,
aout->text_start,
aout->data_start);
assert(n == AOUTSZ);
}
static void
unpack_aout(int order, unsigned char *buf, AOUTHDR *aout)
{
int n;
n = unpack(order,
buf,
"ssllllll",
&aout->magic,
&aout->vstamp,
&aout->tsize,
&aout->dsize,
&aout->bsize,
&aout->entry,
&aout->text_start,
&aout->data_start);
assert(n == AOUTSZ);
}
static void
unpack_reloc(int order, unsigned char *buf, RELOC *rel)
{
int n;
n = unpack(order,
buf,
"lls",
&rel->r_vaddr,
&rel->r_symndx,
&rel->r_type);
assert(n == RELSZ);
}
static void
pack_reloc(int order, unsigned char *buf, RELOC *rel)
{
int n;
n = pack(order,
buf,
"lls",
rel->r_vaddr,
rel->r_symndx,
rel->r_type);
assert(n == RELSZ);
}
static void
unpack_line(int order, unsigned char *buf, LINENO *lp)
{
int n;
n = unpack(order,
buf,
"lls",
&lp->l_symndx,
&lp->l_paddr,
&lp->l_lnno);
assert(n == LINESZ);
}
static void
pack_line(int order, unsigned char *buf, LINENO *lp)
{
int n;
n = pack(order,
buf,
"lls",
lp->l_symndx,
lp->l_paddr,
lp->l_lnno);
assert(n == LINESZ);
}
static int
probe(unsigned char *buf, char **name)
{
struct arch *ap;
for (ap = archs; ap->name; ap++) {
if (ap->magic[0] == buf[0] && ap->magic[1] == buf[1]) {
if (name)
*name = ap->name;
return ap->type;
}
}
return -1;
}
static char *
symname(Coff32 *coff, SYMENT *ent)
{
long off;
if (ent->n_zeroes != 0)
return ent->n_name;
off = ent->n_offset;
if (off >= coff->strsiz)
return NULL;
return &coff->strtbl[off];
}
static int
typeof(Coff32 *coff, SYMENT *ent)
{
int c;
SCNHDR *scn;
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:
if (ent->n_scnum > coff->hdr.f_nscns)
return -1;
scn = &coff->scns[ent->n_scnum-1];
flags = scn->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 int
loadsyms(Obj *obj)
{
int t;
long i;
char *s;
Symbol *sym;
SYMENT *ent;
Coff32 *coff = obj->data;
for (i = 0; i < coff->hdr.f_nsyms; i += ent->n_numaux + 1) {
ent = &coff->ents[i];
if ((t = typeof(coff, ent)) < 0)
return 0;
if ((s = symname(coff, ent)) == NULL)
return 0;
if ((sym = objlookup(obj, s)) == NULL)
return 0;
sym->type = t;
sym->value = ent->n_value;
sym->size = (sym->type == 'C') ? ent->n_value : 0;
}
return 1;
}
static int
readscns(Obj *obj, FILE *fp)
{
FILHDR *hdr;
struct coff32 *coff;
SCNHDR *scn;
long i;
unsigned char buf[SCNHSZ];
coff = obj->data;
hdr = &coff->hdr;
if (hdr->f_nscns > 0) {
scn = calloc(hdr->f_nscns, sizeof(*scn));
if (!scn)
return 0;
coff->scns = scn;
}
if (fseek(fp, hdr->f_opthdr, SEEK_CUR) < 0)
return 0;
for (i = 0; i < hdr->f_nscns; i++) {
if (fread(buf, SCNHSZ, 1, fp) < 0)
return 0;
unpack_scn(ORDER(obj->type), buf, &scn[i]);
}
return 1;
}
static int
readents(Obj *obj, FILE *fp)
{
FILHDR *hdr;
struct coff32 *coff;
SYMENT *ent;
long i;
unsigned char buf[SYMESZ];
coff = obj->data;
hdr = &coff->hdr;
if (hdr->f_nsyms == 0)
return 1;
ent = calloc(hdr->f_nsyms, sizeof(*ent));
if (!ent)
return 0;
coff->ents = ent;
if (!objpos(obj, fp, hdr->f_symptr))
return 0;
for (i = 0; i < hdr->f_nsyms; i++) {
if (fread(buf, SYMESZ, 1, fp) != 1)
return 0;
unpack_ent(ORDER(obj->type), buf, &ent[i]);
}
return 1;
}
static int
readreloc(Obj *obj, FILE *fp)
{
int i, j;
RELOC **rels, *rp;
SCNHDR *scn;
FILHDR *hdr;
struct coff32 *coff;
unsigned char buf[RELSZ];
coff = obj->data;
hdr = &coff->hdr;
rels = calloc(obj->nsecs, sizeof(*rels));
if (!rels)
return 0;
coff->rels = rels;
for (i = 0; i < hdr->f_nscns; i++) {
scn = &coff->scns[i];
if (scn->s_nlnno == 0)
continue;
if (!objpos(obj, fp, scn->s_relptr))
return 0;
rp = calloc(scn->s_nrelloc, sizeof(RELOC));
if (!rp)
return 0;
rels[i] = rp;
for (j = 0; j < scn->s_nrelloc; j++) {
if (fread(buf, RELSZ, 1, fp) != 1)
return 0;
unpack_reloc(ORDER(obj->type), buf, &rp[i]);
}
}
return 1;
}
static int
readlines(Obj *obj, FILE *fp)
{
int i,j;
LINENO **lines, *lp;
FILHDR *hdr;
SCNHDR *scn;
struct coff32 *coff;
unsigned char buf[LINESZ];
coff = obj->data;
hdr = &coff->hdr;
lines = calloc(sizeof(lp), hdr->f_nscns);
if (!lines)
return 0;
coff->lines = lines;
for (i = 0; i < hdr->f_nscns; i++) {
scn = &coff->scns[i];
if (scn->s_nlnno == 0)
continue;
lp = calloc(sizeof(*lp), scn->s_nlnno);
if (!lp)
return 0;
lines[i] = lp;
for (j = 0; j < scn->s_nlnno; j++) {
if (!objpos(obj, fp, scn->s_lnnoptr))
return 0;
if (fread(buf, LINESZ, 1, fp) == 1)
return 0;
unpack_line(ORDER(obj->type), buf, &lp[j]);
}
}
return 1;
}
static int
readstr(Obj *obj, FILE *fp)
{
FILHDR *hdr;
struct coff32 *coff;
long siz;
char *str;
unsigned char buf[10];
coff = obj->data;
hdr = &coff->hdr;
if (hdr->f_nsyms == 0)
return 1;
if (fread(buf, 4, 1, fp) != 1)
return 0;
unpack(ORDER(obj->type), buf, "l", &siz);
siz -= 4;
if (siz < 0)
return 0;
if (siz > 0) {
if (siz > SIZE_MAX)
return 0;
str = malloc(siz);
if (!str)
return 0;
coff->strtbl = str;
coff->strsiz = siz;
if (fread(str, siz, 1, fp) != 1)
return 0;
}
return 1;
}
static int
readhdr(Obj *obj, FILE *fp)
{
FILHDR *hdr;
struct coff32 *coff;
unsigned char buf[FILHSZ];
coff = obj->data;
hdr = &coff->hdr;
if (fread(buf, FILHSZ, 1, fp) != 1)
return 0;
unpack_hdr(ORDER(obj->type), buf, hdr);
return 1;
}
static int
loadsections(Obj *obj, FILE *fp)
{
size_t len;
unsigned sflags, type;
unsigned long flags;
FILHDR *hdr;
struct coff32 *coff;
SCNHDR *scn;
Section *secs, *sp;
coff = obj->data;
hdr = &coff->hdr;
scn = coff->scns;
secs = malloc(sizeof(Section) * hdr->f_nscns);
if (!secs)
return 0;
obj->sections = secs;
for (sp = secs; sp < &secs[hdr->f_nscns]; sp++) {
flags = scn->s_flags;
if (flags & STYP_TEXT) {
type = 'T';
sflags = SALLOC | SRELOC | SLOAD | SEXEC | SREAD;
if (flags & STYP_NOLOAD)
sflags |= SSHARED;
} else if (flags & STYP_DATA) {
type = 'D';
sflags = SALLOC | SRELOC | SLOAD | SWRITE | SREAD;
if (flags & STYP_NOLOAD)
sflags |= SSHARED;
} else if (flags & STYP_BSS) {
type = 'B';
sflags = SALLOC | SREAD | SWRITE;
} else if (flags & STYP_INFO) {
type = 'N';
sflags = 0;
} else if (flags & STYP_LIB) {
type = 'T';
sflags = SRELOC;
} else if (flags & STYP_DSECT) {
type = 'D';
sflags = SRELOC;
} else if (flags & STYP_PAD) {
type = 'D';
sflags = SLOAD;
} else {
type = 'D'; /* We assume that STYP_REG is data */
sflags = SALLOC | SRELOC | SLOAD | SWRITE | SREAD;
}
if (flags & STYP_NOLOAD)
sflags &= ~SLOAD;
len = strlen(scn->s_name) + 1;
if ((sp->name = malloc(len)) == NULL)
return 0;
memcpy(sp->name, scn->s_name, len);
sp->fp = fp;
sp->offset = scn->s_scnptr;
sp->size = scn->s_size;
sp->type = type;
obj->nsecs++;
}
return 1;
}
static int
readaout(Obj *obj, FILE *fp)
{
FILHDR *hdr;
struct coff32 *coff;
unsigned char buf[AOUTSZ];
coff = obj->data;
hdr = &coff->hdr;
if (hdr->f_opthdr == 0)
return 1;
if (fread(buf, AOUTSZ, 1, fp) != 1)
return 0;
coff->aout = malloc(sizeof(AOUTHDR));
if (!coff->aout)
return 0;
unpack_aout(ORDER(obj->type), buf, coff->aout);
return 1;
}
static int
read(Obj *obj, FILE *fp)
{
/* TODO: Add validation of the different fields */
if (fgetpos(fp, &obj->pos))
goto error;
if (!readhdr(obj, fp))
goto error;
if (!readaout(obj, fp))
goto error;
if (!readscns(obj, fp))
goto error;
if (!readents(obj, fp))
goto error;
if (!readstr(obj, fp))
goto error;
if (!readreloc(obj, fp))
goto error;
if (!readlines(obj, fp))
goto error;
if (!loadsyms(obj))
goto error;
if (!loadsections(obj, fp))
goto error;
return 0;
error:
objreset(obj);
return -1;
}
static int
writehdr(Obj *obj, FILE *fp)
{
FILHDR *hdr;
struct coff32 *coff;
unsigned char buf[FILHSZ];
coff = obj->data;
hdr = &coff->hdr;
pack_hdr(ORDER(obj->type), buf, hdr);
if (fwrite(buf, FILHSZ, 1, fp) != 1)
return 0;
return 1;
}
static int
writescns(Obj *obj, FILE *fp)
{
/* TODO */
}
static int
writeents(Obj *obj, FILE *fp)
{
/* TODO */
}
static int
writestr(Obj *obj, FILE *fp)
{
/* TODO */
}
static int
write(Obj *obj, FILE *fp)
{
struct coff32 *coff;
coff = obj->data;
coff->strsiz = 0;
free(coff->strtbl);
if (!writehdr(obj, fp))
return -1;
if (!writescns(obj, fp))
return -1;
if (!writeents(obj, fp))
return -1;
if (!writestr(obj, fp))
return -1;
return 0;
}
static void
del(Obj *obj)
{
struct coff32 *coff = obj->data;
if (coff) {
free(coff->scns);
free(coff->ents);
free(coff->strtbl);
}
free(obj->data);
obj->data = NULL;
}
static int
new(Obj *obj)
{
struct coff32 *coff;
if ((coff = calloc(1, sizeof(*coff))) == NULL)
return -1;
obj->data = coff;
return 0;
}
static void
strip(Obj *obj)
{
struct coff32 *coff = obj->data;
FILHDR *hdr;
hdr = &coff->hdr;
free(coff->ents);
coff->ents = NULL;
hdr->f_nsyms = 0;
hdr->f_symptr = 0;
}
static long
mkindex(int type, long nsymbols, Symdef *head, FILE *fp)
{
return coff32idx(BIG_ENDIAN, nsymbols, head, fp);
}
struct format objcoff32 = {
.probe = probe,
.new = new,
.del = del,
.read = read,
.write = write,
.strip = strip,
.index = mkindex,
};