Use std::vector for section patches

This commit is contained in:
Rangi42
2024-02-24 11:42:42 -05:00
committed by Sylvie
parent b207bff157
commit d792ee4b61
5 changed files with 154 additions and 127 deletions

View File

@@ -7,6 +7,7 @@
// GUIDELINE: external code MUST NOT BE AWARE of the data structure used! // GUIDELINE: external code MUST NOT BE AWARE of the data structure used!
#include <stdint.h> #include <stdint.h>
#include <vector>
#include "link/main.hpp" #include "link/main.hpp"
@@ -45,8 +46,7 @@ struct Section {
uint16_t alignMask; uint16_t alignMask;
uint16_t alignOfs; uint16_t alignOfs;
uint8_t *data; // Array of size `size` uint8_t *data; // Array of size `size`
uint32_t nbPatches; std::vector<struct Patch> *patches;
struct Patch *patches;
// Extra info computed during linking // Extra info computed during linking
struct Symbol **fileSymbols; struct Symbol **fileSymbols;
uint32_t nbSymbols; uint32_t nbSymbols;

View File

@@ -4,6 +4,7 @@
#include <errno.h> #include <errno.h>
#include <inttypes.h> #include <inttypes.h>
#include <limits.h> #include <limits.h>
#include <new>
#include <stdio.h> #include <stdio.h>
#include <stdint.h> #include <stdint.h>
#include <string.h> #include <string.h>
@@ -373,18 +374,18 @@ static void readSection(FILE *file, struct Section *section, char const *fileNam
} }
section->data = data; section->data = data;
tryReadlong(section->nbPatches, file, uint32_t nbPatches;
tryReadlong(nbPatches, file,
"%s: Cannot read \"%s\"'s number of patches: %s", "%s: Cannot read \"%s\"'s number of patches: %s",
fileName, section->name); fileName, section->name);
struct Patch *patches = section->patches = new(std::nothrow) std::vector<struct Patch>();
(struct Patch *)malloc(sizeof(*patches) * section->nbPatches + 1); if (!section->patches)
if (!patches)
err("%s: Unable to read \"%s\"'s patches", fileName, section->name); err("%s: Unable to read \"%s\"'s patches", fileName, section->name);
for (uint32_t i = 0; i < section->nbPatches; i++) section->patches->resize(nbPatches);
readPatch(file, &patches[i], fileName, section->name, i, fileNodes); for (uint32_t i = 0; i < nbPatches; i++)
section->patches = patches; readPatch(file, &(*section->patches)[i], fileName, section->name, i, fileNodes);
} else { } else {
section->data = NULL; // `mergeSections()` expects to be able to always read the ptr section->data = NULL; // `mergeSections()` expects to be able to always read the ptr
} }
@@ -587,8 +588,8 @@ void obj_ReadFile(char const *fileName, unsigned int fileID)
// Give patches' PC section pointers to their sections // Give patches' PC section pointers to their sections
for (uint32_t i = 0; i < nbSections; i++) { for (uint32_t i = 0; i < nbSections; i++) {
if (sect_HasData(fileSections[i]->type)) { if (sect_HasData(fileSections[i]->type)) {
for (uint32_t j = 0; j < fileSections[i]->nbPatches; j++) for (struct Patch &patch : *fileSections[i]->patches)
linkPatchToPCSect(&fileSections[i]->patches[j], fileSections); linkPatchToPCSect(&patch, fileSections);
} }
} }
@@ -667,9 +668,9 @@ static void freeSection(struct Section *section)
free(section->name); free(section->name);
if (sect_HasData(section->type)) { if (sect_HasData(section->type)) {
free(section->data); free(section->data);
for (uint32_t i = 0; i < section->nbPatches; i++) for (struct Patch &patch : *section->patches)
free(section->patches[i].rpnExpression); free(patch.rpnExpression);
free(section->patches); delete section->patches;
} }
free(section->symbols); free(section->symbols);
free(section); free(section);

View File

