Implement compact file stacks in object files

Gets rid of `open_memstream`, enabling Windows compatibility again
Also fixes #491 as a nice bonus!
This commit is contained in:
ISSOtm
2020-09-29 03:40:15 +02:00
parent 930080f556
commit 5a65188ca9
23 changed files with 796 additions and 365 deletions

View File

@@ -46,7 +46,7 @@ if(MSVC)
add_definitions(/D_CRT_SECURE_NO_WARNINGS) add_definitions(/D_CRT_SECURE_NO_WARNINGS)
else() else()
if(DEVELOP) if(DEVELOP)
add_compile_options(-Werror -Wall -Wextra -pedantic add_compile_options(-Werror -Wall -Wextra -pedantic -Wno-type-limits
-Wno-sign-compare -Wformat -Wformat-security -Wformat-overflow=2 -Wno-sign-compare -Wformat -Wformat-security -Wformat-overflow=2
-Wformat-truncation=1 -Wformat-y2k -Wswitch-enum -Wunused -Wformat-truncation=1 -Wformat-y2k -Wswitch-enum -Wunused
-Wuninitialized -Wunknown-pragmas -Wstrict-overflow=5 -Wuninitialized -Wunknown-pragmas -Wstrict-overflow=5

View File

@@ -186,7 +186,7 @@ checkpatch:
# compilation and make the continous integration infrastructure return failure. # compilation and make the continous integration infrastructure return failure.
develop: develop:
$Qenv $(MAKE) -j WARNFLAGS="-Werror -Wall -Wextra -Wpedantic \ $Qenv $(MAKE) -j WARNFLAGS="-Werror -Wall -Wextra -Wpedantic -Wno-type-limits \
-Wno-sign-compare -Wformat -Wformat-security -Wformat-overflow=2 \ -Wno-sign-compare -Wformat -Wformat-security -Wformat-overflow=2 \
-Wformat-truncation=1 -Wformat-y2k -Wswitch-enum -Wunused \ -Wformat-truncation=1 -Wformat-y2k -Wswitch-enum -Wunused \
-Wuninitialized -Wunknown-pragmas -Wstrict-overflow=5 \ -Wuninitialized -Wunknown-pragmas -Wstrict-overflow=5 \

View File

@@ -21,26 +21,44 @@
#include "types.h" #include "types.h"
struct MacroArgs; struct FileStackNode {
struct FileStackNode *parent; /* Pointer to parent node, for error reporting */
/* Line at which the parent context was exited; meaningless for the root level */
uint32_t lineNo;
struct sContext { struct FileStackNode *next; /* Next node in the output linked list */
struct LexerState *lexerState; bool referenced; /* If referenced, don't free! */
struct Symbol const *pMacro; uint32_t ID; /* Set only if referenced: ID within the object file, -1 if not output yet */
struct sContext *next;
char tzFileName[_MAX_PATH + 1]; enum {
struct MacroArgs *macroArgs; NODE_REPT,
uint32_t uniqueID; NODE_FILE,
int32_t nLine; NODE_MACRO,
uint32_t nStatus; } type;
char const *pREPTBlock; };
uint32_t nREPTBlockCount;
uint32_t nREPTBlockSize; struct FileStackReptNode { /* NODE_REPT */
int32_t nREPTBodyFirstLine; struct FileStackNode node;
int32_t nREPTBodyLastLine; uint32_t reptDepth;
/* WARNING: if changing this type, change overflow check in `fstk_Init` */
uint32_t iters[]; /* REPT iteration counts since last named node, in reverse depth order */
};
struct FileStackNamedNode { /* NODE_FILE, NODE_MACRO */
struct FileStackNode node;
char name[]; /* File name for files, file::macro name for macros */
}; };
extern size_t nMaxRecursionDepth; extern size_t nMaxRecursionDepth;
struct MacroArgs;
void fstk_Dump(struct FileStackNode const *node, uint32_t lineNo);
void fstk_DumpCurrent(void);
struct FileStackNode *fstk_GetFileStack(void);
/* The lifetime of the returned chars is until reaching the end of that file */
char const *fstk_GetFileName(void);
void fstk_AddIncludePath(char const *s); void fstk_AddIncludePath(char const *s);
/** /**
* @param path The user-provided file name * @param path The user-provided file name
@@ -53,14 +71,9 @@ bool fstk_FindFile(char const *path, char **fullPath, size_t *size);
bool yywrap(void); bool yywrap(void);
void fstk_RunInclude(char const *path); void fstk_RunInclude(char const *path);
void fstk_RunMacro(char *macroName, struct MacroArgs *args); void fstk_RunMacro(char const *macroName, struct MacroArgs *args);
void fstk_RunRept(uint32_t count, int32_t nReptLineNo, char *body, size_t size); void fstk_RunRept(uint32_t count, int32_t nReptLineNo, char *body, size_t size);
void fstk_Dump(void); void fstk_Init(char const *mainPath, size_t maxRecursionDepth);
char *fstk_DumpToStr(void);
char const *fstk_GetFileName(void);
uint32_t fstk_GetLine(void);
void fstk_Init(char *mainPath, size_t maxRecursionDepth);
#endif /* RGBDS_ASM_FSTACK_H */ #endif /* RGBDS_ASM_FSTACK_H */

View File

@@ -43,6 +43,9 @@ static inline void lexer_SetGfxDigits(char const *digits)
gfxDigits = digits; gfxDigits = digits;
} }
/*
* `path` is referenced, but not held onto..!
*/
struct LexerState *lexer_OpenFile(char const *path); struct LexerState *lexer_OpenFile(char const *path);
struct LexerState *lexer_OpenFileView(char *buf, size_t size, uint32_t lineNo); struct LexerState *lexer_OpenFileView(char *buf, size_t size, uint32_t lineNo);
void lexer_RestartRept(uint32_t lineNo); void lexer_RestartRept(uint32_t lineNo);

View File

@@ -18,6 +18,8 @@ struct Expression;
extern char *tzObjectname; extern char *tzObjectname;
extern struct Section *pSectionList, *pCurrentSection; extern struct Section *pSectionList, *pCurrentSection;
void out_RegisterNode(struct FileStackNode *node);
void out_ReplaceNode(struct FileStackNode *node);
void out_SetFileName(char *s); void out_SetFileName(char *s);
void out_CreatePatch(uint32_t type, struct Expression const *expr, void out_CreatePatch(uint32_t type, struct Expression const *expr,
uint32_t ofs); uint32_t ofs);

View File

