mirror of
https://github.com/netwide-assembler/nasm.git
synced 2024-12-21 09:19:31 +08:00
d0fbb7f0ab
We fopen() the output file in common code but fclose() it in the backend. This is bad for a variety of reasons: 1. it is generally an awkward interface to change ownership. 2. we should use ferror() to test for write errors, and that is better done in common code. 3. it requires more code. 4. we still need to fclose() in common code during error handing. Thus, move the fclose() of the output out of the backends, and add fflush() so we can test ferror() on output. Signed-off-by: H. Peter Anvin <hpa@zytor.com>
570 lines
17 KiB
C
570 lines
17 KiB
C
/* ----------------------------------------------------------------------- *
|
|
*
|
|
* Copyright 1996-2009 The NASM Authors - All Rights Reserved
|
|
* See the file AUTHORS included with the NASM distribution for
|
|
* the specific copyright holders.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following
|
|
* conditions are met:
|
|
*
|
|
* * Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* * Redistributions in binary form must reproduce the above
|
|
* copyright notice, this list of conditions and the following
|
|
* disclaimer in the documentation and/or other materials provided
|
|
* with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
|
|
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
|
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
|
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
|
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
|
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*
|
|
* ----------------------------------------------------------------------- */
|
|
|
|
/*
|
|
* outrdf.c output routines for the Netwide Assembler to produce
|
|
* RDOFF format object files (which are intended mainly
|
|
* for use in proprietary projects, as the code to load and
|
|
* execute them is very simple). They will also be used
|
|
* for device drivers and possibly some executable files
|
|
* in the MOSCOW operating system. See Rdoff.txt for
|
|
* details.
|
|
*/
|
|
|
|
#include "compiler.h"
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
#include <assert.h>
|
|
|
|
#include "nasm.h"
|
|
#include "nasmlib.h"
|
|
#include "output/outform.h"
|
|
|
|
/* VERBOSE_WARNINGS: define this to add some extra warnings... */
|
|
#define VERBOSE_WARNINGS
|
|
|
|
#ifdef OF_RDF
|
|
|
|
typedef int16_t int16; /* not sure if this will be required to be altered
|
|
at all... best to typedef it just in case */
|
|
|
|
static const char *RDOFFId = "RDOFF1"; /* written to start of RDOFF files */
|
|
|
|
/* the records that can be found in the RDOFF header */
|
|
|
|
/* Note that whenever a segment is referred to in the RDOFF file, its number
|
|
* is always half of the segment number that NASM uses to refer to it; this
|
|
* is because NASM only allocates even numbered segments, so as to not
|
|
* waste any of the 16 bits of segment number written to the file - this
|
|
* allows up to 65533 external labels to be defined; otherwise it would be
|
|
* 32764. */
|
|
|
|
struct RelocRec {
|
|
char type; /* must be 1 */
|
|
char segment; /* only 0 for code, or 1 for data supported,
|
|
* but add 64 for relative refs (ie do not require
|
|
* reloc @ loadtime, only linkage) */
|
|
int32_t offset; /* from start of segment in which reference is loc'd */
|
|
char length; /* 1 2 or 4 bytes */
|
|
int16 refseg; /* segment to which reference refers to */
|
|
};
|
|
|
|
struct ImportRec {
|
|
char type; /* must be 2 */
|
|
int16 segment; /* segment number allocated to the label for reloc
|
|
* records - label is assumed to be at offset zero
|
|
* in this segment, so linker must fix up with offset
|
|
* of segment and of offset within segment */
|
|
char label[33]; /* zero terminated... should be written to file until
|
|
* the zero, but not after it - max len = 32 chars */
|
|
};
|
|
|
|
struct ExportRec {
|
|
char type; /* must be 3 */
|
|
char segment; /* segment referred to (0/1) */
|
|
int32_t offset; /* offset within segment */
|
|
char label[33]; /* zero terminated as above. max len = 32 chars */
|
|
};
|
|
|
|
struct DLLRec {
|
|
char type; /* must be 4 */
|
|
char libname[128]; /* name of library to link with at load time */
|
|
};
|
|
|
|
struct BSSRec {
|
|
char type; /* must be 5 */
|
|
int32_t amount; /* number of bytes BSS to reserve */
|
|
};
|
|
|
|
/* code for managing buffers needed to seperate code and data into individual
|
|
* sections until they are ready to be written to the file.
|
|
* We'd better hope that it all fits in memory else we're buggered... */
|
|
|
|
#define BUF_BLOCK_LEN 4088 /* selected to match page size (4096)
|
|
* on 80x86 machines for efficiency */
|
|
|
|
typedef struct memorybuffer {
|
|
int length;
|
|
char buffer[BUF_BLOCK_LEN];
|
|
struct memorybuffer *next;
|
|
} memorybuffer;
|
|
|
|
static memorybuffer *newmembuf(void)
|
|
{
|
|
memorybuffer *t;
|
|
|
|
t = nasm_malloc(sizeof(memorybuffer));
|
|
|
|
t->length = 0;
|
|
t->next = NULL;
|
|
return t;
|
|
}
|
|
|
|
static void membufwrite(memorybuffer * b, void *data, int bytes)
|
|
{
|
|
int16 w;
|
|
int32_t l;
|
|
|
|
if (b->next) { /* memory buffer full - use next buffer */
|
|
membufwrite(b->next, data, bytes);
|
|
return;
|
|
}
|
|
if ((bytes < 0 && b->length - bytes > BUF_BLOCK_LEN)
|
|
|| (bytes > 0 && b->length + bytes > BUF_BLOCK_LEN)) {
|
|
|
|
/* buffer full and no next allocated... allocate and initialize next
|
|
* buffer */
|
|
|
|
b->next = newmembuf();
|
|
membufwrite(b->next, data, bytes);
|
|
return;
|
|
}
|
|
|
|
switch (bytes) {
|
|
case -4: /* convert to little-endian */
|
|
l = *(int32_t *)data;
|
|
b->buffer[b->length++] = l & 0xFF;
|
|
l >>= 8;
|
|
b->buffer[b->length++] = l & 0xFF;
|
|
l >>= 8;
|
|
b->buffer[b->length++] = l & 0xFF;
|
|
l >>= 8;
|
|
b->buffer[b->length++] = l & 0xFF;
|
|
break;
|
|
|
|
case -2:
|
|
w = *(int16 *) data;
|
|
b->buffer[b->length++] = w & 0xFF;
|
|
w >>= 8;
|
|
b->buffer[b->length++] = w & 0xFF;
|
|
break;
|
|
|
|
default:
|
|
while (bytes--) {
|
|
b->buffer[b->length++] = *(*(uint8_t **)&data);
|
|
|
|
(*(uint8_t **)&data)++;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void membufdump(memorybuffer * b, FILE * fp)
|
|
{
|
|
if (!b)
|
|
return;
|
|
|
|
fwrite(b->buffer, 1, b->length, fp);
|
|
|
|
membufdump(b->next, fp);
|
|
}
|
|
|
|
static int membuflength(memorybuffer * b)
|
|
{
|
|
if (!b)
|
|
return 0;
|
|
return b->length + membuflength(b->next);
|
|
}
|
|
|
|
static void freemembuf(memorybuffer * b)
|
|
{
|
|
if (!b)
|
|
return;
|
|
freemembuf(b->next);
|
|
nasm_free(b);
|
|
}
|
|
|
|
/***********************************************************************
|
|
* Actual code to deal with RDOFF ouput format begins here...
|
|
*/
|
|
|
|
/* global variables set during the initialisation phase */
|
|
|
|
static memorybuffer *seg[2]; /* seg 0 = code, seg 1 = data */
|
|
static memorybuffer *header; /* relocation/import/export records */
|
|
|
|
static FILE *ofile;
|
|
|
|
static efunc error;
|
|
|
|
static int segtext, segdata, segbss;
|
|
static int32_t bsslength;
|
|
|
|
static void rdf_init(FILE * fp, efunc errfunc, ldfunc ldef, evalfunc eval)
|
|
{
|
|
maxbits = 64;
|
|
ofile = fp;
|
|
error = errfunc;
|
|
seg[0] = newmembuf();
|
|
seg[1] = newmembuf();
|
|
header = newmembuf();
|
|
segtext = seg_alloc();
|
|
segdata = seg_alloc();
|
|
segbss = seg_alloc();
|
|
if (segtext != 0 || segdata != 2 || segbss != 4)
|
|
error(ERR_PANIC,
|
|
"rdf segment numbers not allocated as expected (%d,%d,%d)",
|
|
segtext, segdata, segbss);
|
|
bsslength = 0;
|
|
}
|
|
|
|
static int32_t rdf_section_names(char *name, int pass, int *bits)
|
|
{
|
|
/*
|
|
* Default is 32 bits.
|
|
*/
|
|
if (!name)
|
|
*bits = 32;
|
|
|
|
if (!name)
|
|
return 0;
|
|
if (!strcmp(name, ".text"))
|
|
return 0;
|
|
else if (!strcmp(name, ".data"))
|
|
return 2;
|
|
else if (!strcmp(name, ".bss"))
|
|
return 4;
|
|
else
|
|
return NO_SEG;
|
|
}
|
|
|
|
static void write_reloc_rec(struct RelocRec *r)
|
|
{
|
|
if (r->refseg != NO_SEG && (r->refseg & 1))
|
|
error(ERR_NONFATAL, "RDF format does not support segment base"
|
|
" references");
|
|
|
|
r->refseg >>= 1; /* adjust segment nos to RDF rather than NASM */
|
|
|
|
membufwrite(header, &r->type, 1);
|
|
membufwrite(header, &r->segment, 1);
|
|
membufwrite(header, &r->offset, -4);
|
|
membufwrite(header, &r->length, 1);
|
|
membufwrite(header, &r->refseg, -2); /* 9 bytes written */
|
|
}
|
|
|
|
static void write_export_rec(struct ExportRec *r)
|
|
{
|
|
r->segment >>= 1;
|
|
|
|
membufwrite(header, &r->type, 1);
|
|
membufwrite(header, &r->segment, 1);
|
|
membufwrite(header, &r->offset, -4);
|
|
membufwrite(header, r->label, strlen(r->label) + 1);
|
|
}
|
|
|
|
static void write_import_rec(struct ImportRec *r)
|
|
{
|
|
r->segment >>= 1;
|
|
|
|
membufwrite(header, &r->type, 1);
|
|
membufwrite(header, &r->segment, -2);
|
|
membufwrite(header, r->label, strlen(r->label) + 1);
|
|
}
|
|
|
|
static void write_bss_rec(struct BSSRec *r)
|
|
{
|
|
membufwrite(header, &r->type, 1);
|
|
membufwrite(header, &r->amount, -4);
|
|
}
|
|
|
|
static void write_dll_rec(struct DLLRec *r)
|
|
{
|
|
membufwrite(header, &r->type, 1);
|
|
membufwrite(header, r->libname, strlen(r->libname) + 1);
|
|
}
|
|
|
|
static void rdf_deflabel(char *name, int32_t segment, int32_t offset,
|
|
int is_global, char *special)
|
|
{
|
|
struct ExportRec r;
|
|
struct ImportRec ri;
|
|
#ifdef VERBOSE_WARNINGS
|
|
static int warned_common = 0;
|
|
#endif
|
|
|
|
if (special)
|
|
error(ERR_NONFATAL, "RDOFF format does not support any"
|
|
" special symbol types");
|
|
|
|
if (name[0] == '.' && name[1] == '.' && name[2] != '@') {
|
|
error(ERR_NONFATAL, "unrecognised special symbol `%s'", name);
|
|
return;
|
|
}
|
|
|
|
if (is_global == 2) {
|
|
#ifdef VERBOSE_WARNINGS
|
|
if (!warned_common) {
|
|
error(ERR_WARNING,
|
|
"common declarations not supported: using extern");
|
|
warned_common = 1;
|
|
}
|
|
#endif
|
|
is_global = 1;
|
|
}
|
|
|
|
if (segment > 4) { /* EXTERN declaration */
|
|
ri.type = 2;
|
|
ri.segment = segment;
|
|
strncpy(ri.label, name, 32);
|
|
ri.label[32] = 0;
|
|
write_import_rec(&ri);
|
|
} else if (is_global) {
|
|
r.type = 3;
|
|
r.segment = segment;
|
|
r.offset = offset;
|
|
strncpy(r.label, name, 32);
|
|
r.label[32] = 0;
|
|
write_export_rec(&r);
|
|
}
|
|
}
|
|
|
|
static void rdf_out(int32_t segto, void *data, uint32_t type,
|
|
int32_t segment, int32_t wrt)
|
|
{
|
|
int32_t bytes = type & OUT_SIZMASK;
|
|
struct RelocRec rr;
|
|
uint8_t databuf[8], *pd;
|
|
|
|
if (segto == NO_SEG) {
|
|
if ((type & OUT_TYPMASK) != OUT_RESERVE)
|
|
error(ERR_NONFATAL,
|
|
"attempt to assemble code in ABSOLUTE space");
|
|
return;
|
|
}
|
|
|
|
segto >>= 1; /* convert NASM segment no to RDF number */
|
|
|
|
if (segto != 0 && segto != 1 && segto != 2) {
|
|
error(ERR_NONFATAL,
|
|
"specified segment not supported by rdf output format");
|
|
return;
|
|
}
|
|
|
|
if (wrt != NO_SEG) {
|
|
wrt = NO_SEG; /* continue to do _something_ */
|
|
error(ERR_NONFATAL, "WRT not supported by rdf output format");
|
|
}
|
|
|
|
type &= OUT_TYPMASK;
|
|
|
|
if (segto == 2 && type != OUT_RESERVE) {
|
|
error(ERR_NONFATAL, "BSS segments may not be initialized");
|
|
|
|
/* just reserve the space for now... */
|
|
|
|
if (type == OUT_REL2ADR)
|
|
bytes = 2;
|
|
else
|
|
bytes = 4;
|
|
type = OUT_RESERVE;
|
|
}
|
|
|
|
if (type == OUT_RESERVE) {
|
|
if (segto == 2) /* BSS segment space reserverd */
|
|
bsslength += bytes;
|
|
else
|
|
while (bytes--)
|
|
membufwrite(seg[segto], databuf, 1);
|
|
} else if (type == OUT_RAWDATA) {
|
|
if (segment != NO_SEG)
|
|
error(ERR_PANIC, "OUT_RAWDATA with other than NO_SEG");
|
|
membufwrite(seg[segto], data, bytes);
|
|
} else if (type == OUT_ADDRESS) {
|
|
|
|
/* if segment == NO_SEG then we are writing an address of an
|
|
object within the same segment - do not produce reloc rec. */
|
|
|
|
if (segment != NO_SEG) {
|
|
|
|
/* it's an address, so we must write a relocation record */
|
|
|
|
rr.type = 1; /* type signature */
|
|
rr.segment = segto; /* segment we're currently in */
|
|
rr.offset = membuflength(seg[segto]); /* current offset */
|
|
rr.length = bytes; /* length of reference */
|
|
rr.refseg = segment; /* segment referred to */
|
|
write_reloc_rec(&rr);
|
|
}
|
|
|
|
pd = databuf; /* convert address to little-endian */
|
|
WRITEADDR(pd, *(int64_t *)data, bytes);
|
|
membufwrite(seg[segto], databuf, bytes);
|
|
} else if (type == OUT_REL2ADR) {
|
|
if (segment == segto)
|
|
error(ERR_PANIC, "intra-segment OUT_REL2ADR");
|
|
if (segment != NO_SEG && segment % 2) {
|
|
error(ERR_NONFATAL,
|
|
"rdf format does not support segment base refs");
|
|
}
|
|
|
|
rr.type = 1; /* type signature */
|
|
rr.segment = segto + 64; /* segment we're currently in + rel flag */
|
|
rr.offset = membuflength(seg[segto]); /* current offset */
|
|
rr.length = 2; /* length of reference */
|
|
rr.refseg = segment; /* segment referred to */
|
|
write_reloc_rec(&rr);
|
|
|
|
/* work out what to put in the code: offset of the end of this operand,
|
|
* subtracted from any data specified, so that loader can just add
|
|
* address of imported symbol onto it to get address relative to end of
|
|
* instruction: import_address + data(offset) - end_of_instrn */
|
|
|
|
rr.offset = *(int64_t *)data - (rr.offset + bytes);
|
|
|
|
membufwrite(seg[segto], &rr.offset, -2);
|
|
} else if (type == OUT_REL4ADR) {
|
|
if ((segment == segto) && (globalbits != 64))
|
|
error(ERR_PANIC, "intra-segment OUT_REL4ADR");
|
|
if (segment != NO_SEG && segment % 2) {
|
|
error(ERR_NONFATAL,
|
|
"rdf format does not support segment base refs");
|
|
}
|
|
|
|
rr.type = 1; /* type signature */
|
|
rr.segment = segto + 64; /* segment we're currently in + rel tag */
|
|
rr.offset = membuflength(seg[segto]); /* current offset */
|
|
rr.length = 4; /* length of reference */
|
|
rr.refseg = segment; /* segment referred to */
|
|
write_reloc_rec(&rr);
|
|
|
|
rr.offset = *(int64_t *)data - (rr.offset + bytes);
|
|
membufwrite(seg[segto], &rr.offset, -4);
|
|
}
|
|
}
|
|
|
|
static void rdf_cleanup(int debuginfo)
|
|
{
|
|
int32_t l;
|
|
uint8_t b[4], *d;
|
|
struct BSSRec bs;
|
|
|
|
(void)debuginfo;
|
|
|
|
/* should write imported & exported symbol declarations to header here */
|
|
|
|
/* generate the output file... */
|
|
fwrite(RDOFFId, 6, 1, ofile); /* file type magic number */
|
|
|
|
if (bsslength != 0) { /* reserve BSS */
|
|
bs.type = 5;
|
|
bs.amount = bsslength;
|
|
write_bss_rec(&bs);
|
|
}
|
|
|
|
l = membuflength(header);
|
|
d = b;
|
|
WRITELONG(d, l);
|
|
|
|
fwrite(b, 4, 1, ofile); /* write length of header */
|
|
membufdump(header, ofile); /* dump header */
|
|
|
|
l = membuflength(seg[0]);
|
|
d = b; /* code segment */
|
|
WRITELONG(d, l);
|
|
|
|
fwrite(b, 4, 1, ofile);
|
|
membufdump(seg[0], ofile);
|
|
|
|
l = membuflength(seg[1]);
|
|
d = b; /* data segment */
|
|
WRITELONG(d, l);
|
|
|
|
fwrite(b, 4, 1, ofile);
|
|
membufdump(seg[1], ofile);
|
|
|
|
freemembuf(header);
|
|
freemembuf(seg[0]);
|
|
freemembuf(seg[1]);
|
|
}
|
|
|
|
static int32_t rdf_segbase(int32_t segment)
|
|
{
|
|
return segment;
|
|
}
|
|
|
|
static int rdf_directive(char *directive, char *value, int pass)
|
|
{
|
|
struct DLLRec r;
|
|
|
|
if (!strcmp(directive, "library")) {
|
|
if (pass == 1) {
|
|
r.type = 4;
|
|
strcpy(r.libname, value);
|
|
write_dll_rec(&r);
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void rdf_filename(char *inname, char *outname, efunc error)
|
|
{
|
|
standard_extension(inname, outname, ".rdf", error);
|
|
}
|
|
|
|
extern macros_t rdf_stdmac[];
|
|
|
|
static int rdf_set_info(enum geninfo type, char **val)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
struct ofmt of_rdf = {
|
|
"Relocatable Dynamic Object File Format v1.1",
|
|
#ifdef OF_RDF2
|
|
"oldrdf",
|
|
#else
|
|
"rdf",
|
|
#endif
|
|
0,
|
|
null_debug_arr,
|
|
&null_debug_form,
|
|
rdf_stdmac,
|
|
rdf_init,
|
|
rdf_set_info,
|
|
rdf_out,
|
|
rdf_deflabel,
|
|
rdf_section_names,
|
|
rdf_segbase,
|
|
rdf_directive,
|
|
rdf_filename,
|
|
rdf_cleanup
|
|
};
|
|
|
|
#endif /* OF_RDF */
|