/* output_tos.c Atari TOS executable output driver for vasm */ /* (c) in 2009-2016 by Frank Wille */ #include "vasm.h" #include "output_tos.h" #if defined(OUTTOS) && defined(VASM_CPU_M68K) static char *copyright="vasm tos output module 1.0a (c) 2009-2016 Frank Wille"; int tos_hisoft_dri = 1; static int tosflags,textbasedsyms; 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 /* TOS 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 int tos_initwrite(section *sec,symbol *sym) { int nsyms = 0; int i; /* 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; 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); } } max_relocs_per_atom = 1; 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; /* count symbols */ for (; sym; sym=sym->next) { /* ignore symbols preceded by a '.' and internal symbols */ if (*sym->name!='.' && *sym->name!=' ') { if (!(sym->flags & (VASMINTERN|COMMON)) && sym->type == LABSYM) { nsyms++; if ((strlen(sym->name) > DRI_NAMELEN) && tos_hisoft_dri) nsyms++; /* extra symbol for long name */ } } else { if (!strcmp(sym->name," TOSFLAGS")) { if (tosflags == 0) /* not defined by command line? */ tosflags = (int)get_sym_value(sym); } sym->flags |= VASMINTERN; } } return no_symbols ? 0 : nsyms; } static void tos_header(FILE *f,unsigned long tsize,unsigned long dsize, unsigned long bsize,unsigned long ssize, unsigned long flags) { PH hdr; setval(1,hdr.ph_branch,2,0x601a); setval(1,hdr.ph_tlen,4,tsize); setval(1,hdr.ph_dlen,4,dsize); setval(1,hdr.ph_blen,4,bsize); setval(1,hdr.ph_slen,4,ssize); setval(1,hdr.ph_magic,4,0); setval(1,hdr.ph_flags,4,flags); setval(1,hdr.ph_abs,2,0); fwdata(f,&hdr,sizeof(PH)); } static void checkdefined(symbol *sym) { if (sym->type == IMPORT) output_error(6,sym->name); } static taddr tos_sym_value(symbol *sym,int textbased) { taddr val = get_sym_value(sym); /* all sections form a contiguous block, so add section offset */ if (textbased && 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, (tos_sym_value(((nreloc *)rl->reloc)->sym,1) + nreloc_real_addend(rl->reloc)) - sdabase); break; case REL_PC: checkdefined(((nreloc *)rl->reloc)->sym); write_reloc68k(a,rl,1, (tos_sym_value(((nreloc *)rl->reloc)->sym,1) + nreloc_real_addend(rl->reloc)) - (pc + ((nreloc *)rl->reloc)->byteoffset)); break; case REL_ABS: 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; } rcnt++; 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 tos_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 void write_dri_sym(FILE *f,char *name,int type,taddr value) { struct DRIsym stab; int longname = (strlen(name) > DRI_NAMELEN) && tos_hisoft_dri; strncpy(stab.name,name,DRI_NAMELEN); setval(1,stab.type,sizeof(stab.type),longname?(type|STYP_LONGNAME):type); setval(1,stab.value,sizeof(stab.value),value); fwdata(f,&stab,sizeof(struct DRIsym)); if (longname) { char rest_of_name[sizeof(struct DRIsym)]; memset(rest_of_name,0,sizeof(struct DRIsym)); strncpy(rest_of_name,name+DRI_NAMELEN,sizeof(struct DRIsym)); fwdata(f,rest_of_name,sizeof(struct DRIsym)); } } static void tos_symboltable(FILE *f,symbol *sym) { static int labtype[] = { STYP_TEXT,STYP_DATA,STYP_BSS }; int t; for (; sym; sym=sym->next) { /* The Devpac DRI symbol table in executables contains all labels, no matter if global or local. But no equates or other types. */ if (!(sym->flags & (VASMINTERN|COMMON)) && sym->type == LABSYM) { if (sym->flags & WEAK) output_error(10,sym->name); /* weak symbol not supported */ t = labtype[sym->sec->idx] | STYP_DEFINED | STYP_GLOBAL; write_dri_sym(f,sym->name,t,tos_sym_value(sym,textbasedsyms)); } } } static int offscmp(const void *offs1,const void *offs2) { return *(int *)offs1 - *(int *)offs2; } static int tos_writerelocs(FILE *f,section *sec) { int n = 0; int *sortoffs = mymalloc(max_relocs_per_atom*sizeof(int)); 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 differences between them */ n += nrel; for (i=0; i 254) { fw8(f,1); diff -= 254; } fw8(f,(uint8_t)diff); } else /* first entry is a 32 bits offset */ fw32(f,newoffs,1); lastoffs = newoffs; } } pc = npc + atom_size(a,sec,npc); } } myfree(sortoffs); return n; } static void write_output(FILE *f,section *sec,symbol *sym) { int nsyms = tos_initwrite(sec,sym); int nrelocs = 0; tos_header(f,secsize[_TEXT],secsize[_DATA],secsize[_BSS], nsyms*sizeof(struct DRIsym),tosflags); tos_writesection(f,sections[_TEXT],SECT_ALIGN); tos_writesection(f,sections[_DATA],SECT_ALIGN); if (nsyms) tos_symboltable(f,sym); nrelocs += tos_writerelocs(f,sections[_TEXT]); nrelocs += tos_writerelocs(f,sections[_DATA]); if (nrelocs) fw8(f,0); else fw32(f,0,1); } static int output_args(char *p) { if (!strncmp(p,"-tos-flags=",11)) { tosflags = atoi(p+11); return 1; } else if (!strcmp(p,"-monst")) { textbasedsyms = 1; return 1; } return 0; } int init_output_tos(char **cp,void (**wo)(FILE *,section *,symbol *), int (**oa)(char *)) { *cp = copyright; *wo = write_output; *oa = output_args; return 1; } #else int init_output_tos(char **cp,void (**wo)(FILE *,section *,symbol *), int (**oa)(char *)) { return 0; } #endif