shithub: rgbds

Download patch

ref: a3d88366716a0c5115a54296d93fb6ee650fd08e
parent: db1eb8fbcbcd06bcbfa7f6244f06ae2807f1e94d
author: ISSOtm <[email protected]>
date: Sun Mar 29 08:18:24 EDT 2020

Prevent assertions outside sections from crashing

--- a/include/link/patch.h
+++ b/include/link/patch.h
@@ -22,6 +22,11 @@
 	// enum AssertionType type; The `patch`'s field is instead re-used
 	struct Section *section;
 	char *message;
+	/*
+	 * This would be redundant with `.section->fileSymbols`... but
+	 * `section` is sometimes NULL!
+	 */
+	struct Symbol ** fileSymbols;
 
 	struct Assertion *next;
 };
--- a/src/asm/output.c
+++ b/src/asm/output.c
@@ -362,7 +362,7 @@
 static void writeassert(struct Assertion *assert, FILE *f)
 {
 	writepatch(assert->patch, f);
-	fputlong(getsectid(assert->section), f);
+	fputlong(assert->section ? getsectid(assert->section) : -1, f);
 	fputstring(assert->message, f);
 }
 
--- a/src/link/object.c
+++ b/src/link/object.c
@@ -356,7 +356,7 @@
 	readPatch(file, &assert->patch, fileName, assertName, 0);
 	tryReadlong(sectionID, file, "%s: Cannot read assertion's section ID: %s",
 		    fileName);
-	assert->section = fileSections[sectionID];
+	assert->section = sectionID == -1 ? NULL : fileSections[sectionID];
 	tryReadstr(assert->message, file, "%s: Cannot read assertion's message: %s",
 		   fileName);
 }
@@ -502,6 +502,7 @@
 		if (!assertion)
 			err(1, "%s: Couldn't create new assertion", fileName);
 		readAssertion(file, assertion, fileName, fileSections, i);
+		assertion->fileSymbols = fileSymbols;
 		assertion->next = assertions;
 		assertions = assertion;
 	}
--- a/src/link/patch.c
+++ b/src/link/patch.c
@@ -117,7 +117,7 @@
 	return *(*expression)++;
 }
 
-static struct Symbol const *getSymbol(struct Symbol ** const symbolList,
+static struct Symbol const *getSymbol(struct Symbol const * const *symbolList,
 				      uint32_t index, char const *fileName)
 {
 	struct Symbol const *symbol = symbolList[index];
@@ -142,7 +142,8 @@
  * @return The patch's value
  */
 static int32_t computeRPNExpr(struct Patch const *patch,
-			      struct Section const *section)
+			      struct Section const *section,
+			      struct Symbol const * const *fileSymbols)
 {
 /* Small shortcut to avoid a lot of repetition */
 #define popRPN() popRPN(patch->fileName)
@@ -256,7 +257,7 @@
 				value |= getRPNByte(&expression, &size,
 						    patch->fileName) << shift;
 
-			value = getSymbol(section->fileSymbols, value,
+			value = getSymbol(fileSymbols, value,
 					  patch->fileName)->section->bank;
 			break;
 
@@ -312,8 +313,7 @@
 				value |= getRPNByte(&expression, &size,
 						    patch->fileName) << shift;
 
-			symbol = getSymbol(section->fileSymbols, value,
-					   patch->fileName);
+			symbol = getSymbol(fileSymbols, value, patch->fileName);
 
 			if (!strcmp(symbol->name, "@")) {
 				value = section->org + patch->offset;
@@ -346,7 +346,9 @@
 	uint8_t failures = 0;
 
 	while (assert) {
-		if (!computeRPNExpr(&assert->patch, assert->section)) {
+		if (!computeRPNExpr(&assert->patch, assert->section,
+				    (struct Symbol const * const *)
+				    			assert->fileSymbols)) {
 			switch ((enum AssertionType)assert->patch.type) {
 			case ASSERT_FATAL:
 				errx(1, "%s: %s", assert->patch.fileName,
@@ -393,7 +395,9 @@
 	verbosePrint("Patching section \"%s\"...\n", section->name);
 	for (uint32_t patchID = 0; patchID < section->nbPatches; patchID++) {
 		struct Patch *patch = &section->patches[patchID];
-		int32_t value = computeRPNExpr(patch, section);
+		int32_t value = computeRPNExpr(patch, section,
+					       (struct Symbol const * const *)
+							section->fileSymbols);
 
 		/* `jr` is quite unlike the others... */
 		if (patch->type == PATCHTYPE_JR) {
--- /dev/null
+++ b/test/asm/assert-nosect.asm
@@ -1,0 +1,5 @@
+assert Sym
+
+SECTION "test", ROM0
+	db 69
+Sym:
--- /dev/null
+++ b/test/asm/assert-nosect.out.bin
@@ -1,0 +1,1 @@
+E
\ No newline at end of file