Files
cc65/src/ld65/o65.c
uz da792b3fd0 Separate processing the linker config file into two phases: The config file is
read when the -t or -C switch is encountered and parts of it are processed.
The remaining parts are processed when all object files and libraries have
been read. To make this work, the expression evaluation in cfgexpr has been
rewritten to generate true expression trees. This means that expressions in
the linker config may use exports from the object files. 

Separation of config file processing is the base for several enhancements, for
example forced imports by linker config.

This code needs more work and is only very, very, very roughly tested.


git-svn-id: svn://svn.cc65.org/cc65/trunk@4840 b7a2c559-68d2-44c3-8de9-860c34a00d81
2010-11-08 21:52:24 +00:00

1425 lines
40 KiB
C

/*****************************************************************************/
/* */
/* o65.c */
/* */
/* Module to handle the o65 binary format */
/* */
/* */
/* */
/* (C) 1999-2010, Ullrich von Bassewitz */
/* Roemerstrasse 52 */
/* D-70794 Filderstadt */
/* EMail: uz@cc65.org */
/* */
/* */
/* This software is provided 'as-is', without any expressed or implied */
/* warranty. In no event will the authors be held liable for any damages */
/* arising from the use of this software. */
/* */
/* Permission is granted to anyone to use this software for any purpose, */
/* including commercial applications, and to alter it and redistribute it */
/* freely, subject to the following restrictions: */
/* */
/* 1. The origin of this software must not be misrepresented; you must not */
/* claim that you wrote the original software. If you use this software */
/* in a product, an acknowledgment in the product documentation would be */
/* appreciated but is not required. */
/* 2. Altered source versions must be plainly marked as such, and must not */
/* be misrepresented as being the original software. */
/* 3. This notice may not be removed or altered from any source */
/* distribution. */
/* */
/*****************************************************************************/
#include <stdio.h>
#include <string.h>
#include <limits.h>
#include <errno.h>
#include <time.h>
/* common */
#include "chartype.h"
#include "check.h"
#include "print.h"
#include "version.h"
#include "xmalloc.h"
/* ld65 */
#include "error.h"
#include "exports.h"
#include "expr.h"
#include "fileio.h"
#include "global.h"
#include "lineinfo.h"
#include "memarea.h"
#include "o65.h"
#include "spool.h"
/*****************************************************************************/
/* Data */
/*****************************************************************************/
/* Header mode bits */
#define MF_CPU_65816 0x8000 /* Executable is for 65816 */
#define MF_CPU_6502 0x0000 /* Executable is for the 6502 */
#define MF_CPU_MASK 0x8000 /* Mask to extract CPU type */
#define MF_RELOC_PAGE 0x4000 /* Page wise relocation */
#define MF_RELOC_BYTE 0x0000 /* Byte wise relocation */
#define MF_RELOC_MASK 0x4000 /* Mask to extract relocation type */
#define MF_SIZE_32BIT 0x2000 /* All size words are 32bit */
#define MF_SIZE_16BIT 0x0000 /* All size words are 16bit */
#define MF_SIZE_MASK 0x2000 /* Mask to extract size */
#define MF_FTYPE_OBJ 0x1000 /* Object file */
#define MF_FTYPE_EXE 0x0000 /* Executable file */
#define MF_FTYPE_MASK 0x1000 /* Mask to extract type */
#define MF_ADDR_SIMPLE 0x0800 /* Simple addressing */
#define MF_ADDR_DEFAULT 0x0000 /* Default addressing */
#define MF_ADDR_MASK 0x0800 /* Mask to extract addressing */
#define MF_ALIGN_1 0x0000 /* Bytewise alignment */
#define MF_ALIGN_2 0x0001 /* Align words */
#define MF_ALIGN_4 0x0002 /* Align longwords */
#define MF_ALIGN_256 0x0003 /* Align pages (256 bytes) */
#define MF_ALIGN_MASK 0x0003 /* Mask to extract alignment */
/* The four o65 segment types. Note: These values are identical to the values
* needed for the segmentID in the o65 spec.
*/
#define O65SEG_UNDEF 0x00
#define O65SEG_ABS 0x01
#define O65SEG_TEXT 0x02
#define O65SEG_DATA 0x03
#define O65SEG_BSS 0x04
#define O65SEG_ZP 0x05
/* Relocation type codes for the o65 format */
#define O65RELOC_WORD 0x80
#define O65RELOC_HIGH 0x40
#define O65RELOC_LOW 0x20
#define O65RELOC_SEGADR 0xC0
#define O65RELOC_SEG 0xA0
#define O65RELOC_MASK 0xE0
/* O65 executable file header */
typedef struct O65Header O65Header;
struct O65Header {
unsigned Version; /* Version number for o65 format */
unsigned Mode; /* Mode word */
unsigned long TextBase; /* Base address of text segment */
unsigned long TextSize; /* Size of text segment */
unsigned long DataBase; /* Base of data segment */
unsigned long DataSize; /* Size of data segment */
unsigned long BssBase; /* Base of bss segment */
unsigned long BssSize; /* Size of bss segment */
unsigned long ZPBase; /* Base of zeropage segment */
unsigned long ZPSize; /* Size of zeropage segment */
unsigned long StackSize; /* Requested stack size */
};
/* An o65 option */
typedef struct O65Option O65Option;
struct O65Option {
O65Option* Next; /* Next in option list */
unsigned char Type; /* Type of option */
unsigned char Len; /* Data length */
unsigned char Data [1]; /* Data, dynamically allocated */
};
/* A o65 relocation table */
typedef struct O65RelocTab O65RelocTab;
struct O65RelocTab {
unsigned Size; /* Size of the table */
unsigned Fill; /* Amount used */
unsigned char* Buf; /* Buffer, dynamically allocated */
};
/* Structure describing the format */
struct O65Desc {
O65Header Header; /* File header */
O65Option* Options; /* List of file options */
ExtSymTab* Exports; /* Table with exported symbols */
ExtSymTab* Imports; /* Table with imported symbols */
unsigned Undef; /* Count of undefined symbols */
FILE* F; /* The file we're writing to */
const char* Filename; /* Name of the output file */
O65RelocTab* TextReloc; /* Relocation table for text segment */
O65RelocTab* DataReloc; /* Relocation table for data segment */
unsigned TextCount; /* Number of segments assigned to .text */
SegDesc** TextSeg; /* Array of text segments */
unsigned DataCount; /* Number of segments assigned to .data */
SegDesc** DataSeg; /* Array of data segments */
unsigned BssCount; /* Number of segments assigned to .bss */
SegDesc** BssSeg; /* Array of bss segments */
unsigned ZPCount; /* Number of segments assigned to .zp */
SegDesc** ZPSeg; /* Array of zp segments */
/* Temporary data for writing segments */
unsigned long SegSize;
O65RelocTab* CurReloc;
long LastOffs;
};
/* Structure for parsing expression trees */
typedef struct ExprDesc ExprDesc;
struct ExprDesc {
O65Desc* D; /* File format descriptor */
long Val; /* The offset value */
int TooComplex; /* Expression too complex */
MemoryArea* MemRef; /* Memory reference if any */
Segment* SegRef; /* Segment reference if any */
Section* SecRef; /* Section reference if any */
ExtSym* ExtRef; /* External reference if any */
};
/*****************************************************************************/
/* Helper functions */
/*****************************************************************************/
static ExprDesc* InitExprDesc (ExprDesc* ED, O65Desc* D)
/* Initialize an ExprDesc structure for use with O65ParseExpr */
{
ED->D = D;
ED->Val = 0;
ED->TooComplex = 0;
ED->MemRef = 0;
ED->SegRef = 0;
ED->SecRef = 0;
ED->ExtRef = 0;
return ED;
}
static void WriteSize (const O65Desc* D, unsigned long Val)
/* Write a "size" word to the file */
{
switch (D->Header.Mode & MF_SIZE_MASK) {
case MF_SIZE_16BIT: Write16 (D->F, (unsigned) Val); break;
case MF_SIZE_32BIT: Write32 (D->F, Val); break;
default: Internal ("Invalid size in header: %04X", D->Header.Mode);
}
}
static unsigned O65SegType (const SegDesc* S)
/* Map our own segment types into something o65 compatible */
{
/* Check the segment type. Readonly segments are assign to the o65
* text segment, writeable segments that contain data are assigned
* to data, bss and zp segments are handled respectively.
* Beware: Zeropage segments have the SF_BSS flag set, so be sure
* to check SF_ZP first.
*/
if (S->Flags & SF_RO) {
return O65SEG_TEXT;
} else if (S->Flags & SF_ZP) {
return O65SEG_ZP;
} else if (S->Flags & SF_BSS) {
return O65SEG_BSS;
} else {
return O65SEG_DATA;
}
}
static void CvtMemoryToSegment (ExprDesc* ED)
/* Convert a memory area into a segment by searching the list of run segments
* in this memory area and assigning the nearest one.
*/
{
/* Get the memory area from the expression */
MemoryArea* M = ED->MemRef;
/* Remember the "nearest" segment and its offset */
Segment* Nearest = 0;
unsigned long Offs = ULONG_MAX;
/* Walk over all segments */
unsigned I;
for (I = 0; I < CollCount (&M->SegList); ++I) {
/* Get the segment and check if it's a run segment */
SegDesc* S = CollAtUnchecked (&M->SegList, I);
if (S->Run == M) {
unsigned long O;
/* Get the segment from the segment descriptor */
Segment* Seg = S->Seg;
/* Check the PC. */
if ((long) Seg->PC <= ED->Val && (O = (ED->Val - Seg->PC)) < Offs) {
/* This is the nearest segment for now */
Offs = O;
Nearest = Seg;
/* If we found an exact match, don't look further */
if (Offs == 0) {
break;
}
}
}
}
/* If we found a segment, use it and adjust the offset */
if (Nearest) {
ED->SegRef = Nearest;
ED->MemRef = 0;
ED->Val -= Nearest->PC;
}
}
static const SegDesc* FindSeg (SegDesc** const List, unsigned Count, const Segment* S)
/* Search for a segment in the given list of segment descriptors and return
* the descriptor for a segment if we found it, and NULL if not.
*/
{
unsigned I;
for (I = 0; I < Count; ++I) {
if (List[I]->Seg == S) {
/* Found */
return List[I];
}
}
/* Not found */
return 0;
}
static const SegDesc* O65FindSeg (const O65Desc* D, const Segment* S)
/* Search for a segment in the segment lists and return it's segment descriptor */
{
const SegDesc* SD;
if ((SD = FindSeg (D->TextSeg, D->TextCount, S)) != 0) {
return SD;
}
if ((SD = FindSeg (D->DataSeg, D->DataCount, S)) != 0) {
return SD;
}
if ((SD = FindSeg (D->BssSeg, D->BssCount, S)) != 0) {
return SD;
}
if ((SD = FindSeg (D->ZPSeg, D->ZPCount, S)) != 0) {
return SD;
}
/* Not found */
return 0;
}
/*****************************************************************************/
/* Expression handling */
/*****************************************************************************/
static void O65ParseExpr (ExprNode* Expr, ExprDesc* D, int Sign)
/* Extract and evaluate all constant factors in an subtree that has only
* additions and subtractions. If anything other than additions and
* subtractions are found, D->TooComplex is set to true.
*/
{
Export* E;
switch (Expr->Op) {
case EXPR_LITERAL:
D->Val += (Sign * Expr->V.IVal);
break;
case EXPR_SYMBOL:
/* Get the referenced Export */
E = GetExprExport (Expr);
/* If this export has a mark set, we've already encountered it.
* This means that the export is used to define it's own value,
* which in turn means, that we have a circular reference.
*/
if (ExportHasMark (E)) {
CircularRefError (E);
} else if (E->Expr == 0) {
/* Dummy export, must be an o65 imported symbol */
ExtSym* S = O65GetImport (D->D, E->Name);
CHECK (S != 0);
if (D->ExtRef) {
/* We cannot have more than one external reference in o65 */
D->TooComplex = 1;
} else {
/* Remember the external reference */
D->ExtRef = S;
}
} else {
MarkExport (E);
O65ParseExpr (E->Expr, D, Sign);
UnmarkExport (E);
}
break;
case EXPR_SECTION:
if (D->SecRef) {
/* We cannot handle more than one segment reference in o65 */
D->TooComplex = 1;
} else {
/* Remember the segment reference */
D->SecRef = GetExprSection (Expr);
/* Add the offset of the section to the constant value */
D->Val += Sign * (D->SecRef->Offs + D->SecRef->Seg->PC);
}
break;
case EXPR_SEGMENT:
if (D->SegRef) {
/* We cannot handle more than one segment reference in o65 */
D->TooComplex = 1;
} else {
/* Remember the segment reference */
D->SegRef = Expr->V.Seg;
/* Add the offset of the segment to the constant value */
D->Val += (Sign * D->SegRef->PC);
}
break;
case EXPR_MEMAREA:
if (D->MemRef) {
/* We cannot handle more than one memory reference in o65 */
D->TooComplex = 1;
} else {
/* Remember the memory area reference */
D->MemRef = Expr->V.Mem;
/* Add the start address of the memory area to the constant
* value
*/
D->Val += (Sign * D->MemRef->Start);
}
break;
case EXPR_PLUS:
O65ParseExpr (Expr->Left, D, Sign);
O65ParseExpr (Expr->Right, D, Sign);
break;
case EXPR_MINUS:
O65ParseExpr (Expr->Left, D, Sign);
O65ParseExpr (Expr->Right, D, -Sign);
break;
default:
/* Expression contains illegal operators */
D->TooComplex = 1;
break;
}
}
/*****************************************************************************/
/* Relocation tables */
/*****************************************************************************/
static O65RelocTab* NewO65RelocTab (void)
/* Create a new relocation table */
{
/* Allocate a new structure */
O65RelocTab* R = xmalloc (sizeof (O65RelocTab));
/* Initialize the data */
R->Size = 0;
R->Fill = 0;
R->Buf = 0;
/* Return the created struct */
return R;
}
static void FreeO65RelocTab (O65RelocTab* R)
/* Free a relocation table */
{
xfree (R->Buf);
xfree (R);
}
static void O65RelocPutByte (O65RelocTab* R, unsigned B)
/* Put the byte into the relocation table */
{
/* Do we have enough space in the buffer? */
if (R->Fill == R->Size) {
/* We need to grow the buffer */
if (R->Size) {
R->Size *= 2;
} else {
R->Size = 1024; /* Initial size */
}
R->Buf = xrealloc (R->Buf, R->Size);
}
/* Put the byte into the buffer */
R->Buf [R->Fill++] = (unsigned char) B;
}
static void O65RelocPutWord (O65RelocTab* R, unsigned W)
/* Put a word into the relocation table */
{
O65RelocPutByte (R, W);
O65RelocPutByte (R, W >> 8);
}
static void O65WriteReloc (O65RelocTab* R, FILE* F)
/* Write the relocation table to the given file */
{
WriteData (F, R->Buf, R->Fill);
}
/*****************************************************************************/
/* Option handling */
/*****************************************************************************/
static O65Option* NewO65Option (unsigned Type, const void* Data, unsigned DataLen)
/* Allocate and initialize a new option struct */
{
O65Option* O;
/* Check the length */
CHECK (DataLen <= 253);
/* Allocate memory */
O = xmalloc (sizeof (O65Option) - 1 + DataLen);
/* Initialize the structure */
O->Next = 0;
O->Type = Type;
O->Len = DataLen;
memcpy (O->Data, Data, DataLen);
/* Return the created struct */
return O;
}
static void FreeO65Option (O65Option* O)
/* Free an O65Option struct */
{
xfree (O);
}
/*****************************************************************************/
/* Subroutines to write o65 sections */
/*****************************************************************************/
static void O65WriteHeader (O65Desc* D)
/* Write the header of the executable to the given file */
{
static unsigned char Trailer [5] = {
0x01, 0x00, 0x6F, 0x36, 0x35
};
O65Option* O;
/* Write the fixed header */
WriteData (D->F, Trailer, sizeof (Trailer));
Write8 (D->F, D->Header.Version);
Write16 (D->F, D->Header.Mode);
WriteSize (D, D->Header.TextBase);
WriteSize (D, D->Header.TextSize);
WriteSize (D, D->Header.DataBase);
WriteSize (D, D->Header.DataSize);
WriteSize (D, D->Header.BssBase);
WriteSize (D, D->Header.BssSize);
WriteSize (D, D->Header.ZPBase);
WriteSize (D, D->Header.ZPSize);
WriteSize (D, D->Header.StackSize);
/* Write the options */
O = D->Options;
while (O) {
Write8 (D->F, O->Len + 2); /* Account for len and type bytes */
Write8 (D->F, O->Type);
if (O->Len) {
WriteData (D->F, O->Data, O->Len);
}
O = O->Next;
}
/* Write the end-of-options byte */
Write8 (D->F, 0);
}
static unsigned O65WriteExpr (ExprNode* E, int Signed, unsigned Size,
unsigned long Offs, void* Data)
/* Called from SegWrite for an expression. Evaluate the expression, check the
* range and write the expression value to the file, update the relocation
* table.
*/
{
long Diff;
unsigned RefCount;
long BinVal;
ExprNode* Expr;
ExprDesc ED;
unsigned char RelocType;
/* Cast the Data pointer to its real type, an O65Desc */
O65Desc* D = (O65Desc*) Data;
/* Check for a constant expression */
if (IsConstExpr (E)) {
/* Write out the constant expression */
return SegWriteConstExpr (((O65Desc*)Data)->F, E, Signed, Size);
}
/* We have a relocatable expression that needs a relocation table entry.
* Calculate the number of bytes between this entry and the last one, and
* setup all necessary intermediate bytes in the relocation table.
*/
Offs += D->SegSize; /* Calulate full offset */
Diff = ((long) Offs) - D->LastOffs;
while (Diff > 0xFE) {
O65RelocPutByte (D->CurReloc, 0xFF);
Diff -= 0xFE;
}
O65RelocPutByte (D->CurReloc, (unsigned char) Diff);
/* Remember this offset for the next time */
D->LastOffs = Offs;
/* Determine the expression to relocate */
Expr = E;
if (E->Op == EXPR_BYTE0 || E->Op == EXPR_BYTE1 ||
E->Op == EXPR_BYTE2 || E->Op == EXPR_BYTE3 ||
E->Op == EXPR_WORD0 || E->Op == EXPR_WORD1) {
/* Use the real expression */
Expr = E->Left;
}
/* Recursively collect information about this expression */
O65ParseExpr (Expr, InitExprDesc (&ED, D), 1);
/* We cannot handle more than one external reference */
RefCount = (ED.MemRef != 0) + (ED.SegRef != 0) +
(ED.SecRef != 0) + (ED.ExtRef != 0);
if (RefCount > 1) {
ED.TooComplex = 1;
}
/* If we have a memory area reference, we need to convert it into a
* segment reference. If we cannot do that, we cannot handle the
* expression.
*/
if (ED.MemRef) {
CvtMemoryToSegment (&ED);
if (ED.SegRef == 0) {
return SEG_EXPR_TOO_COMPLEX;
}
}
/* Bail out if we cannot handle the expression */
if (ED.TooComplex) {
return SEG_EXPR_TOO_COMPLEX;
}
/* Safety: Check that we have exactly one reference */
CHECK (RefCount == 1);
/* Write out the offset that goes into the segment. */
BinVal = ED.Val;
switch (E->Op) {
case EXPR_BYTE0: BinVal &= 0xFF; break;
case EXPR_BYTE1: BinVal = (BinVal >> 8) & 0xFF; break;
case EXPR_BYTE2: BinVal = (BinVal >> 16) & 0xFF; break;
case EXPR_BYTE3: BinVal = (BinVal >> 24) & 0xFF; break;
case EXPR_WORD0: BinVal &= 0xFFFF; break;
case EXPR_WORD1: BinVal = (BinVal >> 16) & 0xFFFF; break;
}
WriteVal (D->F, BinVal, Size);
/* Determine the actual type of relocation entry needed from the
* information gathered about the expression.
*/
if (E->Op == EXPR_BYTE0) {
RelocType = O65RELOC_LOW;
} else if (E->Op == EXPR_BYTE1) {
RelocType = O65RELOC_HIGH;
} else if (E->Op == EXPR_BYTE2) {
RelocType = O65RELOC_SEG;
} else {
switch (Size) {
case 1:
RelocType = O65RELOC_LOW;
break;
case 2:
RelocType = O65RELOC_WORD;
break;
case 3:
RelocType = O65RELOC_SEGADR;
break;
case 4:
/* 4 byte expression not supported by o65 */
return SEG_EXPR_TOO_COMPLEX;
default:
Internal ("O65WriteExpr: Invalid expression size: %u", Size);
RelocType = 0; /* Avoid gcc warnings */
}
}
/* Determine which segment we're referencing */
if (ED.SegRef || ED.SecRef) {
const SegDesc* Seg;
/* Segment or section reference. */
if (ED.SecRef) {
/* Get segment from section */
ED.SegRef = ED.SecRef->Seg;
}
/* Search for the segment and map it to it's o65 segmentID */
Seg = O65FindSeg (D, ED.SegRef);
if (Seg == 0) {
/* For some reason, we didn't find this segment in the list of
* segments written to the o65 file.
*/
return SEG_EXPR_INVALID;
}
RelocType |= O65SegType (Seg);
O65RelocPutByte (D->CurReloc, RelocType);
/* Output additional data if needed */
switch (RelocType & O65RELOC_MASK) {
case O65RELOC_HIGH:
O65RelocPutByte (D->CurReloc, ED.Val & 0xFF);
break;
case O65RELOC_SEG:
O65RelocPutWord (D->CurReloc, ED.Val & 0xFFFF);
break;
}
} else if (ED.ExtRef) {
/* Imported symbol */
RelocType |= O65SEG_UNDEF;
O65RelocPutByte (D->CurReloc, RelocType);
/* Put the number of the imported symbol into the table */
O65RelocPutWord (D->CurReloc, ExtSymNum (ED.ExtRef));
} else {
/* OOPS - something bad happened */
Internal ("External reference not handled");
}
/* Success */
return SEG_EXPR_OK;
}
static void O65WriteSeg (O65Desc* D, SegDesc** Seg, unsigned Count, int DoWrite)
/* Write one segment to the o65 output file */
{
SegDesc* S;
unsigned I;
/* Initialize variables */
D->SegSize = 0;
D->LastOffs = -1;
/* Write out all segments */
for (I = 0; I < Count; ++I) {
/* Get the segment from the list node */
S = Seg [I];
/* Relocate line info for this segment */
RelocLineInfo (S->Seg);
/* Keep the user happy */
Print (stdout, 1, " Writing `%s'\n", GetString (S->Name));
/* Write this segment */
if (DoWrite) {
SegWrite (D->Filename, D->F, S->Seg, O65WriteExpr, D);
}
/* Mark the segment as dumped */
S->Seg->Dumped = 1;
/* Calculate the total size */
D->SegSize += S->Seg->Size;
}
/* Terminate the relocation table for this segment */
if (D->CurReloc) {
O65RelocPutByte (D->CurReloc, 0);
}
/* Check the size of the segment for overflow */
if ((D->Header.Mode & MF_SIZE_MASK) == MF_SIZE_16BIT && D->SegSize > 0xFFFF) {
Error ("Segment overflow in file `%s'", D->Filename);
}
}
static void O65WriteTextSeg (O65Desc* D)
/* Write the code segment to the o65 output file */
{
/* Initialize variables */
D->CurReloc = D->TextReloc;
/* Dump all text segments */
O65WriteSeg (D, D->TextSeg, D->TextCount, 1);
/* Set the size of the segment */
D->Header.TextSize = D->SegSize;
}
static void O65WriteDataSeg (O65Desc* D)
/* Write the data segment to the o65 output file */
{
/* Initialize variables */
D->CurReloc = D->DataReloc;
/* Dump all data segments */
O65WriteSeg (D, D->DataSeg, D->DataCount, 1);
/* Set the size of the segment */
D->Header.DataSize = D->SegSize;
}
static void O65WriteBssSeg (O65Desc* D)
/* "Write" the bss segments to the o65 output file. This will only update
* the relevant header fields.
*/
{
/* Initialize variables */
D->CurReloc = 0;
/* Dump all data segments */
O65WriteSeg (D, D->BssSeg, D->BssCount, 0);
/* Set the size of the segment */
D->Header.BssSize = D->SegSize;
}
static void O65WriteZPSeg (O65Desc* D)
/* "Write" the zeropage segments to the o65 output file. This will only update
* the relevant header fields.
*/
{
/* Initialize variables */
D->CurReloc = 0;
/* Dump all data segments */
O65WriteSeg (D, D->ZPSeg, D->ZPCount, 0);
/* Set the size of the segment */
D->Header.ZPSize = D->SegSize;
}
static void O65WriteImports (O65Desc* D)
/* Write the list of imported symbols to the O65 file */
{
const ExtSym* S;
/* Write the number of imports */
WriteSize (D, ExtSymCount (D->Imports));
/* Write out the symbol names, zero terminated */
S = ExtSymList (D->Imports);
while (S) {
/* Get the name */
const char* Name = GetString (ExtSymName (S));
/* And write it to the output file */
WriteData (D->F, Name, strlen (Name) + 1);
/* Next symbol */
S = ExtSymNext (S);
}
}
static void O65WriteTextReloc (O65Desc* D)
/* Write the relocation for the text segment to the output file */
{
O65WriteReloc (D->TextReloc, D->F);
}
static void O65WriteDataReloc (O65Desc* D)
/* Write the relocation for the data segment to the output file */
{
O65WriteReloc (D->DataReloc, D->F);
}
static void O65WriteExports (O65Desc* D)
/* Write the list of exports */
{
const ExtSym* S;
/* Write the number of exports */
WriteSize (D, ExtSymCount (D->Exports));
/* Write out the symbol information */
S = ExtSymList (D->Exports);
while (S) {
ExprNode* Expr;
unsigned char SegmentID;
ExprDesc ED;
/* Get the name */
unsigned NameIdx = ExtSymName (S);
const char* Name = GetString (NameIdx);
/* Get the export for this symbol. We've checked before that this
* export does really exist, so if it is unresolved, or if we don't
* find it, there is an error in the linker code.
*/
Export* E = FindExport (NameIdx);
if (E == 0 || IsUnresolvedExport (E)) {
Internal ("Unresolved export `%s' found in O65WriteExports", Name);
}
/* Get the expression for the symbol */
Expr = E->Expr;
/* Recursively collect information about this expression */
O65ParseExpr (Expr, InitExprDesc (&ED, D), 1);
/* We cannot handle expressions with imported symbols, or expressions
* with more than one segment reference here
*/
if (ED.ExtRef != 0 || (ED.SegRef != 0 && ED.SecRef != 0)) {
ED.TooComplex = 1;
}
/* Bail out if we cannot handle the expression */
if (ED.TooComplex) {
Error ("Expression for symbol `%s' is too complex", Name);
}
/* Determine the segment id for the expression */
if (ED.SegRef != 0 || ED.SecRef != 0) {
const SegDesc* Seg;
/* Segment or section reference */
if (ED.SecRef != 0) {
ED.SegRef = ED.SecRef->Seg; /* Get segment from section */
}
/* Search for the segment and map it to it's o65 segmentID */
Seg = O65FindSeg (D, ED.SegRef);
if (Seg == 0) {
/* For some reason, we didn't find this segment in the list of
* segments written to the o65 file.
*/
Error ("Segment for symbol `%s' is undefined", Name);
}
SegmentID = O65SegType (Seg);
} else {
/* Absolute value */
SegmentID = O65SEG_ABS;
}
/* Write the name to the output file */
WriteData (D->F, Name, strlen (Name) + 1);
/* Output the segment id followed by the literal value */
Write8 (D->F, SegmentID);
WriteSize (D, ED.Val);
/* Next symbol */
S = ExtSymNext (S);
}
}
/*****************************************************************************/
/* Public code */
/*****************************************************************************/
O65Desc* NewO65Desc (void)
/* Create, initialize and return a new O65 descriptor struct */
{
/* Allocate a new structure */
O65Desc* D = xmalloc (sizeof (O65Desc));
/* Initialize the header */
D->Header.Version = 0;
D->Header.Mode = 0;
D->Header.TextBase = 0;
D->Header.TextSize = 0;
D->Header.DataBase = 0;
D->Header.DataSize = 0;
D->Header.BssBase = 0;
D->Header.BssSize = 0;
D->Header.ZPBase = 0;
D->Header.ZPSize = 0;
D->Header.StackSize = 0; /* Let OS choose a good value */
/* Initialize other data */
D->Options = 0;
D->Exports = NewExtSymTab ();
D->Imports = NewExtSymTab ();
D->Undef = 0;
D->F = 0;
D->Filename = 0;
D->TextReloc = NewO65RelocTab ();
D->DataReloc = NewO65RelocTab ();
D->TextCount = 0;
D->TextSeg = 0;
D->DataCount = 0;
D->DataSeg = 0;
D->BssCount = 0;
D->BssSeg = 0;
D->ZPCount = 0;
D->ZPSeg = 0;
/* Return the created struct */
return D;
}
void FreeO65Desc (O65Desc* D)
/* Delete the descriptor struct with cleanup */
{
/* Free the segment arrays */
xfree (D->ZPSeg);
xfree (D->BssSeg);
xfree (D->DataSeg);
xfree (D->TextSeg);
/* Free the relocation tables */
FreeO65RelocTab (D->DataReloc);
FreeO65RelocTab (D->TextReloc);
/* Free the option list */
while (D->Options) {
O65Option* O = D->Options;
D->Options = D->Options->Next;
FreeO65Option (O);
}
/* Free the external symbol tables */
FreeExtSymTab (D->Exports);
FreeExtSymTab (D->Imports);
/* Free the struct itself */
xfree (D);
}
void O65Set6502 (O65Desc* D)
/* Enable 6502 mode */
{
D->Header.Mode = (D->Header.Mode & ~MF_CPU_MASK) | MF_CPU_6502;
}
void O65Set65816 (O65Desc* D)
/* Enable 816 mode */
{
D->Header.Mode = (D->Header.Mode & ~MF_CPU_MASK) | MF_CPU_65816;
}
void O65SetSmallModel (O65Desc* D)
/* Enable a small memory model executable */
{
D->Header.Mode = (D->Header.Mode & ~MF_SIZE_MASK) | MF_SIZE_16BIT;
}
void O65SetLargeModel (O65Desc* D)
/* Enable a large memory model executable */
{
D->Header.Mode = (D->Header.Mode & ~MF_SIZE_MASK) | MF_SIZE_32BIT;
}
void O65SetAlignment (O65Desc* D, unsigned Align)
/* Set the executable alignment */
{
/* Remove all alignment bits from the mode word */
D->Header.Mode &= ~MF_ALIGN_MASK;
/* Set the alignment bits */
switch (Align) {
case 1: D->Header.Mode |= MF_ALIGN_1; break;
case 2: D->Header.Mode |= MF_ALIGN_2; break;
case 4: D->Header.Mode |= MF_ALIGN_4; break;
case 256: D->Header.Mode |= MF_ALIGN_256; break;
default: Error ("Invalid alignment for O65 format: %u", Align);
}
}
void O65SetOption (O65Desc* D, unsigned Type, const void* Data, unsigned DataLen)
/* Set an o65 header option */
{
/* Create a new option structure */
O65Option* O = NewO65Option (Type, Data, DataLen);
/* Insert it into the linked list */
O->Next = D->Options;
D->Options = O;
}
void O65SetOS (O65Desc* D, unsigned OS, unsigned Version, unsigned Id)
/* Set an option describing the target operating system */
{
/* Setup the option data */
unsigned char Opt[4];
Opt[0] = OS;
Opt[1] = Version;
/* Write the correct option length */
switch (OS) {
case O65OS_CC65:
/* Set the 16 bit id */
Opt[2] = (unsigned char) Id;
Opt[3] = (unsigned char) (Id >> 8);
O65SetOption (D, O65OPT_OS, Opt, 4);
break;
default:
/* No id for OS/A65, Lunix, and unknown OSes */
O65SetOption (D, O65OPT_OS, Opt, 2);
break;
}
}
ExtSym* O65GetImport (O65Desc* D, unsigned Ident)
/* Return the imported symbol or NULL if not found */
{
/* Retrieve the symbol from the table */
return GetExtSym (D->Imports, Ident);
}
void O65SetImport (O65Desc* D, unsigned Ident)
/* Set an imported identifier */
{
/* Insert the entry into the table */
NewExtSym (D->Imports, Ident);
}
ExtSym* O65GetExport (O65Desc* D, unsigned Ident)
/* Return the exported symbol or NULL if not found */
{
/* Retrieve the symbol from the table */
return GetExtSym (D->Exports, Ident);
}
void O65SetExport (O65Desc* D, unsigned Ident)
/* Set an exported identifier */
{
/* Get the export for this symbol and check if it does exist and is
* a resolved symbol.
*/
Export* E = FindExport (Ident);
if (E == 0 || IsUnresolvedExport (E)) {
Error ("Unresolved export: `%s'", GetString (Ident));
}
/* Insert the entry into the table */
NewExtSym (D->Exports, Ident);
}
static void O65SetupSegments (O65Desc* D, File* F)
/* Setup segment assignments */
{
unsigned I;
unsigned TextIdx, DataIdx, BssIdx, ZPIdx;
/* Initialize the counters */
D->TextCount = 0;
D->DataCount = 0;
D->BssCount = 0;
D->ZPCount = 0;
/* Walk over the memory list */
for (I = 0; I < CollCount (&F->MemoryAreas); ++I) {
/* Get this entry */
MemoryArea* M = CollAtUnchecked (&F->MemoryAreas, I);
/* Walk through the segment list and count the segment types */
unsigned J;
for (J = 0; J < CollCount (&M->SegList); ++J) {
/* Get the segment */
SegDesc* S = CollAtUnchecked (&M->SegList, J);
/* Check the segment type. */
switch (O65SegType (S)) {
case O65SEG_TEXT: D->TextCount++; break;
case O65SEG_DATA: D->DataCount++; break;
case O65SEG_BSS: D->BssCount++; break;
case O65SEG_ZP: D->ZPCount++; break;
default: Internal ("Invalid return from O65SegType");
}
}
}
/* Allocate memory according to the numbers */
D->TextSeg = xmalloc (D->TextCount * sizeof (SegDesc*));
D->DataSeg = xmalloc (D->DataCount * sizeof (SegDesc*));
D->BssSeg = xmalloc (D->BssCount * sizeof (SegDesc*));
D->ZPSeg = xmalloc (D->ZPCount * sizeof (SegDesc*));
/* Walk again through the list and setup the segment arrays */
TextIdx = DataIdx = BssIdx = ZPIdx = 0;
for (I = 0; I < CollCount (&F->MemoryAreas); ++I) {
/* Get this entry */
MemoryArea* M = CollAtUnchecked (&F->MemoryAreas, I);
/* Walk over the segment list and check the segment types */
unsigned J;
for (J = 0; J < CollCount (&M->SegList); ++J) {
/* Get the segment */
SegDesc* S = CollAtUnchecked (&M->SegList, J);
/* Check the segment type. */
switch (O65SegType (S)) {
case O65SEG_TEXT: D->TextSeg [TextIdx++] = S; break;
case O65SEG_DATA: D->DataSeg [DataIdx++] = S; break;
case O65SEG_BSS: D->BssSeg [BssIdx++] = S; break;
case O65SEG_ZP: D->ZPSeg [ZPIdx++] = S; break;
default: Internal ("Invalid return from O65SegType");
}
}
}
}
static int O65Unresolved (unsigned Name, void* D)
/* Called if an unresolved symbol is encountered */
{
/* Check if the symbol is an imported o65 symbol */
if (O65GetImport (D, Name) != 0) {
/* This is an external symbol, relax... */
return 1;
} else {
/* This is actually an unresolved external. Bump the counter */
((O65Desc*) D)->Undef++;
return 0;
}
}
static void O65SetupHeader (O65Desc* D)
/* Set additional stuff in the header */
{
/* Set the base addresses of the segments */
if (D->TextCount > 0) {
SegDesc* FirstSeg = D->TextSeg [0];
D->Header.TextBase = FirstSeg->Seg->PC;
}
if (D->DataCount > 0) {
SegDesc* FirstSeg = D->DataSeg [0];
D->Header.DataBase = FirstSeg->Seg->PC;
}
if (D->BssCount > 0) {
SegDesc* FirstSeg = D->BssSeg [0];
D->Header.BssBase = FirstSeg->Seg->PC;
}
if (D->ZPCount > 0) {
SegDesc* FirstSeg = D->ZPSeg [0];
D->Header.ZPBase = FirstSeg->Seg->PC;
}
/* If we have byte wise relocation and an alignment of 1, we can set
* the "simple addressing" bit in the header.
*/
if ((D->Header.Mode & MF_RELOC_MASK) == MF_RELOC_BYTE &&
(D->Header.Mode & MF_ALIGN_MASK) == MF_ALIGN_1) {
D->Header.Mode = (D->Header.Mode & ~MF_ADDR_MASK) | MF_ADDR_SIMPLE;
}
}
void O65WriteTarget (O65Desc* D, File* F)
/* Write an o65 output file */
{
char OptBuf [256]; /* Buffer for option strings */
unsigned OptLen;
time_t T;
/* Place the filename in the control structure */
D->Filename = GetString (F->Name);
/* Check for unresolved symbols. The function O65Unresolved is called
* if we get an unresolved symbol.
*/
D->Undef = 0; /* Reset the counter */
CheckUnresolvedImports (O65Unresolved, D);
if (D->Undef > 0) {
/* We had unresolved symbols, cannot create output file */
Error ("%u unresolved external(s) found - cannot create output file", D->Undef);
}
/* Setup the segment arrays */
O65SetupSegments (D, F);
/* Setup additional stuff in the header */
O65SetupHeader (D);
/* Open the file */
D->F = fopen (D->Filename, "wb");
if (D->F == 0) {
Error ("Cannot open `%s': %s", D->Filename, strerror (errno));
}
/* Keep the user happy */
Print (stdout, 1, "Opened `%s'...\n", D->Filename);
/* Define some more options: A timestamp and the linker version */
T = time (0);
strcpy (OptBuf, ctime (&T));
OptLen = strlen (OptBuf);
while (OptLen > 0 && IsControl (OptBuf[OptLen-1])) {
--OptLen;
}
OptBuf[OptLen] = '\0';
O65SetOption (D, O65OPT_TIMESTAMP, OptBuf, OptLen + 1);
sprintf (OptBuf, "ld65 V%s", GetVersionAsString ());
O65SetOption (D, O65OPT_ASM, OptBuf, strlen (OptBuf) + 1);
/* Write the header */
O65WriteHeader (D);
/* Write the text segment */
O65WriteTextSeg (D);
/* Write the data segment */
O65WriteDataSeg (D);
/* "Write" the bss segments */
O65WriteBssSeg (D);
/* "Write" the zeropage segments */
O65WriteZPSeg (D);
/* Write the undefined references list */
O65WriteImports (D);
/* Write the text segment relocation table */
O65WriteTextReloc (D);
/* Write the data segment relocation table */
O65WriteDataReloc (D);
/* Write the list of exports */
O65WriteExports (D);
/* Seek back to the start and write the updated header */
fseek (D->F, 0, SEEK_SET);
O65WriteHeader (D);
/* Close the file */
if (fclose (D->F) != 0) {
Error ("Cannot write to `%s': %s", D->Filename, strerror (errno));
}
/* Reset the file and filename */
D->F = 0;
D->Filename = 0;
}