mirror of
https://github.com/gbdev/rgbds.git
synced 2025-11-24 03:52:08 +00:00
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:
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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,20 +262,25 @@ 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",
|
||||
fileName, sectName, i);
|
||||
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
|
||||
: fileSections[patch->pcSectionID];
|
||||
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",
|
||||
fileName, sectName, i);
|
||||
@@ -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 = §ion->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);
|
||||
|
||||
112
src/link/patch.c
112
src/link/patch.c
@@ -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;
|
||||
|
||||
symbol = getSymbol(fileSymbols, value);
|
||||
|
||||
if (!symbol) {
|
||||
error("%s: Unknown symbol \"%s\"",
|
||||
patch->fileName,
|
||||
fileSymbols[value]->name);
|
||||
} else if (strcmp(symbol->name, "@")) {
|
||||
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;
|
||||
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 {
|
||||
value = patch->pcOffset + patch->pcSection->org;
|
||||
symbol = getSymbol(fileSymbols, value);
|
||||
|
||||
if (!symbol) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
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,20 +399,20 @@ 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,
|
||||
assert->message[0] ? assert->message
|
||||
: "assert failure");
|
||||
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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user