@@ -35,8 +35,8 @@ struct Symbol {
bool isExported; /* Whether the symbol is to be exported */ bool isExported; /* Whether the symbol is to be exported */
bool isBuiltin; /* Whether the symbol is a built-in */ bool isBuiltin; /* Whether the symbol is a built-in */
struct Section *section; struct Section *section;
char fileName[_MAX_PATH + 1]; /* File where the symbol was defined. */ struct FileStackNode *src; /* Where the symbol was defined */
uint32_t fileLine; /* Line where the symbol was defined. */ uint32_t fileLine; /* Line where the symbol was defined */
bool hasCallback; bool hasCallback;
union { union {

View File

@@ -29,15 +29,45 @@ extern bool beVerbose;
extern bool isWRA0Mode; extern bool isWRA0Mode;
extern bool disablePadding; extern bool disablePadding;
struct FileStackNode {
struct FileStackNode *parent;
/* Line at which the parent context was exited; meaningless for the root level */
uint32_t lineNo;
enum {
NODE_REPT,
NODE_FILE,
NODE_MACRO,
} type;
union {
char *name; /* NODE_FILE, NODE_MACRO */
struct { /* NODE_REPT */
uint32_t reptDepth;
uint32_t *iters;
};
};
};
/* Helper macro for printing verbose-mode messages */ /* Helper macro for printing verbose-mode messages */
#define verbosePrint(...) do { \ #define verbosePrint(...) do { \
if (beVerbose) \ if (beVerbose) \
fprintf(stderr, __VA_ARGS__); \ fprintf(stderr, __VA_ARGS__); \
} while (0) } while (0)
void error(char const *fmt, ...); /**
* Dump a file stack to stderr
* @param node The leaf node to dump the context of
*/
char const *dumpFileStack(struct FileStackNode const *node);
noreturn_ void fatal(char const *fmt, ...); void warning(struct FileStackNode const *where, uint32_t lineNo,
char const *fmt, ...) format_(printf, 3, 4);
void error(struct FileStackNode const *where, uint32_t lineNo,
char const *fmt, ...) format_(printf, 3, 4);
noreturn_ void fatal(struct FileStackNode const *where, uint32_t lineNo,
char const *fmt, ...) format_(printf, 3, 4);
/** /**
* Opens a file if specified, and aborts on error. * Opens a file if specified, and aborts on error.

View File

@@ -14,8 +14,9 @@
/** /**
* Read an object (.o) file, and add its info to the data structures. * Read an object (.o) file, and add its info to the data structures.
* @param fileName A path to the object file to be read * @param fileName A path to the object file to be read
* @param i The ID of the file
*/ */
void obj_ReadFile(char const *fileName); void obj_ReadFile(char const *fileName, unsigned int i);
/** /**
* Perform validation on the object files' contents * Perform validation on the object files' contents
@@ -27,6 +28,12 @@ void obj_DoSanityChecks(void);
*/ */
void obj_CheckAssertions(void); void obj_CheckAssertions(void);
/**
* Sets up object file reading
* @param nbFiles The number of object files that will be read
*/
void obj_Setup(unsigned int nbFiles);
/** /**
* `free`s all object memory that was allocated. * `free`s all object memory that was allocated.
*/ */

View File

@@ -19,6 +19,7 @@
#include "linkdefs.h" #include "linkdefs.h"
struct FileStackNode;
struct Section; struct Section;
struct AttachedSymbol { struct AttachedSymbol {
@@ -27,7 +28,8 @@ struct AttachedSymbol {
}; };
struct Patch { struct Patch {
char *fileName; struct FileStackNode const *src;
uint32_t lineNo;
int32_t offset; int32_t offset;
uint32_t pcSectionID; uint32_t pcSectionID;
uint32_t pcOffset; uint32_t pcOffset;

View File

@@ -16,12 +16,14 @@
#include "linkdefs.h" #include "linkdefs.h"
struct FileStackNode;
struct Symbol { struct Symbol {
/* Info contained in the object files */ /* Info contained in the object files */
char *name; char *name;
enum ExportLevel type; enum ExportLevel type;
char const *objFileName; char const *objFileName;
char *fileName; struct FileStackNode const *src;
int32_t lineNo; int32_t lineNo;
int32_t sectionID; int32_t sectionID;
union { union {

View File

@@ -14,7 +14,7 @@
#define RGBDS_OBJECT_VERSION_STRING "RGB%1u" #define RGBDS_OBJECT_VERSION_STRING "RGB%1u"
#define RGBDS_OBJECT_VERSION_NUMBER 9U #define RGBDS_OBJECT_VERSION_NUMBER 9U
#define RGBDS_OBJECT_REV 5U #define RGBDS_OBJECT_REV 6U
enum AssertionType { enum AssertionType {
ASSERT_WARN, ASSERT_WARN,

View File

@@ -29,27 +29,83 @@
struct Context { struct Context {
struct Context *parent; struct Context *parent;
struct Context *child; struct FileStackNode *fileInfo;
struct LexerState *lexerState; struct LexerState *lexerState;
uint32_t uniqueID; uint32_t uniqueID;
char const *fileName;
char *fileNameBuf;
uint32_t lineNo; /* Line number at which the context was EXITED */
struct Symbol const *macro;
struct MacroArgs *macroArgs; /* Macro args are *saved* here */ struct MacroArgs *macroArgs; /* Macro args are *saved* here */
uint32_t nbReptIters; /* If zero, this isn't a REPT block */ uint32_t nbReptIters;
size_t reptDepth;
uint32_t reptIters[];
}; };
static struct Context *contextStack; static struct Context *contextStack;
static struct Context *topLevelContext;
static size_t contextDepth = 0; static size_t contextDepth = 0;
#define DEFAULT_MAX_DEPTH 64
size_t nMaxRecursionDepth; size_t nMaxRecursionDepth;
static unsigned int nbIncPaths = 0; static unsigned int nbIncPaths = 0;
static char const *includePaths[MAXINCPATHS]; static char const *includePaths[MAXINCPATHS];
char const *dumpNodeAndParents(struct FileStackNode const *node)
{
char const *name;
if (node->type == NODE_REPT) {
assert(node->parent); /* REPT nodes should always have a parent */
struct FileStackReptNode const *reptInfo = (struct FileStackReptNode const *)node;
name = dumpNodeAndParents(node->parent);
fprintf(stderr, "(%" PRIu32 ") -> %s", node->lineNo, name);
for (uint32_t i = reptInfo->reptDepth; i--; )
fprintf(stderr, "::REPT~%" PRIu32, reptInfo->iters[i]);
} else {
name = ((struct FileStackNamedNode const *)node)->name;
if (node->parent) {
dumpNodeAndParents(node->parent);
fprintf(stderr, "(%" PRIu32 ") -> %s", node->lineNo, name);
} else {
fputs(name, stderr);
}
}
return name;
}
void fstk_Dump(struct FileStackNode const *node, uint32_t lineNo)
{
dumpNodeAndParents(node);
fprintf(stderr, "(%" PRIu32 ")", lineNo);
}
void fstk_DumpCurrent(void)
{
if (!contextStack) {
fputs("at top level", stderr);
return;
}
fstk_Dump(contextStack->fileInfo, lexer_GetLineNo());
}
struct FileStackNode *fstk_GetFileStack(void)
{
struct FileStackNode *node = contextStack->fileInfo;
/* Mark node and all of its parents as referenced if not already so they don't get freed */
while (node && !node->referenced) {
node->ID = -1;
node->referenced = true;
node = node->parent;
}
return contextStack->fileInfo;
}
char const *fstk_GetFileName(void)
{
/* Iterating via the nodes themselves skips nested REPTs */
struct FileStackNode const *node = contextStack->fileInfo;
while (node->type != NODE_FILE)
node = node->parent;
return ((struct FileStackNamedNode const *)node)->name;
}
void fstk_AddIncludePath(char const *path) void fstk_AddIncludePath(char const *path)
{ {
if (path[0] == '\0') if (path[0] == '\0')
@@ -141,12 +197,28 @@ bool fstk_FindFile(char const *path, char **fullPath, size_t *size)
bool yywrap(void) bool yywrap(void)
{ {
if (contextStack->nbReptIters) { /* The context is a REPT block, which may loop */ if (contextStack->fileInfo->type == NODE_REPT) { /* The context is a REPT block, which may loop */
contextStack->reptIters[contextStack->reptDepth - 1]++; struct FileStackReptNode *fileInfo = (struct FileStackReptNode *)contextStack->fileInfo;
/* If the node is referenced, we can't edit it; duplicate it */
if (contextStack->fileInfo->referenced) {
struct FileStackReptNode *copy = malloc(sizeof(*copy) + sizeof(copy->iters[0]) * fileInfo->reptDepth);
if (!copy)
fatalerror("Failed to duplicate REPT file node: %s\n", strerror(errno));
/* Copy all info but the referencing */
*copy = *fileInfo;
copy->node.next = NULL;
copy->node.referenced = false;
fileInfo = copy;
contextStack->fileInfo = (struct FileStackNode *)fileInfo;
}
fileInfo->iters[0]++;
/* If this wasn't the last iteration, wrap instead of popping */ /* If this wasn't the last iteration, wrap instead of popping */
if (contextStack->reptIters[contextStack->reptDepth - 1] if (fileInfo->iters[0] <= contextStack->nbReptIters) {
<= contextStack->nbReptIters) { lexer_RestartRept(contextStack->fileInfo->lineNo);
lexer_RestartRept(contextStack->parent->lineNo);
contextStack->uniqueID = macro_UseNewUniqueID(); contextStack->uniqueID = macro_UseNewUniqueID();
return false; return false;
} }
@@ -155,44 +227,52 @@ bool yywrap(void)
} }
dbgPrint("Popping context\n"); dbgPrint("Popping context\n");
/* Free an `INCLUDE`'s path */ struct Context *context = contextStack;
if (contextStack->fileNameBuf)
free(contextStack->fileNameBuf);
contextStack = contextStack->parent; contextStack = contextStack->parent;
contextDepth--; contextDepth--;
lexer_DeleteState(contextStack->child->lexerState); lexer_DeleteState(context->lexerState);
/* Restore args if a macro (not REPT) saved them */ /* Restore args if a macro (not REPT) saved them */
if (contextStack->child->nbReptIters == 0 && contextStack->child->macro) { if (context->fileInfo->type == NODE_MACRO) {
dbgPrint("Restoring macro args %p\n", contextStack->macroArgs); dbgPrint("Restoring macro args %p\n", contextStack->macroArgs);
macro_UseNewArgs(contextStack->macroArgs); macro_UseNewArgs(contextStack->macroArgs);
} }
/* Free the file stack node */
if (!context->fileInfo->referenced)
free(context->fileInfo);
/* Free the entry and make its parent the current entry */ /* Free the entry and make its parent the current entry */
free(contextStack->child); free(context);
contextStack->child = NULL;
lexer_SetState(contextStack->lexerState); lexer_SetState(contextStack->lexerState);
macro_SetUniqueID(contextStack->uniqueID); macro_SetUniqueID(contextStack->uniqueID);
return false; return false;
} }
static void newContext(uint32_t reptDepth) /*
* Make sure not to switch the lexer state before calling this, so the saved line no is correct
* BE CAREFUL!! This modifies the file stack directly, you should have set up the file info first
*/
static void newContext(struct FileStackNode *fileInfo)
{ {
if (++contextDepth >= nMaxRecursionDepth) if (++contextDepth >= nMaxRecursionDepth)
fatalerror("Recursion limit (%zu) exceeded\n", nMaxRecursionDepth); fatalerror("Recursion limit (%zu) exceeded\n", nMaxRecursionDepth);
contextStack->child = malloc(sizeof(*contextStack->child) struct Context *context = malloc(sizeof(*context));
+ reptDepth * sizeof(contextStack->reptIters[0]));
if (!contextStack->child) if (!context)
fatalerror("Failed to allocate memory for new context: %s\n", strerror(errno)); fatalerror("Failed to allocate memory for new context: %s\n", strerror(errno));
fileInfo->parent = contextStack->fileInfo;
fileInfo->lineNo = 0; /* Init to a default value, see struct definition for info */
fileInfo->referenced = false;
fileInfo->lineNo = lexer_GetLineNo();
context->fileInfo = fileInfo;
/*
* Link new entry to its parent so it's reachable later
* ERRORS SHOULD NOT OCCUR AFTER THIS!!
*/
context->parent = contextStack;
contextStack = context;
contextStack->lineNo = lexer_GetLineNo();
/* Link new entry to its parent so it's reachable later */
contextStack->child->parent = contextStack;
contextStack = contextStack->child;
contextStack->child = NULL;
contextStack->reptDepth = reptDepth;
} }
void fstk_RunInclude(char const *path) void fstk_RunInclude(char const *path)
@@ -212,21 +292,27 @@ void fstk_RunInclude(char const *path)
} }
dbgPrint("Full path: \"%s\"\n", fullPath); dbgPrint("Full path: \"%s\"\n", fullPath);
newContext(0); struct FileStackNamedNode *fileInfo = malloc(sizeof(*fileInfo) + size);
contextStack->lexerState = lexer_OpenFile(fullPath);
if (!fileInfo) {
error("Failed to alloc file info for INCLUDE: %s\n", strerror(errno));
return;
}
fileInfo->node.type = NODE_FILE;
strcpy(fileInfo->name, fullPath);
free(fullPath);
newContext((struct FileStackNode *)fileInfo);
contextStack->lexerState = lexer_OpenFile(fileInfo->name);
if (!contextStack->lexerState) if (!contextStack->lexerState)
fatalerror("Failed to set up lexer for file include\n"); fatalerror("Failed to set up lexer for file include\n");
lexer_SetStateAtEOL(contextStack->lexerState); lexer_SetStateAtEOL(contextStack->lexerState);
/* We're back at top-level, so most things are reset */ /* We're back at top-level, so most things are reset */
contextStack->uniqueID = 0; contextStack->uniqueID = 0;
macro_SetUniqueID(0); macro_SetUniqueID(0);
contextStack->fileName = fullPath;
contextStack->fileNameBuf = fullPath;
contextStack->macro = NULL;
contextStack->nbReptIters = 0;
} }
void fstk_RunMacro(char *macroName, struct MacroArgs *args) void fstk_RunMacro(char const *macroName, struct MacroArgs *args)
{ {
dbgPrint("Running macro \"%s\"\n", macroName); dbgPrint("Running macro \"%s\"\n", macroName);
@@ -242,7 +328,53 @@ void fstk_RunMacro(char *macroName, struct MacroArgs *args)
} }
contextStack->macroArgs = macro_GetCurrentArgs(); contextStack->macroArgs = macro_GetCurrentArgs();
newContext(0); /* Compute total length of this node's name: <base name>::<macro> */
size_t reptNameLen = 0;
struct FileStackNode const *node = macro->src;
if (node->type == NODE_REPT) {
struct FileStackReptNode const *reptNode = (struct FileStackReptNode const *)node;
/* 4294967295 = 2^32 - 1, aka UINT32_MAX */
reptNameLen += reptNode->reptDepth * strlen("::REPT~4294967295");
/* Look for next named node */
do {
node = node->parent;
} while (node->type == NODE_REPT);
}
struct FileStackNamedNode const *baseNode = (struct FileStackNamedNode const *)node;
size_t baseLen = strlen(baseNode->name);
size_t macroNameLen = strlen(macro->name);
struct FileStackNamedNode *fileInfo = malloc(sizeof(*fileInfo) + baseLen
+ reptNameLen + 2 + macroNameLen + 1);
if (!fileInfo) {
error("Failed to alloc file info for \"%s\": %s\n", macro->name, strerror(errno));
return;
}
fileInfo->node.type = NODE_MACRO;
/* Print the name... */
char *dest = fileInfo->name;
memcpy(dest, baseNode->name, baseLen);
dest += baseLen;
if (node->type == NODE_REPT) {
struct FileStackReptNode const *reptNode = (struct FileStackReptNode const *)node;
for (uint32_t i = reptNode->reptDepth; i--; ) {
int nbChars = sprintf(dest, "::REPT~%" PRIu32, reptNode->iters[i]);
if (nbChars < 0)
fatalerror("Failed to write macro invocation info: %s\n",
strerror(errno));
dest += nbChars;
}
}
*dest++ = ':';
*dest++ = ':';
memcpy(dest, macro->name, macroNameLen + 1);
newContext((struct FileStackNode *)fileInfo);
/* Line minus 1 because buffer begins with a newline */ /* Line minus 1 because buffer begins with a newline */
contextStack->lexerState = lexer_OpenFileView(macro->macro, macro->macroSize, contextStack->lexerState = lexer_OpenFileView(macro->macro, macro->macroSize,
macro->fileLine - 1); macro->fileLine - 1);
@@ -250,143 +382,93 @@ void fstk_RunMacro(char *macroName, struct MacroArgs *args)
fatalerror("Failed to set up lexer for macro invocation\n"); fatalerror("Failed to set up lexer for macro invocation\n");
lexer_SetStateAtEOL(contextStack->lexerState); lexer_SetStateAtEOL(contextStack->lexerState);
contextStack->uniqueID = macro_UseNewUniqueID(); contextStack->uniqueID = macro_UseNewUniqueID();
contextStack->fileName = macro->fileName;
contextStack->fileNameBuf = NULL;
contextStack->macro = macro;
contextStack->nbReptIters = 0;
macro_UseNewArgs(args); macro_UseNewArgs(args);
} }
void fstk_RunRept(uint32_t count, int32_t nReptLineNo, char *body, size_t size) void fstk_RunRept(uint32_t count, int32_t reptLineNo, char *body, size_t size)
{ {
dbgPrint("Running REPT(%" PRIu32 ")\n", count); dbgPrint("Running REPT(%" PRIu32 ")\n", count);
if (count == 0) if (count == 0)
return; return;
uint32_t reptDepth = contextStack->reptDepth;
newContext(reptDepth + 1); uint32_t reptDepth = contextStack->fileInfo->type == NODE_REPT
contextStack->lexerState = lexer_OpenFileView(body, size, nReptLineNo); ? ((struct FileStackReptNode *)contextStack->fileInfo)->reptDepth
: 0;
struct FileStackReptNode *fileInfo = malloc(sizeof(*fileInfo)
+ (reptDepth + 1) * sizeof(fileInfo->iters[0]));
if (!fileInfo) {
error("Failed to alloc file info for REPT: %s\n", strerror(errno));
return;
}
fileInfo->node.type = NODE_REPT;
fileInfo->reptDepth = reptDepth + 1;
fileInfo->iters[0] = 1;
if (reptDepth)
/* Copy all parent iter counts */
memcpy(&fileInfo->iters[1],
((struct FileStackReptNode *)contextStack->fileInfo)->iters,
reptDepth * sizeof(fileInfo->iters[0]));
newContext((struct FileStackNode *)fileInfo);
/* Correct our line number, which currently points to the `ENDR` line */
contextStack->fileInfo->lineNo = reptLineNo;
contextStack->lexerState = lexer_OpenFileView(body, size, reptLineNo);
if (!contextStack->lexerState) if (!contextStack->lexerState)
fatalerror("Failed to set up lexer for macro invocation\n"); fatalerror("Failed to set up lexer for rept block\n");
lexer_SetStateAtEOL(contextStack->lexerState); lexer_SetStateAtEOL(contextStack->lexerState);
contextStack->uniqueID = macro_UseNewUniqueID(); contextStack->uniqueID = macro_UseNewUniqueID();
contextStack->fileName = contextStack->parent->fileName;
contextStack->fileNameBuf = NULL;
contextStack->macro = contextStack->parent->macro; /* Inherit */
contextStack->nbReptIters = count; contextStack->nbReptIters = count;
/* Copy all of parent's iters, and add ours */
if (reptDepth)
memcpy(contextStack->reptIters, contextStack->parent->reptIters,
sizeof(contextStack->reptIters[0]) * reptDepth);
contextStack->reptIters[reptDepth] = 1;
/* Correct our parent's line number, which currently points to the `ENDR` line */
contextStack->parent->lineNo = nReptLineNo;
} }
static void printContext(FILE *stream, struct Context const *context) void fstk_Init(char const *mainPath, size_t maxRecursionDepth)
{ {
fprintf(stream, "%s", context->fileName); struct LexerState *state = lexer_OpenFile(mainPath);
if (context->macro)
fprintf(stream, "::%s", context->macro->name);
for (size_t i = 0; i < context->reptDepth; i++)
fprintf(stream, "::REPT~%" PRIu32, context->reptIters[i]);
fprintf(stream, "(%" PRId32 ")", context->lineNo);
}
static void dumpToStream(FILE *stream) if (!state)
{
struct Context *context = topLevelContext;
while (context != contextStack) {
printContext(stream, context);
fprintf(stream, " -> ");
context = context->child;
}
contextStack->lineNo = lexer_GetLineNo();
printContext(stream, contextStack);
}
void fstk_Dump(void)
{
dumpToStream(stderr);
}
char *fstk_DumpToStr(void)
{
char *str;
size_t size;
/* `open_memstream` is specified to always include a '\0' at the end of the buffer! */
FILE *stream = open_memstream(&str, &size);
if (!stream)
fatalerror("Failed to dump file stack to string: %s\n", strerror(errno));
dumpToStream(stream);
fclose(stream);
return str;
}
char const *fstk_GetFileName(void)
{
/* FIXME: this is awful, but all callees copy the buffer anyways */
static char fileName[_MAX_PATH + 1];
size_t remainingChars = _MAX_PATH + 1;
char *dest = fileName;
char const *src = contextStack->fileName;
#define append(...) do { \
int nbChars = snprintf(dest, remainingChars, __VA_ARGS__); \
\
if (nbChars >= remainingChars) \
fatalerror("File stack entry too large\n"); \
remainingChars -= nbChars; \
dest += nbChars; \
} while (0)
while (*src && --remainingChars) /* Leave room for terminator */
*dest++ = *src++;
if (remainingChars && contextStack->macro)
append("::%s", contextStack->macro->name);
for (size_t i = 0; i < contextStack->reptDepth; i++)
append("::REPT~%" PRIu32, contextStack->reptIters[i]);
*dest = '\0';
return fileName;
}
uint32_t fstk_GetLine(void)
{
return lexer_GetLineNo();
}
void fstk_Init(char *mainPath, size_t maxRecursionDepth)
{
topLevelContext = malloc(sizeof(*topLevelContext));
if (!topLevelContext)
fatalerror("Failed to allocate memory for initial context: %s\n", strerror(errno));
topLevelContext->parent = NULL;
topLevelContext->child = NULL;
topLevelContext->lexerState = lexer_OpenFile(mainPath);
if (!topLevelContext->lexerState)
fatalerror("Failed to open main file!\n"); fatalerror("Failed to open main file!\n");
lexer_SetState(topLevelContext->lexerState); lexer_SetState(state);
topLevelContext->uniqueID = 0; char const *fileName = lexer_GetFileName();
size_t len = strlen(fileName);
struct Context *context = malloc(sizeof(*contextStack));
struct FileStackNamedNode *fileInfo = malloc(sizeof(*fileInfo) + len + 1);
if (!context)
fatalerror("Failed to allocate memory for main context: %s\n", strerror(errno));
if (!fileInfo)
fatalerror("Failed to allocate memory for main file info: %s\n", strerror(errno));
context->fileInfo = (struct FileStackNode *)fileInfo;
/* lineNo and reptIter are unused on the top-level context */
context->fileInfo->parent = NULL;
context->fileInfo->referenced = false;
context->fileInfo->type = NODE_FILE;
memcpy(fileInfo->name, fileName, len + 1);
context->parent = NULL;
context->lexerState = state;
context->uniqueID = 0;
macro_SetUniqueID(0); macro_SetUniqueID(0);
topLevelContext->fileName = lexer_GetFileName(); context->nbReptIters = 0;
topLevelContext->fileNameBuf = NULL;
topLevelContext->macro = NULL;
topLevelContext->nbReptIters = 0;
topLevelContext->reptDepth = 0;
contextStack = topLevelContext; /* Now that it's set up properly, register the context */
contextStack = context;
if (maxRecursionDepth /*
> (SIZE_MAX - sizeof(*contextStack)) / sizeof(contextStack->reptIters[0])) { * Check that max recursion depth won't allow overflowing node `malloc`s
error("Recursion depth may not be higher than %zu, defaulting to 64\n", * This assumes that the rept node is larger
(SIZE_MAX - sizeof(*contextStack)) / sizeof(contextStack->reptIters[0])); */
nMaxRecursionDepth = 64; #define DEPTH_LIMIT ((SIZE_MAX - sizeof(struct FileStackReptNode)) / sizeof(uint32_t))
if (maxRecursionDepth > DEPTH_LIMIT) {
error("Recursion depth may not be higher than %zu, defaulting to "
EXPAND_AND_STR(DEFAULT_MAX_DEPTH) "\n", DEPTH_LIMIT);
nMaxRecursionDepth = DEFAULT_MAX_DEPTH;
} else { } else {
nMaxRecursionDepth = maxRecursionDepth; nMaxRecursionDepth = maxRecursionDepth;
} }
/* Make sure that the default of 64 is OK, though */
assert(DEPTH_LIMIT >= DEFAULT_MAX_DEPTH);
#undef DEPTH_LIMIT
} }

View File

@@ -12,6 +12,7 @@
#include <assert.h> #include <assert.h>
#include <errno.h> #include <errno.h>
#include <inttypes.h>
#include <stdio.h> #include <stdio.h>
#include <stdint.h> #include <stdint.h>
#include <stdlib.h> #include <stdlib.h>
@@ -33,7 +34,8 @@
#include "platform.h" // strdup #include "platform.h" // strdup
struct Patch { struct Patch {
char *tzFilename; struct FileStackNode const *src;
uint32_t lineNo;
uint32_t nOffset; uint32_t nOffset;
struct Section *pcSection; struct Section *pcSection;
uint32_t pcOffset; uint32_t pcOffset;
@@ -62,19 +64,17 @@ static uint32_t nbSymbols = 0; /* Length of the above list */
static struct Assertion *assertions = NULL; static struct Assertion *assertions = NULL;
static struct FileStackNode *fileStackNodes = NULL;
/* /*
* Count the number of sections used in this object * Count the number of sections used in this object
*/ */
static uint32_t countsections(void) static uint32_t countsections(void)
{ {
struct Section *sect;
uint32_t count = 0; uint32_t count = 0;
sect = pSectionList; for (struct Section const *sect = pSectionList; sect; sect = sect->next)
while (sect) {
count++; count++;
sect = sect->next;
}
return count; return count;
} }
@@ -129,16 +129,60 @@ static void fputstring(char const *s, FILE *f)
fputc(0, f); fputc(0, f);
} }
static uint32_t getNbFileStackNodes(void)
{
return fileStackNodes ? fileStackNodes->ID + 1 : 0;
}
void out_RegisterNode(struct FileStackNode *node)
{
/* If node is not already registered, register it (and parents), and give it a unique ID */
while (node->ID == -1) {
node->ID = getNbFileStackNodes();
if (node->ID == -1)
fatalerror("Reached too many file stack nodes; try splitting the file up\n");
node->next = fileStackNodes;
fileStackNodes = node;
/* Also register the node's parents */
node = node->parent;
if (!node)
break;
}
}
void out_ReplaceNode(struct FileStackNode *node)
{
(void)node;
#if 0
This is code intended to replace a node, which is pretty useless until ref counting is added...
struct FileStackNode **ptr = &fileStackNodes;
/*
* The linked list is supposed to have decrementing IDs, so iterate with less memory reads,
* to hopefully hit the cache less. A debug check is added after, in case a change is made
* that breaks this assumption.
*/
for (uint32_t i = fileStackNodes->ID; i != node->ID; i--)
ptr = &(*ptr)->next;
assert((*ptr)->ID == node->ID);
node->next = (*ptr)->next;
assert(!node->next || node->next->ID == node->ID - 1); /* Catch inconsistencies early */
/* TODO: unreference the node */
*ptr = node;
#endif
}
/* /*
* Return a section's ID * Return a section's ID
*/ */
static uint32_t getsectid(struct Section const *sect) static uint32_t getsectid(struct Section const *sect)
{ {
struct Section const *sec; struct Section const *sec = pSectionList;
uint32_t ID = 0; uint32_t ID = 0;
sec = pSectionList;
while (sec) { while (sec) {
if (sec == sect) if (sec == sect)
return ID; return ID;
@@ -159,7 +203,10 @@ static uint32_t getSectIDIfAny(struct Section const *sect)
*/ */
static void writepatch(struct Patch const *patch, FILE *f) static void writepatch(struct Patch const *patch, FILE *f)
{ {
fputstring(patch->tzFilename, f); assert(patch->src->ID != -1);
fputlong(patch->src->ID, f);
fputlong(patch->lineNo, f);
fputlong(patch->nOffset, f); fputlong(patch->nOffset, f);
fputlong(getSectIDIfAny(patch->pcSection), f); fputlong(getSectIDIfAny(patch->pcSection), f);
fputlong(patch->pcOffset, f); fputlong(patch->pcOffset, f);
@@ -206,26 +253,35 @@ static void writesymbol(struct Symbol const *sym, FILE *f)
if (!sym_IsDefined(sym)) { if (!sym_IsDefined(sym)) {
fputc(SYMTYPE_IMPORT, f); fputc(SYMTYPE_IMPORT, f);
} else { } else {
assert(sym->src->ID != -1);
fputc(sym->isExported ? SYMTYPE_EXPORT : SYMTYPE_LOCAL, f); fputc(sym->isExported ? SYMTYPE_EXPORT : SYMTYPE_LOCAL, f);
fputstring(sym->fileName, f); fputlong(sym->src->ID, f);
fputlong(sym->fileLine, f); fputlong(sym->fileLine, f);
fputlong(getSectIDIfAny(sym_GetSection(sym)), f); fputlong(getSectIDIfAny(sym_GetSection(sym)), f);
fputlong(sym->value, f); fputlong(sym->value, f);
} }
} }
static void registerSymbol(struct Symbol *sym)
{
*objectSymbolsTail = sym;
objectSymbolsTail = &sym->next;
out_RegisterNode(sym->src);
if (nbSymbols == -1)
fatalerror("Registered too many symbols (%" PRIu32
"); try splitting up your files\n", (uint32_t)-1);
sym->ID = nbSymbols++;
}
/* /*
* Returns a symbol's ID within the object file * Returns a symbol's ID within the object file
* If the symbol does not have one, one is assigned by registering the symbol * If the symbol does not have one, one is assigned by registering the symbol
*/ */
static uint32_t getSymbolID(struct Symbol *sym) static uint32_t getSymbolID(struct Symbol *sym)
{ {
if (sym->ID == -1) { if (sym->ID == -1 && !sym_IsPC(sym))
sym->ID = nbSymbols++; registerSymbol(sym);
*objectSymbolsTail = sym;
objectSymbolsTail = &sym->next;
}
return sym->ID; return sym->ID;
} }
@@ -303,22 +359,25 @@ static void writerpn(uint8_t *rpnexpr, uint32_t *rpnptr, uint8_t *rpn,
/* /*
* Allocate a new patch structure and link it into the list * Allocate a new patch structure and link it into the list
* WARNING: all patches are assumed to eventually be written, so the file stack node is registered
*/ */
static struct Patch *allocpatch(uint32_t type, struct Expression const *expr, static struct Patch *allocpatch(uint32_t type, struct Expression const *expr, uint32_t ofs)
uint32_t ofs)
{ {
struct Patch *patch = malloc(sizeof(struct Patch)); struct Patch *patch = malloc(sizeof(struct Patch));
uint32_t rpnSize = expr->isKnown ? 5 : expr->nRPNPatchSize; uint32_t rpnSize = expr->isKnown ? 5 : expr->nRPNPatchSize;
struct FileStackNode *node = fstk_GetFileStack();
if (!patch) if (!patch)
fatalerror("No memory for patch: %s\n", strerror(errno)); fatalerror("No memory for patch: %s\n", strerror(errno));
patch->pRPN = malloc(sizeof(*patch->pRPN) * rpnSize);
patch->pRPN = malloc(sizeof(*patch->pRPN) * rpnSize);
if (!patch->pRPN) if (!patch->pRPN)
fatalerror("No memory for patch's RPN expression: %s\n", strerror(errno)); fatalerror("No memory for patch's RPN expression: %s\n", strerror(errno));
patch->type = type; patch->type = type;
patch->tzFilename = fstk_DumpToStr(); patch->src = node;
out_RegisterNode(node);
patch->lineNo = lexer_GetLineNo();
patch->nOffset = ofs; patch->nOffset = ofs;
patch->pcSection = sect_GetSymbolSection(); patch->pcSection = sect_GetSymbolSection();
patch->pcOffset = sect_GetSymbolOffset(); patch->pcOffset = sect_GetSymbolOffset();
@@ -382,13 +441,28 @@ static void writeassert(struct Assertion *assert, FILE *f)
fputstring(assert->message, f); fputstring(assert->message, f);
} }
static void writeFileStackNode(struct FileStackNode const *node, FILE *f)
{
fputlong(node->parent ? node->parent->ID : -1, f);
fputlong(node->lineNo, f);
fputc(node->type, f);
if (node->type != NODE_REPT) {
fputstring(((struct FileStackNamedNode const *)node)->name, f);
} else {
struct FileStackReptNode const *reptNode = (struct FileStackReptNode const *)node;
fputlong(reptNode->reptDepth, f);
/* Iters are stored by decreasing depth, so reverse the order for output */
for (uint32_t i = reptNode->reptDepth; i--; )
fputlong(reptNode->iters[i], f);
}
}
static void registerExportedSymbol(struct Symbol *symbol, void *arg) static void registerExportedSymbol(struct Symbol *symbol, void *arg)
{ {
(void)arg; (void)arg;
if (sym_IsExported(symbol) && symbol->ID == -1) { if (sym_IsExported(symbol) && symbol->ID == -1) {
*objectSymbolsTail = symbol; registerSymbol(symbol);
objectSymbolsTail = &symbol->next;
nbSymbols++;
} }
} }
@@ -411,6 +485,15 @@ void out_WriteObject(void)
fputlong(nbSymbols, f); fputlong(nbSymbols, f);
fputlong(countsections(), f); fputlong(countsections(), f);
fputlong(getNbFileStackNodes(), f);
for (struct FileStackNode const *node = fileStackNodes; node; node = node->next) {
writeFileStackNode(node, f);
if (node->next && node->next->ID != node->ID - 1)
fatalerror("Internal error: fstack node #%" PRIu32 " follows #%" PRIu32
". Please report this to the developers!\n",
node->next->ID, node->ID);
}
for (struct Symbol const *sym = objectSymbols; sym; sym = sym->next) for (struct Symbol const *sym = objectSymbols; sym; sym = sym->next)
writesymbol(sym, f); writesymbol(sym, f);

View File

@@ -258,8 +258,8 @@ static int32_t shift(int32_t shiftee, int32_t amount)
if (amount >= 0) { if (amount >= 0) {
// Left shift // Left shift
if (amount >= 32) { if (amount >= 32) {
warning(WARNING_SHIFT_AMOUNT, "Shifting left by large amount %" PRId32 "\n", warning(WARNING_SHIFT_AMOUNT, "Shifting left by large amount %"
amount); PRId32 "\n", amount);
return 0; return 0;
} else { } else {

View File

@@ -23,6 +23,7 @@
#include "asm/macro.h" #include "asm/macro.h"
#include "asm/main.h" #include "asm/main.h"
#include "asm/mymath.h" #include "asm/mymath.h"
#include "asm/output.h"
#include "asm/section.h" #include "asm/section.h"
#include "asm/symbol.h" #include "asm/symbol.h"
#include "asm/util.h" #include "asm/util.h"
@@ -121,7 +122,7 @@ static char const *Callback__FILE__(void)
buf[j - 1] = '\\'; buf[j - 1] = '\\';
buf[j] = fileName[i]; buf[j] = fileName[i];
} }
/* Write everything after the loop, to ensure everything has been allocated */ /* Write everything after the loop, to ensure the buffer has been allocated */
buf[0] = '"'; buf[0] = '"';
buf[j++] = '"'; buf[j++] = '"';
buf[j] = '\0'; buf[j] = '\0';
@@ -150,15 +151,35 @@ int32_t sym_GetValue(struct Symbol const *sym)
return sym->value; return sym->value;
} }
static void dumpFilename(struct Symbol const *sym)
{
if (!sym->src)
fputs("<builtin>", stderr);
else
fstk_Dump(sym->src, sym->fileLine);
}
/*
* Set a symbol's definition filename and line
*/
static void setSymbolFilename(struct Symbol *sym)
{
sym->src = fstk_GetFileStack();
sym->fileLine = lexer_GetLineNo();
}
/* /*
* Update a symbol's definition filename and line * Update a symbol's definition filename and line
*/ */
static void updateSymbolFilename(struct Symbol *sym) static void updateSymbolFilename(struct Symbol *sym)
{ {
if (snprintf(sym->fileName, _MAX_PATH + 1, "%s", struct FileStackNode *oldSrc = sym->src;
fstk_GetFileName()) > _MAX_PATH)
fatalerror("%s: File name is too long: '%s'\n", __func__, fstk_GetFileName()); setSymbolFilename(sym);
sym->fileLine = fstk_GetLine(); /* If the old node was referenced, ensure the new one is */
if (oldSrc->referenced && oldSrc->ID != -1)
out_RegisterNode(sym->src);
/* TODO: unref the old node, and use `out_ReplaceNode` instead if deleting it */
} }
/* /*
@@ -178,7 +199,7 @@ static struct Symbol *createsymbol(char const *s)
symbol->isBuiltin = false; symbol->isBuiltin = false;
symbol->hasCallback = false; symbol->hasCallback = false;
symbol->section = NULL; symbol->section = NULL;
updateSymbolFilename(symbol); setSymbolFilename(symbol);
symbol->ID = -1; symbol->ID = -1;
symbol->next = NULL; symbol->next = NULL;
@@ -253,6 +274,7 @@ void sym_Purge(char const *symName)
labelScope = NULL; labelScope = NULL;
hash_RemoveElement(symbols, symbol->name); hash_RemoveElement(symbols, symbol->name);
/* TODO: ideally, also unref the file stack nodes */
free(symbol); free(symbol);
} }
} }
@@ -338,9 +360,11 @@ static struct Symbol *createNonrelocSymbol(char const *symbolName)
if (!symbol) if (!symbol)
symbol = createsymbol(symbolName); symbol = createsymbol(symbolName);
else if (sym_IsDefined(symbol)) else if (sym_IsDefined(symbol)) {
error("'%s' already defined at %s(%" PRIu32 ")\n", symbolName, error("'%s' already defined at ", symbolName);
symbol->fileName, symbol->fileLine); dumpFilename(symbol);
putc('\n', stderr);
}
return symbol; return symbol;
} }
@@ -395,15 +419,17 @@ struct Symbol *sym_AddSet(char const *symName, int32_t value)
{ {
struct Symbol *sym = findsymbol(symName, NULL); struct Symbol *sym = findsymbol(symName, NULL);
if (sym == NULL) if (sym == NULL) {
sym = createsymbol(symName); sym = createsymbol(symName);
else if (sym_IsDefined(sym) && sym->type != SYM_SET) } else if (sym_IsDefined(sym) && sym->type != SYM_SET) {
error("'%s' already defined as %s at %s(%" PRIu32 ")\n", error("'%s' already defined as %s at ",
symName, sym->type == SYM_LABEL ? "label" : "constant", symName, sym->type == SYM_LABEL ? "label" : "constant");
sym->fileName, sym->fileLine); dumpFilename(sym);
else putc('\n', stderr);
/* TODO: can the scope be incorrect when talking over refs? */ } else {
/* TODO: can the scope be incorrect when taking over refs? */
updateSymbolFilename(sym); updateSymbolFilename(sym);
}
sym->type = SYM_SET; sym->type = SYM_SET;
sym->value = value; sym->value = value;
@@ -424,9 +450,12 @@ static struct Symbol *addLabel(char const *name)
if (!sym) { if (!sym) {
sym = createsymbol(name); sym = createsymbol(name);
} else if (sym_IsDefined(sym)) { } else if (sym_IsDefined(sym)) {
error("'%s' already defined in %s(%" PRIu32 ")\n", error("'%s' already defined at ", name);
name, sym->fileName, sym->fileLine); dumpFilename(sym);
putc('\n', stderr);
return NULL; return NULL;
} else {
updateSymbolFilename(sym);
} }
/* If the symbol already exists as a ref, just "take over" it */ /* If the symbol already exists as a ref, just "take over" it */
sym->type = SYM_LABEL; sym->type = SYM_LABEL;
@@ -434,7 +463,6 @@ static struct Symbol *addLabel(char const *name)
if (exportall) if (exportall)
sym->isExported = true; sym->isExported = true;
sym->section = sect_GetSymbolSection(); sym->section = sect_GetSymbolSection();
updateSymbolFilename(sym);
if (sym && !sym->section) if (sym && !sym->section)
error("Label \"%s\" created outside of a SECTION\n", name); error("Label \"%s\" created outside of a SECTION\n", name);
@@ -517,7 +545,7 @@ struct Symbol *sym_AddMacro(char const *symName, int32_t defLineNo, char *body,
sym->type = SYM_MACRO; sym->type = SYM_MACRO;
sym->macroSize = size; sym->macroSize = size;
sym->macro = body; sym->macro = body;
updateSymbolFilename(sym); setSymbolFilename(sym); /* TODO: is this really necessary? */
/* /*
* The symbol is created at the line after the `endm`, * The symbol is created at the line after the `endm`,
* override this with the actual definition line * override this with the actual definition line
@@ -577,10 +605,11 @@ static inline struct Symbol *createBuiltinSymbol(char const *name)
sym->isBuiltin = true; sym->isBuiltin = true;
sym->hasCallback = true; sym->hasCallback = true;
strcpy(sym->fileName, "<builtin>"); sym->src = NULL;
sym->fileLine = 0; sym->fileLine = 0;
return sym; return sym;
} }
/* /*
* Initialize the symboltable * Initialize the symboltable
*/ */

View File

@@ -202,7 +202,7 @@ void printDiag(const char *fmt, va_list args, char const *type,
char const *flagfmt, char const *flag) char const *flagfmt, char const *flag)
{ {
fputs(type, stderr); fputs(type, stderr);
fstk_Dump(); fstk_DumpCurrent();
fprintf(stderr, flagfmt, flag); fprintf(stderr, flagfmt, flag);
vfprintf(stderr, fmt, args); vfprintf(stderr, fmt, args);
lexer_DumpStringExpansions(); lexer_DumpStringExpansions();

View File

@@ -81,14 +81,14 @@ static void processLinkerScript(void)
/* Check if this doesn't conflict with what the code says */ /* Check if this doesn't conflict with what the code says */
if (section->isBankFixed && placement->bank != section->bank) if (section->isBankFixed && placement->bank != section->bank)
error("Linker script contradicts \"%s\"'s bank placement", error(NULL, 0, "Linker script contradicts \"%s\"'s bank placement",
section->name); section->name);
if (section->isAddressFixed && placement->org != section->org) if (section->isAddressFixed && placement->org != section->org)
error("Linker script contradicts \"%s\"'s address placement", error(NULL, 0, "Linker script contradicts \"%s\"'s address placement",
section->name); section->name);
if (section->isAlignFixed if (section->isAlignFixed
&& (placement->org & section->alignMask) != 0) && (placement->org & section->alignMask) != 0)
error("Linker script contradicts \"%s\"'s alignment", error(NULL, 0, "Linker script contradicts \"%s\"'s alignment",
section->name); section->name);
section->isAddressFixed = true; section->isAddressFixed = true;

View File

@@ -6,8 +6,10 @@
* SPDX-License-Identifier: MIT * SPDX-License-Identifier: MIT
*/ */
#include <assert.h>
#include <inttypes.h> #include <inttypes.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdarg.h>
#include <stdio.h> #include <stdio.h>
#include <stdint.h> #include <stdint.h>
#include <stdlib.h> #include <stdlib.h>
@@ -39,11 +41,55 @@ bool disablePadding; /* -x */
static uint32_t nbErrors = 0; static uint32_t nbErrors = 0;
void error(char const *fmt, ...) /***** Helper function to dump a file stack to stderr *****/
char const *dumpFileStack(struct FileStackNode const *node)
{
char const *lastName;
if (node->parent) {
lastName = dumpFileStack(node->parent);
/* REPT nodes use their parent's name */
if (node->type != NODE_REPT)
lastName = node->name;
fprintf(stderr, "(%" PRIu32 ") -> %s", node->lineNo, lastName);
if (node->type == NODE_REPT) {
for (uint32_t i = 0; i < node->reptDepth; i++)
fprintf(stderr, "::REPT~%" PRIu32, node->iters[i]);
}
} else {
assert(node->type != NODE_REPT);
lastName = node->name;
fputs(lastName, stderr);
}
return lastName;
}
void warning(struct FileStackNode const *where, uint32_t lineNo, char const *fmt, ...)
{ {
va_list ap; va_list ap;
fprintf(stderr, "error: "); fputs("warning: ", stderr);
if (where) {
dumpFileStack(where);
fprintf(stderr, "(%" PRIu32 "): ", lineNo);
}
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
va_end(ap);
putc('\n', stderr);
}
void error(struct FileStackNode const *where, uint32_t lineNo, char const *fmt, ...)
{
va_list ap;
fputs("error: ", stderr);
if (where) {
dumpFileStack(where);
fprintf(stderr, "(%" PRIu32 "): ", lineNo);
}
va_start(ap, fmt); va_start(ap, fmt);
vfprintf(stderr, fmt, ap); vfprintf(stderr, fmt, ap);
va_end(ap); va_end(ap);
@@ -53,11 +99,15 @@ void error(char const *fmt, ...)
nbErrors++; nbErrors++;
} }
noreturn_ void fatal(char const *fmt, ...) noreturn_ void fatal(struct FileStackNode const *where, uint32_t lineNo, char const *fmt, ...)
{ {
va_list ap; va_list ap;
fprintf(stderr, "fatal: "); fputs("fatal: ", stderr);
if (where) {
dumpFileStack(where);
fprintf(stderr, "(%" PRIu32 "): ", lineNo);
}
va_start(ap, fmt); va_start(ap, fmt);
vfprintf(stderr, fmt, ap); vfprintf(stderr, fmt, ap);
va_end(ap); va_end(ap);
@@ -177,11 +227,11 @@ int main(int argc, char *argv[])
case 'p': case 'p':
value = strtoul(optarg, &endptr, 0); value = strtoul(optarg, &endptr, 0);
if (optarg[0] == '\0' || *endptr != '\0') { if (optarg[0] == '\0' || *endptr != '\0') {
error("Invalid argument for option 'p'"); error(NULL, 0, "Invalid argument for option 'p'");
value = 0xFF; value = 0xFF;
} }
if (value > 0xFF) { if (value > 0xFF) {
error("Argument for 'p' must be a byte (between 0 and 0xFF)"); error(NULL, 0, "Argument for 'p' must be a byte (between 0 and 0xFF)");
value = 0xFF; value = 0xFF;
} }
padValue = value; padValue = value;
@@ -189,7 +239,7 @@ int main(int argc, char *argv[])
case 's': case 's':
/* FIXME: nobody knows what this does, figure it out */ /* FIXME: nobody knows what this does, figure it out */
(void)optarg; (void)optarg;
warnx("Nobody has any idea what `-s` does"); warning(NULL, 0, "Nobody has any idea what `-s` does");
break; break;
case 't': case 't':
is32kMode = true; is32kMode = true;
@@ -234,8 +284,8 @@ int main(int argc, char *argv[])
bankranges[SECTTYPE_VRAM][1] = BANK_MIN_VRAM; bankranges[SECTTYPE_VRAM][1] = BANK_MIN_VRAM;
/* Read all object files first, */ /* Read all object files first, */
while (curArgIndex < argc) for (obj_Setup(argc - curArgIndex); curArgIndex < argc; curArgIndex++)
obj_ReadFile(argv[curArgIndex++]); obj_ReadFile(argv[curArgIndex], argc - curArgIndex - 1);
/* then process them, */ /* then process them, */
obj_DoSanityChecks(); obj_DoSanityChecks();

View File

@@ -31,6 +31,11 @@ static struct SymbolList {
struct SymbolList *next; struct SymbolList *next;
} *symbolLists; } *symbolLists;
unsigned int nbObjFiles;
static struct {
struct FileStackNode *nodes;
uint32_t nbNodes;
} *nodes;
static struct Assertion *assertions; static struct Assertion *assertions;
/***** Helper functions for reading object files *****/ /***** Helper functions for reading object files *****/
@@ -170,12 +175,56 @@ static char *readstr(FILE *file)
/***** Functions to parse object files *****/ /***** Functions to parse object files *****/
/** /**
* Reads a RGB6 symbol from a file. * Reads a file stack node form a file.
* @param file The file to read from
* @param nodes The file's array of nodes
* @param i The ID of the node in the array
* @param fileName The filename to report in errors
*/
static void readFileStackNode(FILE *file, struct FileStackNode fileNodes[], uint32_t i,
char const *fileName)
{
uint32_t parentID;
tryReadlong(parentID, file,
"%s: Cannot read node #%" PRIu32 "'s parent ID: %s", fileName, i);
fileNodes[i].parent = parentID == -1 ? NULL : &fileNodes[parentID];
tryReadlong(fileNodes[i].lineNo, file,
"%s: Cannot read node #%" PRIu32 "'s line number: %s", fileName, i);
tryGetc(fileNodes[i].type, file, "%s: Cannot read node #%" PRIu32 "'s type: %s",
fileName, i);
switch (fileNodes[i].type) {
case NODE_FILE:
case NODE_MACRO:
tryReadstr(fileNodes[i].name, file,
"%s: Cannot read node #%" PRIu32 "'s file name: %s", fileName, i);
break;
case NODE_REPT:
tryReadlong(fileNodes[i].reptDepth, file,
"%s: Cannot read node #%" PRIu32 "'s rept depth: %s", fileName, i);
fileNodes[i].iters = malloc(sizeof(*fileNodes[i].iters) * fileNodes[i].reptDepth);
if (!fileNodes[i].iters)
fatal(NULL, 0, "%s: Failed to alloc node #%" PRIu32 "'s iters: %s",
fileName, i, strerror(errno));
for (uint32_t k = 0; k < fileNodes[i].reptDepth; k++)
tryReadlong(fileNodes[i].iters[k], file,
"%s: Cannot read node #%" PRIu32 "'s iter #%" PRIu32 ": %s",
fileName, i, k);
if (!fileNodes[i].parent)
fatal(NULL, 0, "%s is not a valid object file: root node (#%"
PRIu32 ") may not be REPT", fileName, i);
}
}
/**
* Reads a symbol from a file.
* @param file The file to read from * @param file The file to read from
* @param symbol The struct to fill * @param symbol The struct to fill
* @param fileName The filename to report in errors * @param fileName The filename to report in errors
*/ */
static void readSymbol(FILE *file, struct Symbol *symbol, char const *fileName) static void readSymbol(FILE *file, struct Symbol *symbol,
char const *fileName, struct FileStackNode fileNodes[])
{ {
tryReadstr(symbol->name, file, "%s: Cannot read symbol name: %s", tryReadstr(symbol->name, file, "%s: Cannot read symbol name: %s",
fileName); fileName);
@@ -184,9 +233,12 @@ static void readSymbol(FILE *file, struct Symbol *symbol, char const *fileName)
/* If the symbol is defined in this file, read its definition */ /* If the symbol is defined in this file, read its definition */
if (symbol->type != SYMTYPE_IMPORT) { if (symbol->type != SYMTYPE_IMPORT) {
symbol->objFileName = fileName; symbol->objFileName = fileName;
tryReadstr(symbol->fileName, file, uint32_t nodeID;
"%s: Cannot read \"%s\"'s file name: %s",
tryReadlong(nodeID, file,
"%s: Cannot read \"%s\"'s node ID: %s",
fileName, symbol->name); fileName, symbol->name);
symbol->src = &fileNodes[nodeID];
tryReadlong(symbol->lineNo, file, tryReadlong(symbol->lineNo, file,
"%s: Cannot read \"%s\"'s line number: %s", "%s: Cannot read \"%s\"'s line number: %s",
fileName, symbol->name); fileName, symbol->name);
@@ -202,7 +254,7 @@ static void readSymbol(FILE *file, struct Symbol *symbol, char const *fileName)
} }
/** /**
* Reads a RGB6 patch from a file. * Reads a patch from a file.
* @param file The file to read from * @param file The file to read from
* @param patch The struct to fill * @param patch The struct to fill
* @param fileName The filename to report in errors * @param fileName The filename to report in errors
@@ -210,10 +262,16 @@ static void readSymbol(FILE *file, struct Symbol *symbol, char const *fileName)
*/ */
static void readPatch(FILE *file, struct Patch *patch, char const *fileName, static void readPatch(FILE *file, struct Patch *patch, char const *fileName,
char const *sectName, uint32_t i, char const *sectName, uint32_t i,
struct Section *fileSections[]) struct Section *fileSections[], struct FileStackNode fileNodes[])
{ {
tryReadstr(patch->fileName, file, uint32_t nodeID;
"%s: Unable to read \"%s\"'s patch #%" PRIu32 "'s name: %s",
tryReadlong(nodeID, file,
"%s: Unable to read \"%s\"'s patch #%" PRIu32 "'s node ID: %s",
fileName, sectName, i);
patch->src = &fileNodes[nodeID];
tryReadlong(patch->lineNo, file,
"%s: Unable to read \"%s\"'s patch #%" PRIu32 "'s line number: %s",
fileName, sectName, i); fileName, sectName, i);
tryReadlong(patch->offset, file, tryReadlong(patch->offset, file,
"%s: Unable to read \"%s\"'s patch #%" PRIu32 "'s offset: %s", "%s: Unable to read \"%s\"'s patch #%" PRIu32 "'s offset: %s",
@@ -221,8 +279,7 @@ static void readPatch(FILE *file, struct Patch *patch, char const *fileName,
tryReadlong(patch->pcSectionID, file, tryReadlong(patch->pcSectionID, file,
"%s: Unable to read \"%s\"'s patch #%" PRIu32 "'s PC offset: %s", "%s: Unable to read \"%s\"'s patch #%" PRIu32 "'s PC offset: %s",
fileName, sectName, i); fileName, sectName, i);
patch->pcSection = patch->pcSectionID == -1 patch->pcSection = patch->pcSectionID == -1 ? NULL
? NULL
: fileSections[patch->pcSectionID]; : fileSections[patch->pcSectionID];
tryReadlong(patch->pcOffset, file, tryReadlong(patch->pcOffset, file,
"%s: Unable to read \"%s\"'s patch #%" PRIu32 "'s PC offset: %s", "%s: Unable to read \"%s\"'s patch #%" PRIu32 "'s PC offset: %s",
@@ -234,16 +291,17 @@ static void readPatch(FILE *file, struct Patch *patch, char const *fileName,
"%s: Unable to read \"%s\"'s patch #%" PRIu32 "'s RPN size: %s", "%s: Unable to read \"%s\"'s patch #%" PRIu32 "'s RPN size: %s",
fileName, sectName, i); fileName, sectName, i);
uint8_t *rpnExpression = patch->rpnExpression = malloc(sizeof(*patch->rpnExpression) * patch->rpnSize);
malloc(sizeof(*rpnExpression) * patch->rpnSize); if (!patch->rpnExpression)
size_t nbElementsRead = fread(rpnExpression, sizeof(*rpnExpression), err(1, "%s: Failed to alloc \"%s\"'s patch #%" PRIu32 "'s RPN expression",
fileName, sectName, i);
size_t nbElementsRead = fread(patch->rpnExpression, sizeof(*patch->rpnExpression),
patch->rpnSize, file); patch->rpnSize, file);
if (nbElementsRead != patch->rpnSize) if (nbElementsRead != patch->rpnSize)
errx(1, "%s: Cannot read \"%s\"'s patch #%" PRIu32 "'s RPN expression: %s", errx(1, "%s: Cannot read \"%s\"'s patch #%" PRIu32 "'s RPN expression: %s",
fileName, sectName, i, fileName, sectName, i,
feof(file) ? "Unexpected end of file" : strerror(errno)); feof(file) ? "Unexpected end of file" : strerror(errno));
patch->rpnExpression = rpnExpression;
} }
/** /**
@@ -252,8 +310,8 @@ static void readPatch(FILE *file, struct Patch *patch, char const *fileName,
* @param section The struct to fill * @param section The struct to fill
* @param fileName The filename to report in errors * @param fileName The filename to report in errors
*/ */
static void readSection(FILE *file, struct Section *section, static void readSection(FILE *file, struct Section *section, char const *fileName,
char const *fileName, struct Section *fileSections[]) struct Section *fileSections[], struct FileStackNode fileNodes[])
{ {
int32_t tmp; int32_t tmp;
uint8_t byte; uint8_t byte;
@@ -280,7 +338,7 @@ static void readSection(FILE *file, struct Section *section,
fileName, section->name); fileName, section->name);
section->isAddressFixed = tmp >= 0; section->isAddressFixed = tmp >= 0;
if (tmp > UINT16_MAX) { if (tmp > UINT16_MAX) {
error("\"%s\"'s org is too large (%" PRId32 ")", error(NULL, 0, "\"%s\"'s org is too large (%" PRId32 ")",
section->name, tmp); section->name, tmp);
tmp = UINT16_MAX; tmp = UINT16_MAX;
} }
@@ -296,7 +354,7 @@ static void readSection(FILE *file, struct Section *section,
tryReadlong(tmp, file, "%s: Cannot read \"%s\"'s alignment offset: %s", tryReadlong(tmp, file, "%s: Cannot read \"%s\"'s alignment offset: %s",
fileName, section->name); fileName, section->name);
if (tmp > UINT16_MAX) { if (tmp > UINT16_MAX) {
error("\"%s\"'s alignment offset is too large (%" PRId32 ")", error(NULL, 0, "\"%s\"'s alignment offset is too large (%" PRId32 ")",
section->name, tmp); section->name, tmp);
tmp = UINT16_MAX; tmp = UINT16_MAX;
} }
@@ -332,7 +390,7 @@ static void readSection(FILE *file, struct Section *section,
section->name); section->name);
for (uint32_t i = 0; i < section->nbPatches; i++) { for (uint32_t i = 0; i < section->nbPatches; i++) {
readPatch(file, &patches[i], fileName, section->name, readPatch(file, &patches[i], fileName, section->name,
i, fileSections); i, fileSections, fileNodes);
} }
section->patches = patches; section->patches = patches;
} }
@@ -375,13 +433,13 @@ static void linkSymToSect(struct Symbol const *symbol, struct Section *section)
*/ */
static void readAssertion(FILE *file, struct Assertion *assert, static void readAssertion(FILE *file, struct Assertion *assert,
char const *fileName, uint32_t i, char const *fileName, uint32_t i,
struct Section *fileSections[]) struct Section *fileSections[], struct FileStackNode fileNodes[])
{ {
char assertName[sizeof("Assertion #" EXPAND_AND_STR(UINT32_MAX))]; char assertName[sizeof("Assertion #" EXPAND_AND_STR(UINT32_MAX))];
snprintf(assertName, sizeof(assertName), "Assertion #%" PRIu32, i); snprintf(assertName, sizeof(assertName), "Assertion #%" PRIu32, i);
readPatch(file, &assert->patch, fileName, assertName, 0, fileSections); readPatch(file, &assert->patch, fileName, assertName, 0, fileSections, fileNodes);
tryReadstr(assert->message, file, "%s: Cannot read assertion's message: %s", tryReadstr(assert->message, file, "%s: Cannot read assertion's message: %s",
fileName); fileName);
} }
@@ -394,11 +452,7 @@ static inline struct Section *getMainSection(struct Section *section)
return section; return section;
} }
/** void obj_ReadFile(char const *fileName, unsigned int fileID)
* Reads an object file of any supported format
* @param fileName The filename to report for errors
*/
void obj_ReadFile(char const *fileName)
{ {
FILE *file = strcmp("-", fileName) ? fopen(fileName, "rb") : stdin; FILE *file = strcmp("-", fileName) ? fopen(fileName, "rb") : stdin;
@@ -438,6 +492,14 @@ void obj_ReadFile(char const *fileName)
nbSectionsToAssign += nbSections; nbSectionsToAssign += nbSections;
tryReadlong(nodes[fileID].nbNodes, file, "%s: Cannot read number of nodes: %s", fileName);
nodes[fileID].nodes = calloc(nodes[fileID].nbNodes, sizeof(nodes[fileID].nodes[0]));
if (!nodes[fileID].nodes)
err(1, "Failed to get memory for %s's nodes", fileName);
verbosePrint("Reading %u nodes...\n", nodes[fileID].nbNodes);
for (uint32_t i = 0; i < nodes[fileID].nbNodes; i++)
readFileStackNode(file, nodes[fileID].nodes, i, fileName);
/* This file's symbols, kept to link sections to them */ /* This file's symbols, kept to link sections to them */
struct Symbol **fileSymbols = struct Symbol **fileSymbols =
malloc(sizeof(*fileSymbols) * nbSymbols + 1); malloc(sizeof(*fileSymbols) * nbSymbols + 1);
@@ -464,7 +526,7 @@ void obj_ReadFile(char const *fileName)
if (!symbol) if (!symbol)
err(1, "%s: Couldn't create new symbol", fileName); err(1, "%s: Couldn't create new symbol", fileName);
readSymbol(file, symbol, fileName); readSymbol(file, symbol, fileName, nodes[fileID].nodes);
fileSymbols[i] = symbol; fileSymbols[i] = symbol;
if (symbol->type == SYMTYPE_EXPORT) if (symbol->type == SYMTYPE_EXPORT)
@@ -485,7 +547,7 @@ void obj_ReadFile(char const *fileName)
err(1, "%s: Couldn't create new section", fileName); err(1, "%s: Couldn't create new section", fileName);
fileSections[i]->nextu = NULL; fileSections[i]->nextu = NULL;
readSection(file, fileSections[i], fileName, fileSections); readSection(file, fileSections[i], fileName, fileSections, nodes[fileID].nodes);
fileSections[i]->fileSymbols = fileSymbols; fileSections[i]->fileSymbols = fileSymbols;
if (nbSymPerSect[i]) { if (nbSymPerSect[i]) {
fileSections[i]->symbols = malloc(nbSymPerSect[i] fileSections[i]->symbols = malloc(nbSymPerSect[i]
@@ -535,7 +597,7 @@ void obj_ReadFile(char const *fileName)
if (!assertion) if (!assertion)
err(1, "%s: Couldn't create new assertion", fileName); err(1, "%s: Couldn't create new assertion", fileName);
readAssertion(file, assertion, fileName, i, fileSections); readAssertion(file, assertion, fileName, i, fileSections, nodes[fileID].nodes);
assertion->fileSymbols = fileSymbols; assertion->fileSymbols = fileSymbols;
assertion->next = assertions; assertion->next = assertions;
assertions = assertion; assertions = assertion;
@@ -555,6 +617,15 @@ void obj_CheckAssertions(void)
patch_CheckAssertions(assertions); patch_CheckAssertions(assertions);
} }
void obj_Setup(unsigned int nbFiles)
{
nbObjFiles = nbFiles;
if (nbFiles > SIZE_MAX / sizeof(*nodes))
fatal(NULL, 0, "Impossible to link more than %zu files!", SIZE_MAX / sizeof(*nodes));
nodes = malloc(sizeof(*nodes) * nbFiles);
}
static void freeSection(struct Section *section, void *arg) static void freeSection(struct Section *section, void *arg)
{ {
(void)arg; (void)arg;
@@ -562,12 +633,8 @@ static void freeSection(struct Section *section, void *arg)
free(section->name); free(section->name);
if (sect_HasData(section->type)) { if (sect_HasData(section->type)) {
free(section->data); free(section->data);
for (int32_t i = 0; i < section->nbPatches; i++) { for (int32_t i = 0; i < section->nbPatches; i++)
struct Patch *patch = &section->patches[i]; free(section->patches[i].rpnExpression);
free(patch->fileName);
free(patch->rpnExpression);
}
free(section->patches); free(section->patches);
} }
free(section->symbols); free(section->symbols);
@@ -577,13 +644,20 @@ static void freeSection(struct Section *section, void *arg)
static void freeSymbol(struct Symbol *symbol) static void freeSymbol(struct Symbol *symbol)
{ {
free(symbol->name); free(symbol->name);
if (symbol->type != SYMTYPE_IMPORT)
free(symbol->fileName);
free(symbol); free(symbol);
} }
void obj_Cleanup(void) void obj_Cleanup(void)
{ {
for (unsigned int i = 0; i < nbObjFiles; i++) {
for (uint32_t j = 0; j < nodes[i].nbNodes; j++) {
if (nodes[i].nodes[j].type == NODE_REPT)
free(nodes[i].nodes[j].iters);
}
free(nodes[i].nodes);
}
free(nodes);
sym_CleanupSymbols(); sym_CleanupSymbols();
sect_ForEach(freeSection, NULL); sect_ForEach(freeSection, NULL);

View File

@@ -6,11 +6,13 @@
* SPDX-License-Identifier: MIT * SPDX-License-Identifier: MIT
*/ */
#include <assert.h>
#include <inttypes.h> #include <inttypes.h>
#include <limits.h> #include <limits.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include "link/object.h"
#include "link/patch.h" #include "link/patch.h"
#include "link/section.h" #include "link/section.h"
#include "link/symbol.h" #include "link/symbol.h"
@@ -104,10 +106,10 @@ static void pushRPN(int32_t value)
stack.size++; stack.size++;
} }
static int32_t popRPN(char const *fileName) static int32_t popRPN(struct FileStackNode const *node, uint32_t lineNo)
{ {
if (stack.size == 0) if (stack.size == 0)
errx(1, "%s: Internal error, RPN stack empty", fileName); fatal(node, lineNo, "Internal error, RPN stack empty");
stack.size--; stack.size--;
return stack.buf[stack.size]; return stack.buf[stack.size];
@@ -121,16 +123,18 @@ static inline void freeRPNStack(void)
/* RPN operators */ /* RPN operators */
static uint32_t getRPNByte(uint8_t const **expression, int32_t *size, static uint32_t getRPNByte(uint8_t const **expression, int32_t *size,
char const *fileName) struct FileStackNode const *node, uint32_t lineNo)
{ {
if (!(*size)--) if (!(*size)--)
errx(1, "%s: RPN expression overread", fileName); fatal(node, lineNo, "Internal error, RPN expression overread");
return *(*expression)++; return *(*expression)++;
} }
static struct Symbol const *getSymbol(struct Symbol const * const *symbolList, static struct Symbol const *getSymbol(struct Symbol const * const *symbolList,
uint32_t index) uint32_t index)
{ {
assert(index != -1); /* PC needs to be handled specially, not here */
struct Symbol const *symbol = symbolList[index]; struct Symbol const *symbol = symbolList[index];
/* If the symbol is defined elsewhere... */ /* If the symbol is defined elsewhere... */
@@ -150,7 +154,7 @@ static int32_t computeRPNExpr(struct Patch const *patch,
struct Symbol const * const *fileSymbols) struct Symbol const * const *fileSymbols)
{ {
/* Small shortcut to avoid a lot of repetition */ /* Small shortcut to avoid a lot of repetition */
#define popRPN() popRPN(patch->fileName) #define popRPN() popRPN(patch->src, patch->lineNo)
uint8_t const *expression = patch->rpnExpression; uint8_t const *expression = patch->rpnExpression;
int32_t size = patch->rpnSize; int32_t size = patch->rpnSize;
@@ -159,7 +163,7 @@ static int32_t computeRPNExpr(struct Patch const *patch,
while (size > 0) { while (size > 0) {
enum RPNCommand command = getRPNByte(&expression, &size, enum RPNCommand command = getRPNByte(&expression, &size,
patch->fileName); patch->src, patch->lineNo);
int32_t value; int32_t value;
/* /*
@@ -187,7 +191,7 @@ static int32_t computeRPNExpr(struct Patch const *patch,
case RPN_DIV: case RPN_DIV:
value = popRPN(); value = popRPN();
if (value == 0) { if (value == 0) {
error("%s: Division by 0", patch->fileName); error(patch->src, patch->lineNo, "Division by 0");
popRPN(); popRPN();
value = INT32_MAX; value = INT32_MAX;
} else { } else {
@@ -197,7 +201,7 @@ static int32_t computeRPNExpr(struct Patch const *patch,
case RPN_MOD: case RPN_MOD:
value = popRPN(); value = popRPN();
if (value == 0) { if (value == 0) {
error("%s: Modulo by 0", patch->fileName); error(patch->src, patch->lineNo, "Modulo by 0");
popRPN(); popRPN();
value = 0; value = 0;
} else { } else {
@@ -269,17 +273,17 @@ static int32_t computeRPNExpr(struct Patch const *patch,
value = 0; value = 0;
for (uint8_t shift = 0; shift < 32; shift += 8) for (uint8_t shift = 0; shift < 32; shift += 8)
value |= getRPNByte(&expression, &size, value |= getRPNByte(&expression, &size,
patch->fileName) << shift; patch->src, patch->lineNo) << shift;
symbol = getSymbol(fileSymbols, value); symbol = getSymbol(fileSymbols, value);
if (!symbol) { if (!symbol) {
error("%s: Requested BANK() of symbol \"%s\", which was not found", error(patch->src, patch->lineNo,
patch->fileName, "Requested BANK() of symbol \"%s\", which was not found",
fileSymbols[value]->name); fileSymbols[value]->name);
value = 1; value = 1;
} else if (!symbol->section) { } else if (!symbol->section) {
error("%s: Requested BANK() of non-label symbol \"%s\"", error(patch->src, patch->lineNo,
patch->fileName, "Requested BANK() of non-label symbol \"%s\"",
fileSymbols[value]->name); fileSymbols[value]->name);
value = 1; value = 1;
} else { } else {
@@ -289,14 +293,15 @@ static int32_t computeRPNExpr(struct Patch const *patch,
case RPN_BANK_SECT: case RPN_BANK_SECT:
name = (char const *)expression; name = (char const *)expression;
while (getRPNByte(&expression, &size, patch->fileName)) while (getRPNByte(&expression, &size, patch->src, patch->lineNo))
; ;
sect = sect_GetSection(name); sect = sect_GetSection(name);
if (!sect) { if (!sect) {
error("%s: Requested BANK() of section \"%s\", which was not found", error(patch->src, patch->lineNo,
patch->fileName, name); "Requested BANK() of section \"%s\", which was not found",
name);
value = 1; value = 1;
} else { } else {
value = sect->bank; value = sect->bank;
@@ -305,7 +310,8 @@ static int32_t computeRPNExpr(struct Patch const *patch,
case RPN_BANK_SELF: case RPN_BANK_SELF:
if (!patch->pcSection) { if (!patch->pcSection) {
error("%s: PC has no bank outside a section"); error(patch->src, patch->lineNo,
"PC has no bank outside a section");
value = 1; value = 1;
} else { } else {
value = patch->pcSection->bank; value = patch->pcSection->bank;
@@ -317,8 +323,8 @@ static int32_t computeRPNExpr(struct Patch const *patch,
if (value < 0 if (value < 0
|| (value > 0xFF && value < 0xFF00) || (value > 0xFF && value < 0xFF00)
|| value > 0xFFFF) || value > 0xFFFF)
error("%s: Value %" PRId32 " is not in HRAM range", error(patch->src, patch->lineNo,
patch->fileName, value); "Value %" PRId32 " is not in HRAM range", value);
value &= 0xFF; value &= 0xFF;
break; break;
@@ -328,8 +334,8 @@ static int32_t computeRPNExpr(struct Patch const *patch,
* They can be easily checked with a bitmask * They can be easily checked with a bitmask
*/ */
if (value & ~0x38) if (value & ~0x38)
error("%s: Value %" PRId32 " is not a RST vector", error(patch->src, patch->lineNo,
patch->fileName, value); "Value %" PRId32 " is not a RST vector", value);
value |= 0xC7; value |= 0xC7;
break; break;
@@ -337,32 +343,35 @@ static int32_t computeRPNExpr(struct Patch const *patch,
value = 0; value = 0;
for (uint8_t shift = 0; shift < 32; shift += 8) for (uint8_t shift = 0; shift < 32; shift += 8)
value |= getRPNByte(&expression, &size, value |= getRPNByte(&expression, &size,
patch->fileName) << shift; patch->src, patch->lineNo) << shift;
break; break;
case RPN_SYM: case RPN_SYM:
value = 0; value = 0;
for (uint8_t shift = 0; shift < 32; shift += 8) for (uint8_t shift = 0; shift < 32; shift += 8)
value |= getRPNByte(&expression, &size, value |= getRPNByte(&expression, &size,
patch->fileName) << shift; patch->src, patch->lineNo) << shift;
if (value == -1) { /* PC */
if (!patch->pcSection) {
error(patch->src, patch->lineNo,
"PC has no value outside a section");
value = 0;
} else {
value = patch->pcOffset + patch->pcSection->org;
}
} else {
symbol = getSymbol(fileSymbols, value); symbol = getSymbol(fileSymbols, value);
if (!symbol) { if (!symbol) {
error("%s: Unknown symbol \"%s\"", error(patch->src, patch->lineNo,
patch->fileName, "Unknown symbol \"%s\"", fileSymbols[value]->name);
fileSymbols[value]->name); } else {
} else if (strcmp(symbol->name, "@")) {
value = symbol->value; value = symbol->value;
/* Symbols attached to sections have offsets */ /* Symbols attached to sections have offsets */
if (symbol->section) if (symbol->section)
value += symbol->section->org; value += symbol->section->org;
} else if (!patch->pcSection) { }
error("%s: PC has no value outside a section",
patch->fileName);
value = 0;
} else {
value = patch->pcOffset + patch->pcSection->org;
} }
break; break;
} }
@@ -371,8 +380,8 @@ static int32_t computeRPNExpr(struct Patch const *patch,
} }
if (stack.size > 1) if (stack.size > 1)
error("%s: RPN stack has %zu entries on exit, not 1", error(patch->src, patch->lineNo,
patch->fileName, stack.size); "RPN stack has %zu entries on exit, not 1", stack.size);
return popRPN(); return popRPN();
@@ -390,18 +399,18 @@ void patch_CheckAssertions(struct Assertion *assert)
assert->fileSymbols)) { assert->fileSymbols)) {
switch ((enum AssertionType)assert->patch.type) { switch ((enum AssertionType)assert->patch.type) {
case ASSERT_FATAL: case ASSERT_FATAL:
fatal("%s: %s", assert->patch.fileName, fatal(assert->patch.src, assert->patch.lineNo, "%s",
assert->message[0] ? assert->message assert->message[0] ? assert->message
: "assert failure"); : "assert failure");
/* Not reached */ /* Not reached */
break; /* Here so checkpatch doesn't complain */ break; /* Here so checkpatch doesn't complain */
case ASSERT_ERROR: case ASSERT_ERROR:
error("%s: %s", assert->patch.fileName, error(assert->patch.src, assert->patch.lineNo, "%s",
assert->message[0] ? assert->message assert->message[0] ? assert->message
: "assert failure"); : "assert failure");
break; break;
case ASSERT_WARN: case ASSERT_WARN:
warnx("%s: %s", assert->patch.fileName, warning(assert->patch.src, assert->patch.lineNo, "%s",
assert->message[0] ? assert->message assert->message[0] ? assert->message
: "assert failure"); : "assert failure");
break; break;
@@ -442,8 +451,9 @@ static void applyFilePatches(struct Section *section, struct Section *dataSectio
int16_t jumpOffset = value - address; int16_t jumpOffset = value - address;
if (jumpOffset < -128 || jumpOffset > 127) if (jumpOffset < -128 || jumpOffset > 127)
error("%s: jr target out of reach (expected -129 < %" PRId16 " < 128)", error(patch->src, patch->lineNo,
patch->fileName, jumpOffset); "jr target out of reach (expected -129 < %" PRId16 " < 128)",
jumpOffset);
dataSection->data[offset] = jumpOffset & 0xFF; dataSection->data[offset] = jumpOffset & 0xFF;
} else { } else {
/* Patch a certain number of bytes */ /* Patch a certain number of bytes */
@@ -459,9 +469,9 @@ static void applyFilePatches(struct Section *section, struct Section *dataSectio
if (value < types[patch->type].min if (value < types[patch->type].min
|| value > types[patch->type].max) || value > types[patch->type].max)
error("%s: Value %#" PRIx32 "%s is not %u-bit", error(patch->src, patch->lineNo,
patch->fileName, value, "Value %#" PRIx32 "%s is not %u-bit",
value < 0 ? " (maybe negative?)" : "", value, value < 0 ? " (maybe negative?)" : "",
types[patch->type].size * 8U); types[patch->type].size * 8U);
for (uint8_t i = 0; i < types[patch->type].size; i++) { for (uint8_t i = 0; i < types[patch->type].size; i++) {
dataSection->data[offset + i] = value & 0xFF; dataSection->data[offset + i] = value & 0xFF;

View File

@@ -8,9 +8,12 @@
#include <inttypes.h> #include <inttypes.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdlib.h>
#include "link/object.h"
#include "link/symbol.h" #include "link/symbol.h"
#include "link/main.h" #include "link/main.h"
#include "extern/err.h" #include "extern/err.h"
#include "hashmap.h" #include "hashmap.h"
@@ -40,11 +43,15 @@ void sym_AddSymbol(struct Symbol *symbol)
/* Check if the symbol already exists */ /* Check if the symbol already exists */
struct Symbol *other = hash_GetElement(symbols, symbol->name); struct Symbol *other = hash_GetElement(symbols, symbol->name);
if (other) if (other) {
errx(1, "\"%s\" both in %s from %s(%" PRId32 ") and in %s from %s(%" PRId32 ")", fprintf(stderr, "error: \"%s\" both in %s from ", symbol->name, symbol->objFileName);
symbol->name, dumpFileStack(symbol->src);
symbol->objFileName, symbol->fileName, symbol->lineNo, fprintf(stderr, "(%" PRIu32 ") and in %s from ",
other->objFileName, other->fileName, other->lineNo); symbol->lineNo, other->objFileName);
dumpFileStack(other->src);
fprintf(stderr, "(%" PRIu32 ")\n", other->lineNo);
exit(1);
}
/* If not, add it */ /* If not, add it */
bool collided = hash_AddElement(symbols, symbol->name, symbol); bool collided = hash_AddElement(symbols, symbol->name, symbol);

View File

@@ -34,9 +34,42 @@ is a 0terminated string of
; Header ; Header
BYTE ID[4] ; "RGB9" BYTE ID[4] ; "RGB9"
LONG RevisionNumber ; The format's revision number this file uses LONG RevisionNumber ; The format's revision number this file uses.
LONG NumberOfSymbols ; The number of symbols used in this file LONG NumberOfSymbols ; The number of symbols used in this file.
LONG NumberOfSections ; The number of sections used in this file LONG NumberOfSections ; The number of sections used in this file.
; File info
LONG NumberOfNodes ; The number of nodes contained in this file.
REPT NumberOfNodes ; IMPORTANT NOTE: the nodes are actually written in
; **reverse** order, meaningthe node with ID 0 is
; the last one in the file!
LONG ParentID ; ID of the parent node, -1 means this is the root.
LONG ParentLineNo ; Line at which the parent context was exited.
; Meaningless on the root node.
BYTE Type ; 0 = REPT node
; 1 = File node
; 2 = Macro node
IF Type != 0 ; If the node is not a REPT...
STRING Name ; The node's name: either a file name, or macro name
; prefixed by its definition file name.
ELSE ; If the node is a REPT, it also contains the iter
; counts of all the parent REPTs.
LONG Depth ; Size of the array below.
LONG Iter[Depth] ; The number of REPT iterations by increasing depth.
ENDC
ENDR
; Symbols ; Symbols
@@ -51,7 +84,7 @@ REPT NumberOfSymbols ; Number of symbols defined in this object file.
IF (Type & 0x7F) != 1 ; If symbol is defined in this object file. IF (Type & 0x7F) != 1 ; If symbol is defined in this object file.
STRING FileName ; File where the symbol is defined. LONG SourceFile ; File where the symbol is defined.
LONG LineNum ; Line number in the file where the symbol is defined. LONG LineNum ; Line number in the file where the symbol is defined.
@@ -107,8 +140,10 @@ REPT NumberOfSections
REPT NumberOfPatches REPT NumberOfPatches
STRING SourceFile ; Name of the source file (for printing error LONG SourceFile ; ID of the source file node (for printing
; messages). ; error messages).
LONG LineNo ; Line at which the patch was created.
LONG Offset ; Offset into the section where patch should LONG Offset ; Offset into the section where patch should
; be applied (in bytes). ; be applied (in bytes).
@@ -145,7 +180,9 @@ LONG NumberOfAssertions
REPT NumberOfAssertions REPT NumberOfAssertions
STRING SourceFile ; Name of the source file (for printing the failure). LONG SourceFile ; ID of the source file node (for printing the failure).
LONG LineNo ; Line at which the assertion was created.
LONG Offset ; Offset into the section where the assertion is located. LONG Offset ; Offset into the section where the assertion is located.
@@ -209,7 +246,7 @@ with some bytes being special prefixes for integers and symbols.
.It Li $50 Ta Li BANK(symbol) , .It Li $50 Ta Li BANK(symbol) ,
a a
.Ar LONG .Ar LONG
Symbol ID follows. Symbol ID follows, where -1 means PC
.It Li $51 Ta Li BANK(section_name) , .It Li $51 Ta Li BANK(section_name) ,
a null-terminated string follows. a null-terminated string follows.
.It Li $52 Ta Li Current BANK() .It Li $52 Ta Li Current BANK()

View File

@@ -1,3 +1,3 @@
ERROR: label-redefinition.asm(7): ERROR: label-redefinition.asm(7):
'Sym' already defined in label-redefinition.asm::m(4) 'Sym' already defined at label-redefinition.asm(6) -> label-redefinition.asm::m(4)
error: Assembly aborted (1 errors)! error: Assembly aborted (1 errors)!