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)
else()
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
-Wformat-truncation=1 -Wformat-y2k -Wswitch-enum -Wunused
-Wuninitialized -Wunknown-pragmas -Wstrict-overflow=5

View File

@@ -186,7 +186,7 @@ checkpatch:
# compilation and make the continous integration infrastructure return failure.
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 \
-Wformat-truncation=1 -Wformat-y2k -Wswitch-enum -Wunused \
-Wuninitialized -Wunknown-pragmas -Wstrict-overflow=5 \

View File

@@ -21,26 +21,44 @@
#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 LexerState *lexerState;
struct Symbol const *pMacro;
struct sContext *next;
char tzFileName[_MAX_PATH + 1];
struct MacroArgs *macroArgs;
uint32_t uniqueID;
int32_t nLine;
uint32_t nStatus;
char const *pREPTBlock;
uint32_t nREPTBlockCount;
uint32_t nREPTBlockSize;
int32_t nREPTBodyFirstLine;
int32_t nREPTBodyLastLine;
struct FileStackNode *next; /* Next node in the output linked list */
bool referenced; /* If referenced, don't free! */
uint32_t ID; /* Set only if referenced: ID within the object file, -1 if not output yet */
enum {
NODE_REPT,
NODE_FILE,
NODE_MACRO,
} type;
};
struct FileStackReptNode { /* NODE_REPT */
struct FileStackNode node;
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;
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);
/**
* @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);
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_Dump(void);
char *fstk_DumpToStr(void);
char const *fstk_GetFileName(void);
uint32_t fstk_GetLine(void);
void fstk_Init(char *mainPath, size_t maxRecursionDepth);
void fstk_Init(char const *mainPath, size_t maxRecursionDepth);
#endif /* RGBDS_ASM_FSTACK_H */

View File

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

View File

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

View File

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

View File

@@ -29,15 +29,45 @@ extern bool beVerbose;
extern bool isWRA0Mode;
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 */
#define verbosePrint(...) do { \
if (beVerbose) \
fprintf(stderr, __VA_ARGS__); \
} 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.

View File

@@ -14,8 +14,9 @@
/**
* 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 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
@@ -27,6 +28,12 @@ void obj_DoSanityChecks(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.
*/

View File

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

View File

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

View File

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

View File