@@ -454,22 +454,21 @@ void patch_CheckAssertions(std::deque<struct Assertion> &assertions)
static void applyFilePatches(struct Section *section, struct Section *dataSection) static void applyFilePatches(struct Section *section, struct Section *dataSection)
{ {
verbosePrint("Patching section \"%s\"...\n", section->name); verbosePrint("Patching section \"%s\"...\n", section->name);
for (uint32_t patchID = 0; patchID < section->nbPatches; patchID++) { for (struct Patch &patch : *section->patches) {
struct Patch *patch = &section->patches[patchID]; int32_t value = computeRPNExpr(&patch,
int32_t value = computeRPNExpr(patch,
(struct Symbol const * const *) (struct Symbol const * const *)
section->fileSymbols); section->fileSymbols);
uint16_t offset = patch->offset + section->offset; uint16_t offset = patch.offset + section->offset;
// `jr` is quite unlike the others... // `jr` is quite unlike the others...
if (patch->type == PATCHTYPE_JR) { if (patch.type == PATCHTYPE_JR) {
// Offset is relative to the byte *after* the operand // Offset is relative to the byte *after* the operand
// PC as operand to `jr` is lower than reference PC by 2 // PC as operand to `jr` is lower than reference PC by 2
uint16_t address = patch->pcSection->org + patch->pcOffset + 2; uint16_t address = patch.pcSection->org + patch.pcOffset + 2;
int16_t jumpOffset = value - address; int16_t jumpOffset = value - address;
if (!isError && (jumpOffset < -128 || jumpOffset > 127)) if (!isError && (jumpOffset < -128 || jumpOffset > 127))
error(patch->src, patch->lineNo, error(patch.src, patch.lineNo,
"jr target out of reach (expected -129 < %" PRId16 " < 128)", "jr target out of reach (expected -129 < %" PRId16 " < 128)",
jumpOffset); jumpOffset);
dataSection->data[offset] = jumpOffset & 0xFF; dataSection->data[offset] = jumpOffset & 0xFF;
@@ -485,13 +484,13 @@ static void applyFilePatches(struct Section *section, struct Section *dataSectio
{ 4, INT32_MIN, INT32_MAX }, // PATCHTYPE_LONG { 4, INT32_MIN, INT32_MAX }, // PATCHTYPE_LONG
}; };
if (!isError && (value < types[patch->type].min if (!isError && (value < types[patch.type].min
|| value > types[patch->type].max)) || value > types[patch.type].max))
error(patch->src, patch->lineNo, error(patch.src, patch.lineNo,
"Value %" PRId32 "%s is not %u-bit", "Value %" PRId32 "%s is not %u-bit",
value, value < 0 ? " (maybe negative?)" : "", value, value < 0 ? " (maybe negative?)" : "",
types[patch->type].size * 8U); types[patch.type].size * 8U);
for (uint8_t i = 0; i < types[patch->type].size; i++) { for (uint8_t i = 0; i < types[patch.type].size; i++) {
dataSection->data[offset + i] = value & 0xFF; dataSection->data[offset + i] = value & 0xFF;
value >>= 8; value >>= 8;
} }

View File

@@ -32,7 +32,8 @@ static void consumeLF(struct FileStackNode const *where, uint32_t lineNo, FILE *
static char const *delim = " \f\n\r\t\v"; // Whitespace according to the C and POSIX locales static char const *delim = " \f\n\r\t\v"; // Whitespace according to the C and POSIX locales
static int nextLine(char **restrict lineBuf, size_t *restrict bufLen, uint32_t *restrict lineNo, struct FileStackNode const *where, FILE *file) { static int nextLine(char **restrict lineBuf, size_t *restrict bufLen, uint32_t *restrict lineNo,
struct FileStackNode const *where, FILE *file) {
retry: retry:
++*lineNo; ++*lineNo;
int firstChar = getc(file); int firstChar = getc(file);
@@ -161,7 +162,8 @@ void sdobj_ReadFile(struct FileStackNode const *where, FILE *file) {
#define expectToken(expected, lineType) do { \ #define expectToken(expected, lineType) do { \
getToken(NULL, "'%c' line is too short", (lineType)); \ getToken(NULL, "'%c' line is too short", (lineType)); \
if (strcasecmp(token, (expected)) != 0) \ if (strcasecmp(token, (expected)) != 0) \
fatal(where, lineNo, "Malformed '%c' line: expected \"%s\", got \"%s\"", (lineType), (expected), token); \ fatal(where, lineNo, "Malformed '%c' line: expected \"%s\", got \"%s\"", \
(lineType), (expected), token); \
} while (0) } while (0)
if (!line) if (!line)
@@ -184,7 +186,8 @@ void sdobj_ReadFile(struct FileStackNode const *where, FILE *file) {
numberType = OCT; numberType = OCT;
break; break;
default: default:
fatal(where, lineNo, "This does not look like a SDCC object file (unknown integer format '%c')", lineType); fatal(where, lineNo, "This does not look like a SDCC object file (unknown integer format '%c')",
lineType);
} }
switch (line[0]) { switch (line[0]) {
@@ -255,7 +258,8 @@ void sdobj_ReadFile(struct FileStackNode const *where, FILE *file) {
case 'A': { case 'A': {
if (nbSections == expectedNbAreas) if (nbSections == expectedNbAreas)
warning(where, lineNo, "Got more 'A' lines than the expected %" PRIu32, expectedNbAreas); warning(where, lineNo, "Got more 'A' lines than the expected %" PRIu32,
expectedNbAreas);
fileSections.resize(nbSections + 1); fileSections.resize(nbSections + 1);
fileSections[nbSections].writeIndex = 0; fileSections[nbSections].writeIndex = 0;
#define curSection (fileSections[nbSections].section) #define curSection (fileSections[nbSections].section)
@@ -268,7 +272,8 @@ void sdobj_ReadFile(struct FileStackNode const *where, FILE *file) {
// The following is required for fragment offsets to be reliably predicted // The following is required for fragment offsets to be reliably predicted
for (size_t i = 0; i < nbSections; ++i) { for (size_t i = 0; i < nbSections; ++i) {
if (!strcmp(token, fileSections[i].section->name)) if (!strcmp(token, fileSections[i].section->name))
fatal(where, lineNo, "Area \"%s\" already defined earlier", token); fatal(where, lineNo, "Area \"%s\" already defined earlier",
token);
} }
char const *sectionName = token; // We'll deal with the section's name depending on type char const *sectionName = token; // We'll deal with the section's name depending on type
@@ -279,7 +284,8 @@ void sdobj_ReadFile(struct FileStackNode const *where, FILE *file) {
uint32_t tmp = parseNumber(where, lineNo, token, numberType); uint32_t tmp = parseNumber(where, lineNo, token, numberType);
if (tmp > UINT16_MAX) if (tmp > UINT16_MAX)
fatal(where, lineNo, "Area \"%s\" is larger than the GB address space!?", curSection->name); fatal(where, lineNo, "Area \"%s\" is larger than the GB address space!?",
curSection->name);
curSection->size = tmp; curSection->size = tmp;
expectToken("flags", 'A'); expectToken("flags", 'A');
@@ -298,12 +304,14 @@ void sdobj_ReadFile(struct FileStackNode const *where, FILE *file) {
curSection->name = (char *)malloc(len + 1); curSection->name = (char *)malloc(len + 1);
if (!curSection->name) if (!curSection->name)
fatal(where, lineNo, "Failed to alloc new area's name: %s", strerror(errno)); fatal(where, lineNo, "Failed to alloc new area's name: %s",
strerror(errno));
sprintf(curSection->name, "%s %s", where->name, sectionName); sprintf(curSection->name, "%s %s", where->name, sectionName);
} else { } else {
curSection->name = strdup(sectionName); // We need a pointer that will live longer curSection->name = strdup(sectionName); // We need a pointer that will live longer
if (!curSection->name) if (!curSection->name)
fatal(where, lineNo, "Failed to alloc new area's name: %s", strerror(errno)); fatal(where, lineNo, "Failed to alloc new area's name: %s",
strerror(errno));
} }
expectToken("addr", 'A'); expectToken("addr", 'A');
@@ -345,8 +353,10 @@ void sdobj_ReadFile(struct FileStackNode const *where, FILE *file) {
curSection->isAlignFixed = false; // No such concept! curSection->isAlignFixed = false; // No such concept!
// The array will be allocated if the section does contain data // The array will be allocated if the section does contain data
curSection->data = NULL; curSection->data = NULL;
curSection->nbPatches = 0; curSection->patches = new(std::nothrow) std::vector<struct Patch>();
curSection->patches = NULL; // Same as `data` if (!curSection->patches)
fatal(where, lineNo, "Failed to alloc new area's patches: %s",
strerror(errno));
curSection->fileSymbols = fileSymbols; // IDs are instead per-section curSection->fileSymbols = fileSymbols; // IDs are instead per-section
curSection->nbSymbols = 0; curSection->nbSymbols = 0;
curSection->symbols = NULL; // Will be allocated on demand as well curSection->symbols = NULL; // Will be allocated on demand as well
@@ -358,7 +368,8 @@ void sdobj_ReadFile(struct FileStackNode const *where, FILE *file) {
case 'S': case 'S':
if (nbSymbols == expectedNbSymbols) if (nbSymbols == expectedNbSymbols)
warning(where, lineNo, "Got more 'S' lines than the expected %" PRIu32, expectedNbSymbols); warning(where, lineNo, "Got more 'S' lines than the expected %" PRIu32,
expectedNbSymbols);
// `realloc` is dangerous, as sections contain a pointer to `fileSymbols`. // `realloc` is dangerous, as sections contain a pointer to `fileSymbols`.
// We can try to be nice, but if the pointer moves, it's game over! // We can try to be nice, but if the pointer moves, it's game over!
if (nbSymbols >= expectedNbSymbols) { if (nbSymbols >= expectedNbSymbols) {
@@ -366,7 +377,8 @@ void sdobj_ReadFile(struct FileStackNode const *where, FILE *file) {
sizeof(*fileSymbols) * (nbSymbols + 1)); sizeof(*fileSymbols) * (nbSymbols + 1));
if (!newFileSymbols) if (!newFileSymbols)
fatal(where, lineNo, "Failed to alloc extra symbols: %s", strerror(errno)); fatal(where, lineNo, "Failed to alloc extra symbols: %s",
strerror(errno));
if (newFileSymbols != fileSymbols) if (newFileSymbols != fileSymbols)
fatal(where, lineNo, "Failed to handle extra 'S' lines (pointer moved)"); fatal(where, lineNo, "Failed to handle extra 'S' lines (pointer moved)");
// No need to assign, obviously // No need to assign, obviously
@@ -437,7 +449,8 @@ void sdobj_ReadFile(struct FileStackNode const *where, FILE *file) {
section->symbols = (struct Symbol **)realloc(section->symbols, section->symbols = (struct Symbol **)realloc(section->symbols,
sizeof(section->symbols[0]) * section->nbSymbols); sizeof(section->symbols[0]) * section->nbSymbols);
if (!section->symbols) if (!section->symbols)
fatal(where, lineNo, "Failed to realloc \"%s\"'s symbol list: %s", section->name, strerror(errno)); fatal(where, lineNo, "Failed to realloc \"%s\"'s symbol list: %s",
section->name, strerror(errno));
section->symbols[section->nbSymbols - 1] = symbol; section->symbols[section->nbSymbols - 1] = symbol;
} }
#undef symbol #undef symbol
@@ -458,7 +471,8 @@ void sdobj_ReadFile(struct FileStackNode const *where, FILE *file) {
dataCapacity *= 2; dataCapacity *= 2;
data = (uint8_t *)realloc(data, sizeof(*data) * dataCapacity); data = (uint8_t *)realloc(data, sizeof(*data) * dataCapacity);
if (!data) if (!data)
fatal(where, lineNo, "Failed to realloc data buffer: %s", strerror(errno)); fatal(where, lineNo, "Failed to realloc data buffer: %s",
strerror(errno));
} }
data[nbBytes] = parseByte(where, lineNo, token, numberType); data[nbBytes] = parseByte(where, lineNo, token, numberType);
++nbBytes; ++nbBytes;
@@ -486,7 +500,8 @@ void sdobj_ReadFile(struct FileStackNode const *where, FILE *file) {
getToken(NULL, "'R' line is too short"); getToken(NULL, "'R' line is too short");
areaIdx |= (uint16_t)parseByte(where, lineNo, token, numberType) << 8; areaIdx |= (uint16_t)parseByte(where, lineNo, token, numberType) << 8;
if (areaIdx >= nbSections) if (areaIdx >= nbSections)
fatal(where, lineNo, "'R' line references area #%" PRIu16 ", but there are only %zu (so far)", areaIdx, nbSections); fatal(where, lineNo, "'R' line references area #%" PRIu16 ", but there are only %zu (so far)",
areaIdx, nbSections);
assert(!fileSections.empty()); // There should be at least one, from the above check assert(!fileSections.empty()); // There should be at least one, from the above check
struct Section *section = fileSections[areaIdx].section; struct Section *section = fileSections[areaIdx].section;
uint16_t *writeIndex = &fileSections[areaIdx].writeIndex; uint16_t *writeIndex = &fileSections[areaIdx].writeIndex;
@@ -495,19 +510,22 @@ void sdobj_ReadFile(struct FileStackNode const *where, FILE *file) {
if (section->isAddressFixed) { if (section->isAddressFixed) {
if (addr < section->org) if (addr < section->org)
fatal(where, lineNo, "'T' line reports address $%04" PRIx16 " in \"%s\", which starts at $%04" PRIx16, addr, section->name, section->org); fatal(where, lineNo, "'T' line reports address $%04" PRIx16 " in \"%s\", which starts at $%04" PRIx16,
addr, section->name, section->org);
addr -= section->org; addr -= section->org;
} }
// Lines are emitted that violate this check but contain no "payload"; // Lines are emitted that violate this check but contain no "payload";
// ignore those. "Empty" lines shouldn't trigger allocation, either. // ignore those. "Empty" lines shouldn't trigger allocation, either.
if (nbBytes != ADDR_SIZE) { if (nbBytes != ADDR_SIZE) {
if (addr != *writeIndex) if (addr != *writeIndex)
fatal(where, lineNo, "'T' lines which don't append to their section are not supported (%" PRIu16 " != %" PRIu16 ")", addr, *writeIndex); fatal(where, lineNo, "'T' lines which don't append to their section are not supported (%" PRIu16 " != %" PRIu16 ")",
addr, *writeIndex);
if (!section->data) { if (!section->data) {
assert(section->size != 0); assert(section->size != 0);
section->data = (uint8_t *)malloc(section->size); section->data = (uint8_t *)malloc(section->size);
if (!section->data) if (!section->data)
fatal(where, lineNo, "Failed to alloc data for \"%s\": %s", section->name, strerror(errno)); fatal(where, lineNo, "Failed to alloc data for \"%s\": %s",
section->name, strerror(errno));
} }
} }
@@ -536,9 +554,11 @@ void sdobj_ReadFile(struct FileStackNode const *where, FILE *file) {
uint8_t offset = parseByte(where, lineNo, token, numberType); uint8_t offset = parseByte(where, lineNo, token, numberType);
if (offset < ADDR_SIZE) if (offset < ADDR_SIZE)
fatal(where, lineNo, "Relocation index cannot point to header (%" PRIu16 " < %u)", offset, ADDR_SIZE); fatal(where, lineNo, "Relocation index cannot point to header (%" PRIu16 " < %u)",
offset, ADDR_SIZE);
if (offset >= nbBytes) if (offset >= nbBytes)
fatal(where, lineNo, "Relocation index is out of bounds (%" PRIu16 " >= %zu)", offset, nbBytes); fatal(where, lineNo, "Relocation index is out of bounds (%" PRIu16 " >= %zu)",
offset, nbBytes);
getToken(NULL, "Incomplete relocation"); getToken(NULL, "Incomplete relocation");
uint16_t idx = parseByte(where, lineNo, token, numberType); uint16_t idx = parseByte(where, lineNo, token, numberType);
@@ -553,43 +573,45 @@ void sdobj_ReadFile(struct FileStackNode const *where, FILE *file) {
warning(where, lineNo, "Unknown reloc flags 0x%x", flags & ~RELOC_ALL_FLAGS); warning(where, lineNo, "Unknown reloc flags 0x%x", flags & ~RELOC_ALL_FLAGS);
// Turn this into a Patch // Turn this into a Patch
section->patches = (struct Patch *)realloc(section->patches, struct Patch &patch = section->patches->emplace_back();
sizeof(section->patches[0]) * (section->nbPatches + 1));
if (!section->patches)
fatal(where, lineNo, "Failed to alloc extra patch for \"%s\"", section->name);
struct Patch *patch = &section->patches[section->nbPatches];
patch->lineNo = lineNo; patch.lineNo = lineNo;
patch->src = where; patch.src = where;
patch->offset = offset - writtenOfs + *writeIndex; patch.offset = offset - writtenOfs + *writeIndex;
if (section->nbPatches != 0 && section->patches[section->nbPatches - 1].offset >= patch->offset) if (section->patches->size() > 1) {
fatal(where, lineNo, "Relocs not sorted by offset are not supported (%" PRIu32 " >= %" PRIu32 ")", section->patches[section->nbPatches - 1].offset, patch->offset); uint32_t prevOffset = (*section->patches)[section->patches->size() - 2].offset;
patch->pcSection = section; // No need to fill `pcSectionID`, then if (prevOffset>= patch.offset)
patch->pcOffset = patch->offset - 1; // For `jr`s fatal(where, lineNo, "Relocs not sorted by offset are not supported (%" PRIu32 " >= %" PRIu32 ")",
prevOffset, patch.offset);
}
patch.pcSection = section; // No need to fill `pcSectionID`, then
patch.pcOffset = patch.offset - 1; // For `jr`s
patch->type = (flags & 1 << RELOC_SIZE) ? PATCHTYPE_BYTE : PATCHTYPE_WORD; patch.type = (flags & 1 << RELOC_SIZE) ? PATCHTYPE_BYTE : PATCHTYPE_WORD;
uint8_t nbBaseBytes = patch->type == PATCHTYPE_BYTE ? ADDR_SIZE : 2; uint8_t nbBaseBytes = patch.type == PATCHTYPE_BYTE ? ADDR_SIZE : 2;
uint32_t baseValue = 0; uint32_t baseValue = 0;
assert(offset < nbBytes); assert(offset < nbBytes);
if (nbBytes - offset < nbBaseBytes) if (nbBytes - offset < nbBaseBytes)
fatal(where, lineNo, "Reloc would patch out of bounds (%" PRIu8 " > %zu)", nbBaseBytes, nbBytes - offset); fatal(where, lineNo, "Reloc would patch out of bounds (%" PRIu8 " > %zu)",
nbBaseBytes, nbBytes - offset);
for (uint8_t i = 0; i < nbBaseBytes; ++i) for (uint8_t i = 0; i < nbBaseBytes; ++i)
baseValue = baseValue | data[offset + i] << (8 * i); baseValue = baseValue | data[offset + i] << (8 * i);
// Extra size that must be reserved for additional operators // Extra size that must be reserved for additional operators
#define RPN_EXTRA_SIZE (5 + 1 + 5 + 1 + 5 + 1) // >> 8 & $FF, then + <baseValue> #define RPN_EXTRA_SIZE (5 + 1 + 5 + 1 + 5 + 1) // >> 8 & $FF, then + <baseValue>
#define allocPatch(size) do { \ #define allocPatch(size) do { \
patch->rpnSize = (size); \ patch.rpnSize = (size); \
patch->rpnExpression = (uint8_t *)malloc(patch->rpnSize + RPN_EXTRA_SIZE); \ patch.rpnExpression = (uint8_t *)malloc(patch.rpnSize + RPN_EXTRA_SIZE); \
if (!patch->rpnExpression) \ if (!patch.rpnExpression) \
fatal(where, lineNo, "Failed to alloc RPN expression: %s", strerror(errno)); \ fatal(where, lineNo, "Failed to alloc RPN expression: %s", strerror(errno)); \
} while (0) } while (0)
// Bit 4 specifies signedness, but I don't think that matters? // Bit 4 specifies signedness, but I don't think that matters?
// Generate a RPN expression from the info and flags // Generate a RPN expression from the info and flags
if (flags & 1 << RELOC_ISSYM) { if (flags & 1 << RELOC_ISSYM) {
if (idx >= nbSymbols) if (idx >= nbSymbols)
fatal(where, lineNo, "Reloc refers to symbol #%" PRIu16 " out of %zu", idx, nbSymbols); fatal(where, lineNo, "Reloc refers to symbol #%" PRIu16 " out of %zu",
idx, nbSymbols);
struct Symbol const *sym = fileSymbols[idx]; struct Symbol const *sym = fileSymbols[idx];
// SDCC has a bunch of "magic symbols" that start with a // SDCC has a bunch of "magic symbols" that start with a
@@ -602,32 +624,34 @@ void sdobj_ReadFile(struct FileStackNode const *where, FILE *file) {
break; break;
} }
if (idx == nbSymbols) if (idx == nbSymbols)
fatal(where, lineNo, "\"%s\" is missing a reference to \"%s\"", sym->name, &sym->name[1]); fatal(where, lineNo, "\"%s\" is missing a reference to \"%s\"",
sym->name, &sym->name[1]);
allocPatch(5); allocPatch(5);
patch->rpnExpression[0] = RPN_BANK_SYM; patch.rpnExpression[0] = RPN_BANK_SYM;
patch->rpnExpression[1] = idx; patch.rpnExpression[1] = idx;
patch->rpnExpression[2] = idx >> 8; patch.rpnExpression[2] = idx >> 8;
patch->rpnExpression[3] = idx >> 16; patch.rpnExpression[3] = idx >> 16;
patch->rpnExpression[4] = idx >> 24; patch.rpnExpression[4] = idx >> 24;
} else if (sym->name[0] == 'l' && sym->name[1] == '_') { } else if (sym->name[0] == 'l' && sym->name[1] == '_') {
allocPatch(1 + strlen(&sym->name[2]) + 1); allocPatch(1 + strlen(&sym->name[2]) + 1);
patch->rpnExpression[0] = RPN_SIZEOF_SECT; patch.rpnExpression[0] = RPN_SIZEOF_SECT;
strcpy((char *)&patch->rpnExpression[1], &sym->name[2]); strcpy((char *)&patch.rpnExpression[1], &sym->name[2]);
} else if (sym->name[0] == 's' && sym->name[1] == '_') { } else if (sym->name[0] == 's' && sym->name[1] == '_') {
allocPatch(1 + strlen(&sym->name[2]) + 1); allocPatch(1 + strlen(&sym->name[2]) + 1);
patch->rpnExpression[0] = RPN_STARTOF_SECT; patch.rpnExpression[0] = RPN_STARTOF_SECT;
strcpy((char *)&patch->rpnExpression[1], &sym->name[2]); strcpy((char *)&patch.rpnExpression[1], &sym->name[2]);
} else { } else {
allocPatch(5); allocPatch(5);
patch->rpnExpression[0] = RPN_SYM; patch.rpnExpression[0] = RPN_SYM;
patch->rpnExpression[1] = idx; patch.rpnExpression[1] = idx;
patch->rpnExpression[2] = idx >> 8; patch.rpnExpression[2] = idx >> 8;
patch->rpnExpression[3] = idx >> 16; patch.rpnExpression[3] = idx >> 16;
patch->rpnExpression[4] = idx >> 24; patch.rpnExpression[4] = idx >> 24;
} }
} else { } else {
if (idx >= nbSections) if (idx >= nbSections)
fatal(where, lineNo, "Reloc refers to area #%" PRIu16 " out of %zu", idx, nbSections); fatal(where, lineNo, "Reloc refers to area #%" PRIu16 " out of %zu",
idx, nbSections);
// It gets funky. If the area is absolute, *actually*, we // It gets funky. If the area is absolute, *actually*, we
// must not add its base address, as the assembler will // must not add its base address, as the assembler will
// already have added it in `baseValue`. // already have added it in `baseValue`.
@@ -651,30 +675,32 @@ void sdobj_ReadFile(struct FileStackNode const *where, FILE *file) {
if (other) if (other)
baseValue += other->size; baseValue += other->size;
allocPatch(1 + strlen(name) + 1); allocPatch(1 + strlen(name) + 1);
patch->rpnSize = 1 + strlen(name) + 1; patch.rpnSize = 1 + strlen(name) + 1;
patch->rpnExpression = (uint8_t *)malloc(patch->rpnSize + RPN_EXTRA_SIZE); patch.rpnExpression = (uint8_t *)malloc(patch.rpnSize + RPN_EXTRA_SIZE);
if (!patch->rpnExpression) if (!patch.rpnExpression)
fatal(where, lineNo, "Failed to alloc RPN expression: %s", strerror(errno)); fatal(where, lineNo, "Failed to alloc RPN expression: %s",
patch->rpnExpression[0] = RPN_STARTOF_SECT; strerror(errno));
patch.rpnExpression[0] = RPN_STARTOF_SECT;
// The cast is fine, it's just different signedness // The cast is fine, it's just different signedness
strcpy((char *)&patch->rpnExpression[1], name); strcpy((char *)&patch.rpnExpression[1], name);
} }
#undef allocPatch #undef allocPatch
patch->rpnExpression[patch->rpnSize] = RPN_CONST; patch.rpnExpression[patch.rpnSize] = RPN_CONST;
patch->rpnExpression[patch->rpnSize + 1] = baseValue; patch.rpnExpression[patch.rpnSize + 1] = baseValue;
patch->rpnExpression[patch->rpnSize + 2] = baseValue >> 8; patch.rpnExpression[patch.rpnSize + 2] = baseValue >> 8;
patch->rpnExpression[patch->rpnSize + 3] = baseValue >> 16; patch.rpnExpression[patch.rpnSize + 3] = baseValue >> 16;
patch->rpnExpression[patch->rpnSize + 4] = baseValue >> 24; patch.rpnExpression[patch.rpnSize + 4] = baseValue >> 24;
patch->rpnExpression[patch->rpnSize + 5] = RPN_ADD; patch.rpnExpression[patch.rpnSize + 5] = RPN_ADD;
patch->rpnSize += 5 + 1; patch.rpnSize += 5 + 1;
if (patch->type == PATCHTYPE_BYTE) { if (patch.type == PATCHTYPE_BYTE) {
// Despite the flag's name, as soon as it is set, 3 bytes // Despite the flag's name, as soon as it is set, 3 bytes
// are present, so we must skip two of them // are present, so we must skip two of them
if (flags & 1 << RELOC_EXPR16) { if (flags & 1 << RELOC_EXPR16) {
if (*writeIndex + (offset - writtenOfs) > section->size) if (*writeIndex + (offset - writtenOfs) > section->size)
fatal(where, lineNo, "'T' line writes past \"%s\"'s end (%u > %" PRIu16 ")", section->name, *writeIndex + (offset - writtenOfs), section->size); fatal(where, lineNo, "'T' line writes past \"%s\"'s end (%u > %" PRIu16 ")",
section->name, *writeIndex + (offset - writtenOfs), section->size);
// Copy all bytes up to those (plus the byte that we'll overwrite) // Copy all bytes up to those (plus the byte that we'll overwrite)
memcpy(&section->data[*writeIndex], &data[writtenOfs], offset - writtenOfs + 1); memcpy(&section->data[*writeIndex], &data[writtenOfs], offset - writtenOfs + 1);
*writeIndex += offset - writtenOfs + 1; *writeIndex += offset - writtenOfs + 1;
@@ -684,49 +710,49 @@ void sdobj_ReadFile(struct FileStackNode const *where, FILE *file) {
// Append the necessary operations... // Append the necessary operations...
if (flags & 1 << RELOC_ISPCREL) { if (flags & 1 << RELOC_ISPCREL) {
// The result must *not* be truncated for those! // The result must *not* be truncated for those!
patch->type = PATCHTYPE_JR; patch.type = PATCHTYPE_JR;
// TODO: check the other flags? // TODO: check the other flags?
} else if (flags & 1 << RELOC_EXPR24 && flags & 1 << RELOC_BANKBYTE) { } else if (flags & 1 << RELOC_EXPR24 && flags & 1 << RELOC_BANKBYTE) {
patch->rpnExpression[patch->rpnSize] = RPN_CONST; patch.rpnExpression[patch.rpnSize] = RPN_CONST;
patch->rpnExpression[patch->rpnSize + 1] = 16; patch.rpnExpression[patch.rpnSize + 1] = 16;
patch->rpnExpression[patch->rpnSize + 2] = 16 >> 8; patch.rpnExpression[patch.rpnSize + 2] = 16 >> 8;
patch->rpnExpression[patch->rpnSize + 3] = 16 >> 16; patch.rpnExpression[patch.rpnSize + 3] = 16 >> 16;
patch->rpnExpression[patch->rpnSize + 4] = 16 >> 24; patch.rpnExpression[patch.rpnSize + 4] = 16 >> 24;
patch->rpnExpression[patch->rpnSize + 5] = (flags & 1 << RELOC_SIGNED) ? RPN_SHR : RPN_USHR; patch.rpnExpression[patch.rpnSize + 5] = (flags & 1 << RELOC_SIGNED) ? RPN_SHR : RPN_USHR;
patch->rpnSize += 5 + 1; patch.rpnSize += 5 + 1;
} else { } else {
if (flags & 1 << RELOC_EXPR16 && flags & 1 << RELOC_WHICHBYTE) { if (flags & 1 << RELOC_EXPR16 && flags & 1 << RELOC_WHICHBYTE) {
patch->rpnExpression[patch->rpnSize] = RPN_CONST; patch.rpnExpression[patch.rpnSize] = RPN_CONST;
patch->rpnExpression[patch->rpnSize + 1] = 8; patch.rpnExpression[patch.rpnSize + 1] = 8;
patch->rpnExpression[patch->rpnSize + 2] = 8 >> 8; patch.rpnExpression[patch.rpnSize + 2] = 8 >> 8;
patch->rpnExpression[patch->rpnSize + 3] = 8 >> 16; patch.rpnExpression[patch.rpnSize + 3] = 8 >> 16;
patch->rpnExpression[patch->rpnSize + 4] = 8 >> 24; patch.rpnExpression[patch.rpnSize + 4] = 8 >> 24;
patch->rpnExpression[patch->rpnSize + 5] = (flags & 1 << RELOC_SIGNED) ? RPN_SHR : RPN_USHR; patch.rpnExpression[patch.rpnSize + 5] = (flags & 1 << RELOC_SIGNED) ? RPN_SHR : RPN_USHR;
patch->rpnSize += 5 + 1; patch.rpnSize += 5 + 1;
} }
patch->rpnExpression[patch->rpnSize] = RPN_CONST; patch.rpnExpression[patch.rpnSize] = RPN_CONST;
patch->rpnExpression[patch->rpnSize + 1] = 0xFF; patch.rpnExpression[patch.rpnSize + 1] = 0xFF;
patch->rpnExpression[patch->rpnSize + 2] = 0xFF >> 8; patch.rpnExpression[patch.rpnSize + 2] = 0xFF >> 8;
patch->rpnExpression[patch->rpnSize + 3] = 0xFF >> 16; patch.rpnExpression[patch.rpnSize + 3] = 0xFF >> 16;
patch->rpnExpression[patch->rpnSize + 4] = 0xFF >> 24; patch.rpnExpression[patch.rpnSize + 4] = 0xFF >> 24;
patch->rpnExpression[patch->rpnSize + 5] = RPN_AND; patch.rpnExpression[patch.rpnSize + 5] = RPN_AND;
patch->rpnSize += 5 + 1; patch.rpnSize += 5 + 1;
} }
} else if (flags & 1 << RELOC_ISPCREL) { } else if (flags & 1 << RELOC_ISPCREL) {
assert(patch->type == PATCHTYPE_WORD); assert(patch.type == PATCHTYPE_WORD);
fatal(where, lineNo, "16-bit PC-relative relocations are not supported"); fatal(where, lineNo, "16-bit PC-relative relocations are not supported");
} else if (flags & (1 << RELOC_EXPR16 | 1 << RELOC_EXPR24)) { } else if (flags & (1 << RELOC_EXPR16 | 1 << RELOC_EXPR24)) {
fatal(where, lineNo, "Flags 0x%x are not supported for 16-bit relocs", flags & (1 << RELOC_EXPR16 | 1 << RELOC_EXPR24)); fatal(where, lineNo, "Flags 0x%x are not supported for 16-bit relocs",
flags & (1 << RELOC_EXPR16 | 1 << RELOC_EXPR24));
} }
++section->nbPatches;
} }
// If there is some data left to append, do so // If there is some data left to append, do so
if (writtenOfs != nbBytes) { if (writtenOfs != nbBytes) {
assert(nbBytes > writtenOfs); assert(nbBytes > writtenOfs);
if (*writeIndex + (nbBytes - writtenOfs) > section->size) if (*writeIndex + (nbBytes - writtenOfs) > section->size)
fatal(where, lineNo, "'T' line writes past \"%s\"'s end (%zu > %" PRIu16 ")", section->name, *writeIndex + (nbBytes - writtenOfs), section->size); fatal(where, lineNo, "'T' line writes past \"%s\"'s end (%zu > %" PRIu16 ")",
section->name, *writeIndex + (nbBytes - writtenOfs), section->size);
memcpy(&section->data[*writeIndex], &data[writtenOfs], nbBytes - writtenOfs); memcpy(&section->data[*writeIndex], &data[writtenOfs], nbBytes - writtenOfs);
*writeIndex += nbBytes - writtenOfs; *writeIndex += nbBytes - writtenOfs;
} }
@@ -756,7 +782,8 @@ void sdobj_ReadFile(struct FileStackNode const *where, FILE *file) {
// RAM sections can have a size, but don't get any data (they shouldn't have any) // RAM sections can have a size, but don't get any data (they shouldn't have any)
if (fileSections[i].writeIndex != section->size && fileSections[i].writeIndex != 0) if (fileSections[i].writeIndex != section->size && fileSections[i].writeIndex != 0)
fatal(where, lineNo, "\"%s\" was not fully written (%" PRIu16 " < %" PRIu16 ")", section->name, fileSections[i].writeIndex, section->size); fatal(where, lineNo, "\"%s\" was not fully written (%" PRIu16 " < %" PRIu16 ")",
section->name, fileSections[i].writeIndex, section->size);
// This must be done last, so that `->data` is not NULL anymore // This must be done last, so that `->data` is not NULL anymore
sect_AddSection(section); sect_AddSection(section);

View File

@@ -160,8 +160,8 @@ static void mergeSections(struct Section *target, struct Section *other, enum Se
other->data = NULL; // Prevent a double free() other->data = NULL; // Prevent a double free()
} }
// Adjust patches' PC offsets // Adjust patches' PC offsets
for (uint32_t patchID = 0; patchID < other->nbPatches; patchID++) for (struct Patch &patch : *other->patches)
other->patches[patchID].pcOffset += other->offset; patch.pcOffset += other->offset;
} else if (target->data) { } else if (target->data) {
assert(other->size == 0); assert(other->size == 0);
} }