diff options
Diffstat (limited to 'vere/ext/nasm/output/outaout.c')
-rw-r--r-- | vere/ext/nasm/output/outaout.c | 923 |
1 files changed, 923 insertions, 0 deletions
diff --git a/vere/ext/nasm/output/outaout.c b/vere/ext/nasm/output/outaout.c new file mode 100644 index 0000000..c59c789 --- /dev/null +++ b/vere/ext/nasm/output/outaout.c @@ -0,0 +1,923 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 1996-2013 The NASM Authors - All Rights Reserved + * See the file AUTHORS included with the NASM distribution for + * the specific copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following + * conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ----------------------------------------------------------------------- */ + +/* + * outaout.c output routines for the Netwide Assembler to produce + * Linux a.out object files + */ + +#include "compiler.h" + +#include "nctype.h" + +#include "nasm.h" +#include "nasmlib.h" +#include "error.h" +#include "saa.h" +#include "raa.h" +#include "stdscan.h" +#include "eval.h" +#include "outform.h" +#include "outlib.h" + +#if defined OF_AOUT || defined OF_AOUTB + +#define RELTYPE_ABSOLUTE 0x00 +#define RELTYPE_RELATIVE 0x01 +#define RELTYPE_GOTPC 0x01 /* no explicit GOTPC in a.out */ +#define RELTYPE_GOTOFF 0x10 +#define RELTYPE_GOT 0x10 /* distinct from GOTOFF bcos sym not sect */ +#define RELTYPE_PLT 0x21 +#define RELTYPE_SYMFLAG 0x08 + +struct Reloc { + struct Reloc *next; + int32_t address; /* relative to _start_ of section */ + int32_t symbol; /* symbol number or -ve section id */ + int bytes; /* 2 or 4 */ + int reltype; /* see above */ +}; + +struct Symbol { + int32_t strpos; /* string table position of name */ + int type; /* symbol type - see flags below */ + int32_t value; /* address, or COMMON variable size */ + int32_t size; /* size for data or function exports */ + int32_t segment; /* back-reference used by gsym_reloc */ + struct Symbol *next; /* list of globals in each section */ + struct Symbol *nextfwd; /* list of unresolved-size symbols */ + char *name; /* for unresolved-size symbols */ + int32_t symnum; /* index into symbol table */ +}; + +/* + * Section IDs - used in Reloc.symbol when negative, and in + * Symbol.type when positive. + */ +#define SECT_ABS 2 /* absolute value */ +#define SECT_TEXT 4 /* text section */ +#define SECT_DATA 6 /* data section */ +#define SECT_BSS 8 /* bss section */ +#define SECT_MASK 0xE /* mask out any of the above */ + +/* + * More flags used in Symbol.type. + */ +#define SYM_GLOBAL 1 /* it's a global symbol */ +#define SYM_DATA 0x100 /* used for shared libs */ +#define SYM_FUNCTION 0x200 /* used for shared libs */ +#define SYM_WITH_SIZE 0x4000 /* not output; internal only */ + +/* + * Bit more explanation of symbol types: SECT_xxx denotes a local + * symbol. SECT_xxx|SYM_GLOBAL denotes a global symbol, defined in + * this module. Just SYM_GLOBAL, with zero value, denotes an + * external symbol referenced in this module. And just SYM_GLOBAL, + * but with a non-zero value, declares a C `common' variable, of + * size `value'. + */ + +struct Section { + struct SAA *data; + uint32_t len, size, nrelocs; + int32_t index; + struct Reloc *head, **tail; + struct Symbol *gsyms, *asym; +}; + +static struct Section stext, sdata, sbss; + +static struct SAA *syms; +static uint32_t nsyms; + +static struct RAA *bsym; + +static struct SAA *strs; +static uint32_t strslen; + +static struct Symbol *fwds; + +static int bsd; +static int is_pic; + +static void aout_write(void); +static void aout_write_relocs(struct Reloc *); +static void aout_write_syms(void); +static void aout_sect_write(struct Section *, const uint8_t *, + uint32_t); +static void aout_pad_sections(void); +static void aout_fixup_relocs(struct Section *); + +/* + * Special section numbers which are used to define special + * symbols, which can be used with WRT to provide PIC relocation + * types. + */ +static int32_t aout_gotpc_sect, aout_gotoff_sect; +static int32_t aout_got_sect, aout_plt_sect; +static int32_t aout_sym_sect; + +static void aoutg_init(void) +{ + stext.data = saa_init(1L); + stext.head = NULL; + stext.tail = &stext.head; + sdata.data = saa_init(1L); + sdata.head = NULL; + sdata.tail = &sdata.head; + stext.len = stext.size = sdata.len = sdata.size = sbss.len = 0; + stext.nrelocs = sdata.nrelocs = 0; + stext.gsyms = sdata.gsyms = sbss.gsyms = NULL; + stext.index = seg_alloc(); + sdata.index = seg_alloc(); + sbss.index = seg_alloc(); + stext.asym = sdata.asym = sbss.asym = NULL; + syms = saa_init((int32_t)sizeof(struct Symbol)); + nsyms = 0; + bsym = raa_init(); + strs = saa_init(1L); + strslen = 0; + fwds = NULL; +} + +#ifdef OF_AOUT + +static void aout_init(void) +{ + bsd = false; + aoutg_init(); + + aout_gotpc_sect = aout_gotoff_sect = aout_got_sect = + aout_plt_sect = aout_sym_sect = NO_SEG; +} + +#endif + +#ifdef OF_AOUTB + +extern const struct ofmt of_aoutb; + +static void aoutb_init(void) +{ + bsd = true; + aoutg_init(); + + is_pic = 0x00; /* may become 0x40 */ + + aout_gotpc_sect = seg_alloc(); + backend_label("..gotpc", aout_gotpc_sect + 1, 0L); + aout_gotoff_sect = seg_alloc(); + backend_label("..gotoff", aout_gotoff_sect + 1, 0L); + aout_got_sect = seg_alloc(); + backend_label("..got", aout_got_sect + 1, 0L); + aout_plt_sect = seg_alloc(); + backend_label("..plt", aout_plt_sect + 1, 0L); + aout_sym_sect = seg_alloc(); + backend_label("..sym", aout_sym_sect + 1, 0L); +} + +#endif + +static void aout_cleanup(void) +{ + struct Reloc *r; + + aout_pad_sections(); + aout_fixup_relocs(&stext); + aout_fixup_relocs(&sdata); + aout_write(); + saa_free(stext.data); + while (stext.head) { + r = stext.head; + stext.head = stext.head->next; + nasm_free(r); + } + saa_free(sdata.data); + while (sdata.head) { + r = sdata.head; + sdata.head = sdata.head->next; + nasm_free(r); + } + saa_free(syms); + raa_free(bsym); + saa_free(strs); +} + +static int32_t aout_section_names(char *name, int *bits) +{ + /* + * Default to 32 bits. + */ + if (!name) { + *bits = 32; + return stext.index; + } + + if (!strcmp(name, ".text")) + return stext.index; + else if (!strcmp(name, ".data")) + return sdata.index; + else if (!strcmp(name, ".bss")) + return sbss.index; + else + return NO_SEG; +} + +static void aout_deflabel(char *name, int32_t segment, int64_t offset, + int is_global, char *special) +{ + int pos = strslen + 4; + struct Symbol *sym; + int special_used = false; + + if (name[0] == '.' && name[1] == '.' && name[2] != '@') { + /* + * This is a NASM special symbol. We never allow it into + * the a.out symbol table, even if it's a valid one. If it + * _isn't_ a valid one, we should barf immediately. + */ + if (strcmp(name, "..gotpc") && strcmp(name, "..gotoff") && + strcmp(name, "..got") && strcmp(name, "..plt") && + strcmp(name, "..sym")) + nasm_nonfatal("unrecognised special symbol `%s'", name); + return; + } + + if (is_global == 3) { + struct Symbol **s; + /* + * Fix up a forward-reference symbol size from the first + * pass. + */ + for (s = &fwds; *s; s = &(*s)->nextfwd) + if (!strcmp((*s)->name, name)) { + struct tokenval tokval; + expr *e; + char *p = special; + + p = nasm_skip_spaces(nasm_skip_word(p)); + stdscan_reset(); + stdscan_set(p); + tokval.t_type = TOKEN_INVALID; + e = evaluate(stdscan, NULL, &tokval, NULL, 1, NULL); + if (e) { + if (!is_simple(e)) + nasm_nonfatal("cannot use relocatable" + " expression as symbol size"); + else + (*s)->size = reloc_value(e); + } + + /* + * Remove it from the list of unresolved sizes. + */ + nasm_free((*s)->name); + *s = (*s)->nextfwd; + return; + } + return; /* it wasn't an important one */ + } + + saa_wbytes(strs, name, (int32_t)(1 + strlen(name))); + strslen += 1 + strlen(name); + + sym = saa_wstruct(syms); + + sym->strpos = pos; + sym->type = is_global ? SYM_GLOBAL : 0; + sym->segment = segment; + if (segment == NO_SEG) + sym->type |= SECT_ABS; + else if (segment == stext.index) { + sym->type |= SECT_TEXT; + if (is_global) { + sym->next = stext.gsyms; + stext.gsyms = sym; + } else if (!stext.asym) + stext.asym = sym; + } else if (segment == sdata.index) { + sym->type |= SECT_DATA; + if (is_global) { + sym->next = sdata.gsyms; + sdata.gsyms = sym; + } else if (!sdata.asym) + sdata.asym = sym; + } else if (segment == sbss.index) { + sym->type |= SECT_BSS; + if (is_global) { + sym->next = sbss.gsyms; + sbss.gsyms = sym; + } else if (!sbss.asym) + sbss.asym = sym; + } else + sym->type = SYM_GLOBAL; + if (is_global == 2) + sym->value = offset; + else + sym->value = (sym->type == SYM_GLOBAL ? 0 : offset); + + if (is_global && sym->type != SYM_GLOBAL) { + /* + * Global symbol exported _from_ this module. We must check + * the special text for type information. + */ + + if (special) { + int n = strcspn(special, " "); + + if (!nasm_strnicmp(special, "function", n)) + sym->type |= SYM_FUNCTION; + else if (!nasm_strnicmp(special, "data", n) || + !nasm_strnicmp(special, "object", n)) + sym->type |= SYM_DATA; + else + nasm_nonfatal("unrecognised symbol type `%.*s'", + n, special); + if (special[n]) { + struct tokenval tokval; + expr *e; + int fwd = false; + char *saveme = stdscan_get(); + + if (!bsd) { + nasm_nonfatal("Linux a.out does not support" + " symbol size information"); + } else { + while (special[n] && nasm_isspace(special[n])) + n++; + /* + * We have a size expression; attempt to + * evaluate it. + */ + sym->type |= SYM_WITH_SIZE; + stdscan_reset(); + stdscan_set(special + n); + tokval.t_type = TOKEN_INVALID; + e = evaluate(stdscan, NULL, &tokval, &fwd, 0, NULL); + if (fwd) { + sym->nextfwd = fwds; + fwds = sym; + sym->name = nasm_strdup(name); + } else if (e) { + if (!is_simple(e)) + nasm_nonfatal("cannot use relocatable" + " expression as symbol size"); + else + sym->size = reloc_value(e); + } + } + stdscan_set(saveme); + } + special_used = true; + } + } + + /* + * define the references from external-symbol segment numbers + * to these symbol records. + */ + if (segment != NO_SEG && segment != stext.index && + segment != sdata.index && segment != sbss.index) + bsym = raa_write(bsym, segment, nsyms); + sym->symnum = nsyms; + + nsyms++; + if (sym->type & SYM_WITH_SIZE) + nsyms++; /* and another for the size */ + + if (special && !special_used) + nasm_nonfatal("no special symbol features supported here"); +} + +static void aout_add_reloc(struct Section *sect, int32_t segment, + int reltype, int bytes) +{ + struct Reloc *r; + + r = *sect->tail = nasm_malloc(sizeof(struct Reloc)); + sect->tail = &r->next; + r->next = NULL; + + r->address = sect->len; + r->symbol = (segment == NO_SEG ? -SECT_ABS : + segment == stext.index ? -SECT_TEXT : + segment == sdata.index ? -SECT_DATA : + segment == sbss.index ? -SECT_BSS : + raa_read(bsym, segment)); + r->reltype = reltype; + if (r->symbol >= 0) + r->reltype |= RELTYPE_SYMFLAG; + r->bytes = bytes; + + sect->nrelocs++; +} + +/* + * This routine deals with ..got and ..sym relocations: the more + * complicated kinds. In shared-library writing, some relocations + * with respect to global symbols must refer to the precise symbol + * rather than referring to an offset from the base of the section + * _containing_ the symbol. Such relocations call to this routine, + * which searches the symbol list for the symbol in question. + * + * RELTYPE_GOT references require the _exact_ symbol address to be + * used; RELTYPE_ABSOLUTE references can be at an offset from the + * symbol. The boolean argument `exact' tells us this. + * + * Return value is the adjusted value of `addr', having become an + * offset from the symbol rather than the section. Should always be + * zero when returning from an exact call. + * + * Limitation: if you define two symbols at the same place, + * confusion will occur. + * + * Inefficiency: we search, currently, using a linked list which + * isn't even necessarily sorted. + */ +static int32_t aout_add_gsym_reloc(struct Section *sect, + int32_t segment, int32_t offset, + int type, int bytes, int exact) +{ + struct Symbol *sym, *sm, *shead; + struct Reloc *r; + + /* + * First look up the segment to find whether it's text, data, + * bss or an external symbol. + */ + shead = NULL; + if (segment == stext.index) + shead = stext.gsyms; + else if (segment == sdata.index) + shead = sdata.gsyms; + else if (segment == sbss.index) + shead = sbss.gsyms; + if (!shead) { + if (exact && offset != 0) + nasm_nonfatal("unable to find a suitable global symbol" + " for this reference"); + else + aout_add_reloc(sect, segment, type, bytes); + return offset; + } + + if (exact) { + /* + * Find a symbol pointing _exactly_ at this one. + */ + list_for_each(sym, shead) + if (sym->value == offset) + break; + } else { + /* + * Find the nearest symbol below this one. + */ + sym = NULL; + list_for_each(sm, shead) + if (sm->value <= offset && (!sym || sm->value > sym->value)) + sym = sm; + } + if (!sym && exact) { + nasm_nonfatal("unable to find a suitable global symbol" + " for this reference"); + return 0; + } + + r = *sect->tail = nasm_malloc(sizeof(struct Reloc)); + sect->tail = &r->next; + r->next = NULL; + + r->address = sect->len; + r->symbol = sym->symnum; + r->reltype = type | RELTYPE_SYMFLAG; + r->bytes = bytes; + + sect->nrelocs++; + + return offset - sym->value; +} + +/* + * This routine deals with ..gotoff relocations. These _must_ refer + * to a symbol, due to a perversity of *BSD's PIC implementation, + * and it must be a non-global one as well; so we store `asym', the + * first nonglobal symbol defined in each section, and always work + * from that. Relocation type is always RELTYPE_GOTOFF. + * + * Return value is the adjusted value of `addr', having become an + * offset from the `asym' symbol rather than the section. + */ +static int32_t aout_add_gotoff_reloc(struct Section *sect, int32_t segment, + int32_t offset, int bytes) +{ + struct Reloc *r; + struct Symbol *asym; + + /* + * First look up the segment to find whether it's text, data, + * bss or an external symbol. + */ + asym = NULL; + if (segment == stext.index) + asym = stext.asym; + else if (segment == sdata.index) + asym = sdata.asym; + else if (segment == sbss.index) + asym = sbss.asym; + if (!asym) + nasm_nonfatal("`..gotoff' relocations require a non-global" + " symbol in the section"); + + r = *sect->tail = nasm_malloc(sizeof(struct Reloc)); + sect->tail = &r->next; + r->next = NULL; + + r->address = sect->len; + r->symbol = asym->symnum; + r->reltype = RELTYPE_GOTOFF; + r->bytes = bytes; + + sect->nrelocs++; + + return offset - asym->value; +} + +static void aout_out(int32_t segto, const void *data, + enum out_type type, uint64_t size, + int32_t segment, int32_t wrt) +{ + struct Section *s; + int32_t addr; + uint8_t mydata[4], *p; + + if (segto == stext.index) + s = &stext; + else if (segto == sdata.index) + s = &sdata; + else if (segto == sbss.index) + s = NULL; + else { + nasm_warn(WARN_OTHER, "attempt to assemble code in" + " segment %d: defaulting to `.text'", segto); + s = &stext; + } + + if (!s && type != OUT_RESERVE) { + nasm_warn(WARN_OTHER, "attempt to initialize memory in the" + " BSS section: ignored"); + sbss.len += realsize(type, size); + return; + } + + memset(mydata, 0, sizeof(mydata)); + + if (type == OUT_RESERVE) { + if (s) { + nasm_warn(WARN_ZEROING, "uninitialized space declared in" + " %s section: zeroing", + (segto == stext.index ? "code" : "data")); + aout_sect_write(s, NULL, size); + } else + sbss.len += size; + } else if (type == OUT_RAWDATA) { + aout_sect_write(s, data, size); + } else if (type == OUT_ADDRESS) { + int asize = abs((int)size); + addr = *(int64_t *)data; + if (segment != NO_SEG) { + if (segment % 2) { + nasm_nonfatal("a.out format does not support" + " segment base references"); + } else { + if (wrt == NO_SEG) { + aout_add_reloc(s, segment, RELTYPE_ABSOLUTE, asize); + } else if (!bsd) { + nasm_nonfatal("Linux a.out format does not support" + " any use of WRT"); + wrt = NO_SEG; /* we can at least _try_ to continue */ + } else if (wrt == aout_gotpc_sect + 1) { + is_pic = 0x40; + aout_add_reloc(s, segment, RELTYPE_GOTPC, asize); + } else if (wrt == aout_gotoff_sect + 1) { + is_pic = 0x40; + addr = aout_add_gotoff_reloc(s, segment, addr, asize); + } else if (wrt == aout_got_sect + 1) { + is_pic = 0x40; + addr = aout_add_gsym_reloc(s, segment, addr, RELTYPE_GOT, + asize, true); + } else if (wrt == aout_sym_sect + 1) { + addr = aout_add_gsym_reloc(s, segment, addr, + RELTYPE_ABSOLUTE, asize, + false); + } else if (wrt == aout_plt_sect + 1) { + is_pic = 0x40; + nasm_nonfatal("a.out format cannot produce non-PC-" + "relative PLT references"); + } else { + nasm_nonfatal("a.out format does not support this" + " use of WRT"); + wrt = NO_SEG; /* we can at least _try_ to continue */ + } + } + } + p = mydata; + if (asize == 2) + WRITESHORT(p, addr); + else + WRITELONG(p, addr); + aout_sect_write(s, mydata, asize); + } else if (type == OUT_REL2ADR) { + if (segment != NO_SEG && segment % 2) { + nasm_nonfatal("a.out format does not support" + " segment base references"); + } else { + if (wrt == NO_SEG) { + aout_add_reloc(s, segment, RELTYPE_RELATIVE, 2); + } else if (!bsd) { + nasm_nonfatal("Linux a.out format does not support" + " any use of WRT"); + wrt = NO_SEG; /* we can at least _try_ to continue */ + } else if (wrt == aout_plt_sect + 1) { + is_pic = 0x40; + aout_add_reloc(s, segment, RELTYPE_PLT, 2); + } else if (wrt == aout_gotpc_sect + 1 || + wrt == aout_gotoff_sect + 1 || + wrt == aout_got_sect + 1) { + nasm_nonfatal("a.out format cannot produce PC-" + "relative GOT references"); + } else { + nasm_nonfatal("a.out format does not support this" + " use of WRT"); + wrt = NO_SEG; /* we can at least _try_ to continue */ + } + } + p = mydata; + WRITESHORT(p, *(int64_t *)data - (size + s->len)); + aout_sect_write(s, mydata, 2L); + } else if (type == OUT_REL4ADR) { + if (segment != NO_SEG && segment % 2) { + nasm_nonfatal("a.out format does not support" + " segment base references"); + } else { + if (wrt == NO_SEG) { + aout_add_reloc(s, segment, RELTYPE_RELATIVE, 4); + } else if (!bsd) { + nasm_nonfatal("Linux a.out format does not support" + " any use of WRT"); + wrt = NO_SEG; /* we can at least _try_ to continue */ + } else if (wrt == aout_plt_sect + 1) { + is_pic = 0x40; + aout_add_reloc(s, segment, RELTYPE_PLT, 4); + } else if (wrt == aout_gotpc_sect + 1 || + wrt == aout_gotoff_sect + 1 || + wrt == aout_got_sect + 1) { + nasm_nonfatal("a.out format cannot produce PC-" + "relative GOT references"); + } else { + nasm_nonfatal("a.out format does not support this" + " use of WRT"); + wrt = NO_SEG; /* we can at least _try_ to continue */ + } + } + p = mydata; + WRITELONG(p, *(int64_t *)data - (size + s->len)); + aout_sect_write(s, mydata, 4L); + } +} + +static void aout_pad_sections(void) +{ + static uint8_t pad[] = { 0x90, 0x90, 0x90, 0x90 }; + /* + * Pad each of the text and data sections with NOPs until their + * length is a multiple of four. (NOP == 0x90.) Also increase + * the length of the BSS section similarly. + */ + aout_sect_write(&stext, pad, (-(int32_t)stext.len) & 3); + aout_sect_write(&sdata, pad, (-(int32_t)sdata.len) & 3); + sbss.len = ALIGN(sbss.len, 4); +} + +/* + * a.out files have the curious property that all references to + * things in the data or bss sections are done by addresses which + * are actually relative to the start of the _text_ section, in the + * _file_. (No relation to what happens after linking. No idea why + * this should be so. It's very strange.) So we have to go through + * the relocation table, _after_ the final size of each section is + * known, and fix up the relocations pointed to. + */ +static void aout_fixup_relocs(struct Section *sect) +{ + struct Reloc *r; + + saa_rewind(sect->data); + list_for_each(r, sect->head) { + uint8_t *p, *q, blk[4]; + int32_t l; + + saa_fread(sect->data, r->address, blk, (int32_t)r->bytes); + p = q = blk; + l = *p++; + if (r->bytes > 1) { + l += ((int32_t)*p++) << 8; + if (r->bytes == 4) { + l += ((int32_t)*p++) << 16; + l += ((int32_t)*p++) << 24; + } + } + if (r->symbol == -SECT_DATA) + l += stext.len; + else if (r->symbol == -SECT_BSS) + l += stext.len + sdata.len; + if (r->bytes == 4) + WRITELONG(q, l); + else if (r->bytes == 2) + WRITESHORT(q, l); + else + *q++ = l & 0xFF; + saa_fwrite(sect->data, r->address, blk, (int32_t)r->bytes); + } +} + +static void aout_write(void) +{ + /* + * Emit the a.out header. + */ + /* OMAGIC, M_386 or MID_I386, no flags */ + fwriteint32_t(bsd ? 0x07018600 | is_pic : 0x640107L, ofile); + fwriteint32_t(stext.len, ofile); + fwriteint32_t(sdata.len, ofile); + fwriteint32_t(sbss.len, ofile); + fwriteint32_t(nsyms * 12, ofile); /* length of symbol table */ + fwriteint32_t(0L, ofile); /* object files have no entry point */ + fwriteint32_t(stext.nrelocs * 8, ofile); /* size of text relocs */ + fwriteint32_t(sdata.nrelocs * 8, ofile); /* size of data relocs */ + + /* + * Write out the code section and the data section. + */ + saa_fpwrite(stext.data, ofile); + saa_fpwrite(sdata.data, ofile); + + /* + * Write out the relocations. + */ + aout_write_relocs(stext.head); + aout_write_relocs(sdata.head); + + /* + * Write the symbol table. + */ + aout_write_syms(); + + /* + * And the string table. + */ + fwriteint32_t(strslen + 4, ofile); /* length includes length count */ + saa_fpwrite(strs, ofile); +} + +static void aout_write_relocs(struct Reloc *r) +{ + list_for_each(r, r) { + uint32_t word2; + + fwriteint32_t(r->address, ofile); + + if (r->symbol >= 0) + word2 = r->symbol; + else + word2 = -r->symbol; + word2 |= r->reltype << 24; + word2 |= (r->bytes == 1 ? 0 : + r->bytes == 2 ? 0x2000000L : 0x4000000L); + fwriteint32_t(word2, ofile); + } +} + +static void aout_write_syms(void) +{ + uint32_t i; + + saa_rewind(syms); + for (i = 0; i < nsyms; i++) { + struct Symbol *sym = saa_rstruct(syms); + fwriteint32_t(sym->strpos, ofile); + fwriteint32_t((int32_t)sym->type & ~SYM_WITH_SIZE, ofile); + /* + * Fix up the symbol value now we know the final section + * sizes. + */ + if ((sym->type & SECT_MASK) == SECT_DATA) + sym->value += stext.len; + if ((sym->type & SECT_MASK) == SECT_BSS) + sym->value += stext.len + sdata.len; + fwriteint32_t(sym->value, ofile); + /* + * Output a size record if necessary. + */ + if (sym->type & SYM_WITH_SIZE) { + fwriteint32_t(sym->strpos, ofile); + fwriteint32_t(0x0DL, ofile); /* special value: means size */ + fwriteint32_t(sym->size, ofile); + i++; /* use up another of `nsyms' */ + } + } +} + +static void aout_sect_write(struct Section *sect, + const uint8_t *data, uint32_t len) +{ + saa_wbytes(sect->data, data, len); + sect->len += len; +} + +extern macros_t aout_stdmac[]; + +#endif /* OF_AOUT || OF_AOUTB */ + +#ifdef OF_AOUT + +const struct ofmt of_aout = { + "Linux a.out", + "aout", + ".o", + 0, + 32, + null_debug_arr, + &null_debug_form, + aout_stdmac, + aout_init, + null_reset, + nasm_do_legacy_output, + aout_out, + aout_deflabel, + aout_section_names, + NULL, + null_sectalign, + null_segbase, + null_directive, + aout_cleanup, + NULL /* pragma list */ +}; + +#endif + +#ifdef OF_AOUTB + +const struct ofmt of_aoutb = { + "NetBSD/FreeBSD a.out", + "aoutb", + ".o", + 0, + 32, + null_debug_arr, + &null_debug_form, + aout_stdmac, + aoutb_init, + null_reset, + nasm_do_legacy_output, + aout_out, + aout_deflabel, + aout_section_names, + NULL, + null_sectalign, + null_segbase, + null_directive, + aout_cleanup, + NULL /* pragma list */ +}; + +#endif |