1 | /* ----------------------------------------------------------------------- * |
2 | * |
3 | * Copyright 1996-2017 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 | * outas86.c output routines for the Netwide Assembler to produce |
36 | * Linux as86 (bin86-0.3) 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 "outform.h" |
52 | #include "outlib.h" |
53 | |
54 | #ifdef OF_AS86 |
55 | |
56 | struct Piece { |
57 | struct Piece *next; |
58 | int type; /* 0 = absolute, 1 = seg, 2 = sym */ |
59 | int32_t offset; /* relative offset */ |
60 | int number; /* symbol/segment number (4=bss) */ |
61 | int32_t bytes; /* size of reloc or of absolute data */ |
62 | bool relative; /* relative address? */ |
63 | }; |
64 | |
65 | struct Symbol { |
66 | int32_t strpos; /* string table position of name */ |
67 | int flags; /* symbol flags */ |
68 | int segment; /* 4=bss at this point */ |
69 | int32_t value; /* address, or COMMON variable size */ |
70 | }; |
71 | |
72 | /* |
73 | * Section IDs - used in Piece.number and Symbol.segment. |
74 | */ |
75 | #define SECT_TEXT 0 /* text section */ |
76 | #define SECT_DATA 3 /* data section */ |
77 | #define SECT_BSS 4 /* bss section */ |
78 | |
79 | /* |
80 | * Flags used in Symbol.flags. |
81 | */ |
82 | #define SYM_ENTRY (1<<8) |
83 | #define SYM_EXPORT (1<<7) |
84 | #define SYM_IMPORT (1<<6) |
85 | #define SYM_ABSOLUTE (1<<4) |
86 | |
87 | struct Section { |
88 | struct SAA *data; |
89 | uint32_t datalen, size, len; |
90 | int32_t index; |
91 | struct Piece *head, *last, **tail; |
92 | }; |
93 | |
94 | static struct Section stext, sdata; |
95 | static uint32_t bsslen; |
96 | static int32_t bssindex; |
97 | |
98 | static struct SAA *syms; |
99 | static uint32_t nsyms; |
100 | |
101 | static struct RAA *bsym; |
102 | |
103 | static struct SAA *strs; |
104 | static size_t strslen; |
105 | |
106 | static int as86_reloc_size; |
107 | |
108 | static void as86_write(void); |
109 | static void as86_write_section(struct Section *, int); |
110 | static size_t as86_add_string(const char *name); |
111 | static void as86_sect_write(struct Section *, const uint8_t *, |
112 | uint32_t); |
113 | |
114 | static void as86_init(void) |
115 | { |
116 | stext.data = saa_init(1L); |
117 | stext.datalen = 0L; |
118 | stext.head = stext.last = NULL; |
119 | stext.tail = &stext.head; |
120 | sdata.data = saa_init(1L); |
121 | sdata.datalen = 0L; |
122 | sdata.head = sdata.last = NULL; |
123 | sdata.tail = &sdata.head; |
124 | bsslen = |
125 | stext.len = stext.datalen = stext.size = |
126 | sdata.len = sdata.datalen = sdata.size = 0; |
127 | stext.index = seg_alloc(); |
128 | sdata.index = seg_alloc(); |
129 | bssindex = seg_alloc(); |
130 | syms = saa_init((int32_t)sizeof(struct Symbol)); |
131 | nsyms = 0; |
132 | bsym = raa_init(); |
133 | strs = saa_init(1L); |
134 | strslen = 0; |
135 | |
136 | /* as86 module name = input file minus extension */ |
137 | as86_add_string(filename_set_extension(inname, "" )); |
138 | } |
139 | |
140 | static void as86_cleanup(void) |
141 | { |
142 | struct Piece *p; |
143 | |
144 | as86_write(); |
145 | saa_free(stext.data); |
146 | while (stext.head) { |
147 | p = stext.head; |
148 | stext.head = stext.head->next; |
149 | nasm_free(p); |
150 | } |
151 | saa_free(sdata.data); |
152 | while (sdata.head) { |
153 | p = sdata.head; |
154 | sdata.head = sdata.head->next; |
155 | nasm_free(p); |
156 | } |
157 | saa_free(syms); |
158 | raa_free(bsym); |
159 | saa_free(strs); |
160 | } |
161 | |
162 | static int32_t as86_section_names(char *name, int pass, int *bits) |
163 | { |
164 | |
165 | (void)pass; |
166 | |
167 | /* |
168 | * Default is 16 bits. |
169 | */ |
170 | if (!name) { |
171 | *bits = 16; |
172 | return stext.index; |
173 | } |
174 | |
175 | if (!strcmp(name, ".text" )) |
176 | return stext.index; |
177 | else if (!strcmp(name, ".data" )) |
178 | return sdata.index; |
179 | else if (!strcmp(name, ".bss" )) |
180 | return bssindex; |
181 | else |
182 | return NO_SEG; |
183 | } |
184 | |
185 | static size_t as86_add_string(const char *name) |
186 | { |
187 | size_t pos = strslen; |
188 | size_t length = strlen(name); |
189 | |
190 | saa_wbytes(strs, name, length + 1); |
191 | strslen += 1 + length; |
192 | |
193 | return pos; |
194 | } |
195 | |
196 | static void as86_deflabel(char *name, int32_t segment, int64_t offset, |
197 | int is_global, char *special) |
198 | { |
199 | bool is_start = false; |
200 | struct Symbol *sym; |
201 | |
202 | if (special) |
203 | nasm_error(ERR_NONFATAL, "as86 format does not support any" |
204 | " special symbol types" ); |
205 | |
206 | |
207 | if (name[0] == '.' && name[1] == '.' && name[2] != '@') { |
208 | if (strcmp(name, "..start" )) { |
209 | nasm_error(ERR_NONFATAL, "unrecognised special symbol `%s'" , name); |
210 | return; |
211 | } else { |
212 | is_start = true; |
213 | } |
214 | } |
215 | |
216 | sym = saa_wstruct(syms); |
217 | |
218 | sym->strpos = as86_add_string(name); |
219 | sym->flags = 0; |
220 | |
221 | if (is_start) |
222 | sym->flags = SYM_ENTRY; |
223 | |
224 | if (segment == NO_SEG) |
225 | sym->flags |= SYM_ABSOLUTE, sym->segment = 0; |
226 | else if (segment == stext.index) |
227 | sym->segment = SECT_TEXT; |
228 | else if (segment == sdata.index) |
229 | sym->segment = SECT_DATA; |
230 | else if (segment == bssindex) |
231 | sym->segment = SECT_BSS; |
232 | else { |
233 | sym->flags |= SYM_IMPORT; |
234 | sym->segment = 15; |
235 | } |
236 | |
237 | if (is_global == 2) |
238 | sym->segment = 3; /* already have IMPORT */ |
239 | |
240 | if (is_global && !(sym->flags & SYM_IMPORT)) |
241 | sym->flags |= SYM_EXPORT; |
242 | |
243 | sym->value = offset; |
244 | |
245 | /* |
246 | * define the references from external-symbol segment numbers |
247 | * to these symbol records. |
248 | */ |
249 | if (segment != NO_SEG && segment != stext.index && |
250 | segment != sdata.index && segment != bssindex) |
251 | bsym = raa_write(bsym, segment, nsyms); |
252 | |
253 | nsyms++; |
254 | } |
255 | |
256 | static void as86_add_piece(struct Section *sect, int type, int32_t offset, |
257 | int32_t segment, int32_t bytes, int relative) |
258 | { |
259 | struct Piece *p; |
260 | |
261 | sect->len += bytes; |
262 | |
263 | if (type == 0 && sect->last && sect->last->type == 0) { |
264 | sect->last->bytes += bytes; |
265 | return; |
266 | } |
267 | |
268 | p = sect->last = *sect->tail = nasm_malloc(sizeof(struct Piece)); |
269 | sect->tail = &p->next; |
270 | p->next = NULL; |
271 | |
272 | p->type = type; |
273 | p->offset = offset; |
274 | p->bytes = bytes; |
275 | p->relative = relative; |
276 | |
277 | if (type == 1 && segment == stext.index) |
278 | p->number = SECT_TEXT; |
279 | else if (type == 1 && segment == sdata.index) |
280 | p->number = SECT_DATA; |
281 | else if (type == 1 && segment == bssindex) |
282 | p->number = SECT_BSS; |
283 | else if (type == 1) |
284 | p->number = raa_read(bsym, segment), p->type = 2; |
285 | } |
286 | |
287 | static void as86_out(int32_t segto, const void *data, |
288 | enum out_type type, uint64_t size, |
289 | int32_t segment, int32_t wrt) |
290 | { |
291 | struct Section *s; |
292 | int32_t offset; |
293 | uint8_t mydata[4], *p; |
294 | |
295 | if (wrt != NO_SEG) { |
296 | wrt = NO_SEG; /* continue to do _something_ */ |
297 | nasm_error(ERR_NONFATAL, "WRT not supported by as86 output format" ); |
298 | } |
299 | |
300 | /* |
301 | * handle absolute-assembly (structure definitions) |
302 | */ |
303 | if (segto == NO_SEG) { |
304 | if (type != OUT_RESERVE) |
305 | nasm_error(ERR_NONFATAL, "attempt to assemble code in [ABSOLUTE]" |
306 | " space" ); |
307 | return; |
308 | } |
309 | |
310 | if (segto == stext.index) |
311 | s = &stext; |
312 | else if (segto == sdata.index) |
313 | s = &sdata; |
314 | else if (segto == bssindex) |
315 | s = NULL; |
316 | else { |
317 | nasm_error(ERR_WARNING, "attempt to assemble code in" |
318 | " segment %d: defaulting to `.text'" , segto); |
319 | s = &stext; |
320 | } |
321 | |
322 | if (!s && type != OUT_RESERVE) { |
323 | nasm_error(ERR_WARNING, "attempt to initialize memory in the" |
324 | " BSS section: ignored" ); |
325 | bsslen += realsize(type, size); |
326 | return; |
327 | } |
328 | |
329 | memset(mydata, 0, sizeof(mydata)); |
330 | |
331 | if (type == OUT_RESERVE) { |
332 | if (s) { |
333 | nasm_error(ERR_WARNING, "uninitialized space declared in" |
334 | " %s section: zeroing" , |
335 | (segto == stext.index ? "code" : "data" )); |
336 | as86_sect_write(s, NULL, size); |
337 | as86_add_piece(s, 0, 0L, 0L, size, 0); |
338 | } else |
339 | bsslen += size; |
340 | } else if (type == OUT_RAWDATA) { |
341 | if (segment != NO_SEG) |
342 | nasm_panic(0, "OUT_RAWDATA with other than NO_SEG" ); |
343 | as86_sect_write(s, data, size); |
344 | as86_add_piece(s, 0, 0L, 0L, size, 0); |
345 | } else if (type == OUT_ADDRESS) { |
346 | int asize = abs((int)size); |
347 | if (segment != NO_SEG) { |
348 | if (segment % 2) { |
349 | nasm_error(ERR_NONFATAL, "as86 format does not support" |
350 | " segment base references" ); |
351 | } else { |
352 | offset = *(int64_t *)data; |
353 | as86_add_piece(s, 1, offset, segment, asize, 0); |
354 | } |
355 | } else { |
356 | p = mydata; |
357 | WRITELONG(p, *(int64_t *)data); |
358 | as86_sect_write(s, data, asize); |
359 | as86_add_piece(s, 0, 0L, 0L, asize, 0); |
360 | } |
361 | } else if (type == OUT_REL2ADR) { |
362 | if (segment == segto) |
363 | nasm_panic(0, "intra-segment OUT_REL2ADR" ); |
364 | if (segment != NO_SEG) { |
365 | if (segment % 2) { |
366 | nasm_error(ERR_NONFATAL, "as86 format does not support" |
367 | " segment base references" ); |
368 | } else { |
369 | offset = *(int64_t *)data; |
370 | as86_add_piece(s, 1, offset - size + 2, segment, 2L, |
371 | 1); |
372 | } |
373 | } |
374 | } else if (type == OUT_REL4ADR) { |
375 | if (segment == segto) |
376 | nasm_panic(0, "intra-segment OUT_REL4ADR" ); |
377 | if (segment != NO_SEG) { |
378 | if (segment % 2) { |
379 | nasm_error(ERR_NONFATAL, "as86 format does not support" |
380 | " segment base references" ); |
381 | } else { |
382 | offset = *(int64_t *)data; |
383 | as86_add_piece(s, 1, offset - size + 4, segment, 4L, |
384 | 1); |
385 | } |
386 | } |
387 | } |
388 | } |
389 | |
390 | static void as86_write(void) |
391 | { |
392 | uint32_t i; |
393 | int32_t symlen, seglen, segsize; |
394 | |
395 | /* |
396 | * First, go through the symbol records working out how big |
397 | * each will be. Also fix up BSS references at this time, and |
398 | * set the flags words up completely. |
399 | */ |
400 | symlen = 0; |
401 | saa_rewind(syms); |
402 | for (i = 0; i < nsyms; i++) { |
403 | struct Symbol *sym = saa_rstruct(syms); |
404 | if (sym->segment == SECT_BSS) |
405 | sym->segment = SECT_DATA, sym->value += sdata.len; |
406 | sym->flags |= sym->segment; |
407 | if (sym->value == 0) |
408 | sym->flags |= 0 << 14, symlen += 4; |
409 | else if (sym->value >= 0 && sym->value <= 255) |
410 | sym->flags |= 1 << 14, symlen += 5; |
411 | else if (sym->value >= 0 && sym->value <= 65535L) |
412 | sym->flags |= 2 << 14, symlen += 6; |
413 | else |
414 | sym->flags |= 3 << 14, symlen += 8; |
415 | } |
416 | |
417 | /* |
418 | * Now do the same for the segments, and get the segment size |
419 | * descriptor word at the same time. |
420 | */ |
421 | seglen = segsize = 0; |
422 | if ((uint32_t)stext.len > 65535L) |
423 | segsize |= 0x03000000L, seglen += 4; |
424 | else |
425 | segsize |= 0x02000000L, seglen += 2; |
426 | if ((uint32_t)sdata.len > 65535L) |
427 | segsize |= 0xC0000000L, seglen += 4; |
428 | else |
429 | segsize |= 0x80000000L, seglen += 2; |
430 | |
431 | /* |
432 | * Emit the as86 header. |
433 | */ |
434 | fwriteint32_t(0x000186A3L, ofile); |
435 | fputc(0x2A, ofile); |
436 | fwriteint32_t(27 + symlen + seglen + strslen, ofile); /* header length */ |
437 | fwriteint32_t(stext.len + sdata.len + bsslen, ofile); |
438 | fwriteint16_t(strslen, ofile); |
439 | fwriteint16_t(0, ofile); /* class = revision = 0 */ |
440 | fwriteint32_t(0x55555555L, ofile); /* segment max sizes: always this */ |
441 | fwriteint32_t(segsize, ofile); /* segment size descriptors */ |
442 | if (segsize & 0x01000000L) |
443 | fwriteint32_t(stext.len, ofile); |
444 | else |
445 | fwriteint16_t(stext.len, ofile); |
446 | if (segsize & 0x40000000L) |
447 | fwriteint32_t(sdata.len + bsslen, ofile); |
448 | else |
449 | fwriteint16_t(sdata.len + bsslen, ofile); |
450 | fwriteint16_t(nsyms, ofile); |
451 | |
452 | /* |
453 | * Write the symbol table. |
454 | */ |
455 | saa_rewind(syms); |
456 | for (i = 0; i < nsyms; i++) { |
457 | struct Symbol *sym = saa_rstruct(syms); |
458 | fwriteint16_t(sym->strpos, ofile); |
459 | fwriteint16_t(sym->flags, ofile); |
460 | switch (sym->flags & (3 << 14)) { |
461 | case 0 << 14: |
462 | break; |
463 | case 1 << 14: |
464 | fputc(sym->value, ofile); |
465 | break; |
466 | case 2 << 14: |
467 | fwriteint16_t(sym->value, ofile); |
468 | break; |
469 | case 3 << 14: |
470 | fwriteint32_t(sym->value, ofile); |
471 | break; |
472 | } |
473 | } |
474 | |
475 | /* |
476 | * Write out the string table. |
477 | */ |
478 | saa_fpwrite(strs, ofile); |
479 | |
480 | /* |
481 | * Write the program text. |
482 | */ |
483 | as86_reloc_size = -1; |
484 | as86_write_section(&stext, SECT_TEXT); |
485 | as86_write_section(&sdata, SECT_DATA); |
486 | /* |
487 | * Append the BSS section to the .data section |
488 | */ |
489 | if (bsslen > 65535L) { |
490 | fputc(0x13, ofile); |
491 | fwriteint32_t(bsslen, ofile); |
492 | } else if (bsslen > 255) { |
493 | fputc(0x12, ofile); |
494 | fwriteint16_t(bsslen, ofile); |
495 | } else if (bsslen) { |
496 | fputc(0x11, ofile); |
497 | fputc(bsslen, ofile); |
498 | } |
499 | |
500 | fputc(0, ofile); /* termination */ |
501 | } |
502 | |
503 | static void as86_set_rsize(int size) |
504 | { |
505 | if (as86_reloc_size != size) { |
506 | switch (as86_reloc_size = size) { |
507 | case 1: |
508 | fputc(0x01, ofile); |
509 | break; |
510 | case 2: |
511 | fputc(0x02, ofile); |
512 | break; |
513 | case 4: |
514 | fputc(0x03, ofile); |
515 | break; |
516 | default: |
517 | nasm_panic(0, "bizarre relocation size %d" , size); |
518 | break; |
519 | } |
520 | } |
521 | } |
522 | |
523 | static void as86_write_section(struct Section *sect, int index) |
524 | { |
525 | struct Piece *p; |
526 | uint32_t s; |
527 | int32_t length; |
528 | |
529 | fputc(0x20 + index, ofile); /* select the right section */ |
530 | |
531 | saa_rewind(sect->data); |
532 | |
533 | for (p = sect->head; p; p = p->next) |
534 | switch (p->type) { |
535 | case 0: |
536 | /* |
537 | * Absolute data. Emit it in chunks of at most 64 |
538 | * bytes. |
539 | */ |
540 | length = p->bytes; |
541 | do { |
542 | char buf[64]; |
543 | int32_t tmplen = (length > 64 ? 64 : length); |
544 | fputc(0x40 | (tmplen & 0x3F), ofile); |
545 | saa_rnbytes(sect->data, buf, tmplen); |
546 | nasm_write(buf, tmplen, ofile); |
547 | length -= tmplen; |
548 | } while (length > 0); |
549 | break; |
550 | case 1: |
551 | /* |
552 | * A segment-type relocation. First fix up the BSS. |
553 | */ |
554 | if (p->number == SECT_BSS) |
555 | p->number = SECT_DATA, p->offset += sdata.len; |
556 | as86_set_rsize(p->bytes); |
557 | fputc(0x80 | (p->relative ? 0x20 : 0) | p->number, ofile); |
558 | if (as86_reloc_size == 2) |
559 | fwriteint16_t(p->offset, ofile); |
560 | else |
561 | fwriteint32_t(p->offset, ofile); |
562 | break; |
563 | case 2: |
564 | /* |
565 | * A symbol-type relocation. |
566 | */ |
567 | as86_set_rsize(p->bytes); |
568 | s = p->offset; |
569 | if (s > 65535L) |
570 | s = 3; |
571 | else if (s > 255) |
572 | s = 2; |
573 | else if (s > 0) |
574 | s = 1; |
575 | else |
576 | s = 0; |
577 | fputc(0xC0 | |
578 | (p->relative ? 0x20 : 0) | |
579 | (p->number > 255 ? 0x04 : 0) | s, ofile); |
580 | if (p->number > 255) |
581 | fwriteint16_t(p->number, ofile); |
582 | else |
583 | fputc(p->number, ofile); |
584 | switch (s) { |
585 | case 0: |
586 | break; |
587 | case 1: |
588 | fputc(p->offset, ofile); |
589 | break; |
590 | case 2: |
591 | fwriteint16_t(p->offset, ofile); |
592 | break; |
593 | case 3: |
594 | fwriteint32_t(p->offset, ofile); |
595 | break; |
596 | } |
597 | break; |
598 | } |
599 | } |
600 | |
601 | static void as86_sect_write(struct Section *sect, |
602 | const uint8_t *data, uint32_t len) |
603 | { |
604 | saa_wbytes(sect->data, data, len); |
605 | sect->datalen += len; |
606 | } |
607 | |
608 | static int32_t as86_segbase(int32_t segment) |
609 | { |
610 | return segment; |
611 | } |
612 | |
613 | extern macros_t as86_stdmac[]; |
614 | |
615 | const struct ofmt of_as86 = { |
616 | "Linux as86 (bin86 version 0.3) object files" , |
617 | "as86" , |
618 | ".o" , |
619 | 0, |
620 | 32, |
621 | null_debug_arr, |
622 | &null_debug_form, |
623 | as86_stdmac, |
624 | as86_init, |
625 | null_reset, |
626 | nasm_do_legacy_output, |
627 | as86_out, |
628 | as86_deflabel, |
629 | as86_section_names, |
630 | NULL, |
631 | null_sectalign, |
632 | as86_segbase, |
633 | null_directive, |
634 | as86_cleanup, |
635 | NULL /* pragma list */ |
636 | }; |
637 | |
638 | #endif /* OF_AS86 */ |
639 | |