mirror of
https://github.com/gbdev/rgbds.git
synced 2025-11-20 10:12:06 +00:00
Make some RGBLINK errors non-fatal
This commit is contained in:
@@ -14,6 +14,8 @@
|
|||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include "helpers.h"
|
||||||
|
|
||||||
/* Variables related to CLI options */
|
/* Variables related to CLI options */
|
||||||
extern bool isDmgMode;
|
extern bool isDmgMode;
|
||||||
extern char const *linkerScriptName;
|
extern char const *linkerScriptName;
|
||||||
@@ -32,6 +34,10 @@ extern bool isWRA0Mode;
|
|||||||
fprintf(stderr, __VA_ARGS__); \
|
fprintf(stderr, __VA_ARGS__); \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
|
void error(char const *fmt, ...);
|
||||||
|
|
||||||
|
noreturn_ void fatal(char const *fmt, ...);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Opens a file if specified, and aborts on error.
|
* Opens a file if specified, and aborts on error.
|
||||||
* @param fileName The name of the file to open; if NULL, no file will be opened
|
* @param fileName The name of the file to open; if NULL, no file will be opened
|
||||||
|
|||||||
@@ -80,15 +80,15 @@ 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)
|
||||||
errx(1, "Linker script contradicts \"%s\"'s bank placement",
|
error("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)
|
||||||
errx(1, "Linker script contradicts \"%s\"'s address placement",
|
error("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)
|
||||||
errx(1, "Linker script contradicts \"%s\"'s alignment",
|
error("Linker script contradicts \"%s\"'s alignment",
|
||||||
section->name);
|
section->name);
|
||||||
|
|
||||||
section->isAddressFixed = true;
|
section->isAddressFixed = true;
|
||||||
section->org = placement->org;
|
section->org = placement->org;
|
||||||
|
|||||||
@@ -35,6 +35,40 @@ bool is32kMode; /* -t */
|
|||||||
bool beVerbose; /* -v */
|
bool beVerbose; /* -v */
|
||||||
bool isWRA0Mode; /* -w */
|
bool isWRA0Mode; /* -w */
|
||||||
|
|
||||||
|
static uint32_t nbErrors = 0;
|
||||||
|
|
||||||
|
void error(char const *fmt, ...)
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
|
||||||
|
fprintf(stderr, "error: ");
|
||||||
|
va_start(ap, fmt);
|
||||||
|
vfprintf(stderr, fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
putc('\n', stderr);
|
||||||
|
|
||||||
|
if (nbErrors != UINT32_MAX)
|
||||||
|
nbErrors++;
|
||||||
|
}
|
||||||
|
|
||||||
|
noreturn_ void fatal(char const *fmt, ...)
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
|
||||||
|
fprintf(stderr, "fatal: ");
|
||||||
|
va_start(ap, fmt);
|
||||||
|
vfprintf(stderr, fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
putc('\n', stderr);
|
||||||
|
|
||||||
|
if (nbErrors != UINT32_MAX)
|
||||||
|
nbErrors++;
|
||||||
|
|
||||||
|
fprintf(stderr, "Linking aborted after %u error%s\n", nbErrors,
|
||||||
|
nbErrors != 1 ? "s" : "");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
FILE *openFile(char const *fileName, char const *mode)
|
FILE *openFile(char const *fileName, char const *mode)
|
||||||
{
|
{
|
||||||
if (!fileName)
|
if (!fileName)
|
||||||
@@ -138,10 +172,14 @@ int main(int argc, char *argv[])
|
|||||||
break;
|
break;
|
||||||
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') {
|
||||||
errx(1, "Invalid argument for option 'p'");
|
error("Invalid argument for option 'p'");
|
||||||
if (value > 0xFF)
|
value = 0xFF;
|
||||||
errx(1, "Argument for 'p' must be a byte (between 0 and 0xFF)");
|
}
|
||||||
|
if (value > 0xFF) {
|
||||||
|
error("Argument for 'p' must be a byte (between 0 and 0xFF)");
|
||||||
|
value = 0xFF;
|
||||||
|
}
|
||||||
padValue = value;
|
padValue = value;
|
||||||
break;
|
break;
|
||||||
case 's':
|
case 's':
|
||||||
@@ -171,7 +209,7 @@ int main(int argc, char *argv[])
|
|||||||
|
|
||||||
/* If no input files were specified, the user must have screwed up */
|
/* If no input files were specified, the user must have screwed up */
|
||||||
if (curArgIndex == argc) {
|
if (curArgIndex == argc) {
|
||||||
fputs("FATAL: no input files\n", stderr);
|
fputs("fatal: no input files\n", stderr);
|
||||||
printUsage();
|
printUsage();
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
@@ -198,6 +236,11 @@ int main(int argc, char *argv[])
|
|||||||
|
|
||||||
/* and finally output the result. */
|
/* and finally output the result. */
|
||||||
patch_ApplyPatches();
|
patch_ApplyPatches();
|
||||||
|
if (nbErrors) {
|
||||||
|
fprintf(stderr, "Linking failed with %u error%s\n", nbErrors,
|
||||||
|
nbErrors != 1 ? "s" : "");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
out_WriteFiles();
|
out_WriteFiles();
|
||||||
|
|
||||||
/* Do cleanup before quitting, though. */
|
/* Do cleanup before quitting, though. */
|
||||||
|
|||||||
@@ -118,19 +118,13 @@ static uint32_t getRPNByte(uint8_t const **expression, int32_t *size,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static struct Symbol const *getSymbol(struct Symbol const * const *symbolList,
|
static struct Symbol const *getSymbol(struct Symbol const * const *symbolList,
|
||||||
uint32_t index, char const *fileName)
|
uint32_t index)
|
||||||
{
|
{
|
||||||
struct Symbol const *symbol = symbolList[index];
|
struct Symbol const *symbol = symbolList[index];
|
||||||
|
|
||||||
/* If the symbol is defined elsewhere... */
|
/* If the symbol is defined elsewhere... */
|
||||||
if (symbol->type == SYMTYPE_IMPORT) {
|
if (symbol->type == SYMTYPE_IMPORT)
|
||||||
struct Symbol const *symbolDefinition =
|
return sym_GetSymbol(symbol->name);
|
||||||
sym_GetSymbol(symbol->name);
|
|
||||||
if (!symbolDefinition)
|
|
||||||
errx(1, "%s: Unknown symbol \"%s\"", fileName,
|
|
||||||
symbol->name);
|
|
||||||
symbol = symbolDefinition;
|
|
||||||
}
|
|
||||||
|
|
||||||
return symbol;
|
return symbol;
|
||||||
}
|
}
|
||||||
@@ -182,9 +176,13 @@ static int32_t computeRPNExpr(struct Patch const *patch,
|
|||||||
break;
|
break;
|
||||||
case RPN_DIV:
|
case RPN_DIV:
|
||||||
value = popRPN();
|
value = popRPN();
|
||||||
if (value == 0)
|
if (value == 0) {
|
||||||
errx(1, "%s: Division by 0", patch->fileName);
|
error("%s: Division by 0", patch->fileName);
|
||||||
value = popRPN() / value;
|
popRPN();
|
||||||
|
value = INT32_MAX;
|
||||||
|
} else {
|
||||||
|
value = popRPN() / value;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case RPN_MOD:
|
case RPN_MOD:
|
||||||
value = popRPN();
|
value = popRPN();
|
||||||
@@ -256,9 +254,16 @@ static int32_t computeRPNExpr(struct Patch const *patch,
|
|||||||
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->fileName) << shift;
|
||||||
|
symbol = getSymbol(fileSymbols, value);
|
||||||
|
|
||||||
value = getSymbol(fileSymbols, value,
|
if (!symbol) {
|
||||||
patch->fileName)->section->bank;
|
error("%s: Requested BANK() of symbol \"%s\", which was not found",
|
||||||
|
patch->fileName,
|
||||||
|
fileSymbols[value]->name);
|
||||||
|
value = 1;
|
||||||
|
} else {
|
||||||
|
value = symbol->section->bank;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case RPN_BANK_SECT:
|
case RPN_BANK_SECT:
|
||||||
@@ -268,11 +273,13 @@ static int32_t computeRPNExpr(struct Patch const *patch,
|
|||||||
|
|
||||||
sect = sect_GetSection(name);
|
sect = sect_GetSection(name);
|
||||||
|
|
||||||
if (!sect)
|
if (!sect) {
|
||||||
errx(1, "%s: Requested BANK() of section \"%s\", which was not found",
|
error("%s: Requested BANK() of section \"%s\", which was not found",
|
||||||
patch->fileName, name);
|
patch->fileName, name);
|
||||||
|
value = 1;
|
||||||
value = sect->bank;
|
} else {
|
||||||
|
value = sect->bank;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case RPN_BANK_SELF:
|
case RPN_BANK_SELF:
|
||||||
@@ -284,8 +291,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)
|
||||||
errx(1, "%s: Value %d is not in HRAM range",
|
error("%s: Value %d is not in HRAM range",
|
||||||
patch->fileName, value);
|
patch->fileName, value);
|
||||||
value &= 0xFF;
|
value &= 0xFF;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -295,8 +302,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)
|
||||||
errx(1, "%s: Value %d is not a RST vector",
|
error("%s: Value %d is not a RST vector",
|
||||||
patch->fileName, value);
|
patch->fileName, value);
|
||||||
value |= 0xC7;
|
value |= 0xC7;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -313,7 +320,7 @@ static int32_t computeRPNExpr(struct Patch const *patch,
|
|||||||
value |= getRPNByte(&expression, &size,
|
value |= getRPNByte(&expression, &size,
|
||||||
patch->fileName) << shift;
|
patch->fileName) << shift;
|
||||||
|
|
||||||
symbol = getSymbol(fileSymbols, value, patch->fileName);
|
symbol = getSymbol(fileSymbols, value);
|
||||||
|
|
||||||
if (!strcmp(symbol->name, "@")) {
|
if (!strcmp(symbol->name, "@")) {
|
||||||
value = section->org + patch->offset;
|
value = section->org + patch->offset;
|
||||||
@@ -330,7 +337,7 @@ static int32_t computeRPNExpr(struct Patch const *patch,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (stack.size > 1)
|
if (stack.size > 1)
|
||||||
warnx("%s: RPN stack has %lu entries on exit, not 1",
|
error("%s: RPN stack has %lu entries on exit, not 1",
|
||||||
patch->fileName, stack.size);
|
patch->fileName, stack.size);
|
||||||
|
|
||||||
return popRPN();
|
return popRPN();
|
||||||
@@ -343,25 +350,21 @@ void patch_CheckAssertions(struct Assertion *assert)
|
|||||||
verbosePrint("Checking assertions...");
|
verbosePrint("Checking assertions...");
|
||||||
initRPNStack();
|
initRPNStack();
|
||||||
|
|
||||||
uint8_t failures = 0;
|
|
||||||
|
|
||||||
while (assert) {
|
while (assert) {
|
||||||
if (!computeRPNExpr(&assert->patch, assert->section,
|
if (!computeRPNExpr(&assert->patch, assert->section,
|
||||||
(struct Symbol const * const *)
|
(struct Symbol const * const *)
|
||||||
assert->fileSymbols)) {
|
assert->fileSymbols)) {
|
||||||
switch ((enum AssertionType)assert->patch.type) {
|
switch ((enum AssertionType)assert->patch.type) {
|
||||||
case ASSERT_FATAL:
|
case ASSERT_FATAL:
|
||||||
errx(1, "%s: %s", assert->patch.fileName,
|
fatal("%s: %s", assert->patch.fileName,
|
||||||
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:
|
||||||
fprintf(stderr, "%s: %s\n",
|
error("%s: %s", assert->patch.fileName,
|
||||||
assert->patch.fileName,
|
assert->message[0] ? assert->message
|
||||||
assert->message[0] ? assert->message
|
: "assert failure");
|
||||||
: "assert failure");
|
|
||||||
failures++;
|
|
||||||
break;
|
break;
|
||||||
case ASSERT_WARN:
|
case ASSERT_WARN:
|
||||||
warnx("%s: %s", assert->patch.fileName,
|
warnx("%s: %s", assert->patch.fileName,
|
||||||
@@ -377,9 +380,6 @@ void patch_CheckAssertions(struct Assertion *assert)
|
|||||||
}
|
}
|
||||||
|
|
||||||
freeRPNStack();
|
freeRPNStack();
|
||||||
|
|
||||||
if (failures)
|
|
||||||
errx(1, "%u assertions failed!", failures);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -406,8 +406,8 @@ static void applyFilePatches(struct Section *section)
|
|||||||
int16_t offset = value - address;
|
int16_t offset = value - address;
|
||||||
|
|
||||||
if (offset < -128 || offset > 127)
|
if (offset < -128 || offset > 127)
|
||||||
errx(1, "%s: jr target out of reach (expected -129 < %d < 128)",
|
error("%s: jr target out of reach (expected -129 < %d < 128)",
|
||||||
patch->fileName, offset);
|
patch->fileName, offset);
|
||||||
section->data[patch->offset] = offset & 0xFF;
|
section->data[patch->offset] = offset & 0xFF;
|
||||||
} else {
|
} else {
|
||||||
/* Patch a certain number of bytes */
|
/* Patch a certain number of bytes */
|
||||||
@@ -423,10 +423,10 @@ static void applyFilePatches(struct Section *section)
|
|||||||
|
|
||||||
if (value < types[patch->type].min
|
if (value < types[patch->type].min
|
||||||
|| value > types[patch->type].max)
|
|| value > types[patch->type].max)
|
||||||
errx(1, "%s: Value %#x%s is not %u-bit",
|
error("%s: Value %#x%s is not %u-bit",
|
||||||
patch->fileName, value,
|
patch->fileName, value,
|
||||||
value < 0 ? " (maybe negative?)" : "",
|
value < 0 ? " (maybe negative?)" : "",
|
||||||
types[patch->type].size * 8);
|
types[patch->type].size * 8);
|
||||||
for (uint8_t i = 0; i < types[patch->type].size; i++) {
|
for (uint8_t i = 0; i < types[patch->type].size; i++) {
|
||||||
section->data[patch->offset + i] = value & 0xFF;
|
section->data[patch->offset + i] = value & 0xFF;
|
||||||
value >>= 8;
|
value >>= 8;
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
warning: assert.asm(7): Worry about me, but not too much.
|
warning: assert.asm(7): Worry about me, but not too much.
|
||||||
assert.asm(8): Okay, this is getting serious!
|
error: assert.asm(8): Okay, this is getting serious!
|
||||||
error: assert.asm(9): It all ends now.
|
fatal: assert.asm(9): It all ends now.
|
||||||
|
Linking aborted after 2 errors
|
||||||
|
|||||||
@@ -1 +1,2 @@
|
|||||||
error: rst-bad.asm(2): Value 1 is not a RST vector
|
error: rst-bad.asm(2): Value 1 is not a RST vector
|
||||||
|
Linking failed with 1 error
|
||||||
|
|||||||
@@ -1 +1,2 @@
|
|||||||
error: Linker script contradicts "sec"'s alignment
|
error: Linker script contradicts "sec"'s alignment
|
||||||
|
Linking failed with 1 error
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
section-union/assert.asm(11): Force failing the build
|
error: section-union/assert.asm(11): Force failing the build
|
||||||
error: 1 assertions failed!
|
Linking failed with 1 error
|
||||||
---
|
---
|
||||||
ERROR: -(30):
|
ERROR: -(30):
|
||||||
Assertion failed: Force failing the build
|
Assertion failed: Force failing the build
|
||||||
|
|||||||
Reference in New Issue
Block a user