summaryrefslogtreecommitdiff
path: root/outaout.c
blob: 2b5a381c2b61841705bcbe9bddc94cc9f06e2779 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
/* outaout.c	output routines for the Netwide Assembler to produce
 *		Linux a.out object files
 *
 * The Netwide Assembler is copyright (C) 1996 Simon Tatham and
 * Julian Hall. All rights reserved. The software is
 * redistributable under the licence given in the file "Licence"
 * distributed in the NASM archive.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#include "nasm.h"
#include "nasmlib.h"
#include "outform.h"

#ifdef OF_AOUT

struct Reloc {
    struct Reloc *next;
    long address;		       /* relative to _start_ of section */
    long symbol;		       /* symbol number or -ve section id */
    int bytes;			       /* 2 or 4 */
    int relative;		       /* TRUE or FALSE */
};

struct Symbol {
    long strpos;		       /* string table position of name */
    int type;			       /* symbol type - see flags below */
    long value;			       /* address, or COMMON variable size */
};

/*
 * 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 */

/*
 * Another flag used in Symbol.type.
 */
#define SYM_GLOBAL 1		       /* it's a global symbol */

/*
 * 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;
    unsigned long len, size, nrelocs;
    long index;
    struct Reloc *head, **tail;
};

static struct Section stext, sdata;
static unsigned long bsslen;
static long bssindex;

static struct SAA *syms;
static unsigned long nsyms;

static struct RAA *bsym;

static struct SAA *strs;
static unsigned long strslen;

static FILE *aoutfp;
static efunc error;

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 *, unsigned char *, unsigned long);
static void aout_pad_sections(void);
static void aout_fixup_relocs(struct Section *);

static void aout_init(FILE *fp, efunc errfunc, ldfunc ldef) {
    aoutfp = fp;
    error = errfunc;
    (void) ldef;		       /* placate optimisers */
    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 = bsslen = 0;
    stext.nrelocs = sdata.nrelocs = 0;
    stext.index = seg_alloc();
    sdata.index = seg_alloc();
    bssindex = seg_alloc();
    syms = saa_init((long)sizeof(struct Symbol));
    nsyms = 0;
    bsym = raa_init();
    strs = saa_init(1L);
    strslen = 0;
}

static void aout_cleanup(void) {
    struct Reloc *r;

    aout_pad_sections();
    aout_fixup_relocs(&stext);
    aout_fixup_relocs(&sdata);
    aout_write();
    fclose (aoutfp);
    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 long aout_section_names (char *name, int pass, int *bits) {
    /*
     * Default to 32 bits.
     */
    if (!name)
	*bits = 32;

    if (!name)
	return stext.index;

    if (!strcmp(name, ".text"))
	return stext.index;
    else if (!strcmp(name, ".data"))
	return sdata.index;
    else if (!strcmp(name, ".bss"))
	return bssindex;
    else
	return NO_SEG;
}

static void aout_deflabel (char *name, long segment, long offset,
			   int is_global) {
    int pos = strslen+4;
    struct Symbol *sym;

    if (name[0] == '.' && name[1] == '.' && name[2] != '@') {
	error (ERR_NONFATAL, "unrecognised special symbol `%s'", name);
	return;
    }

    saa_wbytes (strs, name, (long)(1+strlen(name)));
    strslen += 1+strlen(name);

    sym = saa_wstruct (syms);

    sym->strpos = pos;
    sym->type = is_global ? SYM_GLOBAL : 0;
    if (segment == NO_SEG)
	sym->type |= SECT_ABS;
    else if (segment == stext.index)
	sym->type |= SECT_TEXT;
    else if (segment == sdata.index)
	sym->type |= SECT_DATA;
    else if (segment == bssindex)
	sym->type |= SECT_BSS;
    else
	sym->type = SYM_GLOBAL;
    if (is_global == 2)
	sym->value = offset;
    else
	sym->value = (sym->type == SYM_GLOBAL ? 0 : offset);

    /*
     * define the references from external-symbol segment numbers
     * to these symbol records.
     */
    if (segment != NO_SEG && segment != stext.index &&
	segment != sdata.index && segment != bssindex)
	bsym = raa_write (bsym, segment, nsyms);

    nsyms++;
}

static void aout_add_reloc (struct Section *sect, long segment,
			    int relative, 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 == bssindex ? -SECT_BSS :
		 raa_read(bsym, segment));
    r->relative = relative;
    r->bytes = bytes;

    sect->nrelocs++;
}