@@ -29,27 +29,83 @@
struct Context {
struct Context *parent;
struct Context *child;
struct FileStackNode *fileInfo;
struct LexerState *lexerState;
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 */
uint32_t nbReptIters; /* If zero, this isn't a REPT block */
size_t reptDepth;
uint32_t reptIters[];
uint32_t nbReptIters;
};
static struct Context *contextStack;
static struct Context *topLevelContext;
static size_t contextDepth = 0;
#define DEFAULT_MAX_DEPTH 64
size_t nMaxRecursionDepth;
static unsigned int nbIncPaths = 0;
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)
{
if (path[0] == '\0')
@@ -141,12 +197,28 @@ bool fstk_FindFile(char const *path, char **fullPath, size_t *size)
bool yywrap(void)
{
if (contextStack->nbReptIters) { /* The context is a REPT block, which may loop */
contextStack->reptIters[contextStack->reptDepth - 1]++;
if (contextStack->fileInfo->type == NODE_REPT) { /* The context is a REPT block, which may loop */
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 (contextStack->reptIters[contextStack->reptDepth - 1]
<= contextStack->nbReptIters) {
lexer_RestartRept(contextStack->parent->lineNo);
if (fileInfo->iters[0] <= contextStack->nbReptIters) {
lexer_RestartRept(contextStack->fileInfo->lineNo);
contextStack->uniqueID = macro_UseNewUniqueID();
return false;
}
@@ -155,44 +227,52 @@ bool yywrap(void)
}
dbgPrint("Popping context\n");
/* Free an `INCLUDE`'s path */
if (contextStack->fileNameBuf)
free(contextStack->fileNameBuf);
struct Context *context = contextStack;
contextStack = contextStack->parent;
contextDepth--;
lexer_DeleteState(contextStack->child->lexerState);
lexer_DeleteState(context->lexerState);
/* 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);
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(contextStack->child);
free(context);
contextStack->child = NULL;
lexer_SetState(contextStack->lexerState);
macro_SetUniqueID(contextStack->uniqueID);
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)
fatalerror("Recursion limit (%zu) exceeded\n", nMaxRecursionDepth);
contextStack->child = malloc(sizeof(*contextStack->child)
+ reptDepth * sizeof(contextStack->reptIters[0]));
if (!contextStack->child)
struct Context *context = malloc(sizeof(*context));
if (!context)
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)
@@ -212,21 +292,27 @@ void fstk_RunInclude(char const *path)
}
dbgPrint("Full path: \"%s\"\n", fullPath);
newContext(0);
contextStack->lexerState = lexer_OpenFile(fullPath);
struct FileStackNamedNode *fileInfo = malloc(sizeof(*fileInfo) + size);
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)
fatalerror("Failed to set up lexer for file include\n");
lexer_SetStateAtEOL(contextStack->lexerState);
/* We're back at top-level, so most things are reset */
contextStack->uniqueID = 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);
@@ -242,7 +328,53 @@ void fstk_RunMacro(char *macroName, struct MacroArgs *args)
}
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 */
contextStack->lexerState = lexer_OpenFileView(macro->macro, macro->macroSize,
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");
lexer_SetStateAtEOL(contextStack->lexerState);
contextStack->uniqueID = macro_UseNewUniqueID();
contextStack->fileName = macro->fileName;
contextStack->fileNameBuf = NULL;
contextStack->macro = macro;
contextStack->nbReptIters = 0;
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);
if (count == 0)
return;
uint32_t reptDepth = contextStack->reptDepth;
newContext(reptDepth + 1);
contextStack->lexerState = lexer_OpenFileView(body, size, nReptLineNo);
uint32_t reptDepth = contextStack->fileInfo->type == NODE_REPT
? ((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)
fatalerror("Failed to set up lexer for macro invocation\n");
fatalerror("Failed to set up lexer for rept block\n");
lexer_SetStateAtEOL(contextStack->lexerState);
contextStack->uniqueID = macro_UseNewUniqueID();
contextStack->fileName = contextStack->parent->fileName;
contextStack->fileNameBuf = NULL;
contextStack->macro = contextStack->parent->macro; /* Inherit */
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);
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);
}
struct LexerState *state = lexer_OpenFile(mainPath);
static void dumpToStream(FILE *stream)
{
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)
if (!state)
fatalerror("Failed to open main file!\n");
lexer_SetState(topLevelContext->lexerState);
topLevelContext->uniqueID = 0;
lexer_SetState(state);
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);
topLevelContext->fileName = lexer_GetFileName();
topLevelContext->fileNameBuf = NULL;
topLevelContext->macro = NULL;
topLevelContext->nbReptIters = 0;
topLevelContext->reptDepth = 0;
context->nbReptIters = 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])) {
error("Recursion depth may not be higher than %zu, defaulting to 64\n",
(SIZE_MAX - sizeof(*contextStack)) / sizeof(contextStack->reptIters[0]));
nMaxRecursionDepth = 64;
/*
* Check that max recursion depth won't allow overflowing node `malloc`s
* This assumes that the rept node is larger
*/
#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 {
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 <errno.h>
#include <inttypes.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
@@ -33,7 +34,8 @@
#include "platform.h" // strdup
struct Patch {
char *tzFilename;
struct FileStackNode const *src;
uint32_t lineNo;
uint32_t nOffset;
struct Section *pcSection;
uint32_t pcOffset;
@@ -62,19 +64,17 @@ static uint32_t nbSymbols = 0; /* Length of the above list */
static struct Assertion *assertions = NULL;
static struct FileStackNode *fileStackNodes = NULL;
/*
* Count the number of sections used in this object
*/
static uint32_t countsections(void)
{
struct Section *sect;
uint32_t count = 0;
sect = pSectionList;
while (sect) {
for (struct Section const *sect = pSectionList; sect; sect = sect->next)
count++;
sect = sect->next;
}
return count;
}
@@ -129,16 +129,60 @@ static void fputstring(char const *s, FILE *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
*/
static uint32_t getsectid(struct Section const *sect)
{
struct Section const *sec;
struct Section const *sec = pSectionList;
uint32_t ID = 0;
sec = pSectionList;
while (sec) {
if (sec == sect)
return ID;
@@ -159,7 +203,10 @@ static uint32_t getSectIDIfAny(struct Section const *sect)
*/
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(getSectIDIfAny(patch->pcSection), f);
fputlong(patch->pcOffset, f);
@@ -206,26 +253,35 @@ static void writesymbol(struct Symbol const *sym, FILE *f)
if (!sym_IsDefined(sym)) {
fputc(SYMTYPE_IMPORT, f);
} else {
assert(sym->src->ID != -1);
fputc(sym->isExported ? SYMTYPE_EXPORT : SYMTYPE_LOCAL, f);
fputstring(sym->fileName, f);
fputlong(sym->src->ID, f);
fputlong(sym->fileLine, f);
fputlong(getSectIDIfAny(sym_GetSection(sym)), 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
* If the symbol does not have one, one is assigned by registering the symbol
*/
static uint32_t getSymbolID(struct Symbol *sym)
{
if (sym->ID == -1) {
sym->ID = nbSymbols++;
*objectSymbolsTail = sym;
objectSymbolsTail = &sym->next;
}
if (sym->ID == -1 && !sym_IsPC(sym))
registerSymbol(sym);
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
* 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,
uint32_t ofs)
static struct Patch *allocpatch(uint32_t type, struct Expression const *expr, uint32_t ofs)
{
struct Patch *patch = malloc(sizeof(struct Patch));
uint32_t rpnSize = expr->isKnown ? 5 : expr->nRPNPatchSize;
struct FileStackNode *node = fstk_GetFileStack();
if (!patch)
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)
fatalerror("No memory for patch's RPN expression: %s\n", strerror(errno));
patch->type = type;
patch->tzFilename = fstk_DumpToStr();
patch->src = node;
out_RegisterNode(node);
patch->lineNo = lexer_GetLineNo();
patch->nOffset = ofs;
patch->pcSection = sect_GetSymbolSection();
patch->pcOffset = sect_GetSymbolOffset();
@@ -382,13 +441,28 @@ static void writeassert(struct Assertion *assert, FILE *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)
{
(void)arg;
if (sym_IsExported(symbol) && symbol->ID == -1) {
*objectSymbolsTail = symbol;
objectSymbolsTail = &symbol->next;
nbSymbols++;
registerSymbol(symbol);
}
}
@@ -411,6 +485,15 @@ void out_WriteObject(void)
fputlong(nbSymbols, 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)
writesymbol(sym, f);

View File

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

View File

@@ -23,6 +23,7 @@
#include "asm/macro.h"
#include "asm/main.h"
#include "asm/mymath.h"
#include "asm/output.h"
#include "asm/section.h"
#include "asm/symbol.h"
#include "asm/util.h"
@@ -121,7 +122,7 @@ static char const *Callback__FILE__(void)
buf[j - 1] = '\\';
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[j++] = '"';
buf[j] = '\0';
@@ -150,15 +151,35 @@ int32_t sym_GetValue(struct Symbol const *sym)
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
*/
static void updateSymbolFilename(struct Symbol *sym)
{
if (snprintf(sym->fileName, _MAX_PATH + 1, "%s",
fstk_GetFileName()) > _MAX_PATH)
fatalerror("%s: File name is too long: '%s'\n", __func__, fstk_GetFileName());
sym->fileLine = fstk_GetLine();
struct FileStackNode *oldSrc = sym->src;
setSymbolFilename(sym);
/* 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->hasCallback = false;
symbol->section = NULL;
updateSymbolFilename(symbol);
setSymbolFilename(symbol);
symbol->ID = -1;
symbol->next = NULL;
@@ -253,6 +274,7 @@ void sym_Purge(char const *symName)
labelScope = NULL;
hash_RemoveElement(symbols, symbol->name);
/* TODO: ideally, also unref the file stack nodes */
free(symbol);
}
}
@@ -338,9 +360,11 @@ static struct Symbol *createNonrelocSymbol(char const *symbolName)
if (!symbol)
symbol = createsymbol(symbolName);
else if (sym_IsDefined(symbol))
error("'%s' already defined at %s(%" PRIu32 ")\n", symbolName,
symbol->fileName, symbol->fileLine);
else if (sym_IsDefined(symbol)) {
error("'%s' already defined at ", symbolName);
dumpFilename(symbol);
putc('\n', stderr);
}
return symbol;
}
@@ -395,15 +419,17 @@ struct Symbol *sym_AddSet(char const *symName, int32_t value)
{
struct Symbol *sym = findsymbol(symName, NULL);
if (sym == NULL)
if (sym == NULL) {
sym = createsymbol(symName);
else if (sym_IsDefined(sym) && sym->type != SYM_SET)
error("'%s' already defined as %s at %s(%" PRIu32 ")\n",
symName, sym->type == SYM_LABEL ? "label" : "constant",
sym->fileName, sym->fileLine);
else
/* TODO: can the scope be incorrect when talking over refs? */
} else if (sym_IsDefined(sym) && sym->type != SYM_SET) {
error("'%s' already defined as %s at ",
symName, sym->type == SYM_LABEL ? "label" : "constant");
dumpFilename(sym);
putc('\n', stderr);
} else {
/* TODO: can the scope be incorrect when taking over refs? */
updateSymbolFilename(sym);
}
sym->type = SYM_SET;
sym->value = value;
@@ -424,9 +450,12 @@ static struct Symbol *addLabel(char const *name)
if (!sym) {
sym = createsymbol(name);
} else if (sym_IsDefined(sym)) {
error("'%s' already defined in %s(%" PRIu32 ")\n",
name, sym->fileName, sym->fileLine);
error("'%s' already defined at ", name);
dumpFilename(sym);
putc('\n', stderr);
return NULL;
} else {
updateSymbolFilename(sym);
}
/* If the symbol already exists as a ref, just "take over" it */
sym->type = SYM_LABEL;
@@ -434,7 +463,6 @@ static struct Symbol *addLabel(char const *name)
if (exportall)
sym->isExported = true;
sym->section = sect_GetSymbolSection();
updateSymbolFilename(sym);
if (sym && !sym->section)
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->macroSize = size;
sym->macro = body;
updateSymbolFilename(sym);
setSymbolFilename(sym); /* TODO: is this really necessary? */
/*
* The symbol is created at the line after the `endm`,
* override this with the actual definition line
@@ -577,10 +605,11 @@ static inline struct Symbol *createBuiltinSymbol(char const *name)
sym->isBuiltin = true;
sym->hasCallback = true;
strcpy(sym->fileName, "<builtin>");
sym->src = NULL;
sym->fileLine = 0;
return sym;
}
/*
* 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)
{
fputs(type, stderr);
fstk_Dump();
fstk_DumpCurrent();
fprintf(stderr, flagfmt, flag);
vfprintf(stderr, fmt, args);
lexer_DumpStringExpansions();

View File

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

View File

@@ -6,8 +6,10 @@
* SPDX-License-Identifier: MIT
*/
#include <assert.h>
#include <inttypes.h>
#include <stdbool.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
@@ -39,11 +41,55 @@ bool disablePadding; /* -x */
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;
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);
vfprintf(stderr, fmt, ap);
va_end(ap);
@@ -53,11 +99,15 @@ void error(char const *fmt, ...)
nbErrors++;
}
noreturn_ void fatal(char const *fmt, ...)
noreturn_ void fatal(struct FileStackNode const *where, uint32_t lineNo, char const *fmt, ...)
{
va_list ap;
fprintf(stderr, "fatal: ");
fputs("fatal: ", stderr);
if (where) {
dumpFileStack(where);
fprintf(stderr, "(%" PRIu32 "): ", lineNo);
}
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
va_end(ap);
@@ -177,11 +227,11 @@ int main(int argc, char *argv[])
case 'p':
value = strtoul(optarg, &endptr, 0);
if (optarg[0] == '\0' || *endptr != '\0') {
error("Invalid argument for option 'p'");
error(NULL, 0, "Invalid argument for option 'p'");
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;
}
padValue = value;
@@ -189,7 +239,7 @@ int main(int argc, char *argv[])
case 's':
/* FIXME: nobody knows what this does, figure it out */
(void)optarg;
warnx("Nobody has any idea what `-s` does");
warning(NULL, 0, "Nobody has any idea what `-s` does");
break;
case 't':
is32kMode = true;
@@ -234,8 +284,8 @@ int main(int argc, char *argv[])
bankranges[SECTTYPE_VRAM][1] = BANK_MIN_VRAM;
/* Read all object files first, */
while (curArgIndex < argc)
obj_ReadFile(argv[curArgIndex++]);
for (obj_Setup(argc - curArgIndex); curArgIndex < argc; curArgIndex++)
obj_ReadFile(argv[curArgIndex], argc - curArgIndex - 1);
/* then process them, */
obj_DoSanityChecks();

View File

@@ -31,6 +31,11 @@ static struct SymbolList {
struct SymbolList *next;
} *symbolLists;
unsigned int nbObjFiles;
static struct {
struct FileStackNode *nodes;
uint32_t nbNodes;
} *nodes;
static struct Assertion *assertions;
/***** Helper functions for reading object files *****/
@@ -170,12 +175,56 @@ static char *readstr(FILE *file)
/***** 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 symbol The struct to fill
* @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",
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 (symbol->type != SYMTYPE_IMPORT) {
symbol->objFileName = fileName;
tryReadstr(symbol->fileName, file,
"%s: Cannot read \"%s\"'s file name: %s",
uint32_t nodeID;
tryReadlong(nodeID, file,
"%s: Cannot read \"%s\"'s node ID: %s",
fileName, symbol->name);
symbol->src = &fileNodes[nodeID];
tryReadlong(symbol->lineNo, file,
"%s: Cannot read \"%s\"'s line number: %s",
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 patch The struct to fill
* @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,
char const *sectName, uint32_t i,
struct Section *fileSections[])
struct Section *fileSections[], struct FileStackNode fileNodes[])
{
tryReadstr(patch->fileName, file,
"%s: Unable to read \"%s\"'s patch #%" PRIu32 "'s name: %s",
uint32_t nodeID;
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);
tryReadlong(patch->offset, file,
"%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,
"%s: Unable to read \"%s\"'s patch #%" PRIu32 "'s PC offset: %s",
fileName, sectName, i);
patch->pcSection = patch->pcSectionID == -1
? NULL
patch->pcSection = patch->pcSectionID == -1 ? NULL
: fileSections[patch->pcSectionID];
tryReadlong(patch->pcOffset, file,
"%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",
fileName, sectName, i);
uint8_t *rpnExpression =
malloc(sizeof(*rpnExpression) * patch->rpnSize);
size_t nbElementsRead = fread(rpnExpression, sizeof(*rpnExpression),
patch->rpnExpression = malloc(sizeof(*patch->rpnExpression) * patch->rpnSize);
if (!patch->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);
if (nbElementsRead != patch->rpnSize)
errx(1, "%s: Cannot read \"%s\"'s patch #%" PRIu32 "'s RPN expression: %s",
fileName, sectName, i,
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 fileName The filename to report in errors
*/
static void readSection(FILE *file, struct Section *section,
char const *fileName, struct Section *fileSections[])
static void readSection(FILE *file, struct Section *section, char const *fileName,
struct Section *fileSections[], struct FileStackNode fileNodes[])
{
int32_t tmp;
uint8_t byte;
@@ -280,7 +338,7 @@ static void readSection(FILE *file, struct Section *section,
fileName, section->name);
section->isAddressFixed = tmp >= 0;
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);
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",
fileName, section->name);
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);
tmp = UINT16_MAX;
}
@@ -332,7 +390,7 @@ static void readSection(FILE *file, struct Section *section,
section->name);
for (uint32_t i = 0; i < section->nbPatches; i++) {
readPatch(file, &patches[i], fileName, section->name,
i, fileSections);
i, fileSections, fileNodes);
}
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,
char const *fileName, uint32_t i,
struct Section *fileSections[])
struct Section *fileSections[], struct FileStackNode fileNodes[])
{
char assertName[sizeof("Assertion #" EXPAND_AND_STR(UINT32_MAX))];
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",
fileName);
}
@@ -394,11 +452,7 @@ static inline struct Section *getMainSection(struct Section *section)
return section;
}
/**
* Reads an object file of any supported format
* @param fileName The filename to report for errors
*/
void obj_ReadFile(char const *fileName)
void obj_ReadFile(char const *fileName, unsigned int fileID)
{
FILE *file = strcmp("-", fileName) ? fopen(fileName, "rb") : stdin;
@@ -438,6 +492,14 @@ void obj_ReadFile(char const *fileName)
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 */
struct Symbol **fileSymbols =
malloc(sizeof(*fileSymbols) * nbSymbols + 1);
@@ -464,7 +526,7 @@ void obj_ReadFile(char const *fileName)
if (!symbol)
err(1, "%s: Couldn't create new symbol", fileName);
readSymbol(file, symbol, fileName);
readSymbol(file, symbol, fileName, nodes[fileID].nodes);
fileSymbols[i] = symbol;
if (symbol->type == SYMTYPE_EXPORT)
@@ -485,7 +547,7 @@ void obj_ReadFile(char const *fileName)
err(1, "%s: Couldn't create new section", fileName);
fileSections[i]->nextu = NULL;
readSection(file, fileSections[i], fileName, fileSections);
readSection(file, fileSections[i], fileName, fileSections, nodes[fileID].nodes);
fileSections[i]->fileSymbols = fileSymbols;
if (nbSymPerSect[i]) {
fileSections[i]->symbols = malloc(nbSymPerSect[i]
@@ -535,7 +597,7 @@ void obj_ReadFile(char const *fileName)
if (!assertion)
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->next = assertions;
assertions = assertion;
@@ -555,6 +617,15 @@ void obj_CheckAssertions(void)
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)
{
(void)arg;
@@ -562,12 +633,8 @@ static void freeSection(struct Section *section, void *arg)
free(section->name);
if (sect_HasData(section->type)) {
free(section->data);
for (int32_t i = 0; i < section->nbPatches; i++) {
struct Patch *patch = &section->patches[i];
free(patch->fileName);
free(patch->rpnExpression);
}
for (int32_t i = 0; i < section->nbPatches; i++)
free(section->patches[i].rpnExpression);
free(section->patches);
}
free(section->symbols);
@@ -577,13 +644,20 @@ static void freeSection(struct Section *section, void *arg)
static void freeSymbol(struct Symbol *symbol)
{
free(symbol->name);
if (symbol->type != SYMTYPE_IMPORT)
free(symbol->fileName);
free(symbol);
}
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();
sect_ForEach(freeSection, NULL);

View File

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

View File

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

View File

@@ -34,9 +34,42 @@ is a 0terminated string of
; Header
BYTE ID[4] ; "RGB9"
LONG RevisionNumber ; The format's revision number this file uses
LONG NumberOfSymbols ; The number of symbols used in this file
LONG NumberOfSections ; The number of sections used in this file
LONG RevisionNumber ; The format's revision number this file uses.
LONG NumberOfSymbols ; The number of symbols 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
@@ -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.
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.
@@ -107,8 +140,10 @@ REPT NumberOfSections
REPT NumberOfPatches
STRING SourceFile ; Name of the source file (for printing error
; messages).
LONG SourceFile ; ID of the source file node (for printing
; error messages).
LONG LineNo ; Line at which the patch was created.
LONG Offset ; Offset into the section where patch should
; be applied (in bytes).
@@ -145,7 +180,9 @@ LONG 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.
@@ -209,7 +246,7 @@ with some bytes being special prefixes for integers and symbols.
.It Li $50 Ta Li BANK(symbol) ,
a
.Ar LONG
Symbol ID follows.
Symbol ID follows, where -1 means PC
.It Li $51 Ta Li BANK(section_name) ,
a null-terminated string follows.
.It Li $52 Ta Li Current BANK()

View File

@@ -1,3 +1,3 @@
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)!