mirror of
https://github.com/netwide-assembler/nasm.git
synced 2025-01-18 16:25:05 +08:00
fe501957c0
Concentrate compiler dependencies to compiler.h; make sure compiler.h is included first in every .c file (since some prototypes may depend on the presence of feature request macros.) Actually use the conditional inclusion of various functions (totally broken in previous releases.)
587 lines
14 KiB
C
587 lines
14 KiB
C
/* rdoff.c library of routines for manipulating rdoff 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.
|
|
*
|
|
* Permission to use this file in your own projects is granted, as int32_t
|
|
* as acknowledgement is given in an appropriate manner to its authors,
|
|
* with instructions of how to obtain a copy via ftp.
|
|
*/
|
|
|
|
/* TODO: The functions in this module assume they are running
|
|
* on a little-endian machine. This should be fixed to
|
|
* make it portable.
|
|
*/
|
|
|
|
#include "compiler.h"
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#include <inttypes.h>
|
|
|
|
#define RDOFF_UTILS
|
|
|
|
#include "rdoff.h"
|
|
|
|
#define newstr(str) strcpy(malloc(strlen(str) + 1),str)
|
|
#define newstrcat(s1,s2) strcat(strcpy(malloc(strlen(s1) + strlen(s2) + 1), \
|
|
s1),s2)
|
|
|
|
/*
|
|
* Comment this out to allow the module to read & write header record types
|
|
* that it isn't aware of. With this defined, unrecognised header records
|
|
* will generate error number 8, reported as 'unknown extended header record'.
|
|
*/
|
|
|
|
#define STRICT_ERRORS
|
|
|
|
/* ========================================================================
|
|
* Code for memory buffers (for delayed writing of header until we know
|
|
* how int32_t it is).
|
|
* ======================================================================== */
|
|
|
|
memorybuffer *newmembuf()
|
|
{
|
|
memorybuffer *t;
|
|
|
|
t = malloc(sizeof(memorybuffer));
|
|
if (!t)
|
|
return NULL;
|
|
|
|
t->length = 0;
|
|
t->next = NULL;
|
|
return t;
|
|
}
|
|
|
|
void membufwrite(memorybuffer * const b, void *data, int bytes)
|
|
{
|
|
uint16_t 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 initialise 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 = *(uint16_t *) 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;
|
|
}
|
|
}
|
|
|
|
void membufdump(memorybuffer * b, FILE * fp)
|
|
{
|
|
if (!b)
|
|
return;
|
|
|
|
fwrite(b->buffer, 1, b->length, fp);
|
|
|
|
membufdump(b->next, fp);
|
|
}
|
|
|
|
int membuflength(memorybuffer * b)
|
|
{
|
|
if (!b)
|
|
return 0;
|
|
return b->length + membuflength(b->next);
|
|
}
|
|
|
|
void freemembuf(memorybuffer * b)
|
|
{
|
|
if (!b)
|
|
return;
|
|
freemembuf(b->next);
|
|
free(b);
|
|
}
|
|
|
|
/* =========================================================================
|
|
General purpose routines and variables used by the library functions
|
|
========================================================================= */
|
|
|
|
/*
|
|
* translateint32_t() and translateint16_t()
|
|
*
|
|
* translate from little endian to local representation
|
|
*/
|
|
int32_t translateint32_t(int32_t in)
|
|
{
|
|
int32_t r;
|
|
uint8_t *i;
|
|
|
|
i = (uint8_t *)∈
|
|
r = i[3];
|
|
r = (r << 8) + i[2];
|
|
r = (r << 8) + i[1];
|
|
r = (r << 8) + *i;
|
|
|
|
return r;
|
|
}
|
|
|
|
uint16_t translateint16_t(uint16_t in)
|
|
{
|
|
uint16_t r;
|
|
uint8_t *i;
|
|
|
|
i = (uint8_t *)∈
|
|
r = (i[1] << 8) + i[0];
|
|
|
|
return r;
|
|
}
|
|
|
|
/* Segment types */
|
|
static char *knownsegtypes[8] = {
|
|
"NULL", "text", "data", "object comment",
|
|
"linked comment", "loader comment",
|
|
"symbolic debug", "line number debug"
|
|
};
|
|
|
|
/* Get a textual string describing the segment type */
|
|
char *translatesegmenttype(uint16_t type)
|
|
{
|
|
if (type < 8)
|
|
return knownsegtypes[type];
|
|
if (type < 0x0020)
|
|
return "reserved";
|
|
if (type < 0x1000)
|
|
return "reserved - Moscow";
|
|
if (type < 0x8000)
|
|
return "reserved - system dependant";
|
|
if (type < 0xFFFF)
|
|
return "reserved - other";
|
|
if (type == 0xFFFF)
|
|
return "invalid type code";
|
|
return "type code out of range";
|
|
}
|
|
|
|
/* This signature is written to the start of RDOFF files */
|
|
const char *RDOFFId = RDOFF2_SIGNATURE;
|
|
|
|
/* Error messages. Must correspond to the codes defined in rdoff.h */
|
|
const char *rdf_errors[11] = {
|
|
/* 0 */ "no error occurred",
|
|
/* 1 */ "could not open file",
|
|
/* 2 */ "invalid file format",
|
|
/* 3 */ "error reading file",
|
|
/* 4 */ "unknown error",
|
|
/* 5 */ "header not read",
|
|
/* 6 */ "out of memory",
|
|
/* 7 */ "RDOFF v1 not supported",
|
|
/* 8 */ "unknown extended header record",
|
|
/* 9 */ "header record of known type but unknown length",
|
|
/* 10 */ "no such segment"
|
|
};
|
|
|
|
int rdf_errno = 0;
|
|
|
|
/* ========================================================================
|
|
The library functions
|
|
======================================================================== */
|
|
|
|
int rdfopen(rdffile * f, const char *name)
|
|
{
|
|
FILE *fp;
|
|
|
|
fp = fopen(name, "rb");
|
|
if (!fp)
|
|
return rdf_errno = RDF_ERR_OPEN;
|
|
|
|
return rdfopenhere(f, fp, NULL, name);
|
|
}
|
|
|
|
int rdfopenhere(rdffile * f, FILE * fp, int *refcount, const char *name)
|
|
{
|
|
char buf[8];
|
|
int32_t initpos;
|
|
int32_t l;
|
|
uint16_t s;
|
|
|
|
if (translateint32_t(0x01020304) != 0x01020304) {
|
|
/* fix this to be portable! */
|
|
fputs("*** this program requires a little endian machine\n",
|
|
stderr);
|
|
fprintf(stderr, "01020304h = %08"PRIx32"h\n", translateint32_t(0x01020304));
|
|
exit(3);
|
|
}
|
|
|
|
f->fp = fp;
|
|
initpos = ftell(fp);
|
|
|
|
fread(buf, 6, 1, f->fp); /* read header */
|
|
buf[6] = 0;
|
|
|
|
if (strcmp(buf, RDOFFId)) {
|
|
fclose(f->fp);
|
|
if (!strcmp(buf, "RDOFF1"))
|
|
return rdf_errno = RDF_ERR_VER;
|
|
return rdf_errno = RDF_ERR_FORMAT;
|
|
}
|
|
|
|
if (fread(&l, 1, 4, f->fp) != 4
|
|
|| fread(&f->header_len, 1, 4, f->fp) != 4) {
|
|
fclose(f->fp);
|
|
return rdf_errno = RDF_ERR_READ;
|
|
}
|
|
|
|
f->header_ofs = ftell(f->fp);
|
|
f->eof_offset = f->header_ofs + translateint32_t(l) - 4;
|
|
|
|
if (fseek(f->fp, f->header_len, SEEK_CUR)) {
|
|
fclose(f->fp);
|
|
return rdf_errno = RDF_ERR_FORMAT; /* seek past end of file...? */
|
|
}
|
|
|
|
if (fread(&s, 1, 2, f->fp) != 2) {
|
|
fclose(f->fp);
|
|
return rdf_errno = RDF_ERR_READ;
|
|
}
|
|
|
|
f->nsegs = 0;
|
|
|
|
while (s != 0) {
|
|
f->seg[f->nsegs].type = s;
|
|
if (fread(&f->seg[f->nsegs].number, 1, 2, f->fp) != 2 ||
|
|
fread(&f->seg[f->nsegs].reserved, 1, 2, f->fp) != 2 ||
|
|
fread(&f->seg[f->nsegs].length, 1, 4, f->fp) != 4) {
|
|
fclose(f->fp);
|
|
return rdf_errno = RDF_ERR_READ;
|
|
}
|
|
|
|
f->seg[f->nsegs].offset = ftell(f->fp);
|
|
if (fseek(f->fp, f->seg[f->nsegs].length, SEEK_CUR)) {
|
|
fclose(f->fp);
|
|
return rdf_errno = RDF_ERR_FORMAT;
|
|
}
|
|
f->nsegs++;
|
|
|
|
if (fread(&s, 1, 2, f->fp) != 2) {
|
|
fclose(f->fp);
|
|
return rdf_errno = RDF_ERR_READ;
|
|
}
|
|
}
|
|
|
|
if (f->eof_offset != ftell(f->fp) + 8) { /* +8 = skip null segment header */
|
|
fprintf(stderr, "warning: eof_offset [%"PRId32"] and actual eof offset "
|
|
"[%ld] don't match\n", f->eof_offset, ftell(f->fp) + 8);
|
|
}
|
|
fseek(f->fp, initpos, SEEK_SET);
|
|
f->header_loc = NULL;
|
|
|
|
f->name = newstr(name);
|
|
f->refcount = refcount;
|
|
if (refcount)
|
|
(*refcount)++;
|
|
return RDF_OK;
|
|
}
|
|
|
|
int rdfclose(rdffile * f)
|
|
{
|
|
if (!f->refcount || !--(*f->refcount)) {
|
|
fclose(f->fp);
|
|
f->fp = NULL;
|
|
}
|
|
free(f->name);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Print the message for last error (from rdf_errno)
|
|
*/
|
|
void rdfperror(const char *app, const char *name)
|
|
{
|
|
fprintf(stderr, "%s:%s: %s\n", app, name, rdf_errors[rdf_errno]);
|
|
if (rdf_errno == RDF_ERR_OPEN || rdf_errno == RDF_ERR_READ) {
|
|
perror(app);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Find the segment by its number.
|
|
* Returns segment array index, or -1 if segment with such number was not found.
|
|
*/
|
|
int rdffindsegment(rdffile * f, int segno)
|
|
{
|
|
int i;
|
|
for (i = 0; i < f->nsegs; i++)
|
|
if (f->seg[i].number == segno)
|
|
return i;
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
* Load the segment. Returns status.
|
|
*/
|
|
int rdfloadseg(rdffile * f, int segment, void *buffer)
|
|
{
|
|
int32_t fpos, slen;
|
|
|
|
switch (segment) {
|
|
case RDOFF_HEADER:
|
|
fpos = f->header_ofs;
|
|
slen = f->header_len;
|
|
f->header_loc = (uint8_t *) buffer;
|
|
f->header_fp = 0;
|
|
break;
|
|
default:
|
|
if (segment < f->nsegs) {
|
|
fpos = f->seg[segment].offset;
|
|
slen = f->seg[segment].length;
|
|
f->seg[segment].data = (uint8_t *) buffer;
|
|
} else {
|
|
return rdf_errno = RDF_ERR_SEGMENT;
|
|
}
|
|
}
|
|
|
|
if (fseek(f->fp, fpos, SEEK_SET))
|
|
return rdf_errno = RDF_ERR_UNKNOWN;
|
|
|
|
if (fread(buffer, 1, slen, f->fp) != slen)
|
|
return rdf_errno = RDF_ERR_READ;
|
|
|
|
return RDF_OK;
|
|
}
|
|
|
|
/* Macros for reading integers from header in memory */
|
|
|
|
#define RI8(v) v = f->header_loc[f->header_fp++]
|
|
#define RI16(v) { v = (f->header_loc[f->header_fp] + \
|
|
(f->header_loc[f->header_fp+1] << 8)); \
|
|
f->header_fp += 2; }
|
|
|
|
#define RI32(v) { v = (f->header_loc[f->header_fp] + \
|
|
(f->header_loc[f->header_fp+1] << 8) + \
|
|
(f->header_loc[f->header_fp+2] << 16) + \
|
|
(f->header_loc[f->header_fp+3] << 24)); \
|
|
f->header_fp += 4; }
|
|
|
|
#define RS(str,max) { for(i=0;i<max;i++){\
|
|
RI8(str[i]); if (!str[i]) break;} str[i]=0; }
|
|
|
|
/*
|
|
* Read a header record.
|
|
* Returns the address of record, or NULL in case of error.
|
|
*/
|
|
rdfheaderrec *rdfgetheaderrec(rdffile * f)
|
|
{
|
|
static rdfheaderrec r;
|
|
int i;
|
|
|
|
if (!f->header_loc) {
|
|
rdf_errno = RDF_ERR_HEADER;
|
|
return NULL;
|
|
}
|
|
|
|
if (f->header_fp >= f->header_len)
|
|
return 0;
|
|
|
|
RI8(r.type);
|
|
RI8(r.g.reclen);
|
|
|
|
switch (r.type) {
|
|
case RDFREC_RELOC: /* Relocation record */
|
|
case RDFREC_SEGRELOC:
|
|
if (r.r.reclen != 8) {
|
|
rdf_errno = RDF_ERR_RECLEN;
|
|
return NULL;
|
|
}
|
|
RI8(r.r.segment);
|
|
RI32(r.r.offset);
|
|
RI8(r.r.length);
|
|
RI16(r.r.refseg);
|
|
break;
|
|
|
|
case RDFREC_IMPORT: /* Imported symbol record */
|
|
case RDFREC_FARIMPORT:
|
|
RI8(r.i.flags);
|
|
RI16(r.i.segment);
|
|
RS(r.i.label, EXIM_LABEL_MAX);
|
|
break;
|
|
|
|
case RDFREC_GLOBAL: /* Exported symbol record */
|
|
RI8(r.e.flags);
|
|
RI8(r.e.segment);
|
|
RI32(r.e.offset);
|
|
RS(r.e.label, EXIM_LABEL_MAX);
|
|
break;
|
|
|
|
case RDFREC_DLL: /* DLL record */
|
|
RS(r.d.libname, MODLIB_NAME_MAX);
|
|
break;
|
|
|
|
case RDFREC_BSS: /* BSS reservation record */
|
|
if (r.r.reclen != 4) {
|
|
rdf_errno = RDF_ERR_RECLEN;
|
|
return NULL;
|
|
}
|
|
RI32(r.b.amount);
|
|
break;
|
|
|
|
case RDFREC_MODNAME: /* Module name record */
|
|
RS(r.m.modname, MODLIB_NAME_MAX);
|
|
break;
|
|
|
|
case RDFREC_COMMON: /* Common variable */
|
|
RI16(r.c.segment);
|
|
RI32(r.c.size);
|
|
RI16(r.c.align);
|
|
RS(r.c.label, EXIM_LABEL_MAX);
|
|
break;
|
|
|
|
default:
|
|
#ifdef STRICT_ERRORS
|
|
rdf_errno = RDF_ERR_RECTYPE; /* unknown header record */
|
|
return NULL;
|
|
#else
|
|
for (i = 0; i < r.g.reclen; i++)
|
|
RI8(r.g.data[i]);
|
|
#endif
|
|
}
|
|
return &r;
|
|
}
|
|
|
|
/*
|
|
* Rewind to the beginning of the file
|
|
*/
|
|
void rdfheaderrewind(rdffile * f)
|
|
{
|
|
f->header_fp = 0;
|
|
}
|
|
|
|
rdf_headerbuf *rdfnewheader(void)
|
|
{
|
|
rdf_headerbuf *hb = malloc(sizeof(rdf_headerbuf));
|
|
if (hb == NULL)
|
|
return NULL;
|
|
|
|
hb->buf = newmembuf();
|
|
hb->nsegments = 0;
|
|
hb->seglength = 0;
|
|
|
|
return hb;
|
|
}
|
|
|
|
int rdfaddheader(rdf_headerbuf * h, rdfheaderrec * r)
|
|
{
|
|
#ifndef STRICT_ERRORS
|
|
int i;
|
|
#endif
|
|
membufwrite(h->buf, &r->type, 1);
|
|
membufwrite(h->buf, &r->g.reclen, 1);
|
|
|
|
switch (r->type) {
|
|
case RDFREC_GENERIC: /* generic */
|
|
membufwrite(h->buf, &r->g.data, r->g.reclen);
|
|
break;
|
|
case RDFREC_RELOC:
|
|
case RDFREC_SEGRELOC:
|
|
membufwrite(h->buf, &r->r.segment, 1);
|
|
membufwrite(h->buf, &r->r.offset, -4);
|
|
membufwrite(h->buf, &r->r.length, 1);
|
|
membufwrite(h->buf, &r->r.refseg, -2); /* 9 bytes written */
|
|
break;
|
|
|
|
case RDFREC_IMPORT: /* import */
|
|
case RDFREC_FARIMPORT:
|
|
membufwrite(h->buf, &r->i.flags, 1);
|
|
membufwrite(h->buf, &r->i.segment, -2);
|
|
membufwrite(h->buf, &r->i.label, strlen(r->i.label) + 1);
|
|
break;
|
|
|
|
case RDFREC_GLOBAL: /* export */
|
|
membufwrite(h->buf, &r->e.flags, 1);
|
|
membufwrite(h->buf, &r->e.segment, 1);
|
|
membufwrite(h->buf, &r->e.offset, -4);
|
|
membufwrite(h->buf, &r->e.label, strlen(r->e.label) + 1);
|
|
break;
|
|
|
|
case RDFREC_DLL: /* DLL */
|
|
membufwrite(h->buf, &r->d.libname, strlen(r->d.libname) + 1);
|
|
break;
|
|
|
|
case RDFREC_BSS: /* BSS */
|
|
membufwrite(h->buf, &r->b.amount, -4);
|
|
break;
|
|
|
|
case RDFREC_MODNAME: /* Module name */
|
|
membufwrite(h->buf, &r->m.modname, strlen(r->m.modname) + 1);
|
|
break;
|
|
|
|
default:
|
|
#ifdef STRICT_ERRORS
|
|
return rdf_errno = RDF_ERR_RECTYPE;
|
|
#else
|
|
for (i = 0; i < r->g.reclen; i++)
|
|
membufwrite(h->buf, r->g.data[i], 1);
|
|
#endif
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int rdfaddsegment(rdf_headerbuf * h, int32_t seglength)
|
|
{
|
|
h->nsegments++;
|
|
h->seglength += seglength;
|
|
return 0;
|
|
}
|
|
|
|
int rdfwriteheader(FILE * fp, rdf_headerbuf * h)
|
|
{
|
|
int32_t l, l2;
|
|
|
|
fwrite(RDOFFId, 1, strlen(RDOFFId), fp);
|
|
|
|
l = membuflength(h->buf);
|
|
l2 = l + 14 + 10 * h->nsegments + h->seglength;
|
|
l = translateint32_t(l);
|
|
l2 = translateint32_t(l2);
|
|
fwrite(&l2, 4, 1, fp); /* object length */
|
|
fwrite(&l, 4, 1, fp); /* header length */
|
|
|
|
membufdump(h->buf, fp);
|
|
|
|
return 0; /* no error handling in here... CHANGE THIS! */
|
|
}
|
|
|
|
void rdfdoneheader(rdf_headerbuf * h)
|
|
{
|
|
freemembuf(h->buf);
|
|
free(h);
|
|
}
|