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