/* output_xfile.c Sharp X68000 Xfile output driver for vasm */ /* (c) in 2018 by Frank Wille */ #include "vasm.h" #include "output_xfile.h" #if defined(OUTXFIL) && defined(VASM_CPU_M68K) static char *copyright="vasm xfile output module 0.1 (c) 2018 Frank Wille"; static int max_relocs_per_atom; static section *sections[3]; static utaddr secsize[3]; static utaddr secoffs[3]; static utaddr sdabase,lastoffs; #define SECT_ALIGN 2 /* Xfile sections have to be aligned to 16 bits */ static int get_sec_type(section *s) /* scan section attributes for type, 0=text, 1=data, 2=bss */ { char *a = s->attr; 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 void xfile_initwrite(section *sec,symbol *sym) { int nsyms = 0; int i; /* find exactly one .text, .data and .bss section for xfile */ sections[_TEXT] = sections[_DATA] = sections[_BSS] = NULL; secsize[_TEXT] = secsize[_DATA] = secsize[_BSS] = 0; for (; sec; sec=sec->next) { /* 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 ((sec->pc - sec->org) > 0 || (sec->flags & HAS_SYMBOLS)) { i = get_sec_type(sec); if (!sections[i]) { sections[i] = sec; secsize[i] = (get_sec_size(sec) + SECT_ALIGN - 1) / SECT_ALIGN * SECT_ALIGN; sec->idx = i; /* section index 0:text, 1:data, 2:bss */ } else output_error(7,sec->name); } } secoffs[_TEXT] = 0; secoffs[_DATA] = secsize[_TEXT] + balign(secsize[_TEXT],SECT_ALIGN); secoffs[_BSS] = secoffs[_DATA] + secsize[_DATA] + balign(secsize[_DATA],SECT_ALIGN); /* define small data base as .data+32768 @@@FIXME! */ sdabase = secoffs[_DATA] + 0x8000; } static void xfile_header(FILE *f,unsigned long tsize,unsigned long dsize, unsigned long bsize) { XFILE hdr; memset(&hdr,0,sizeof(XFILE)); setval(1,hdr.x_id,2,0x4855); /* "HU" ID for Human68k */ setval(1,hdr.x_textsz,4,tsize); setval(1,hdr.x_datasz,4,dsize); setval(1,hdr.x_heapsz,4,bsize); /* x_relocsz and x_syminfsz will be patched later */ fwdata(f,&hdr,sizeof(XFILE)); } static void checkdefined(symbol *sym) { if (sym->type == IMPORT) output_error(6,sym->name); } static taddr xfile_sym_value(symbol *sym) { taddr val = get_sym_value(sym); /* all sections form a contiguous block, so add section offset */ if (sym->type==LABSYM && sym->sec!=NULL) val += secoffs[sym->sec->idx]; return val; } static int write_reloc68k(atom *a,rlist *rl,int signedval,taddr val) { nreloc *nrel; char *p; if (rl->type > LAST_STANDARD_RELOC) { unsupp_reloc_error(rl); return 0; } nrel = (nreloc *)rl->reloc; if (field_overflow(signedval,nrel->size,val)) { output_atom_error(12,a,rl->type,(unsigned long)nrel->mask,nrel->sym->name, (unsigned long)nrel->addend,nrel->size); return 0; } if (a->type == DATA) p = a->content.db->data + nrel->byteoffset; else if (a->type == SPACE) p = (char *)a->content.sb->fill; /* @@@ ignore offset completely? */ else return 1; setbits(1,p,(nrel->bitoffset+nrel->size+7)&~7,nrel->bitoffset,nrel->size,val); return 1; } static void do_relocs(taddr pc,atom *a) /* Try to resolve all relocations in a DATA or SPACE atom. Very simple implementation which can only handle basic 68k relocs. */ { int rcnt = 0; section *sec; rlist *rl; if (a->type == DATA) rl = a->content.db->relocs; else if (a->type == SPACE) rl = a->content.sb->relocs; else rl = NULL; while (rl) { switch (rl->type) { case REL_SD: checkdefined(((nreloc *)rl->reloc)->sym); write_reloc68k(a,rl,1, (xfile_sym_value(((nreloc *)rl->reloc)->sym) + nreloc_real_addend(rl->reloc)) - sdabase); break; case REL_PC: checkdefined(((nreloc *)rl->reloc)->sym); write_reloc68k(a,rl,1, (xfile_sym_value(((nreloc *)rl->reloc)->sym) + nreloc_real_addend(rl->reloc)) - (pc + ((nreloc *)rl->reloc)->byteoffset)); break; case REL_ABS: rcnt++; checkdefined(((nreloc *)rl->reloc)->sym); sec = ((nreloc *)rl->reloc)->sym->sec; if (!write_reloc68k(a,rl,0, secoffs[sec?sec->idx:0] + ((nreloc *)rl->reloc)->addend)) break; /* field overflow */ if (((nreloc *)rl->reloc)->size == 32) break; /* only support 32-bit absolute */ default: unsupp_reloc_error(rl); break; } if (a->type == SPACE) break; /* all SPACE relocs are identical, one is enough */ rl = rl->next; } if (rcnt > max_relocs_per_atom) max_relocs_per_atom = rcnt; } static void xfile_writesection(FILE *f,section *sec,taddr sec_align) { if (sec) { utaddr pc = secoffs[sec->idx]; utaddr npc; atom *a; for (a=sec->first; a; a=a->next) { npc = fwpcalign(f,a,sec,pc); do_relocs(npc,a); 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 size_t xfile_symboltable(FILE *f,symbol *sym) /* The symbol table only contains expression (absolute) and label symbols, but doesn't differentiate between local and global scope. */ { static int labtype[] = { XSYM_TEXT,XSYM_DATA,XSYM_BSS }; size_t len = 0; char *p; for (; sym; sym=sym->next) { if (!(sym->flags & VASMINTERN) && (sym->type==LABSYM || sym->type==EXPRESSION)) { if (sym->flags & WEAK) output_error(10,sym->name); /* weak symbol not supported */ /* symbol type */ if (sym->type == LABSYM) fw16(f,labtype[sym->sec->idx],1); else /* EXPRESSION */ fw16(f,XSYM_ABS,1); /* symbol value */ fw32(f,xfile_sym_value(sym),1); len += 6; /* 2 bytes type, 4 bytes value */ /* symbol name - 16-bit aligned */ p = sym->name; do { fw8(f,*p); len++; } while (*p++); if (len & 1) { fw8(f,0); len++; } } } return len; } static int offscmp(const void *offs1,const void *offs2) { return *(int *)offs1 - *(int *)offs2; } static size_t xfile_writerelocs(FILE *f,section *sec) { int *sortoffs = mymalloc(max_relocs_per_atom*sizeof(int)); size_t sz = 0; if (sec) { utaddr pc = secoffs[sec->idx]; utaddr npc; atom *a; rlist *rl; for (a=sec->first; a; a=a->next) { int nrel = 0; npc = pcalign(a,pc); if (a->type == DATA) rl = a->content.db->relocs; else if (a->type == SPACE) rl = a->content.sb->relocs; else rl = NULL; while (rl) { if (rl->type==REL_ABS && ((nreloc *)rl->reloc)->size==32) sortoffs[nrel++] = ((nreloc *)rl->reloc)->byteoffset; rl = rl->next; } if (nrel) { int i; /* first sort the atom's relocs */ if (nrel > 1) qsort(sortoffs,nrel,sizeof(int),offscmp); /* write distances in bytes between them */ for (i=0; i 0xffff) { /* write a large distance >= 64K */ fw16(f,1,1); fw32(f,diff,1); sz += 6; } else { fw16(f,diff,1); sz += 2; } lastoffs = newoffs; } } pc = npc + atom_size(a,sec,npc); } } myfree(sortoffs); return sz; } static void write_output(FILE *f,section *sec,symbol *sym) { size_t relocsz,syminfsz; xfile_initwrite(sec,sym); max_relocs_per_atom = 1; xfile_header(f,secsize[_TEXT],secsize[_DATA],secsize[_BSS]); xfile_writesection(f,sections[_TEXT],SECT_ALIGN); xfile_writesection(f,sections[_DATA],SECT_ALIGN); relocsz = xfile_writerelocs(f,sections[_TEXT]); relocsz += xfile_writerelocs(f,sections[_DATA]); syminfsz = no_symbols ? 0 : xfile_symboltable(f,sym); /* finally patch reloc- and symbol-table size into the header */ fseek(f,offsetof(XFILE,x_relocsz),SEEK_SET); fw32(f,relocsz,1); fseek(f,offsetof(XFILE,x_syminfsz),SEEK_SET); fw32(f,syminfsz,1); } static int output_args(char *p) { #if 0 if (!strncmp(p,"-startoffs=",11)) { startoffs = atoi(p+11); return 1; } #endif return 0; } int init_output_xfile(char **cp,void (**wo)(FILE *,section *,symbol *), int (**oa)(char *)) { *cp = copyright; *wo = write_output; *oa = output_args; return 1; } #else int init_output_xfile(char **cp,void (**wo)(FILE *,section *,symbol *), int (**oa)(char *)) { return 0; } #endif