/* output_aout.c a.out output driver for vasm */ /* (c) in 2008-2016 by Frank Wille */ #include "vasm.h" #include "output_aout.h" #if defined(OUTAOUT) && defined(MID) static char *copyright="vasm a.out output module 0.7c (c) 2008-2016 Frank Wille"; static section *sections[3]; static utaddr secsize[3]; static utaddr secoffs[3]; static int sectype[] = { N_TEXT, N_DATA, N_BSS }; static int secweak[] = { N_WEAKT, N_WEAKD, N_WEAKB }; static struct SymTabList aoutsymlist; static struct StrTabList aoutstrlist; static struct list treloclist; static struct list dreloclist; static int mid = -1; static int isPIC = 1; #define SECT_ALIGN 4 /* .text and .data are aligned to 32 bits */ static int get_sec_type(section *s) /* scan section attributes for type, 0=text, 1=data, 2=bss, -1: ORG-section at an absolute address */ { char *a = s->attr; if (s->flags & ABSOLUTE) return -1; while (*a) { switch (*a++) { case 'c': return _TEXT; case 'd': return _DATA; case 'u': return _BSS; } } output_error(3,s->attr); /* section attributes not supported */ return 0; } static int aout_getinfo(symbol *sym) { int type; switch (TYPE(sym)) { case TYPE_UNKNOWN: case TYPE_FILE: case TYPE_SECTION: /* this will be ignored later */ type = AUX_UNKNOWN; break; case TYPE_OBJECT: type = AUX_OBJECT; break; case TYPE_FUNCTION: type = AUX_FUNC; break; default: ierror(0); break; } return type; } static int aout_getbind(symbol *sym) { if (sym->flags & WEAK) return BIND_WEAK; else if (sym->type!=IMPORT && !(sym->flags & EXPORT)) return BIND_LOCAL; else if ((sym->type!=IMPORT && (sym->flags & EXPORT)) || (sym->type==IMPORT && (sym->flags & COMMON))) return BIND_GLOBAL; else ierror(0); return -1; } static uint32_t aoutstd_getrinfo(rlist **rl,int xtern,char *sname,int be) /* Convert vasm relocation type into standard a.out relocations, */ /* as used by M68k and x86 targets. */ /* For xtern=-1, return true when this relocation requires a base symbol. */ { nreloc *nr; if (nr = (nreloc *)(*rl)->reloc) { rlist *rl2 = (*rl)->next; uint32_t r=0,s=4; nreloc *nr2; int b=0; switch ((*rl)->type) { case REL_ABS: b=-1; break; case REL_PC: b=RSTDB_pcrel; break; case REL_SD: b=RSTDB_baserel; break; default: goto unsupp_reloc; } if (xtern == -1) /* just query symbol-based relocation */ return b==RSTDB_baserel || b==RSTDB_jmptable; nr2 = rl2!=NULL ? (nreloc *)rl2->reloc : NULL; if (nr->bitoffset==0 && (nr2==NULL || nr2->byteoffset!=nr->byteoffset) && (nr->mask & MAKEMASK(nr->size)) == MAKEMASK(nr->size)) { switch (nr->size) { case 8: s=0; break; case 16: s=1; break; case 32: s=2; break; } } #ifdef VASM_CPU_JAGRISC else if (nr->size==16 && nr2!=NULL && nr2->size==16 && nr2->byteoffset==nr->byteoffset && ((nr->mask==0xffff && nr2->mask==0xffff0000) || (nr->mask==0xffff0000 && nr2->mask==0xffff)) && ((nr->bitoffset==0 && nr2->bitoffset==16) || (nr->bitoffset==16 && nr2->bitoffset==0))) { /* Jaguar RISC MOVEI instruction with swapped words, indicated by a set RSTDB_copy bit. */ b = RSTDB_copy; s = 2; *rl = (*rl)->next; /* skip additional entry */ } #endif if (b!=0 && s<4) { if (b > 0) setbits(be,&r,sizeof(r)<<3,(unsigned)b,1,1); setbits(be,&r,sizeof(r)<<3,RSTDB_length,RSTDS_length,s); setbits(be,&r,sizeof(r)<<3,RSTDB_extern,RSTDS_extern,xtern?1:0); return readbits(be,&r,sizeof(r)<<3,RELB_reloc,RELS_reloc); } } unsupp_reloc: unsupp_reloc_error(*rl); return ~0; } static void aout_initwrite(section *firstsec) { section *sec; if (mid == -1) mid = MID; initlist(&aoutstrlist.l); aoutstrlist.hashtab = mycalloc(STRHTABSIZE*sizeof(struct StrTabNode *)); aoutstrlist.nextoffset = 4; /* first string is always at offset 4 */ initlist(&aoutsymlist.l); aoutsymlist.hashtab = mycalloc(SYMHTABSIZE*sizeof(struct SymbolNode *)); aoutsymlist.nextindex = 0; initlist(&treloclist); initlist(&dreloclist); /* find exactly one .text, .data and .bss section for a.out */ sections[_TEXT] = sections[_DATA] = sections[_BSS] = NULL; secsize[_TEXT] = secsize[_DATA] = secsize[_BSS] = 0; for (sec=firstsec; sec; sec=sec->next) { int i; /* section size is assumed to be in in (sec->pc - sec->org), otherwise we would have to calculate it from the atoms and store it there */ if (get_sec_size(sec) > 0 || (sec->flags & HAS_SYMBOLS)) { i = get_sec_type(sec); if (i < 0) continue; /* ignore ORG sections for later */ if (!sections[i]) { sections[i] = sec; secsize[i] = get_sec_size(sec); sec->idx = i; /* section index 0:text, 1:data, 2:bss */ } else output_error(7,sec->name); } } /* now scan for absolute ORG-sections and add their aligned size to .text */ for (sec=firstsec; sec; sec=sec->next) { if (sec->flags & ABSOLUTE) secsize[_TEXT] += balign(secsize[_TEXT],sec->align) + get_sec_size(sec); } secoffs[_TEXT] = 0; secoffs[_DATA] = secsize[_TEXT] + balign(secsize[_TEXT],SECT_ALIGN); secoffs[_BSS] = secoffs[_DATA] + secsize[_DATA] + balign(secsize[_DATA],SECT_ALIGN); } static uint32_t aout_addstr(char *s) /* add a new symbol name to the string table and return its offset */ { struct StrTabNode **chain; struct StrTabNode *sn; if (s == NULL) return 0; if (*s == '\0') return 0; /* search string in hash table */ chain = &aoutstrlist.hashtab[hashcode(s)%STRHTABSIZE]; while (sn = *chain) { if (!strcmp(s,sn->str)) return (sn->offset); /* it's already in, return offset */ chain = &sn->hashchain; } /* new string table entry */ *chain = sn = mymalloc(sizeof(struct StrTabNode)); sn->hashchain = NULL; sn->str = s; sn->offset = aoutstrlist.nextoffset; addtail(&aoutstrlist.l,&sn->n); aoutstrlist.nextoffset += strlen(s) + 1; return sn->offset; } static struct SymbolNode *aout_addsym(char *name,uint8_t type,int8_t other, int16_t desc,uint32_t value,int be) /* append a new symbol to the symbol list */ { struct SymbolNode *sym = mycalloc(sizeof(struct SymbolNode)); sym->name = name!=NULL ? name : emptystr; sym->index = aoutsymlist.nextindex++; setval(be,&sym->s.n_strx,4,aout_addstr(name)); sym->s.n_type = type; sym->s.n_other = other; setval(be,&sym->s.n_desc,2,desc); setval(be,&sym->s.n_value,4,value); addtail(&aoutsymlist.l,&sym->n); return sym; } static uint32_t aout_addsymhash(char *name,taddr value,int bind, int info,int type,int desc,int be) /* add a new symbol, return its symbol table index */ { struct SymbolNode **chain,*sym; chain = &aoutsymlist.hashtab[hashcode(name?name:emptystr)%SYMHTABSIZE]; while (sym = *chain) chain = &sym->hashchain; /* new symbol table entry */ *chain = sym = aout_addsym(name,type,((bind&0xf)<<4)|(info&0xf), desc,value,be); return sym->index; } static int aout_findsym(char *name,int be) /* find a symbol by its name, return symbol table index or -1 */ { struct SymbolNode **chain = &aoutsymlist.hashtab[hashcode(name)%SYMHTABSIZE]; struct SymbolNode *sym; while (sym = *chain) { if (!strcmp(name,sym->name) && !(sym->s.n_type & N_STAB)) return ((int)sym->index); chain = &sym->hashchain; } return (-1); } static void aout_symconvert(symbol *sym,int symbind,int syminfo,int be) /* convert vasm symbol into a.out symbol(s) */ { taddr val = get_sym_value(sym); taddr size = get_sym_size(sym); int ext = (symbind == BIND_GLOBAL) ? N_EXT : 0; int type = 0; if (TYPE(sym) == TYPE_SECTION) { return; /* section symbols are ignored in a.out! */ } else if (TYPE(sym) == TYPE_FILE) { type = N_FN | N_EXT; /* special case: file name symbol */ size = 0; } else { if (sym->flags & COMMON) { /* common symbol */ #if 0 /* GNU binutils prefers N_UNDF with val!=0 instead of N_COMM! */ type = N_COMM | ext; #else type = N_UNDF | N_EXT; #endif val = size; size = 0; } else if (sym->flags & WEAK) { /* weak symbol */ switch (sym->type) { case LABSYM: type=secweak[sym->sec->idx]; break; case IMPORT: type=N_WEAKU; break; case EXPRESSION: type=N_WEAKA; break; default: ierror(0); break; } } else if (sym->sec) { /* address symbol */ if (!(sym->sec->flags & ABSOLUTE)) { type = sectype[sym->sec->idx] | ext; val += secoffs[sym->sec->idx]; /* a.out requires to add sec. offset */ } else /* absolute ORG section: convert labels to ABS symbols */ type = N_ABS | ext; } else if (sym->type==EXPRESSION) { if (sym->flags & EXPORT) { /* absolute symbol */ type = N_ABS | ext; } else return; /* ignore local expressions */ } /* @@@ else if (indirect symbols?) { aout_addsymhash(sym->name,0,symbind,0,N_INDR|ext,0,be); aout_addsymhash(sym->indir_name,0,0,0,N_UNDF|N_EXT,0,be); return; }*/ else ierror(0); } aout_addsymhash(sym->name,val,symbind,syminfo,type,0,be); if (size) { /* append N_SIZE symbol declaring the previous symbol's size */ aout_addsymhash(sym->name,size,symbind,syminfo,N_SIZE,0,be); } } static void aout_addsymlist(symbol *sym,int bind,int type,int be) /* add all symbols with specified bind and type to the a.out symbol list */ { for (; sym; sym=sym->next) { /* ignore symbols preceded by a '.' and internal symbols */ if ((sym->type!=IMPORT || (sym->flags&WEAK) || (sym->flags&COMMON)) && *sym->name != '.' && *sym->name!=' ' && !(sym->flags&VASMINTERN)) { int syminfo = aout_getinfo(sym); int symbind = aout_getbind(sym); if (symbind == bind && (!type || (syminfo == type))) { aout_symconvert(sym,symbind,syminfo,be); } } } } static void aout_debugsyms(int be) /* add stabs to the a.out symbol list */ { struct stabdef *nlist = first_nlist; uint32_t val; while (nlist != NULL) { val = nlist->value; if (nlist->base != NULL) { /* include section base offset in symbol value */ if (LOCREF(nlist->base)) { if (!(nlist->base->sec->flags & ABSOLUTE)) val += secoffs[nlist->base->sec->idx]; } else ierror(0); /* @@@ handle external references! How? */ } aout_addsym(nlist->name.ptr,nlist->type,nlist->other,nlist->desc,val,be); nlist = nlist->next; } } static void aout_addreloclist(struct list *rlst,uint32_t raddr, uint32_t rindex,uint32_t rinfo,int be) /* add new relocation_info to .text or .data reloc-list */ { struct RelocNode *rn = mymalloc(sizeof(struct RelocNode)); setval(be,rn->r.r_address,4,raddr); setbits(be,rn->r.r_info,32,RELB_symbolnum,RELS_symbolnum,rindex); setbits(be,rn->r.r_info,32,RELB_reloc,RELS_reloc,rinfo); addtail(rlst,&rn->n); if (isPIC && !readbits(be,rn->r.r_info,32,RSTDB_pcrel,1) && !readbits(be,rn->r.r_info,32,RSTDB_baserel,1)) { /* the relocation is probably absolute, so it is no PIC anymore */ isPIC = 0; } } static uint32_t aout_convert_rlist(int be,atom *a,int secid, struct list *rlst,taddr pc, uint32_t (*getrinfo) (rlist **,int,char *,int)) /* convert all of an atom's relocs into a.out relocations */ { uint32_t rsize = 0; rlist *rl; if (a->type == DATA) rl = a->content.db->relocs; else if (a->type == SPACE) rl = a->content.sb->relocs; else rl = NULL; if (rl == NULL) return 0; /* no relocs or not the right atom type */ do { nreloc *r = (nreloc *)rl->reloc; symbol *refsym = r->sym; taddr val = get_sym_value(refsym); taddr add = nreloc_real_addend(r); #if SDAHACK int based = getrinfo(&rl,-1,sections[secid]->name,be) != 0; #endif if (LOCREF(refsym)) { /* this is a local relocation */ int rsecid = refsym->sec->idx; aout_addreloclist(rlst,pc+r->byteoffset,sectype[rsecid], getrinfo(&rl,0,sections[secid]->name,be), be); #if SDAHACK if (!based) /* @@@ 'based' does not really happen under Unix */ #endif val += secoffs[rsecid]; rsize += sizeof(struct relocation_info); } else if (EXTREF(refsym)) { /* this is an external symbol reference */ int symidx; if ((symidx = aout_findsym(refsym->name,be)) == -1) symidx = aout_addsymhash(refsym->name,0,0,0,N_UNDF|N_EXT,0,be); aout_addreloclist(rlst,pc+r->byteoffset,symidx, getrinfo(&rl,1,sections[secid]->name,be), be); rsize += sizeof(struct relocation_info); } else ierror(0); /* patch addend for a.out */ if (rl->type == REL_PC) val -= pc + r->byteoffset; if (a->type == DATA) setval(be,a->content.db->data+r->byteoffset,r->size>>3,val+add); else if (a->type==SPACE && a->content.sb->space!=0) { setval(be,a->content.sb->fill,r->size>>3,val+add); a->content.sb->space = 0; /* we only need to patch 'fill' once */ } } while (rl = rl->next); return rsize; } static uint32_t aout_addrelocs(int be,int secid,struct list *rlst, uint32_t (*getrinfo)(rlist **,int,char *,int)) /* creates a.out relocations for a single section (.text or .data) */ { uint32_t rtabsize=0; if (sections[secid]) { atom *a; taddr pc=0,npc; for (a=sections[secid]->first; a; a=a->next) { npc = pcalign(a,pc); rtabsize += aout_convert_rlist(be,a,secid,rlst,npc,getrinfo); pc = npc + atom_size(a,sections[secid],npc); } } return rtabsize; } static void aout_header(FILE *f,uint32_t mag,uint32_t flag, uint32_t tsize,uint32_t dsize,uint32_t bsize, uint32_t syms,uint32_t entry, uint32_t trsize,uint32_t drsize,int be) /* write an a.out header */ { struct aout_hdr h; SETMIDMAG(&h,mag,mid,flag); setval(be,h.a_text,4,tsize); setval(be,h.a_data,4,dsize); setval(be,h.a_bss,4,bsize); setval(be,h.a_syms,4,syms); setval(be,h.a_entry,4,entry); setval(be,h.a_trsize,4,trsize); setval(be,h.a_drsize,4,drsize); fwdata(f,&h,sizeof(struct aout_hdr)); } static void aout_writesection(FILE *f,section *sec,taddr sec_align) { if (sec) { atom *a; taddr pc=0,npc; for (a=sec->first; a; a=a->next) { npc = fwpcalign(f,a,sec,pc); if (a->type == DATA) fwdata(f,a->content.db->data,a->content.db->size); else if (a->type == SPACE) fwsblock(f,a->content.sb); pc = npc + atom_size(a,sec,npc); } fwalign(f,pc,sec_align); } } static void aout_writeorg(FILE *f,section *sec,taddr sec_align) /* write all absolute ORG-sections appended to .text */ { taddr pc = get_sec_size(sections[_TEXT]); taddr npc; atom *a; for (; sec; sec=sec->next) { if (sec->flags & ABSOLUTE) { fwalign(f,pc,sec->align); for (a=sec->first; a; a=a->next) { npc = fwpcalign(f,a,sec,pc); if (a->type == DATA) fwdata(f,a->content.db->data,a->content.db->size); else if (a->type == SPACE) fwsblock(f,a->content.sb); pc = npc + atom_size(a,sec,npc); } } } fwalign(f,pc,sec_align); } void aout_writerelocs(FILE *f,struct list *l) { struct RelocNode *rn; while (rn = (struct RelocNode *)remhead(l)) fwdata(f,&rn->r,sizeof(struct relocation_info)); } void aout_writesymbols(FILE *f) { struct SymbolNode *sym; while (sym = (struct SymbolNode *)remhead(&aoutsymlist.l)) fwdata(f,&sym->s,sizeof(struct nlist32)); } void aout_writestrings(FILE *f,int be) { if (aoutstrlist.nextoffset > 4) { struct StrTabNode *stn; fw32(f,aoutstrlist.nextoffset,be); while (stn = (struct StrTabNode *)remhead(&aoutstrlist.l)) fwdata(f,stn->str,strlen(stn->str)+1); } } static void write_output(FILE *f,section *sec,symbol *sym) { int be = BIGENDIAN; uint32_t trsize,drsize; aout_initwrite(sec); aout_addsymlist(sym,BIND_GLOBAL,0,be); aout_addsymlist(sym,BIND_WEAK,0,be); if (!no_symbols) { aout_addsymlist(sym,BIND_LOCAL,0,be); aout_debugsyms(be); } trsize = aout_addrelocs(be,_TEXT,&treloclist,aoutstd_getrinfo); drsize = aout_addrelocs(be,_DATA,&dreloclist,aoutstd_getrinfo); aout_header(f,OMAGIC,isPIC?EX_PIC:0, secsize[_TEXT] + balign(secsize[_TEXT],SECT_ALIGN), secsize[_DATA] + balign(secsize[_DATA],SECT_ALIGN), secsize[_BSS], aoutsymlist.nextindex * sizeof(struct nlist32), 0,trsize,drsize,be); aout_writesection(f,sections[_TEXT],0); aout_writeorg(f,sec,SECT_ALIGN); aout_writesection(f,sections[_DATA],SECT_ALIGN); aout_writerelocs(f,&treloclist); aout_writerelocs(f,&dreloclist); aout_writesymbols(f); aout_writestrings(f,be); } static int output_args(char *p) { if (!strncmp(p,"-mid=",5)) { mid = atoi(p+5); return 1; } return 0; } int init_output_aout(char **cp,void (**wo)(FILE *,section *,symbol *), int (**oa)(char *)) { *cp = copyright; *wo = write_output; *oa = output_args; return 1; } #else int init_output_aout(char **cp,void (**wo)(FILE *,section *,symbol *), int (**oa)(char *)) { return 0; } #endif