/*
 * rdoff.h	RDOFF Object File manipulation routines header file
 *
 * The Netwide Assembler is copyright (C) 1996 Simon Tatham and
 * Julian Hall. All rights reserved. The software is
 * redistributable under the license given in the file "LICENSE"
 * 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.
 */

#ifndef RDOFF_RDOFF_H
#define RDOFF_RDOFF_H 1

#include <inttypes.h>

/*
 * RDOFF definitions. They are used by RDOFF utilities and by NASM's
 * 'outrdf2.c' output module.
 */

/* RDOFF format revision (currently used only when printing the version) */
#define RDOFF2_REVISION		"0.6.1"

/* RDOFF2 file signature */
#define RDOFF2_SIGNATURE	"RDOFF2"

/* Maximum size of an import/export label (including trailing zero) */
#define EXIM_LABEL_MAX		64

/* Maximum size of library or module name (including trailing zero) */
#define MODLIB_NAME_MAX		128

/* Maximum number of segments that we can handle in one file */
#define RDF_MAXSEGS		64

/* Record types that may present the RDOFF header */
#define RDFREC_GENERIC		0
#define RDFREC_RELOC		1
#define RDFREC_IMPORT		2
#define RDFREC_GLOBAL		3
#define RDFREC_DLL		4
#define RDFREC_BSS		5
#define RDFREC_SEGRELOC		6
#define RDFREC_FARIMPORT	7
#define RDFREC_MODNAME		8
#define RDFREC_COMMON		10

/*
 * Generic record - contains the type and length field, plus a 128 byte
 * array 'data'
 */
struct GenericRec {
    uint8_t type;
    uint8_t reclen;
    char data[128];
};

/*
 * Relocation record
 */
struct RelocRec {
    uint8_t type;                  /* must be 1 */
    uint8_t reclen;                /* content length */
    uint8_t 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 */
    uint8_t length;                /* 1 2 or 4 bytes */
    uint16_t refseg;              /* segment to which reference refers to */
};

/*
 * Extern/import record
 */
struct ImportRec {
    uint8_t type;                  /* must be 2 */
    uint8_t reclen;                /* content length */
    uint8_t flags;                 /* SYM_* flags (see below) */
    uint16_t 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[EXIM_LABEL_MAX]; /* zero terminated, should be written to file
                                   until the zero, but not after it */
};

/*
 * Public/export record
 */
struct ExportRec {
    uint8_t type;                  /* must be 3 */
    uint8_t reclen;                /* content length */
    uint8_t flags;                 /* SYM_* flags (see below) */
    uint8_t segment;               /* segment referred to (0/1/2) */
    int32_t offset;                /* offset within segment */
    char label[EXIM_LABEL_MAX]; /* zero terminated as in import */
};

/*
 * DLL record
 */
struct DLLRec {
    uint8_t type;                  /* must be 4 */
    uint8_t reclen;                /* content length */
    char libname[MODLIB_NAME_MAX];      /* name of library to link with at load time */
};

/*
 * BSS record
 */
struct BSSRec {
    uint8_t type;                  /* must be 5 */
    uint8_t reclen;                /* content length */
    int32_t amount;                /* number of bytes BSS to reserve */
};

/*
 * Module name record
 */
struct ModRec {
    uint8_t type;                  /* must be 8 */
    uint8_t reclen;                /* content length */
    char modname[MODLIB_NAME_MAX];      /* module name */
};

/*
 * Common variable record
 */
struct CommonRec {
    uint8_t type;                  /* must be 10 */
    uint8_t reclen;                /* equals 7+label length */
    uint16_t segment;             /* segment number */
    int32_t size;                  /* size of common variable */
    uint16_t align;               /* alignment (power of two) */
    char label[EXIM_LABEL_MAX]; /* zero terminated as in import */
};

/* Flags for ExportRec */
#define SYM_DATA	1
#define SYM_FUNCTION	2
#define SYM_GLOBAL	4
#define SYM_IMPORT	8

/*** The following part is used only by the utilities *************************/

#ifdef RDOFF_UTILS

/* Some systems don't define this automatically */
#if !defined(strdup)
extern char *strdup(const char *);
#endif

