regularized spelling of license to match name of LICENSE file
[platform/upstream/nasm.git] / output / outaout.c
1 /* outaout.c    output routines for the Netwide Assembler to produce
2  *              Linux a.out object files
3  *
4  * The Netwide Assembler is copyright (C) 1996 Simon Tatham and
5  * Julian Hall. All rights reserved. The software is
6  * redistributable under the license given in the file "LICENSE"
7  * distributed in the NASM archive.
8  */
9
10 #include "compiler.h"
11
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <string.h>
15 #include <ctype.h>
16 #include <inttypes.h>
17
18 #include "nasm.h"
19 #include "nasmlib.h"
20 #include "stdscan.h"
21 #include "outform.h"
22
23 #if defined OF_AOUT || defined OF_AOUTB
24
25 #define RELTYPE_ABSOLUTE 0x00
26 #define RELTYPE_RELATIVE 0x01
27 #define RELTYPE_GOTPC    0x01   /* no explicit GOTPC in a.out */
28 #define RELTYPE_GOTOFF   0x10
29 #define RELTYPE_GOT      0x10   /* distinct from GOTOFF bcos sym not sect */
30 #define RELTYPE_PLT      0x21
31 #define RELTYPE_SYMFLAG  0x08
32
33 struct Reloc {
34     struct Reloc *next;
35     int32_t address;               /* relative to _start_ of section */
36     int32_t symbol;                /* symbol number or -ve section id */
37     int bytes;                  /* 2 or 4 */
38     int reltype;                /* see above */
39 };
40
41 struct Symbol {
42     int32_t strpos;                /* string table position of name */
43     int type;                   /* symbol type - see flags below */
44     int32_t value;                 /* address, or COMMON variable size */
45     int32_t size;                  /* size for data or function exports */
46     int32_t segment;               /* back-reference used by gsym_reloc */
47     struct Symbol *next;        /* list of globals in each section */
48     struct Symbol *nextfwd;     /* list of unresolved-size symbols */
49     char *name;                 /* for unresolved-size symbols */
50     int32_t symnum;                /* index into symbol table */
51 };
52
53 /*
54  * Section IDs - used in Reloc.symbol when negative, and in
55  * Symbol.type when positive.
56  */
57 #define SECT_ABS 2              /* absolute value */
58 #define SECT_TEXT 4             /* text section */
59 #define SECT_DATA 6             /* data section */
60 #define SECT_BSS 8              /* bss section */
61 #define SECT_MASK 0xE           /* mask out any of the above */
62
63 /*
64  * More flags used in Symbol.type.
65  */
66 #define SYM_GLOBAL 1            /* it's a global symbol */
67 #define SYM_DATA 0x100          /* used for shared libs */
68 #define SYM_FUNCTION 0x200      /* used for shared libs */
69 #define SYM_WITH_SIZE 0x4000    /* not output; internal only */
70
71 /*
72  * Bit more explanation of symbol types: SECT_xxx denotes a local
73  * symbol. SECT_xxx|SYM_GLOBAL denotes a global symbol, defined in
74  * this module. Just SYM_GLOBAL, with zero value, denotes an
75  * external symbol referenced in this module. And just SYM_GLOBAL,
76  * but with a non-zero value, declares a C `common' variable, of
77  * size `value'.
78  */
79
80 struct Section {
81     struct SAA *data;
82     uint32_t len, size, nrelocs;
83     int32_t index;
84     struct Reloc *head, **tail;
85     struct Symbol *gsyms, *asym;
86 };
87
88 static struct Section stext, sdata, sbss;
89
90 static struct SAA *syms;
91 static uint32_t nsyms;
92
93 static struct RAA *bsym;
94
95 static struct SAA *strs;
96 static uint32_t strslen;
97
98 static struct Symbol *fwds;
99
100 static FILE *aoutfp;
101 static efunc error;
102 static evalfunc evaluate;
103
104 static int bsd;
105 static int is_pic;
106
107 static void aout_write(void);
108 static void aout_write_relocs(struct Reloc *);
109 static void aout_write_syms(void);
110 static void aout_sect_write(struct Section *, const uint8_t *,
111                             uint32_t);
112 static void aout_pad_sections(void);
113 static void aout_fixup_relocs(struct Section *);
114
115 /*
116  * Special section numbers which are used to define special
117  * symbols, which can be used with WRT to provide PIC relocation
118  * types.
119  */
120 static int32_t aout_gotpc_sect, aout_gotoff_sect;
121 static int32_t aout_got_sect, aout_plt_sect;
122 static int32_t aout_sym_sect;
123
124 static void aoutg_init(FILE * fp, efunc errfunc, ldfunc ldef,
125                        evalfunc eval)
126 {
127     aoutfp = fp;
128     error = errfunc;
129     evaluate = eval;
130     (void)ldef;                 /* placate optimisers */
131     stext.data = saa_init(1L);
132     stext.head = NULL;
133     stext.tail = &stext.head;
134     sdata.data = saa_init(1L);
135     sdata.head = NULL;
136     sdata.tail = &sdata.head;
137     stext.len = stext.size = sdata.len = sdata.size = sbss.len = 0;
138     stext.nrelocs = sdata.nrelocs = 0;
139     stext.gsyms = sdata.gsyms = sbss.gsyms = NULL;
140     stext.index = seg_alloc();
141     sdata.index = seg_alloc();
142     sbss.index = seg_alloc();
143     stext.asym = sdata.asym = sbss.asym = NULL;
144     syms = saa_init((int32_t)sizeof(struct Symbol));
145     nsyms = 0;
146     bsym = raa_init();
147     strs = saa_init(1L);
148     strslen = 0;
149     fwds = NULL;
150 }
151
152 #ifdef OF_AOUT
153
154 static void aout_init(FILE * fp, efunc errfunc, ldfunc ldef, evalfunc eval)
155 {
156     bsd = false;
157     aoutg_init(fp, errfunc, ldef, eval);
158
159     aout_gotpc_sect = aout_gotoff_sect = aout_got_sect =
160         aout_plt_sect = aout_sym_sect = NO_SEG;
161 }
162
163 #endif
164
165 #ifdef OF_AOUTB
166
167 extern struct ofmt of_aoutb;
168
169 static void aoutb_init(FILE * fp, efunc errfunc, ldfunc ldef,
170                        evalfunc eval)
171 {
172     bsd = true;
173     aoutg_init(fp, errfunc, ldef, eval);
174
175     is_pic = 0x00;              /* may become 0x40 */
176
177     aout_gotpc_sect = seg_alloc();
178     ldef("..gotpc", aout_gotpc_sect + 1, 0L, NULL, false, false, &of_aoutb,
179          error);
180     aout_gotoff_sect = seg_alloc();
181     ldef("..gotoff", aout_gotoff_sect + 1, 0L, NULL, false, false,
182          &of_aoutb, error);
183     aout_got_sect = seg_alloc();
184     ldef("..got", aout_got_sect + 1, 0L, NULL, false, false, &of_aoutb,
185          error);
186     aout_plt_sect = seg_alloc();
187     ldef("..plt", aout_plt_sect + 1, 0L, NULL, false, false, &of_aoutb,
188          error);
189     aout_sym_sect = seg_alloc();
190     ldef("..sym", aout_sym_sect + 1, 0L, NULL, false, false, &of_aoutb,
191          error);
192 }
193
194 #endif
195
196 static void aout_cleanup(int debuginfo)
197 {
198     struct Reloc *r;
199
200     (void)debuginfo;
201
202     aout_pad_sections();
203     aout_fixup_relocs(&stext);
204     aout_fixup_relocs(&sdata);
205     aout_write();
206     fclose(aoutfp);
207     saa_free(stext.data);
208     while (stext.head) {
209         r = stext.head;
210         stext.head = stext.head->next;
211         nasm_free(r);
212     }
213     saa_free(sdata.data);
214     while (sdata.head) {
215         r = sdata.head;
216         sdata.head = sdata.head->next;
217         nasm_free(r);
218     }
219     saa_free(syms);
220     raa_free(bsym);
221     saa_free(strs);
222 }
223
224 static int32_t aout_section_names(char *name, int pass, int *bits)
225 {
226
227     (void)pass;
228
229     /*
230      * Default to 32 bits.
231      */
232     if (!name)
233         *bits = 32;
234
235     if (!name)
236         return stext.index;
237
238     if (!strcmp(name, ".text"))
239         return stext.index;
240     else if (!strcmp(name, ".data"))
241         return sdata.index;
242     else if (!strcmp(name, ".bss"))
243         return sbss.index;
244     else
245         return NO_SEG;
246 }
247
248 static void aout_deflabel(char *name, int32_t segment, int64_t offset,
249                           int is_global, char *special)
250 {
251     int pos = strslen + 4;
252     struct Symbol *sym;
253     int special_used = false;
254
255     if (name[0] == '.' && name[1] == '.' && name[2] != '@') {
256         /*
257          * This is a NASM special symbol. We never allow it into
258          * the a.out symbol table, even if it's a valid one. If it
259          * _isn't_ a valid one, we should barf immediately.
260          */
261         if (strcmp(name, "..gotpc") && strcmp(name, "..gotoff") &&
262             strcmp(name, "..got") && strcmp(name, "..plt") &&
263             strcmp(name, "..sym"))
264             error(ERR_NONFATAL, "unrecognised special symbol `%s'", name);
265         return;
266     }
267
268     if (is_global == 3) {
269         struct Symbol **s;
270         /*
271          * Fix up a forward-reference symbol size from the first
272          * pass.
273          */
274         for (s = &fwds; *s; s = &(*s)->nextfwd)
275             if (!strcmp((*s)->name, name)) {
276                 struct tokenval tokval;
277                 expr *e;
278                 char *p = special;
279
280                 while (*p && !isspace(*p))
281                     p++;
282                 while (*p && isspace(*p))
283                     p++;
284                 stdscan_reset();
285                 stdscan_bufptr = p;
286                 tokval.t_type = TOKEN_INVALID;
287                 e = evaluate(stdscan, NULL, &tokval, NULL, 1, error, NULL);
288                 if (e) {
289                     if (!is_simple(e))
290                         error(ERR_NONFATAL, "cannot use relocatable"
291                               " expression as symbol size");
292                     else
293                         (*s)->size = reloc_value(e);
294                 }
295
296                 /*
297                  * Remove it from the list of unresolved sizes.
298                  */
299                 nasm_free((*s)->name);
300                 *s = (*s)->nextfwd;
301                 return;
302             }
303         return;                 /* it wasn't an important one */
304     }
305
306     saa_wbytes(strs, name, (int32_t)(1 + strlen(name)));
307     strslen += 1 + strlen(name);
308
309     sym = saa_wstruct(syms);
310
311     sym->strpos = pos;
312     sym->type = is_global ? SYM_GLOBAL : 0;
313     sym->segment = segment;
314     if (segment == NO_SEG)
315         sym->type |= SECT_ABS;
316     else if (segment == stext.index) {
317         sym->type |= SECT_TEXT;
318         if (is_global) {
319             sym->next = stext.gsyms;
320             stext.gsyms = sym;
321         } else if (!stext.asym)
322             stext.asym = sym;
323     } else if (segment == sdata.index) {
324         sym->type |= SECT_DATA;
325         if (is_global) {
326             sym->next = sdata.gsyms;
327             sdata.gsyms = sym;
328         } else if (!sdata.asym)
329             sdata.asym = sym;
330     } else if (segment == sbss.index) {
331         sym->type |= SECT_BSS;
332         if (is_global) {
333             sym->next = sbss.gsyms;
334             sbss.gsyms = sym;
335         } else if (!sbss.asym)
336             sbss.asym = sym;
337     } else
338         sym->type = SYM_GLOBAL;
339     if (is_global == 2)
340         sym->value = offset;
341     else
342         sym->value = (sym->type == SYM_GLOBAL ? 0 : offset);
343
344     if (is_global && sym->type != SYM_GLOBAL) {
345         /*
346          * Global symbol exported _from_ this module. We must check
347          * the special text for type information.
348          */
349
350         if (special) {
351             int n = strcspn(special, " ");
352
353             if (!nasm_strnicmp(special, "function", n))
354                 sym->type |= SYM_FUNCTION;
355             else if (!nasm_strnicmp(special, "data", n) ||
356                      !nasm_strnicmp(special, "object", n))
357                 sym->type |= SYM_DATA;
358             else
359                 error(ERR_NONFATAL, "unrecognised symbol type `%.*s'",
360                       n, special);
361             if (special[n]) {
362                 struct tokenval tokval;
363                 expr *e;
364                 int fwd = false;
365                 char *saveme = stdscan_bufptr;  /* bugfix? fbk 8/10/00 */
366
367                 if (!bsd) {
368                     error(ERR_NONFATAL, "Linux a.out does not support"
369                           " symbol size information");
370                 } else {
371                     while (special[n] && isspace(special[n]))
372                         n++;
373                     /*
374                      * We have a size expression; attempt to
375                      * evaluate it.
376                      */
377                     sym->type |= SYM_WITH_SIZE;
378                     stdscan_reset();
379                     stdscan_bufptr = special + n;
380                     tokval.t_type = TOKEN_INVALID;
381                     e = evaluate(stdscan, NULL, &tokval, &fwd, 0, error,
382                                  NULL);
383                     if (fwd) {
384                         sym->nextfwd = fwds;
385                         fwds = sym;
386                         sym->name = nasm_strdup(name);
387                     } else if (e) {
388                         if (!is_simple(e))
389                             error(ERR_NONFATAL, "cannot use relocatable"
390                                   " expression as symbol size");
391                         else
392                             sym->size = reloc_value(e);
393                     }
394                 }
395                 stdscan_bufptr = saveme;        /* bugfix? fbk 8/10/00 */
396             }
397             special_used = true;
398         }
399     }
400
401     /*
402      * define the references from external-symbol segment numbers
403      * to these symbol records.
404      */
405     if (segment != NO_SEG && segment != stext.index &&
406         segment != sdata.index && segment != sbss.index)
407         bsym = raa_write(bsym, segment, nsyms);
408     sym->symnum = nsyms;
409
410     nsyms++;
411     if (sym->type & SYM_WITH_SIZE)
412         nsyms++;                /* and another for the size */
413
414     if (special && !special_used)
415         error(ERR_NONFATAL, "no special symbol features supported here");
416 }
417
418 static void aout_add_reloc(struct Section *sect, int32_t segment,
419                            int reltype, int bytes)
420 {
421     struct Reloc *r;
422
423     r = *sect->tail = nasm_malloc(sizeof(struct Reloc));
424     sect->tail = &r->next;
425     r->next = NULL;
426
427     r->address = sect->len;
428     r->symbol = (segment == NO_SEG ? -SECT_ABS :
429                  segment == stext.index ? -SECT_TEXT :
430                  segment == sdata.index ? -SECT_DATA :
431                  segment == sbss.index ? -SECT_BSS :
432                  raa_read(bsym, segment));
433     r->reltype = reltype;
434     if (r->symbol >= 0)
435         r->reltype |= RELTYPE_SYMFLAG;
436     r->bytes = bytes;
437
438     sect->nrelocs++;
439 }
440
441 /*
442  * This routine deals with ..got and ..sym relocations: the more
443  * complicated kinds. In shared-library writing, some relocations
444  * with respect to global symbols must refer to the precise symbol
445  * rather than referring to an offset from the base of the section
446  * _containing_ the symbol. Such relocations call to this routine,
447  * which searches the symbol list for the symbol in question.
448  *
449  * RELTYPE_GOT references require the _exact_ symbol address to be
450  * used; RELTYPE_ABSOLUTE references can be at an offset from the
451  * symbol. The boolean argument `exact' tells us this.
452  *
453  * Return value is the adjusted value of `addr', having become an
454  * offset from the symbol rather than the section. Should always be
455  * zero when returning from an exact call.
456  *
457  * Limitation: if you define two symbols at the same place,
458  * confusion will occur.
459  *
460  * Inefficiency: we search, currently, using a linked list which
461  * isn't even necessarily sorted.
462  */
463 static int32_t aout_add_gsym_reloc(struct Section *sect,
464                                 int32_t segment, int32_t offset,
465                                 int type, int bytes, int exact)
466 {
467     struct Symbol *sym, *sm, *shead;
468     struct Reloc *r;
469
470     /*
471      * First look up the segment to find whether it's text, data,
472      * bss or an external symbol.
473      */
474     shead = NULL;
475     if (segment == stext.index)
476         shead = stext.gsyms;
477     else if (segment == sdata.index)
478         shead = sdata.gsyms;
479     else if (segment == sbss.index)
480         shead = sbss.gsyms;
481     if (!shead) {
482         if (exact && offset != 0)
483             error(ERR_NONFATAL, "unable to find a suitable global symbol"
484                   " for this reference");
485         else
486             aout_add_reloc(sect, segment, type, bytes);
487         return offset;
488     }
489
490     if (exact) {
491         /*
492          * Find a symbol pointing _exactly_ at this one.
493          */
494         for (sym = shead; sym; sym = sym->next)
495             if (sym->value == offset)
496                 break;
497     } else {
498         /*
499          * Find the nearest symbol below this one.
500          */
501         sym = NULL;
502         for (sm = shead; sm; sm = sm->next)
503             if (sm->value <= offset && (!sym || sm->value > sym->value))
504                 sym = sm;
505     }
506     if (!sym && exact) {
507         error(ERR_NONFATAL, "unable to find a suitable global symbol"
508               " for this reference");
509         return 0;
510     }
511
512     r = *sect->tail = nasm_malloc(sizeof(struct Reloc));
513     sect->tail = &r->next;
514     r->next = NULL;
515
516     r->address = sect->len;
517     r->symbol = sym->symnum;
518     r->reltype = type | RELTYPE_SYMFLAG;
519     r->bytes = bytes;
520
521     sect->nrelocs++;
522
523     return offset - sym->value;
524 }
525
526 /*
527  * This routine deals with ..gotoff relocations. These _must_ refer
528  * to a symbol, due to a perversity of *BSD's PIC implementation,
529  * and it must be a non-global one as well; so we store `asym', the
530  * first nonglobal symbol defined in each section, and always work
531  * from that. Relocation type is always RELTYPE_GOTOFF.
532  *
533  * Return value is the adjusted value of `addr', having become an
534  * offset from the `asym' symbol rather than the section.
535  */
536 static int32_t aout_add_gotoff_reloc(struct Section *sect, int32_t segment,
537                                   int32_t offset, int bytes)
538 {
539     struct Reloc *r;
540     struct Symbol *asym;
541
542     /*
543      * First look up the segment to find whether it's text, data,
544      * bss or an external symbol.
545      */
546     asym = NULL;
547     if (segment == stext.index)
548         asym = stext.asym;
549     else if (segment == sdata.index)
550         asym = sdata.asym;
551     else if (segment == sbss.index)
552         asym = sbss.asym;
553     if (!asym)
554         error(ERR_NONFATAL, "`..gotoff' relocations require a non-global"
555               " symbol in the section");
556
557     r = *sect->tail = nasm_malloc(sizeof(struct Reloc));
558     sect->tail = &r->next;
559     r->next = NULL;
560
561     r->address = sect->len;
562     r->symbol = asym->symnum;
563     r->reltype = RELTYPE_GOTOFF;
564     r->bytes = bytes;
565
566     sect->nrelocs++;
567
568     return offset - asym->value;
569 }
570
571 static void aout_out(int32_t segto, const void *data,
572                      enum out_type type, uint64_t size,
573                      int32_t segment, int32_t wrt)
574 {
575     struct Section *s;
576     int32_t addr;
577     uint8_t mydata[4], *p;
578
579     /*
580      * handle absolute-assembly (structure definitions)
581      */
582     if (segto == NO_SEG) {
583         if (type != OUT_RESERVE)
584             error(ERR_NONFATAL, "attempt to assemble code in [ABSOLUTE]"
585                   " space");
586         return;
587     }
588
589     if (segto == stext.index)
590         s = &stext;
591     else if (segto == sdata.index)
592         s = &sdata;
593     else if (segto == sbss.index)
594         s = NULL;
595     else {
596         error(ERR_WARNING, "attempt to assemble code in"
597               " segment %d: defaulting to `.text'", segto);
598         s = &stext;
599     }
600
601     if (!s && type != OUT_RESERVE) {
602         error(ERR_WARNING, "attempt to initialize memory in the"
603               " BSS section: ignored");
604         if (type == OUT_REL2ADR)
605             size = 2;
606         else if (type == OUT_REL4ADR)
607             size = 4;
608         sbss.len += size;
609         return;
610     }
611
612     if (type == OUT_RESERVE) {
613         if (s) {
614             error(ERR_WARNING, "uninitialized space declared in"
615                   " %s section: zeroing",
616                   (segto == stext.index ? "code" : "data"));
617             aout_sect_write(s, NULL, size);
618         } else
619             sbss.len += size;
620     } else if (type == OUT_RAWDATA) {
621         if (segment != NO_SEG)
622             error(ERR_PANIC, "OUT_RAWDATA with other than NO_SEG");
623         aout_sect_write(s, data, size);
624     } else if (type == OUT_ADDRESS) {
625         addr = *(int64_t *)data;
626         if (segment != NO_SEG) {
627             if (segment % 2) {
628                 error(ERR_NONFATAL, "a.out format does not support"
629                       " segment base references");
630             } else {
631                 if (wrt == NO_SEG) {
632                     aout_add_reloc(s, segment, RELTYPE_ABSOLUTE,
633                                    size);
634                 } else if (!bsd) {
635                     error(ERR_NONFATAL,
636                           "Linux a.out format does not support"
637                           " any use of WRT");
638                     wrt = NO_SEG;       /* we can at least _try_ to continue */
639                 } else if (wrt == aout_gotpc_sect + 1) {
640                     is_pic = 0x40;
641                     aout_add_reloc(s, segment, RELTYPE_GOTPC, size);
642                 } else if (wrt == aout_gotoff_sect + 1) {
643                     is_pic = 0x40;
644                     addr = aout_add_gotoff_reloc(s, segment,
645                                                  addr, size);
646                 } else if (wrt == aout_got_sect + 1) {
647                     is_pic = 0x40;
648                     addr =
649                         aout_add_gsym_reloc(s, segment, addr, RELTYPE_GOT,
650                                             size, true);
651                 } else if (wrt == aout_sym_sect + 1) {
652                     addr = aout_add_gsym_reloc(s, segment, addr,
653                                                RELTYPE_ABSOLUTE, size,
654                                                false);
655                 } else if (wrt == aout_plt_sect + 1) {
656                     is_pic = 0x40;
657                     error(ERR_NONFATAL,
658                           "a.out format cannot produce non-PC-"
659                           "relative PLT references");
660                 } else {
661                     error(ERR_NONFATAL,
662                           "a.out format does not support this"
663                           " use of WRT");
664                     wrt = NO_SEG;       /* we can at least _try_ to continue */
665                 }
666             }
667         }
668         p = mydata;
669         if (size == 2)
670             WRITESHORT(p, addr);
671         else
672             WRITELONG(p, addr);
673         aout_sect_write(s, mydata, size);
674     } else if (type == OUT_REL2ADR) {
675         if (segment == segto)
676             error(ERR_PANIC, "intra-segment OUT_REL2ADR");
677         if (segment != NO_SEG && segment % 2) {
678             error(ERR_NONFATAL, "a.out format does not support"
679                   " segment base references");
680         } else {
681             if (wrt == NO_SEG) {
682                 aout_add_reloc(s, segment, RELTYPE_RELATIVE, 2);
683             } else if (!bsd) {
684                 error(ERR_NONFATAL, "Linux a.out format does not support"
685                       " any use of WRT");
686                 wrt = NO_SEG;   /* we can at least _try_ to continue */
687             } else if (wrt == aout_plt_sect + 1) {
688                 is_pic = 0x40;
689                 aout_add_reloc(s, segment, RELTYPE_PLT, 2);
690             } else if (wrt == aout_gotpc_sect + 1 ||
691                        wrt == aout_gotoff_sect + 1 ||
692                        wrt == aout_got_sect + 1) {
693                 error(ERR_NONFATAL, "a.out format cannot produce PC-"
694                       "relative GOT references");
695             } else {
696                 error(ERR_NONFATAL, "a.out format does not support this"
697                       " use of WRT");
698                 wrt = NO_SEG;   /* we can at least _try_ to continue */
699             }
700         }
701         p = mydata;
702         WRITESHORT(p, *(int64_t *)data - (size + s->len));
703         aout_sect_write(s, mydata, 2L);
704     } else if (type == OUT_REL4ADR) {
705         if (segment == segto)
706             error(ERR_PANIC, "intra-segment OUT_REL4ADR");
707         if (segment != NO_SEG && segment % 2) {
708             error(ERR_NONFATAL, "a.out format does not support"
709                   " segment base references");
710         } else {
711             if (wrt == NO_SEG) {
712                 aout_add_reloc(s, segment, RELTYPE_RELATIVE, 4);
713             } else if (!bsd) {
714                 error(ERR_NONFATAL, "Linux a.out format does not support"
715                       " any use of WRT");
716                 wrt = NO_SEG;   /* we can at least _try_ to continue */
717             } else if (wrt == aout_plt_sect + 1) {
718                 is_pic = 0x40;
719                 aout_add_reloc(s, segment, RELTYPE_PLT, 4);
720             } else if (wrt == aout_gotpc_sect + 1 ||
721                        wrt == aout_gotoff_sect + 1 ||
722                        wrt == aout_got_sect + 1) {
723                 error(ERR_NONFATAL, "a.out format cannot produce PC-"
724                       "relative GOT references");
725             } else {
726                 error(ERR_NONFATAL, "a.out format does not support this"
727                       " use of WRT");
728                 wrt = NO_SEG;   /* we can at least _try_ to continue */
729             }
730         }
731         p = mydata;
732         WRITELONG(p, *(int64_t *)data - (size + s->len));
733         aout_sect_write(s, mydata, 4L);
734     }
735 }
736
737 static void aout_pad_sections(void)
738 {
739     static uint8_t pad[] = { 0x90, 0x90, 0x90, 0x90 };
740     /*
741      * Pad each of the text and data sections with NOPs until their
742      * length is a multiple of four. (NOP == 0x90.) Also increase
743      * the length of the BSS section similarly.
744      */
745     aout_sect_write(&stext, pad, (-(int32_t)stext.len) & 3);
746     aout_sect_write(&sdata, pad, (-(int32_t)sdata.len) & 3);
747     sbss.len = (sbss.len + 3) & ~3;
748 }
749
750 /*
751  * a.out files have the curious property that all references to
752  * things in the data or bss sections are done by addresses which
753  * are actually relative to the start of the _text_ section, in the
754  * _file_. (No relation to what happens after linking. No idea why
755  * this should be so. It's very strange.) So we have to go through
756  * the relocation table, _after_ the final size of each section is
757  * known, and fix up the relocations pointed to.
758  */
759 static void aout_fixup_relocs(struct Section *sect)
760 {
761     struct Reloc *r;
762
763     saa_rewind(sect->data);
764     for (r = sect->head; r; r = r->next) {
765         uint8_t *p, *q, blk[4];
766         int32_t l;
767
768         saa_fread(sect->data, r->address, blk, (int32_t)r->bytes);
769         p = q = blk;
770         l = *p++;
771         if (r->bytes > 1) {
772             l += ((int32_t)*p++) << 8;
773             if (r->bytes == 4) {
774                 l += ((int32_t)*p++) << 16;
775                 l += ((int32_t)*p++) << 24;
776             }
777         }
778         if (r->symbol == -SECT_DATA)
779             l += stext.len;
780         else if (r->symbol == -SECT_BSS)
781             l += stext.len + sdata.len;
782         if (r->bytes == 4)
783             WRITELONG(q, l);
784         else if (r->bytes == 2)
785             WRITESHORT(q, l);
786         else
787             *q++ = l & 0xFF;
788         saa_fwrite(sect->data, r->address, blk, (int32_t)r->bytes);
789     }
790 }
791
792 static void aout_write(void)
793 {
794     /*
795      * Emit the a.out header.
796      */
797     /* OMAGIC, M_386 or MID_I386, no flags */
798     fwriteint32_t(bsd ? 0x07018600 | is_pic : 0x640107L, aoutfp);
799     fwriteint32_t(stext.len, aoutfp);
800     fwriteint32_t(sdata.len, aoutfp);
801     fwriteint32_t(sbss.len, aoutfp);
802     fwriteint32_t(nsyms * 12, aoutfp);     /* length of symbol table */
803     fwriteint32_t(0L, aoutfp);     /* object files have no entry point */
804     fwriteint32_t(stext.nrelocs * 8, aoutfp);      /* size of text relocs */
805     fwriteint32_t(sdata.nrelocs * 8, aoutfp);      /* size of data relocs */
806
807     /*
808      * Write out the code section and the data section.
809      */
810     saa_fpwrite(stext.data, aoutfp);
811     saa_fpwrite(sdata.data, aoutfp);
812
813     /*
814      * Write out the relocations.
815      */
816     aout_write_relocs(stext.head);
817     aout_write_relocs(sdata.head);
818
819     /*
820      * Write the symbol table.
821      */
822     aout_write_syms();
823
824     /*
825      * And the string table.
826      */
827     fwriteint32_t(strslen + 4, aoutfp);    /* length includes length count */
828     saa_fpwrite(strs, aoutfp);
829 }
830
831 static void aout_write_relocs(struct Reloc *r)
832 {
833     while (r) {
834         uint32_t word2;
835
836         fwriteint32_t(r->address, aoutfp);
837
838         if (r->symbol >= 0)
839             word2 = r->symbol;
840         else
841             word2 = -r->symbol;
842         word2 |= r->reltype << 24;
843         word2 |= (r->bytes == 1 ? 0 :
844                   r->bytes == 2 ? 0x2000000L : 0x4000000L);
845         fwriteint32_t(word2, aoutfp);
846
847         r = r->next;
848     }
849 }
850
851 static void aout_write_syms(void)
852 {
853     uint32_t i;
854
855     saa_rewind(syms);
856     for (i = 0; i < nsyms; i++) {
857         struct Symbol *sym = saa_rstruct(syms);
858         fwriteint32_t(sym->strpos, aoutfp);
859         fwriteint32_t((int32_t)sym->type & ~SYM_WITH_SIZE, aoutfp);
860         /*
861          * Fix up the symbol value now we know the final section
862          * sizes.
863          */
864         if ((sym->type & SECT_MASK) == SECT_DATA)
865             sym->value += stext.len;
866         if ((sym->type & SECT_MASK) == SECT_BSS)
867             sym->value += stext.len + sdata.len;
868         fwriteint32_t(sym->value, aoutfp);
869         /*
870          * Output a size record if necessary.
871          */
872         if (sym->type & SYM_WITH_SIZE) {
873             fwriteint32_t(sym->strpos, aoutfp);
874             fwriteint32_t(0x0DL, aoutfp);  /* special value: means size */
875             fwriteint32_t(sym->size, aoutfp);
876             i++;                /* use up another of `nsyms' */
877         }
878     }
879 }
880
881 static void aout_sect_write(struct Section *sect,
882                             const uint8_t *data, uint32_t len)
883 {
884     saa_wbytes(sect->data, data, len);
885     sect->len += len;
886 }
887
888 static int32_t aout_segbase(int32_t segment)
889 {
890     return segment;
891 }
892
893 static int aout_directive(char *directive, char *value, int pass)
894 {
895     (void)directive;
896     (void)value;
897     (void)pass;
898     return 0;
899 }
900
901 static void aout_filename(char *inname, char *outname, efunc error)
902 {
903     standard_extension(inname, outname, ".o", error);
904 }
905
906 static const char *aout_stdmac[] = {
907     "%define __SECT__ [section .text]",
908     "%macro __NASM_CDecl__ 1",
909     "%endmacro",
910     NULL
911 };
912
913 static int aout_set_info(enum geninfo type, char **val)
914 {
915     (void)type;
916     (void)val;
917     return 0;
918 }
919 #endif                          /* OF_AOUT || OF_AOUTB */
920
921 #ifdef OF_AOUT
922
923 struct ofmt of_aout = {
924     "Linux a.out object files",
925     "aout",
926     NULL,
927     null_debug_arr,
928     &null_debug_form,
929     aout_stdmac,
930     aout_init,
931     aout_set_info,
932     aout_out,
933     aout_deflabel,
934     aout_section_names,
935     aout_segbase,
936     aout_directive,
937     aout_filename,
938     aout_cleanup
939 };
940
941 #endif
942
943 #ifdef OF_AOUTB
944
945 struct ofmt of_aoutb = {
946     "NetBSD/FreeBSD a.out object files",
947     "aoutb",
948     NULL,
949     null_debug_arr,
950     &null_debug_form,
951     aout_stdmac,
952     aoutb_init,
953     aout_set_info,
954     aout_out,
955     aout_deflabel,
956     aout_section_names,
957     aout_segbase,
958     aout_directive,
959     aout_filename,
960     aout_cleanup
961 };
962
963 #endif