static void aout_out (long segto, void *data, unsigned long type,
		      long segment, long wrt) {
    struct Section *s;
    long realbytes = type & OUT_SIZMASK;
    unsigned char mydata[4], *p;

    if (wrt != NO_SEG) {
	wrt = NO_SEG;		       /* continue to do _something_ */
	error (ERR_NONFATAL, "WRT not supported by a.out output format");
    }

    type &= OUT_TYPMASK;

    /*
     * handle absolute-assembly (structure definitions)
     */
    if (segto == NO_SEG) {
	if (type != OUT_RESERVE)
	    error (ERR_NONFATAL, "attempt to assemble code in [ABSOLUTE]"
		   " space");
	return;
    }

    if (segto == stext.index)
	s = &stext;
    else if (segto == sdata.index)
	s = &sdata;
    else if (segto == bssindex)
	s = NULL;
    else {
	error(ERR_WARNING, "attempt to assemble code in"
	      " segment %d: defaulting to `.text'", segto);
	s = &stext;
    }

    if (!s && type != OUT_RESERVE) {
	error(ERR_WARNING, "attempt to initialise memory in the"
	      " BSS section: ignored");
	if (type == OUT_REL2ADR)
	    realbytes = 2;
	else if (type == OUT_REL4ADR)
	    realbytes = 4;
	bsslen += realbytes;
	return;
    }

    if (type == OUT_RESERVE) {
	if (s) {
	    error(ERR_WARNING, "uninitialised space declared in"
		  " %s section: zeroing",
		  (segto == stext.index ? "code" : "data"));
	    aout_sect_write (s, NULL, realbytes);
	} else
	    bsslen += realbytes;
    } else if (type == OUT_RAWDATA) {
	if (segment != NO_SEG)
	    error(ERR_PANIC, "OUT_RAWDATA with other than NO_SEG");
	aout_sect_write (s, data, realbytes);
    } else if (type == OUT_ADDRESS) {
	if (segment != NO_SEG) {
	    if (segment % 2) {
		error(ERR_NONFATAL, "a.out format does not support"
		      " segment base references");
	    } else
		aout_add_reloc (s, segment, FALSE, realbytes);
	}
	p = mydata;
	if (realbytes == 2)
	    WRITESHORT (p, *(long *)data);
	else
	    WRITELONG (p, *(long *)data);
	aout_sect_write (s, mydata, realbytes);
    } else if (type == OUT_REL2ADR) {
	if (segment == segto)
	    error(ERR_PANIC, "intra-segment OUT_REL2ADR");
	if (segment != NO_SEG && segment % 2) {
	    error(ERR_NONFATAL, "a.out format does not support"
		  " segment base references");
	} else
	    aout_add_reloc (s, segment, TRUE, 2);
	p = mydata;
	WRITESHORT (p, *(long*)data-(realbytes + s->len));
	aout_sect_write (s, mydata, 2L);
    } else if (type == OUT_REL4ADR) {
	if (segment == segto)
	    error(ERR_PANIC, "intra-segment OUT_REL4ADR");
	if (segment != NO_SEG && segment % 2) {
	    error(ERR_NONFATAL, "a.out format does not support"
		  " segment base references");
	} else
	    aout_add_reloc (s, segment, TRUE, 4);
	p = mydata;
	WRITELONG (p, *(long*)data-(realbytes + s->len));
	aout_sect_write (s, mydata, 4L);
    }
}

static void aout_pad_sections(void) {
    static unsigned char 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, (-stext.len) & 3);
    aout_sect_write (&sdata, pad, (-sdata.len) & 3);
    bsslen = (bsslen + 3) & ~3;
}

/*
 * 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);
    for (r = sect->head; r; r = r->next) {
	unsigned char *p, *q, blk[4];
	long l;

	saa_fread (sect->data, r->address, blk, (long)r->bytes);
	p = q = blk;
	l = *p++;
	l += ((long)*p++) << 8;
	if (r->bytes == 4) {
	    l += ((long)*p++) << 16;
	    l += ((long)*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
	    WRITESHORT(q, l);
	saa_fwrite (sect->data, r->address, blk, (long)r->bytes);
    }
}

static void aout_write(void) {
    /*
     * Emit the a.out header.
     */
    fwritelong (0x640107L, aoutfp);    /* OMAGIC, M_386, no flags */
    fwritelong (stext.len, aoutfp);
    fwritelong (sdata.len, aoutfp);
    fwritelong (bsslen, aoutfp);
    fwritelong (nsyms * 12, aoutfp);   /* length of symbol table */
    fwritelong (0L, aoutfp);	       /* object files have no entry point */
    fwritelong (stext.nrelocs * 8, aoutfp);   /* size of text relocs */
    fwritelong (sdata.nrelocs * 8, aoutfp);   /* size of data relocs */

    /*
     * Write out the code section and the data section.
     */
    saa_fpwrite (stext.data, aoutfp);
    saa_fpwrite (sdata.data, aoutfp);

    /*
     * 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.
     */
    fwritelong (strslen+4, aoutfp);    /* length includes length count */
    saa_fpwrite (strs, aoutfp);
}

static void aout_write_relocs (struct Reloc *r) {
    while (r) {
	unsigned long word2;

	fwritelong (r->address, aoutfp);

	if (r->symbol >= 0)
	    word2 = r->symbol | 0x8000000L;
	else
	    word2 = -r->symbol;
	if (r->relative)
	    word2 |= 0x1000000L;
	word2 |= (r->bytes == 2 ? 0x2000000L : 0x4000000L);
	fwritelong (word2, aoutfp);

	r = r->next;
    }
}

static void aout_write_syms (void) {
    int i;

    saa_rewind (syms);
    for (i=0; i<nsyms; i++) {
	struct Symbol *sym = saa_rstruct(syms);
	fwritelong (sym->strpos, aoutfp);
	fwritelong ((long)sym->type, aoutfp);
	/*
	 * 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;
	fwritelong (sym->value, aoutfp);
    }
}

static void aout_sect_write (struct Section *sect,
			     unsigned char *data, unsigned long len) {
    saa_wbytes (sect->data, data, len);
    sect->len += len;
}

static long aout_segbase (long segment) {
    return segment;
}

static int aout_directive (char *directive, char *value, int pass) {
    return 0;
}

static void aout_filename (char *inname, char *outname, efunc error) {
    standard_extension (inname, outname, ".o", error);
}

struct ofmt of_aout = {
    "GNU a.out (i386) object files (e.g. Linux)",
    "aout",
    aout_init,
    aout_out,
    aout_deflabel,
    aout_section_names,
    aout_segbase,
    aout_directive,
    aout_filename,
    aout_cleanup
};

#endif /* OF_AOUT */