typedef union RDFHeaderRec {
    char type;                  /* invariant throughout all below */
    struct GenericRec g;        /* type 0 */
    struct RelocRec r;          /* type == 1 / 6 */
    struct ImportRec i;         /* type == 2 / 7 */
    struct ExportRec e;         /* type == 3 */
    struct DLLRec d;            /* type == 4 */
    struct BSSRec b;            /* type == 5 */
    struct ModRec m;            /* type == 8 */
    struct CommonRec c;         /* type == 10 */
} rdfheaderrec;

struct SegmentHeaderRec {
    /* information from file */
    uint16_t type;
    uint16_t number;
    uint16_t reserved;
    int32_t length;

    /* information built up here */
    int32_t offset;
    uint8_t *data;                 /* pointer to segment data if it exists in memory */
};

typedef struct RDFFileInfo {
    FILE *fp;                   /* file descriptor; must be open to use this struct */
    int rdoff_ver;              /* should be 1; any higher => not guaranteed to work */
    int32_t header_len;
    int32_t header_ofs;

    uint8_t *header_loc;           /* keep location of header */
    int32_t header_fp;             /* current location within header for reading */

    struct SegmentHeaderRec seg[RDF_MAXSEGS];
    int nsegs;

    int32_t eof_offset;            /* offset of the first uint8_t beyond the end of this
                                   module */

    char *name;                 /* name of module in libraries */
    int *refcount;              /* pointer to reference count on file, or NULL */
} rdffile;

#define BUF_BLOCK_LEN 4088      /* selected to match page size (4096)
                                 * on 80x86 machines for efficiency */
typedef struct memorybuffer {
    int length;
    uint8_t buffer[BUF_BLOCK_LEN];
    struct memorybuffer *next;
} memorybuffer;

typedef struct {
    memorybuffer *buf;          /* buffer containing header records */
    int nsegments;              /* number of segments to be written */
    int32_t seglength;             /* total length of all the segments */
} rdf_headerbuf;

/* segments used by RDOFF, understood by rdoffloadseg */
#define RDOFF_CODE	0
#define RDOFF_DATA	1
#define RDOFF_HEADER	-1
/* mask for 'segment' in relocation records to find if relative relocation */
#define RDOFF_RELATIVEMASK 64
/* mask to find actual segment value in relocation records */
#define RDOFF_SEGMENTMASK 63

extern int rdf_errno;

/* rdf_errno can hold these error codes */
enum {
    /* 0 */ RDF_OK,
    /* 1 */ RDF_ERR_OPEN,
    /* 2 */ RDF_ERR_FORMAT,
    /* 3 */ RDF_ERR_READ,
    /* 4 */ RDF_ERR_UNKNOWN,
    /* 5 */ RDF_ERR_HEADER,
    /* 6 */ RDF_ERR_NOMEM,
    /* 7 */ RDF_ERR_VER,
    /* 8 */ RDF_ERR_RECTYPE,
    /* 9 */ RDF_ERR_RECLEN,
    /* 10 */ RDF_ERR_SEGMENT
};

/* utility functions */
int32_t translateint32_t(int32_t in);
uint16_t translateint16_t(uint16_t in);
char *translatesegmenttype(uint16_t type);

/* RDOFF file manipulation functions */
int rdfopen(rdffile * f, const char *name);
int rdfopenhere(rdffile * f, FILE * fp, int *refcount, const char *name);
int rdfclose(rdffile * f);
int rdffindsegment(rdffile * f, int segno);
int rdfloadseg(rdffile * f, int segment, void *buffer);
rdfheaderrec *rdfgetheaderrec(rdffile * f);     /* returns static storage */
void rdfheaderrewind(rdffile * f);      /* back to start of header */
void rdfperror(const char *app, const char *name);

/* functions to write a new RDOFF header to a file -
   use rdfnewheader to allocate a header, rdfaddheader to add records to it,
   rdfaddsegment to notify the header routines that a segment exists, and
   to tell it how int32_t the segment will be.
   rdfwriteheader to write the file id, object length, and header
   to a file, and then rdfdoneheader to dispose of the header */

rdf_headerbuf *rdfnewheader(void);
int rdfaddheader(rdf_headerbuf * h, rdfheaderrec * r);
int rdfaddsegment(rdf_headerbuf * h, int32_t seglength);
int rdfwriteheader(FILE * fp, rdf_headerbuf * h);
void rdfdoneheader(rdf_headerbuf * h);

#endif                          /* RDOFF_UTILS */

#endif                          /* RDOFF_RDOFF_H */
