Run clang-format on everything (#1332)

This commit is contained in:
Sylvie
2024-03-04 14:22:49 -05:00
committed by GitHub
parent b004648a13
commit e74073e480
66 changed files with 6091 additions and 4957 deletions

View File

@@ -1,5 +1,7 @@
/* SPDX-License-Identifier: MIT */
#include "link/assign.hpp"
#include <algorithm>
#include <deque>
#include <inttypes.h>
@@ -8,18 +10,17 @@
#include <string.h>
#include <vector>
#include "link/assign.hpp"
#include "link/section.hpp"
#include "link/symbol.hpp"
#include "link/object.hpp"
#include "link/main.hpp"
#include "link/output.hpp"
#include "error.hpp"
#include "helpers.hpp"
#include "itertools.hpp"
#include "linkdefs.hpp"
#include "link/main.hpp"
#include "link/object.hpp"
#include "link/output.hpp"
#include "link/section.hpp"
#include "link/symbol.hpp"
struct MemoryLocation {
uint16_t address;
uint32_t bank;
@@ -36,15 +37,13 @@ std::vector<std::deque<FreeSpace>> memory[SECTTYPE_INVALID];
uint64_t nbSectionsToAssign;
// Init the free space-modelling structs
static void initFreeSpace()
{
static void initFreeSpace() {
for (enum SectionType type : EnumSeq(SECTTYPE_INVALID)) {
memory[type].resize(nbbanks(type));
for (std::deque<FreeSpace> &bankMem : memory[type]) {
bankMem.push_back({
.address = sectionTypeInfo[type].startAddr,
.size = sectionTypeInfo[type].size
});
bankMem.push_back(
{.address = sectionTypeInfo[type].startAddr, .size = sectionTypeInfo[type].size}
);
}
}
}
@@ -54,8 +53,7 @@ static void initFreeSpace()
* @param section The section to assign
* @param location The location to assign the section to
*/
static void assignSection(Section &section, MemoryLocation const &location)
{
static void assignSection(Section &section, MemoryLocation const &location) {
// Propagate the assigned location to all UNIONs/FRAGMENTs
// so `jr` patches in them will have the correct offset
for (Section *next = &section; next != nullptr; next = next->nextu) {
@@ -77,9 +75,9 @@ static void assignSection(Section &section, MemoryLocation const &location)
* @param location The location to attempt placing the section at
* @return True if the location is suitable, false otherwise.
*/
static bool isLocationSuitable(Section const &section, FreeSpace const &freeSpace,
MemoryLocation const &location)
{
static bool isLocationSuitable(
Section const &section, FreeSpace const &freeSpace, MemoryLocation const &location
) {
if (section.isAddressFixed && section.org != location.address)
return false;
@@ -99,8 +97,7 @@ static bool isLocationSuitable(Section const &section, FreeSpace const &freeSpac
* @return The index into `memory[section->type]` of the free space encompassing the location,
* or -1 if none was found
*/
static ssize_t getPlacement(Section const &section, MemoryLocation &location)
{
static ssize_t getPlacement(Section const &section, MemoryLocation &location) {
SectionTypeInfo const &typeInfo = sectionTypeInfo[section.type];
static uint16_t curScrambleROM = 0;
@@ -166,16 +163,16 @@ static ssize_t getPlacement(Section const &section, MemoryLocation &location)
// If that location is past the current block's end,
// go forwards until that is no longer the case.
while (spaceIdx < bankMem.size() && location.address >=
bankMem[spaceIdx].address + bankMem[spaceIdx].size)
while (spaceIdx < bankMem.size()
&& location.address >= bankMem[spaceIdx].address + bankMem[spaceIdx].size)
spaceIdx++;
// Try again with the new location/free space combo
}
// Try again in the next bank, if one is available.
// Try scrambled banks in descending order until no bank in the scrambled range is available.
// Otherwise, try in ascending order.
// Try scrambled banks in descending order until no bank in the scrambled range is
// available. Otherwise, try in ascending order.
if (section.isBankFixed) {
return -1;
} else if (scrambleROMX && section.type == SECTTYPE_ROMX && location.bank <= scrambleROMX) {
@@ -213,20 +210,17 @@ static ssize_t getPlacement(Section const &section, MemoryLocation &location)
* sections of decreasing size.
* @param section The section to place
*/
static void placeSection(Section &section)
{
static void placeSection(Section &section) {
MemoryLocation location;
// Specially handle 0-byte SECTIONs, as they can't overlap anything
if (section.size == 0) {
// Unless the SECTION's address was fixed, the starting address
// is fine for any alignment, as checked in sect_DoSanityChecks.
location.address = section.isAddressFixed
? section.org
: sectionTypeInfo[section.type].startAddr;
location.bank = section.isBankFixed
? section.bank
: sectionTypeInfo[section.type].firstBank;
location.address =
section.isAddressFixed ? section.org : sectionTypeInfo[section.type].startAddr;
location.bank =
section.isBankFixed ? section.bank : sectionTypeInfo[section.type].firstBank;
assignSection(section, location);
return;
}
@@ -234,14 +228,14 @@ static void placeSection(Section &section)
// Place section using first-fit decreasing algorithm
// https://en.wikipedia.org/wiki/Bin_packing_problem#First-fit_algorithm
if (ssize_t spaceIdx = getPlacement(section, location); spaceIdx != -1) {
std::deque<FreeSpace> &bankMem = memory[section.type][location.bank -
sectionTypeInfo[section.type].firstBank];
std::deque<FreeSpace> &bankMem =
memory[section.type][location.bank - sectionTypeInfo[section.type].firstBank];
FreeSpace &freeSpace = bankMem[spaceIdx];
assignSection(section, location);
// Update the free space
bool noLeftSpace = freeSpace.address == section.org;
bool noLeftSpace = freeSpace.address == section.org;
bool noRightSpace = freeSpace.address + freeSpace.size == section.org + section.size;
if (noLeftSpace && noRightSpace) {
// The free space is entirely deleted
@@ -249,11 +243,12 @@ static void placeSection(Section &section)
} else if (!noLeftSpace && !noRightSpace) {
// The free space is split in two
// Append the new space after the original one
bankMem.insert(bankMem.begin() + spaceIdx + 1, {
.address = (uint16_t)(section.org + section.size),
.size = (uint16_t)(freeSpace.address + freeSpace.size -
section.org - section.size)
});
bankMem.insert(
bankMem.begin() + spaceIdx + 1,
{.address = (uint16_t)(section.org + section.size),
.size =
(uint16_t)(freeSpace.address + freeSpace.size - section.org - section.size)}
);
// Resize the original space (address is unmodified)
freeSpace.size = section.org - freeSpace.address;
} else {
@@ -271,41 +266,66 @@ static void placeSection(Section &section)
if (section.isBankFixed && nbbanks(section.type) != 1) {
if (section.isAddressFixed)
snprintf(where, sizeof(where), "at $%02" PRIx32 ":%04" PRIx16,
section.bank, section.org);
snprintf(
where, sizeof(where), "at $%02" PRIx32 ":%04" PRIx16, section.bank, section.org
);
else if (section.isAlignFixed)
snprintf(where, sizeof(where), "in bank $%02" PRIx32 " with align mask %" PRIx16,
section.bank, (uint16_t)~section.alignMask);
snprintf(
where,
sizeof(where),
"in bank $%02" PRIx32 " with align mask %" PRIx16,
section.bank,
(uint16_t)~section.alignMask
);
else
snprintf(where, sizeof(where), "in bank $%02" PRIx32, section.bank);
} else {
if (section.isAddressFixed)
snprintf(where, sizeof(where), "at address $%04" PRIx16, section.org);
else if (section.isAlignFixed)
snprintf(where, sizeof(where), "with align mask %" PRIx16 " and offset %" PRIx16,
(uint16_t)~section.alignMask, section.alignOfs);
snprintf(
where,
sizeof(where),
"with align mask %" PRIx16 " and offset %" PRIx16,
(uint16_t)~section.alignMask,
section.alignOfs
);
else
strcpy(where, "anywhere");
}
// If a section failed to go to several places, nothing we can report
if (!section.isBankFixed || !section.isAddressFixed)
errx("Unable to place \"%s\" (%s section) %s",
section.name.c_str(), sectionTypeInfo[section.type].name.c_str(), where);
errx(
"Unable to place \"%s\" (%s section) %s",
section.name.c_str(),
sectionTypeInfo[section.type].name.c_str(),
where
);
// If the section just can't fit the bank, report that
else if (section.org + section.size > endaddr(section.type) + 1)
errx("Unable to place \"%s\" (%s section) %s: section runs past end of region ($%04x > $%04x)",
section.name.c_str(), sectionTypeInfo[section.type].name.c_str(), where,
section.org + section.size, endaddr(section.type) + 1);
errx(
"Unable to place \"%s\" (%s section) %s: section runs past end of region ($%04x > "
"$%04x)",
section.name.c_str(),
sectionTypeInfo[section.type].name.c_str(),
where,
section.org + section.size,
endaddr(section.type) + 1
);
// Otherwise there is overlap with another section
else
errx("Unable to place \"%s\" (%s section) %s: section overlaps with \"%s\"",
section.name.c_str(), sectionTypeInfo[section.type].name.c_str(), where,
out_OverlappingSection(section)->name.c_str());
errx(
"Unable to place \"%s\" (%s section) %s: section overlaps with \"%s\"",
section.name.c_str(),
sectionTypeInfo[section.type].name.c_str(),
where,
out_OverlappingSection(section)->name.c_str()
);
}
#define BANK_CONSTRAINED (1 << 2)
#define ORG_CONSTRAINED (1 << 1)
#define BANK_CONSTRAINED (1 << 2)
#define ORG_CONSTRAINED (1 << 1)
#define ALIGN_CONSTRAINED (1 << 0)
static std::deque<Section *> unassignedSections[1 << 3];
@@ -314,8 +334,7 @@ static std::deque<Section *> unassignedSections[1 << 3];
* This is so the most-constrained sections are placed first
* @param section The section to categorize
*/
static void categorizeSection(Section &section)
{
static void categorizeSection(Section &section) {
uint8_t constraints = 0;
if (section.isBankFixed)
@@ -337,8 +356,7 @@ static void categorizeSection(Section &section)
nbSectionsToAssign++;
}
void assign_AssignSections()
{
void assign_AssignSections() {
verbosePrint("Beginning assignment...\n");
// Initialize assignment
@@ -368,8 +386,7 @@ void assign_AssignSections()
for (int8_t constraints = BANK_CONSTRAINED | ALIGN_CONSTRAINED; constraints >= 0;
constraints--) {
for (Section *section : unassignedSections[constraints]) {
fprintf(stderr, "%c \"%s\"", nbSections == 0 ? ';': ',',
section->name.c_str());
fprintf(stderr, "%c \"%s\"", nbSections == 0 ? ';' : ',', section->name.c_str());
nbSections++;
if (nbSections == 10)
goto max_out;
@@ -384,7 +401,8 @@ max_out:
}
// Assign all remaining sections by decreasing constraint order
for (int8_t constraints = BANK_CONSTRAINED | ALIGN_CONSTRAINED; constraints >= 0; constraints--) {
for (int8_t constraints = BANK_CONSTRAINED | ALIGN_CONSTRAINED; constraints >= 0;
constraints--) {
for (Section *section : unassignedSections[constraints])
placeSection(*section);

View File

@@ -1,51 +1,51 @@
/* SPDX-License-Identifier: MIT */
#include <sys/stat.h>
#include <sys/types.h>
#include <assert.h>
#include <inttypes.h>
#include <limits.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <string>
#include <variant>
#include <vector>
#include "error.hpp"
#include "extern/getopt.hpp"
#include "itertools.hpp"
#include "linkdefs.hpp"
#include "platform.hpp"
#include "script.hpp"
#include "version.hpp"
#include "link/assign.hpp"
#include "link/object.hpp"
#include "link/output.hpp"
#include "link/patch.hpp"
#include "link/section.hpp"
#include "script.hpp"
#include "link/symbol.hpp"
#include "extern/getopt.hpp"
#include "error.hpp"
#include "itertools.hpp"
#include "linkdefs.hpp"
#include "platform.hpp"
#include "version.hpp"
bool isDmgMode; // -d
char *linkerScriptName; // -l
char const *mapFileName; // -m
bool noSymInMap; // -M
char const *symFileName; // -n
char const *overlayFileName; // -O
char const *outputFileName; // -o
uint8_t padValue; // -p
bool isDmgMode; // -d
char *linkerScriptName; // -l
char const *mapFileName; // -m
bool noSymInMap; // -M
char const *symFileName; // -n
char const *overlayFileName; // -O
char const *outputFileName; // -o
uint8_t padValue; // -p
// Setting these three to 0 disables the functionality
uint16_t scrambleROMX = 0; // -S
uint16_t scrambleROMX = 0; // -S
uint8_t scrambleWRAMX = 0;
uint8_t scrambleSRAM = 0;
bool is32kMode; // -t
bool beVerbose; // -v
bool isWRAM0Mode; // -w
bool disablePadding; // -x
bool is32kMode; // -t
bool beVerbose; // -v
bool isWRAM0Mode; // -w
bool disablePadding; // -x
FILE *linkerScript;
@@ -72,8 +72,7 @@ std::string const &FileStackNode::name() const {
}
// Helper function to dump a file stack to stderr
std::string const *FileStackNode::dumpFileStack() const
{
std::string const *FileStackNode::dumpFileStack() const {
std::string const *lastName;
if (parent) {
@@ -95,9 +94,9 @@ std::string const *FileStackNode::dumpFileStack() const
return lastName;
}
void printDiag(char const *fmt, va_list args, char const *type,
FileStackNode const *where, uint32_t lineNo)
{
void printDiag(
char const *fmt, va_list args, char const *type, FileStackNode const *where, uint32_t lineNo
) {
fputs(type, stderr);
fputs(": ", stderr);
if (where) {
@@ -108,8 +107,7 @@ void printDiag(char const *fmt, va_list args, char const *type,
putc('\n', stderr);
}
void warning(FileStackNode const *where, uint32_t lineNo, char const *fmt, ...)
{
void warning(FileStackNode const *where, uint32_t lineNo, char const *fmt, ...) {
va_list args;
va_start(args, fmt);
@@ -117,8 +115,7 @@ void warning(FileStackNode const *where, uint32_t lineNo, char const *fmt, ...)
va_end(args);
}
void error(FileStackNode const *where, uint32_t lineNo, char const *fmt, ...)
{
void error(FileStackNode const *where, uint32_t lineNo, char const *fmt, ...) {
va_list args;
va_start(args, fmt);
@@ -129,8 +126,7 @@ void error(FileStackNode const *where, uint32_t lineNo, char const *fmt, ...)
nbErrors++;
}
void argErr(char flag, char const *fmt, ...)
{
void argErr(char flag, char const *fmt, ...) {
va_list args;
fprintf(stderr, "error: Invalid argument for option '%c': ", flag);
@@ -143,8 +139,7 @@ void argErr(char flag, char const *fmt, ...)
nbErrors++;
}
[[noreturn]] void fatal(FileStackNode const *where, uint32_t lineNo, char const *fmt, ...)
{
[[noreturn]] void fatal(FileStackNode const *where, uint32_t lineNo, char const *fmt, ...) {
va_list args;
va_start(args, fmt);
@@ -154,8 +149,9 @@ void argErr(char flag, char const *fmt, ...)
if (nbErrors != UINT32_MAX)
nbErrors++;
fprintf(stderr, "Linking aborted after %" PRIu32 " error%s\n", nbErrors,
nbErrors == 1 ? "" : "s");
fprintf(
stderr, "Linking aborted after %" PRIu32 " error%s\n", nbErrors, nbErrors == 1 ? "" : "s"
);
exit(1);
}
@@ -173,41 +169,41 @@ static const char *optstring = "dl:m:Mn:O:o:p:S:s:tVvWwx";
* over short opt matching
*/
static option const longopts[] = {
{ "dmg", no_argument, nullptr, 'd' },
{ "linkerscript", required_argument, nullptr, 'l' },
{ "map", required_argument, nullptr, 'm' },
{ "no-sym-in-map", no_argument, nullptr, 'M' },
{ "sym", required_argument, nullptr, 'n' },
{ "overlay", required_argument, nullptr, 'O' },
{ "output", required_argument, nullptr, 'o' },
{ "pad", required_argument, nullptr, 'p' },
{ "scramble", required_argument, nullptr, 'S' },
{ "smart", required_argument, nullptr, 's' },
{ "tiny", no_argument, nullptr, 't' },
{ "version", no_argument, nullptr, 'V' },
{ "verbose", no_argument, nullptr, 'v' },
{ "wramx", no_argument, nullptr, 'w' },
{ "nopad", no_argument, nullptr, 'x' },
{ nullptr, no_argument, nullptr, 0 }
{"dmg", no_argument, nullptr, 'd'},
{"linkerscript", required_argument, nullptr, 'l'},
{"map", required_argument, nullptr, 'm'},
{"no-sym-in-map", no_argument, nullptr, 'M'},
{"sym", required_argument, nullptr, 'n'},
{"overlay", required_argument, nullptr, 'O'},
{"output", required_argument, nullptr, 'o'},
{"pad", required_argument, nullptr, 'p'},
{"scramble", required_argument, nullptr, 'S'},
{"smart", required_argument, nullptr, 's'},
{"tiny", no_argument, nullptr, 't'},
{"version", no_argument, nullptr, 'V'},
{"verbose", no_argument, nullptr, 'v'},
{"wramx", no_argument, nullptr, 'w'},
{"nopad", no_argument, nullptr, 'x'},
{nullptr, no_argument, nullptr, 0 }
};
static void printUsage()
{
static void printUsage() {
fputs(
"Usage: rgblink [-dMtVvwx] [-l script] [-m map_file] [-n sym_file]\n"
" [-O overlay_file] [-o out_file] [-p pad_value]\n"
" [-S spec] [-s symbol] <file> ...\n"
"Useful options:\n"
" -l, --linkerscript <path> set the input linker script\n"
" -m, --map <path> set the output map file\n"
" -n, --sym <path> set the output symbol list file\n"
" -o, --output <path> set the output file\n"
" -p, --pad <value> set the value to pad between sections with\n"
" -x, --nopad disable padding of output binary\n"
" -V, --version print RGBLINK version and exits\n"
"\n"
"For help, use `man rgblink' or go to https://rgbds.gbdev.io/docs/\n",
stderr);
"Usage: rgblink [-dMtVvwx] [-l script] [-m map_file] [-n sym_file]\n"
" [-O overlay_file] [-o out_file] [-p pad_value]\n"
" [-S spec] [-s symbol] <file> ...\n"
"Useful options:\n"
" -l, --linkerscript <path> set the input linker script\n"
" -m, --map <path> set the output map file\n"
" -n, --sym <path> set the output symbol list file\n"
" -o, --output <path> set the output file\n"
" -p, --pad <value> set the value to pad between sections with\n"
" -x, --nopad disable padding of output binary\n"
" -V, --version print RGBLINK version and exits\n"
"\n"
"For help, use `man rgblink' or go to https://rgbds.gbdev.io/docs/\n",
stderr
);
}
enum ScrambledRegion {
@@ -222,13 +218,12 @@ struct {
char const *name;
uint16_t max;
} scrambleSpecs[SCRAMBLE_UNK] = {
{ "romx", 65535 }, // SCRAMBLE_ROMX
{ "sram", 255 }, // SCRAMBLE_SRAM
{ "wramx", 7 }, // SCRAMBLE_WRAMX
{"romx", 65535}, // SCRAMBLE_ROMX
{"sram", 255 }, // SCRAMBLE_SRAM
{"wramx", 7 }, // SCRAMBLE_WRAMX
};
static void parseScrambleSpec(char const *spec)
{
static void parseScrambleSpec(char const *spec) {
// Skip any leading whitespace
spec += strspn(spec, " \t");
@@ -251,7 +246,7 @@ static void parseScrambleSpec(char const *spec)
if (*spec == '\0')
break;
if (*spec == '=') // Skip the limit, too
if (*spec == '=') // Skip the limit, too
spec = strchr(&spec[1], ','); // Skip to next comma, if any
goto next;
}
@@ -259,8 +254,9 @@ static void parseScrambleSpec(char const *spec)
// Find the next non-blank char after the region name's end
spec += regionNameLen + strspn(&spec[regionNameLen], " \t");
if (*spec != '\0' && *spec != ',' && *spec != '=') {
argErr('S', "Unexpected '%c' after region name \"%.*s\"",
regionNamePrintLen, regionName);
argErr(
'S', "Unexpected '%c' after region name \"%.*s\"", regionNamePrintLen, regionName
);
// Skip to next ',' or '=' (or NUL) and keep parsing
spec += 1 + strcspn(&spec[1], ",=");
}
@@ -285,22 +281,30 @@ static void parseScrambleSpec(char const *spec)
char *endptr;
if (*spec == '\0' || *spec == ',') {
argErr('S', "Empty limit for region \"%.*s\"",
regionNamePrintLen, regionName);
argErr('S', "Empty limit for region \"%.*s\"", regionNamePrintLen, regionName);
goto next;
}
limit = strtoul(spec, &endptr, 10);
endptr += strspn(endptr, " \t");
if (*endptr != '\0' && *endptr != ',') {
argErr('S', "Invalid non-numeric limit for region \"%.*s\"",
regionNamePrintLen, regionName);
argErr(
'S',
"Invalid non-numeric limit for region \"%.*s\"",
regionNamePrintLen,
regionName
);
endptr = strchr(endptr, ',');
}
spec = endptr;
if (region != SCRAMBLE_UNK && limit > scrambleSpecs[region].max) {
argErr('S', "Limit for region \"%.*s\" may not exceed %" PRIu16,
regionNamePrintLen, regionName, scrambleSpecs[region].max);
argErr(
'S',
"Limit for region \"%.*s\" may not exceed %" PRIu16,
regionNamePrintLen,
regionName,
scrambleSpecs[region].max
);
limit = scrambleSpecs[region].max;
}
@@ -321,8 +325,7 @@ static void parseScrambleSpec(char const *spec)
// Only WRAMX can be implied, since ROMX and SRAM size may vary
scrambleWRAMX = 7;
} else {
argErr('S', "Cannot imply limit for region \"%.*s\"",
regionNamePrintLen, regionName);
argErr('S', "Cannot imply limit for region \"%.*s\"", regionNamePrintLen, regionName);
}
next:
@@ -337,13 +340,13 @@ next:
}
[[noreturn]] void reportErrors() {
fprintf(stderr, "Linking failed with %" PRIu32 " error%s\n",
nbErrors, nbErrors == 1 ? "" : "s");
fprintf(
stderr, "Linking failed with %" PRIu32 " error%s\n", nbErrors, nbErrors == 1 ? "" : "s"
);
exit(1);
}
static void freeSection(Section &section)
{
static void freeSection(Section &section) {
Section *next = &section;
for (Section *nextu; next; next = nextu) {
@@ -352,13 +355,11 @@ static void freeSection(Section &section)
};
}
static void freeSections()
{
static void freeSections() {
sect_ForEach(freeSection);
}
int main(int argc, char *argv[])
{
int main(int argc, char *argv[]) {
// Parse options
for (int ch; (ch = musl_getopt_long_only(argc, argv, optstring, longopts, nullptr)) != -1;) {
switch (ch) {
@@ -444,8 +445,9 @@ int main(int argc, char *argv[])
// If no input files were specified, the user must have screwed up
if (curArgIndex == argc) {
fputs("FATAL: Please specify an input file (pass `-` to read from standard input)\n",
stderr);
fputs(
"FATAL: Please specify an input file (pass `-` to read from standard input)\n", stderr
);
printUsage();
exit(1);
}

View File

@@ -1,30 +1,31 @@
/* SPDX-License-Identifier: MIT */
#include "link/object.hpp"
#include <algorithm>
#include <deque>
#include <errno.h>
#include <inttypes.h>
#include <limits.h>
#include <new>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <vector>
#include "link/assign.hpp"
#include "link/main.hpp"
#include "link/object.hpp"
#include "link/patch.hpp"
#include "link/sdas_obj.hpp"
#include "link/section.hpp"
#include "link/symbol.hpp"
#include "error.hpp"
#include "helpers.hpp"
#include "linkdefs.hpp"
#include "version.hpp"
#include "link/assign.hpp"
#include "link/main.hpp"
#include "link/patch.hpp"
#include "link/sdas_obj.hpp"
#include "link/section.hpp"
#include "link/symbol.hpp"
static std::deque<std::vector<Symbol>> symbolLists;
static std::vector<std::vector<FileStackNode>> nodes;
static std::deque<Assertion> assertions;
@@ -33,25 +34,23 @@ static std::deque<Assertion> assertions;
// Internal, DO NOT USE.
// For helper wrapper macros defined below, such as `tryReadlong`
#define tryRead(func, type, errval, vartype, var, file, ...) do { \
FILE *tmpFile = file; \
type tmpVal = func(tmpFile); \
/* TODO: maybe mark the condition as `unlikely`; how to do that portably? */ \
if (tmpVal == (errval)) { \
errx(__VA_ARGS__, feof(tmpFile) \
? "Unexpected end of file" \
: strerror(errno)); \
} \
var = (vartype)tmpVal; \
} while (0)
#define tryRead(func, type, errval, vartype, var, file, ...) \
do { \
FILE *tmpFile = file; \
type tmpVal = func(tmpFile); \
/* TODO: maybe mark the condition as `unlikely`; how to do that portably? */ \
if (tmpVal == (errval)) { \
errx(__VA_ARGS__, feof(tmpFile) ? "Unexpected end of file" : strerror(errno)); \
} \
var = (vartype)tmpVal; \
} while (0)
/*
* Reads an unsigned long (32-bit) value from a file.
* @param file The file to read from. This will read 4 bytes from the file.
* @return The value read, cast to a int64_t, or -1 on failure.
*/
static int64_t readlong(FILE *file)
{
static int64_t readlong(FILE *file) {
uint32_t value = 0;
// Read the little-endian value byte by byte
@@ -92,8 +91,7 @@ static int64_t readlong(FILE *file)
* @param ... A format string and related arguments; note that an extra string
* argument is provided, the reason for failure
*/
#define tryGetc(type, var, file, ...) \
tryRead(getc, int, EOF, type, var, file, __VA_ARGS__)
#define tryGetc(type, var, file, ...) tryRead(getc, int, EOF, type, var, file, __VA_ARGS__)
/*
* Helper macro for readings '\0'-terminated strings from a file, and errors out if it fails to.
@@ -103,19 +101,18 @@ static int64_t readlong(FILE *file)
* @param ... A format string and related arguments; note that an extra string
* argument is provided, the reason for failure
*/
#define tryReadstring(var, file, ...) do { \
FILE *tmpFile = file; \
std::string &tmpVal = var; \
for (int tmpByte = getc(tmpFile); tmpByte != '\0'; tmpByte = getc(tmpFile)) { \
if (tmpByte == EOF) { \
errx(__VA_ARGS__, feof(tmpFile) \
? "Unexpected end of file" \
: strerror(errno)); \
} else { \
tmpVal.push_back(tmpByte); \
} \
}; \
} while (0)
#define tryReadstring(var, file, ...) \
do { \
FILE *tmpFile = file; \
std::string &tmpVal = var; \
for (int tmpByte = getc(tmpFile); tmpByte != '\0'; tmpByte = getc(tmpFile)) { \
if (tmpByte == EOF) { \
errx(__VA_ARGS__, feof(tmpFile) ? "Unexpected end of file" : strerror(errno)); \
} else { \
tmpVal.push_back(tmpByte); \
} \
}; \
} while (0)
// Functions to parse object files
@@ -126,39 +123,55 @@ static int64_t readlong(FILE *file)
* @param i The ID of the node in the array
* @param fileName The filename to report in errors
*/
static void readFileStackNode(FILE *file, std::vector<FileStackNode> &fileNodes, uint32_t i,
char const *fileName)
{
static void readFileStackNode(
FILE *file, std::vector<FileStackNode> &fileNodes, uint32_t i, char const *fileName
) {
FileStackNode &node = fileNodes[i];
uint32_t parentID;
tryReadlong(parentID, file,
"%s: Cannot read node #%" PRIu32 "'s parent ID: %s", fileName, i);
tryReadlong(parentID, file, "%s: Cannot read node #%" PRIu32 "'s parent ID: %s", fileName, i);
node.parent = parentID != (uint32_t)-1 ? &fileNodes[parentID] : nullptr;
tryReadlong(node.lineNo, file,
"%s: Cannot read node #%" PRIu32 "'s line number: %s", fileName, i);
tryGetc(enum FileStackNodeType, node.type, file,
"%s: Cannot read node #%" PRIu32 "'s type: %s", fileName, i);
tryReadlong(
node.lineNo, file, "%s: Cannot read node #%" PRIu32 "'s line number: %s", fileName, i
);
tryGetc(
enum FileStackNodeType,
node.type,
file,
"%s: Cannot read node #%" PRIu32 "'s type: %s",
fileName,
i
);
switch (node.type) {
case NODE_FILE:
case NODE_MACRO:
node.data = "";
tryReadstring(node.name(), file,
"%s: Cannot read node #%" PRIu32 "'s file name: %s", fileName, i);
tryReadstring(
node.name(), file, "%s: Cannot read node #%" PRIu32 "'s file name: %s", fileName, i
);
break;
uint32_t depth;
case NODE_REPT:
tryReadlong(depth, file,
"%s: Cannot read node #%" PRIu32 "'s rept depth: %s", fileName, i);
tryReadlong(depth, file, "%s: Cannot read node #%" PRIu32 "'s rept depth: %s", fileName, i);
node.data = std::vector<uint32_t>(depth);
for (uint32_t k = 0; k < depth; k++)
tryReadlong(node.iters()[k], file,
"%s: Cannot read node #%" PRIu32 "'s iter #%" PRIu32 ": %s",
fileName, i, k);
tryReadlong(
node.iters()[k],
file,
"%s: Cannot read node #%" PRIu32 "'s iter #%" PRIu32 ": %s",
fileName,
i,
k
);
if (!node.parent)
fatal(nullptr, 0, "%s is not a valid object file: root node (#%"
PRIu32 ") may not be REPT", fileName, i);
fatal(
nullptr,
0,
"%s is not a valid object file: root node (#%" PRIu32 ") may not be REPT",
fileName,
i
);
}
}
@@ -168,34 +181,52 @@ static void readFileStackNode(FILE *file, std::vector<FileStackNode> &fileNodes,
* @param symbol The symbol to fill
* @param fileName The filename to report in errors
*/
static void readSymbol(FILE *file, Symbol &symbol, char const *fileName,
std::vector<FileStackNode> const &fileNodes)
{
static void readSymbol(
FILE *file, Symbol &symbol, char const *fileName, std::vector<FileStackNode> const &fileNodes
) {
tryReadstring(symbol.name, file, "%s: Cannot read symbol name: %s", fileName);
tryGetc(enum ExportLevel, symbol.type, file, "%s: Cannot read \"%s\"'s type: %s",
fileName, symbol.name.c_str());
tryGetc(
enum ExportLevel,
symbol.type,
file,
"%s: Cannot read \"%s\"'s type: %s",
fileName,
symbol.name.c_str()
);
// If the symbol is defined in this file, read its definition
if (symbol.type != SYMTYPE_IMPORT) {
symbol.objFileName = fileName;
uint32_t nodeID;
tryReadlong(nodeID, file, "%s: Cannot read \"%s\"'s node ID: %s",
fileName, symbol.name.c_str());
tryReadlong(
nodeID, file, "%s: Cannot read \"%s\"'s node ID: %s", fileName, symbol.name.c_str()
);
symbol.src = &fileNodes[nodeID];
tryReadlong(symbol.lineNo, file, "%s: Cannot read \"%s\"'s line number: %s",
fileName, symbol.name.c_str());
tryReadlong(
symbol.lineNo,
file,
"%s: Cannot read \"%s\"'s line number: %s",
fileName,
symbol.name.c_str()
);
int32_t sectionID, value;
tryReadlong(sectionID, file, "%s: Cannot read \"%s\"'s section ID: %s",
fileName, symbol.name.c_str());
tryReadlong(value, file, "%s: Cannot read \"%s\"'s value: %s",
fileName, symbol.name.c_str());
tryReadlong(
sectionID,
file,
"%s: Cannot read \"%s\"'s section ID: %s",
fileName,
symbol.name.c_str()
);
tryReadlong(
value, file, "%s: Cannot read \"%s\"'s value: %s", fileName, symbol.name.c_str()
);
if (sectionID == -1) {
symbol.data = value;
} else {
symbol.data = Label{
.sectionID = sectionID,
.offset = value,
// Set the `.section` later based on the `.sectionID`
.section = nullptr,
.sectionID = sectionID,
.offset = value,
// Set the `.section` later based on the `.sectionID`
.section = nullptr,
};
}
} else {
@@ -210,53 +241,96 @@ static void readSymbol(FILE *file, Symbol &symbol, char const *fileName,
* @param fileName The filename to report in errors
* @param i The number of the patch to report in errors
*/
static void readPatch(FILE *file, Patch &patch, char const *fileName, std::string const &sectName,
uint32_t i, std::vector<FileStackNode> const &fileNodes)
{
static void readPatch(
FILE *file,
Patch &patch,
char const *fileName,
std::string const &sectName,
uint32_t i,
std::vector<FileStackNode> const &fileNodes
) {
uint32_t nodeID, rpnSize;
enum PatchType type;
tryReadlong(nodeID, file,
"%s: Unable to read \"%s\"'s patch #%" PRIu32 "'s node ID: %s",
fileName, sectName.c_str(), i);
tryReadlong(
nodeID,
file,
"%s: Unable to read \"%s\"'s patch #%" PRIu32 "'s node ID: %s",
fileName,
sectName.c_str(),
i
);
patch.src = &fileNodes[nodeID];
tryReadlong(patch.lineNo, file,
"%s: Unable to read \"%s\"'s patch #%" PRIu32 "'s line number: %s",
fileName, sectName.c_str(), i);
tryReadlong(patch.offset, file,
"%s: Unable to read \"%s\"'s patch #%" PRIu32 "'s offset: %s",
fileName, sectName.c_str(), i);
tryReadlong(patch.pcSectionID, file,
"%s: Unable to read \"%s\"'s patch #%" PRIu32 "'s PC offset: %s",
fileName, sectName.c_str(), i);
tryReadlong(patch.pcOffset, file,
"%s: Unable to read \"%s\"'s patch #%" PRIu32 "'s PC offset: %s",
fileName, sectName.c_str(), i);
tryGetc(enum PatchType, type, file,
"%s: Unable to read \"%s\"'s patch #%" PRIu32 "'s type: %s",
fileName, sectName.c_str(), i);
tryReadlong(
patch.lineNo,
file,
"%s: Unable to read \"%s\"'s patch #%" PRIu32 "'s line number: %s",
fileName,
sectName.c_str(),
i
);
tryReadlong(
patch.offset,
file,
"%s: Unable to read \"%s\"'s patch #%" PRIu32 "'s offset: %s",
fileName,
sectName.c_str(),
i
);
tryReadlong(
patch.pcSectionID,
file,
"%s: Unable to read \"%s\"'s patch #%" PRIu32 "'s PC offset: %s",
fileName,
sectName.c_str(),
i
);
tryReadlong(
patch.pcOffset,
file,
"%s: Unable to read \"%s\"'s patch #%" PRIu32 "'s PC offset: %s",
fileName,
sectName.c_str(),
i
);
tryGetc(
enum PatchType,
type,
file,
"%s: Unable to read \"%s\"'s patch #%" PRIu32 "'s type: %s",
fileName,
sectName.c_str(),
i
);
patch.type = type;
tryReadlong(rpnSize, file,
"%s: Unable to read \"%s\"'s patch #%" PRIu32 "'s RPN size: %s",
fileName, sectName.c_str(), i);
tryReadlong(
rpnSize,
file,
"%s: Unable to read \"%s\"'s patch #%" PRIu32 "'s RPN size: %s",
fileName,
sectName.c_str(),
i
);
patch.rpnExpression.resize(rpnSize);
size_t nbElementsRead = fread(patch.rpnExpression.data(), 1, rpnSize, file);
if (nbElementsRead != rpnSize)
errx("%s: Cannot read \"%s\"'s patch #%" PRIu32 "'s RPN expression: %s",
fileName, sectName.c_str(), i,
feof(file) ? "Unexpected end of file" : strerror(errno));
errx(
"%s: Cannot read \"%s\"'s patch #%" PRIu32 "'s RPN expression: %s",
fileName,
sectName.c_str(),
i,
feof(file) ? "Unexpected end of file" : strerror(errno)
);
}
/*
* Sets a patch's pcSection from its pcSectionID.
* @param patch The patch to fix
*/
static void linkPatchToPCSect(Patch &patch, std::vector<Section *> const &fileSections)
{
patch.pcSection = patch.pcSectionID != (uint32_t)-1 ? fileSections[patch.pcSectionID]
: nullptr;
static void linkPatchToPCSect(Patch &patch, std::vector<Section *> const &fileSections) {
patch.pcSection = patch.pcSectionID != (uint32_t)-1 ? fileSections[patch.pcSectionID] : nullptr;
}
/*
@@ -265,9 +339,9 @@ static void linkPatchToPCSect(Patch &patch, std::vector<Section *> const &fileSe
* @param section The section to fill
* @param fileName The filename to report in errors
*/
static void readSection(FILE *file, Section &section, char const *fileName,
std::vector<FileStackNode> const &fileNodes)
{
static void readSection(
FILE *file, Section &section, char const *fileName, std::vector<FileStackNode> const &fileNodes
) {
int32_t tmp;
uint8_t byte;
@@ -277,8 +351,9 @@ static void readSection(FILE *file, Section &section, char const *fileName,
errx("\"%s\"'s section size (%" PRId32 ") is invalid", section.name.c_str(), tmp);
section.size = tmp;
section.offset = 0;
tryGetc(uint8_t, byte, file, "%s: Cannot read \"%s\"'s type: %s", fileName,
section.name.c_str());
tryGetc(
uint8_t, byte, file, "%s: Cannot read \"%s\"'s type: %s", fileName, section.name.c_str()
);
section.type = (enum SectionType)(byte & 0x3F);
if (byte >> 7)
section.modifier = SECTION_UNION;
@@ -296,17 +371,29 @@ static void readSection(FILE *file, Section &section, char const *fileName,
tryReadlong(tmp, file, "%s: Cannot read \"%s\"'s bank: %s", fileName, section.name.c_str());
section.isBankFixed = tmp >= 0;
section.bank = tmp;
tryGetc(uint8_t, byte, file, "%s: Cannot read \"%s\"'s alignment: %s", fileName,
section.name.c_str());
tryGetc(
uint8_t,
byte,
file,
"%s: Cannot read \"%s\"'s alignment: %s",
fileName,
section.name.c_str()
);
if (byte > 16)
byte = 16;
section.isAlignFixed = byte != 0;
section.alignMask = (1 << byte) - 1;
tryReadlong(tmp, file, "%s: Cannot read \"%s\"'s alignment offset: %s", fileName,
section.name.c_str());
tryReadlong(
tmp, file, "%s: Cannot read \"%s\"'s alignment offset: %s", fileName, section.name.c_str()
);
if (tmp > UINT16_MAX) {
error(nullptr, 0, "\"%s\"'s alignment offset is too large (%" PRId32 ")",
section.name.c_str(), tmp);
error(
nullptr,
0,
"\"%s\"'s alignment offset is too large (%" PRId32 ")",
section.name.c_str(),
tmp
);
tmp = UINT16_MAX;
}
section.alignOfs = tmp;
@@ -316,15 +403,23 @@ static void readSection(FILE *file, Section &section, char const *fileName,
section.data.resize(section.size);
if (size_t nbRead = fread(section.data.data(), 1, section.size, file);
nbRead != section.size)
errx("%s: Cannot read \"%s\"'s data: %s", fileName, section.name.c_str(),
feof(file) ? "Unexpected end of file" : strerror(errno));
errx(
"%s: Cannot read \"%s\"'s data: %s",
fileName,
section.name.c_str(),
feof(file) ? "Unexpected end of file" : strerror(errno)
);
}
uint32_t nbPatches;
tryReadlong(nbPatches, file,
"%s: Cannot read \"%s\"'s number of patches: %s", fileName,
section.name.c_str());
tryReadlong(
nbPatches,
file,
"%s: Cannot read \"%s\"'s number of patches: %s",
fileName,
section.name.c_str()
);
section.patches.resize(nbPatches);
for (uint32_t i = 0; i < nbPatches; i++)
@@ -337,8 +432,7 @@ static void readSection(FILE *file, Section &section, char const *fileName,
* @param symbol The symbol to link
* @param section The section to link
*/
static void linkSymToSect(Symbol &symbol, Section &section)
{
static void linkSymToSect(Symbol &symbol, Section &section) {
uint32_t a = 0, b = section.symbols.size();
int32_t symbolOffset = symbol.label().offset;
@@ -361,9 +455,13 @@ static void linkSymToSect(Symbol &symbol, Section &section)
* @param assert The assertion to fill
* @param fileName The filename to report in errors
*/
static void readAssertion(FILE *file, Assertion &assert, char const *fileName, uint32_t i,
std::vector<FileStackNode> const &fileNodes)
{
static void readAssertion(
FILE *file,
Assertion &assert,
char const *fileName,
uint32_t i,
std::vector<FileStackNode> const &fileNodes
) {
char assertName[sizeof("Assertion #4294967295")]; // UINT32_MAX
snprintf(assertName, sizeof(assertName), "Assertion #%" PRIu32, i);
@@ -372,13 +470,11 @@ static void readAssertion(FILE *file, Assertion &assert, char const *fileName, u
tryReadstring(assert.message, file, "%s: Cannot read assertion's message: %s", fileName);
}
static Section *getMainSection(Section &section)
{
static Section *getMainSection(Section &section) {
return section.modifier != SECTION_NORMAL ? sect_GetSection(section.name) : &section;
}
void obj_ReadFile(char const *fileName, unsigned int fileID)
{
void obj_ReadFile(char const *fileName, unsigned int fileID) {
FILE *file;
if (strcmp(fileName, "-")) {
@@ -405,12 +501,9 @@ void obj_ReadFile(char const *fileName, unsigned int fileID)
default: // This is (probably) a SDCC object file, defer the rest of detection to it
// Since SDCC does not provide line info, everything will be reported as coming from the
// object file. It's better than nothing.
nodes[fileID].push_back({
.parent = nullptr,
.lineNo = 0,
.type = NODE_FILE,
.data = fileName
});
nodes[fileID].push_back(
{.parent = nullptr, .lineNo = 0, .type = NODE_FILE, .data = fileName}
);
std::vector<Symbol> &fileSymbols = symbolLists.emplace_front();
@@ -422,7 +515,7 @@ void obj_ReadFile(char const *fileName, unsigned int fileID)
int matchedElems;
if (fscanf(file, RGBDS_OBJECT_VERSION_STRING "%n", &matchedElems) == 1
&& matchedElems != strlen(RGBDS_OBJECT_VERSION_STRING))
&& matchedElems != strlen(RGBDS_OBJECT_VERSION_STRING))
errx("%s: Not a RGBDS object file", fileName);
verbosePrint("Reading object file %s\n", fileName);
@@ -431,10 +524,16 @@ void obj_ReadFile(char const *fileName, unsigned int fileID)
tryReadlong(revNum, file, "%s: Cannot read revision number: %s", fileName);
if (revNum != RGBDS_OBJECT_REV)
errx("%s: Unsupported object file for rgblink %s; try rebuilding \"%s\"%s"
" (expected revision %d, got %d)", fileName, get_package_version_string(),
fileName, revNum > RGBDS_OBJECT_REV ? " or updating rgblink" : "",
RGBDS_OBJECT_REV, revNum);
errx(
"%s: Unsupported object file for rgblink %s; try rebuilding \"%s\"%s"
" (expected revision %d, got %d)",
fileName,
get_package_version_string(),
fileName,
revNum > RGBDS_OBJECT_REV ? " or updating rgblink" : "",
RGBDS_OBJECT_REV,
revNum
);
uint32_t nbNodes;
uint32_t nbSymbols;
@@ -448,7 +547,7 @@ void obj_ReadFile(char const *fileName, unsigned int fileID)
tryReadlong(nbNodes, file, "%s: Cannot read number of nodes: %s", fileName);
nodes[fileID].resize(nbNodes);
verbosePrint("Reading %u nodes...\n", nbNodes);
for (uint32_t i = nbNodes; i--; )
for (uint32_t i = nbNodes; i--;)
readFileStackNode(file, nodes[fileID], i, fileName);
// This file's symbols, kept to link sections to them
@@ -474,7 +573,7 @@ void obj_ReadFile(char const *fileName, unsigned int fileID)
verbosePrint("Reading %" PRIu32 " sections...\n", nbSections);
for (uint32_t i = 0; i < nbSections; i++) {
// Read section
fileSections[i] = new(std::nothrow) Section();
fileSections[i] = new (std::nothrow) Section();
if (!fileSections[i])
err("%s: Failed to create new section", fileName);
@@ -527,12 +626,10 @@ void obj_ReadFile(char const *fileName, unsigned int fileID)
fclose(file);
}
void obj_CheckAssertions()
{
void obj_CheckAssertions() {
patch_CheckAssertions(assertions);
}
void obj_Setup(unsigned int nbFiles)
{
void obj_Setup(unsigned int nbFiles) {
nodes.resize(nbFiles);
}

View File

@@ -1,5 +1,7 @@
/* SPDX-License-Identifier: MIT */
#include "link/output.hpp"
#include <algorithm>
#include <assert.h>
#include <deque>
@@ -10,17 +12,15 @@
#include <string.h>
#include <vector>
#include "link/output.hpp"
#include "error.hpp"
#include "extern/utf8decoder.hpp"
#include "itertools.hpp"
#include "linkdefs.hpp"
#include "link/main.hpp"
#include "link/section.hpp"
#include "link/symbol.hpp"
#include "extern/utf8decoder.hpp"
#include "error.hpp"
#include "itertools.hpp"
#include "linkdefs.hpp"
#define BANK_SIZE 0x4000
FILE *outputFile;
@@ -42,42 +42,45 @@ static std::deque<SortedSections> sections[SECTTYPE_INVALID];
// Defines the order in which types are output to the sym and map files
static enum SectionType typeMap[SECTTYPE_INVALID] = {
SECTTYPE_ROM0,
SECTTYPE_ROMX,
SECTTYPE_VRAM,
SECTTYPE_SRAM,
SECTTYPE_WRAM0,
SECTTYPE_WRAMX,
SECTTYPE_OAM,
SECTTYPE_HRAM
SECTTYPE_ROM0,
SECTTYPE_ROMX,
SECTTYPE_VRAM,
SECTTYPE_SRAM,
SECTTYPE_WRAM0,
SECTTYPE_WRAMX,
SECTTYPE_OAM,
SECTTYPE_HRAM,
};
void out_AddSection(Section const &section)
{
void out_AddSection(Section const &section) {
static const uint32_t maxNbBanks[SECTTYPE_INVALID] = {
1, // SECTTYPE_WRAM0
2, // SECTTYPE_VRAM
UINT32_MAX, // SECTTYPE_ROMX
1, // SECTTYPE_ROM0
1, // SECTTYPE_HRAM
7, // SECTTYPE_WRAMX
UINT32_MAX, // SECTTYPE_SRAM
1, // SECTTYPE_OAM
1, // SECTTYPE_WRAM0
2, // SECTTYPE_VRAM
UINT32_MAX, // SECTTYPE_ROMX
1, // SECTTYPE_ROM0
1, // SECTTYPE_HRAM
7, // SECTTYPE_WRAMX
UINT32_MAX, // SECTTYPE_SRAM
1, // SECTTYPE_OAM
};
uint32_t targetBank = section.bank - sectionTypeInfo[section.type].firstBank;
uint32_t minNbBanks = targetBank + 1;
if (minNbBanks > maxNbBanks[section.type])
errx("Section \"%s\" has an invalid bank range (%" PRIu32 " > %" PRIu32 ")",
section.name.c_str(), section.bank, maxNbBanks[section.type] - 1);
errx(
"Section \"%s\" has an invalid bank range (%" PRIu32 " > %" PRIu32 ")",
section.name.c_str(),
section.bank,
maxNbBanks[section.type] - 1
);
for (uint32_t i = sections[section.type].size(); i < minNbBanks; i++)
sections[section.type].emplace_back();
std::deque<Section const *> &bankSections = section.size
? sections[section.type][targetBank].sections
: sections[section.type][targetBank].zeroLenSections;
std::deque<Section const *> &bankSections =
section.size ? sections[section.type][targetBank].sections
: sections[section.type][targetBank].zeroLenSections;
auto pos = bankSections.begin();
while (pos != bankSections.end() && (*pos)->org < section.org)
@@ -86,8 +89,7 @@ void out_AddSection(Section const &section)
bankSections.insert(pos, &section);
}
Section const *out_OverlappingSection(Section const &section)
{
Section const *out_OverlappingSection(Section const &section) {
uint32_t bank = section.bank - sectionTypeInfo[section.type].firstBank;
for (Section const *ptr : sections[section.type][bank].sections) {
@@ -101,8 +103,7 @@ Section const *out_OverlappingSection(Section const &section)
* Performs sanity checks on the overlay file.
* @return The number of ROM banks in the overlay file
*/
static uint32_t checkOverlaySize()
{
static uint32_t checkOverlaySize() {
if (!overlayFile)
return 0;
@@ -136,14 +137,13 @@ static uint32_t checkOverlaySize()
* covered by any sections.
* @param nbOverlayBanks The number of banks in the overlay file
*/
static void coverOverlayBanks(uint32_t nbOverlayBanks)
{
static void coverOverlayBanks(uint32_t nbOverlayBanks) {
// 2 if is32kMode, 1 otherwise
uint32_t nbRom0Banks = sectionTypeInfo[SECTTYPE_ROM0].size / BANK_SIZE;
// Discount ROM0 banks to avoid outputting too much
uint32_t nbUncoveredBanks = nbOverlayBanks - nbRom0Banks > sections[SECTTYPE_ROMX].size()
? nbOverlayBanks - nbRom0Banks
: 0;
? nbOverlayBanks - nbRom0Banks
: 0;
if (nbUncoveredBanks > sections[SECTTYPE_ROMX].size()) {
for (uint32_t i = sections[SECTTYPE_ROMX].size(); i < nbUncoveredBanks; i++)
@@ -157,8 +157,8 @@ static void coverOverlayBanks(uint32_t nbOverlayBanks)
* @param baseOffset The address of the bank's first byte in GB address space
* @param size The size of the bank
*/
static void writeBank(std::deque<Section const *> *bankSections, uint16_t baseOffset, uint16_t size)
{
static void
writeBank(std::deque<Section const *> *bankSections, uint16_t baseOffset, uint16_t size) {
uint16_t offset = 0;
if (bankSections) {
@@ -190,8 +190,7 @@ static void writeBank(std::deque<Section const *> *bankSections, uint16_t baseOf
}
// Writes a ROM file to the output.
static void writeROM()
{
static void writeROM() {
if (outputFileName) {
if (strcmp(outputFileName, "-")) {
outputFile = fopen(outputFileName, "wb");
@@ -220,12 +219,18 @@ static void writeROM()
coverOverlayBanks(nbOverlayBanks);
if (outputFile) {
writeBank(!sections[SECTTYPE_ROM0].empty() ? &sections[SECTTYPE_ROM0][0].sections : nullptr,
sectionTypeInfo[SECTTYPE_ROM0].startAddr, sectionTypeInfo[SECTTYPE_ROM0].size);
writeBank(
!sections[SECTTYPE_ROM0].empty() ? &sections[SECTTYPE_ROM0][0].sections : nullptr,
sectionTypeInfo[SECTTYPE_ROM0].startAddr,
sectionTypeInfo[SECTTYPE_ROM0].size
);
for (uint32_t i = 0 ; i < sections[SECTTYPE_ROMX].size(); i++)
writeBank(&sections[SECTTYPE_ROMX][i].sections,
sectionTypeInfo[SECTTYPE_ROMX].startAddr, sectionTypeInfo[SECTTYPE_ROMX].size);
for (uint32_t i = 0; i < sections[SECTTYPE_ROMX].size(); i++)
writeBank(
&sections[SECTTYPE_ROMX][i].sections,
sectionTypeInfo[SECTTYPE_ROMX].startAddr,
sectionTypeInfo[SECTTYPE_ROMX].size
);
}
if (outputFile)
@@ -235,23 +240,20 @@ static void writeROM()
}
// Checks whether this character is legal as the first character of a symbol's name in a sym file
static bool canStartSymName(char c)
{
static bool canStartSymName(char c) {
return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || c == '_';
}
// Checks whether this character is legal in a symbol's name in a sym file
static bool isLegalForSymName(char c)
{
return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') ||
c == '_' || c == '@' || c == '#' || c == '$' || c == '.';
static bool isLegalForSymName(char c) {
return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '_'
|| c == '@' || c == '#' || c == '$' || c == '.';
}
// Prints a symbol's name to `symFile`, assuming that the first character is legal.
// Illegal characters are UTF-8-decoded (errors are replaced by U+FFFD) and emitted as `\u`/`\U`.
static void printSymName(char const *name)
{
for (char const *ptr = name; *ptr != '\0'; ) {
static void printSymName(char const *name) {
for (char const *ptr = name; *ptr != '\0';) {
char c = *ptr;
if (isLegalForSymName(c)) {
@@ -277,16 +279,14 @@ static void printSymName(char const *name)
++ptr;
} while (state != 0);
fprintf(symFile, codepoint <= 0xFFFF ? "\\u%04" PRIx32 : "\\U%08" PRIx32,
codepoint);
fprintf(symFile, codepoint <= 0xFFFF ? "\\u%04" PRIx32 : "\\U%08" PRIx32, codepoint);
}
}
}
// Comparator function for `std::stable_sort` to sort symbols
// Symbols are ordered by address, then by parentage
static int compareSymbols(SortedSymbol const &sym1, SortedSymbol const &sym2)
{
static int compareSymbols(SortedSymbol const &sym1, SortedSymbol const &sym2) {
if (sym1.addr != sym2.addr)
return sym1.addr < sym2.addr ? -1 : 1;
@@ -315,24 +315,24 @@ static int compareSymbols(SortedSymbol const &sym1, SortedSymbol const &sym2)
* Write a bank's contents to the sym file
* @param bankSections The bank's sections
*/
static void writeSymBank(SortedSections const &bankSections, enum SectionType type, uint32_t bank)
{
#define forEachSortedSection(sect, ...) do { \
for (auto it = bankSections.zeroLenSections.begin(); it != bankSections.zeroLenSections.end(); it++) { \
for (Section const *sect = *it; sect; sect = sect->nextu) \
__VA_ARGS__ \
} \
for (auto it = bankSections.sections.begin(); it != bankSections.sections.end(); it++) { \
for (Section const *sect = *it; sect; sect = sect->nextu) \
__VA_ARGS__ \
} \
} while (0)
static void writeSymBank(SortedSections const &bankSections, enum SectionType type, uint32_t bank) {
#define forEachSortedSection(sect, ...) \
do { \
for (auto it = bankSections.zeroLenSections.begin(); \
it != bankSections.zeroLenSections.end(); \
it++) { \
for (Section const *sect = *it; sect; sect = sect->nextu) \
__VA_ARGS__ \
} \
for (auto it = bankSections.sections.begin(); it != bankSections.sections.end(); it++) { \
for (Section const *sect = *it; sect; sect = sect->nextu) \
__VA_ARGS__ \
} \
} while (0)
uint32_t nbSymbols = 0;
forEachSortedSection(sect, {
nbSymbols += sect->symbols.size();
});
forEachSortedSection(sect, { nbSymbols += sect->symbols.size(); });
if (!nbSymbols)
return;
@@ -345,10 +345,8 @@ static void writeSymBank(SortedSections const &bankSections, enum SectionType ty
for (Symbol const *sym : sect->symbols) {
// Don't output symbols that begin with an illegal character
if (!sym->name.empty() && canStartSymName(sym->name[0]))
symList.push_back({
.sym = sym,
.addr = (uint16_t)(sym->label().offset + sect->org)
});
symList.push_back({.sym = sym, .addr = (uint16_t)(sym->label().offset + sect->org)}
);
}
});
@@ -365,23 +363,31 @@ static void writeSymBank(SortedSections const &bankSections, enum SectionType ty
}
}
static void writeEmptySpace(uint16_t begin, uint16_t end)
{
static void writeEmptySpace(uint16_t begin, uint16_t end) {
if (begin < end) {
uint16_t len = end - begin;
fprintf(mapFile, "\tEMPTY: $%04x-$%04x ($%04" PRIx16 " byte%s)\n",
begin, end - 1, len, len == 1 ? "" : "s");
fprintf(
mapFile,
"\tEMPTY: $%04x-$%04x ($%04" PRIx16 " byte%s)\n",
begin,
end - 1,
len,
len == 1 ? "" : "s"
);
}
}
/*
* Write a bank's contents to the map file
*/
static void writeMapBank(SortedSections const &sectList, enum SectionType type, uint32_t bank)
{
fprintf(mapFile, "\n%s bank #%" PRIu32 ":\n", sectionTypeInfo[type].name.c_str(),
bank + sectionTypeInfo[type].firstBank);
static void writeMapBank(SortedSections const &sectList, enum SectionType type, uint32_t bank) {
fprintf(
mapFile,
"\n%s bank #%" PRIu32 ":\n",
sectionTypeInfo[type].name.c_str(),
bank + sectionTypeInfo[type].firstBank
);
uint16_t used = 0;
auto section = sectList.sections.begin();
@@ -390,9 +396,10 @@ static void writeMapBank(SortedSections const &sectList, enum SectionType type,
while (section != sectList.sections.end() || zeroLenSection != sectList.zeroLenSections.end()) {
// Pick the lowest section by address out of the two
auto &pickedSection = section == sectList.sections.end() ? zeroLenSection
: zeroLenSection == sectList.zeroLenSections.end() ? section
: (*section)->org < (*zeroLenSection)->org ? section : zeroLenSection;
auto &pickedSection = section == sectList.sections.end() ? zeroLenSection
: zeroLenSection == sectList.zeroLenSections.end() ? section
: (*section)->org < (*zeroLenSection)->org ? section
: zeroLenSection;
Section const *sect = *pickedSection;
used += sect->size;
@@ -403,31 +410,41 @@ static void writeMapBank(SortedSections const &sectList, enum SectionType type,
prevEndAddr = sect->org + sect->size;
if (sect->size != 0)
fprintf(mapFile, "\tSECTION: $%04" PRIx16 "-$%04x ($%04" PRIx16
" byte%s) [\"%s\"]\n",
sect->org, prevEndAddr - 1, sect->size, sect->size == 1 ? "" : "s",
sect->name.c_str());
fprintf(
mapFile,
"\tSECTION: $%04" PRIx16 "-$%04x ($%04" PRIx16 " byte%s) [\"%s\"]\n",
sect->org,
prevEndAddr - 1,
sect->size,
sect->size == 1 ? "" : "s",
sect->name.c_str()
);
else
fprintf(mapFile, "\tSECTION: $%04" PRIx16 " (0 bytes) [\"%s\"]\n",
sect->org, sect->name.c_str());
fprintf(
mapFile,
"\tSECTION: $%04" PRIx16 " (0 bytes) [\"%s\"]\n",
sect->org,
sect->name.c_str()
);
if (!noSymInMap) {
// Also print symbols in the following "pieces"
for (uint16_t org = sect->org; sect; sect = sect->nextu) {
for (Symbol *sym : sect->symbols)
// Space matches "\tSECTION: $xxxx ..."
fprintf(mapFile, "\t $%04" PRIx32 " = %s\n",
sym->label().offset + org,
sym->name.c_str());
fprintf(
mapFile,
"\t $%04" PRIx32 " = %s\n",
sym->label().offset + org,
sym->name.c_str()
);
if (sect->nextu) {
// Announce the following "piece"
if (sect->nextu->modifier == SECTION_UNION)
fprintf(mapFile,
"\t ; Next union\n");
fprintf(mapFile, "\t ; Next union\n");
else if (sect->nextu->modifier == SECTION_FRAGMENT)
fprintf(mapFile,
"\t ; Next fragment\n");
fprintf(mapFile, "\t ; Next fragment\n");
}
}
}
@@ -444,16 +461,14 @@ static void writeMapBank(SortedSections const &sectList, enum SectionType type,
uint16_t slack = sectionTypeInfo[type].size - used;
fprintf(mapFile, "\tTOTAL EMPTY: $%04" PRIx16 " byte%s\n", slack,
slack == 1 ? "" : "s");
fprintf(mapFile, "\tTOTAL EMPTY: $%04" PRIx16 " byte%s\n", slack, slack == 1 ? "" : "s");
}
}
/*
* Write the total used and free space by section type to the map file
*/
static void writeMapSummary()
{
static void writeMapSummary() {
fputs("SUMMARY:\n", mapFile);
for (uint8_t i = 0; i < SECTTYPE_INVALID; i++) {
@@ -480,9 +495,9 @@ static void writeMapSummary()
|| zeroLenSection != sectList.zeroLenSections.end()) {
// Pick the lowest section by address out of the two
auto &pickedSection = section == sectList.sections.end() ? zeroLenSection
: zeroLenSection == sectList.zeroLenSections.end() ? section
: (*section)->org < (*zeroLenSection)->org ? section
: zeroLenSection;
: zeroLenSection == sectList.zeroLenSections.end() ? section
: (*section)->org < (*zeroLenSection)->org ? section
: zeroLenSection;
used += (*pickedSection)->size;
pickedSection++;
@@ -491,19 +506,22 @@ static void writeMapSummary()
usedTotal += used;
}
fprintf(mapFile, "\t%s: %" PRId32 " byte%s used / %" PRId32 " free",
sectionTypeInfo[type].name.c_str(), usedTotal, usedTotal == 1 ? "" : "s",
nbBanks * sectionTypeInfo[type].size - usedTotal);
if (sectionTypeInfo[type].firstBank != sectionTypeInfo[type].lastBank
|| nbBanks > 1)
fprintf(
mapFile,
"\t%s: %" PRId32 " byte%s used / %" PRId32 " free",
sectionTypeInfo[type].name.c_str(),
usedTotal,
usedTotal == 1 ? "" : "s",
nbBanks * sectionTypeInfo[type].size - usedTotal
);
if (sectionTypeInfo[type].firstBank != sectionTypeInfo[type].lastBank || nbBanks > 1)
fprintf(mapFile, " in %u bank%s", nbBanks, nbBanks == 1 ? "" : "s");
putc('\n', mapFile);
}
}
// Writes the sym file, if applicable.
static void writeSym()
{
static void writeSym() {
if (!symFileName)
return;
@@ -529,8 +547,7 @@ static void writeSym()
}
// Writes the map file, if applicable.
static void writeMap()
{
static void writeMap() {
if (!mapFileName)
return;
@@ -555,8 +572,7 @@ static void writeMap()
fclose(mapFile);
}
void out_WriteFiles()
{
void out_WriteFiles() {
writeROM();
writeSym();
writeMap();

View File

@@ -1,5 +1,7 @@
/* SPDX-License-Identifier: MIT */
#include "link/patch.hpp"
#include <assert.h>
#include <deque>
#include <inttypes.h>
@@ -8,17 +10,16 @@
#include <string.h>
#include <variant>
#include "link/object.hpp"
#include "link/patch.hpp"
#include "link/section.hpp"
#include "link/symbol.hpp"
#include "error.hpp"
#include "helpers.hpp"
#include "linkdefs.hpp"
#include "opmath.hpp"
#include "platform.hpp"
#include "link/object.hpp"
#include "link/section.hpp"
#include "link/symbol.hpp"
struct RPNStackEntry {
int32_t value;
bool errorFlag; // Whether the value is a placeholder inserted for error recovery
@@ -26,17 +27,15 @@ struct RPNStackEntry {
std::deque<RPNStackEntry> rpnStack;
static void pushRPN(int32_t value, bool comesFromError)
{
rpnStack.push_front({ .value = value, .errorFlag = comesFromError });
static void pushRPN(int32_t value, bool comesFromError) {
rpnStack.push_front({.value = value, .errorFlag = comesFromError});
}
// This flag tracks whether the RPN op that is currently being evaluated
// has popped any values with the error flag set.
static bool isError = false;
static int32_t popRPN(FileStackNode const *node, uint32_t lineNo)
{
static int32_t popRPN(FileStackNode const *node, uint32_t lineNo) {
if (rpnStack.empty())
fatal(node, lineNo, "Internal error, RPN stack empty");
@@ -49,17 +48,16 @@ static int32_t popRPN(FileStackNode const *node, uint32_t lineNo)
// RPN operators
static uint32_t getRPNByte(uint8_t const *&expression, int32_t &size,
FileStackNode const *node, uint32_t lineNo)
{
static uint32_t getRPNByte(
uint8_t const *&expression, int32_t &size, FileStackNode const *node, uint32_t lineNo
) {
if (!size--)
fatal(node, lineNo, "Internal error, RPN expression overread");
return *expression++;
}
static Symbol const *getSymbol(std::vector<Symbol> const &symbolList, uint32_t index)
{
static Symbol const *getSymbol(std::vector<Symbol> const &symbolList, uint32_t index) {
assert(index != (uint32_t)-1); // PC needs to be handled specially, not here
Symbol const &symbol = symbolList[index];
@@ -78,8 +76,7 @@ static Symbol const *getSymbol(std::vector<Symbol> const &symbolList, uint32_t i
* @return isError Set if an error occurred during evaluation, and further
* errors caused by the value should be suppressed.
*/
static int32_t computeRPNExpr(Patch const &patch, std::vector<Symbol> const &fileSymbols)
{
static int32_t computeRPNExpr(Patch const &patch, std::vector<Symbol> const &fileSymbols) {
// Small shortcut to avoid a lot of repetition
#define popRPN() popRPN(patch.src, patch.lineNo)
@@ -89,8 +86,8 @@ static int32_t computeRPNExpr(Patch const &patch, std::vector<Symbol> const &fil
rpnStack.clear();
while (size > 0) {
enum RPNCommand command = (enum RPNCommand)getRPNByte(expression, size,
patch.src, patch.lineNo);
enum RPNCommand command =
(enum RPNCommand)getRPNByte(expression, size, patch.src, patch.lineNo);
int32_t value;
isError = false;
@@ -218,22 +215,27 @@ static int32_t computeRPNExpr(Patch const &patch, std::vector<Symbol> const &fil
case RPN_BANK_SYM:
value = 0;
for (uint8_t shift = 0; shift < 32; shift += 8)
value |= getRPNByte(expression, size,
patch.src, patch.lineNo) << shift;
value |= getRPNByte(expression, size, patch.src, patch.lineNo) << shift;
symbol = getSymbol(fileSymbols, value);
if (!symbol) {
error(patch.src, patch.lineNo,
"Requested BANK() of symbol \"%s\", which was not found",
fileSymbols[value].name.c_str());
error(
patch.src,
patch.lineNo,
"Requested BANK() of symbol \"%s\", which was not found",
fileSymbols[value].name.c_str()
);
isError = true;
value = 1;
} else if (Label const *label = std::get_if<Label>(&symbol->data); label) {
value = label->section->bank;
} else {
error(patch.src, patch.lineNo,
"Requested BANK() of non-label symbol \"%s\"",
fileSymbols[value].name.c_str());
error(
patch.src,
patch.lineNo,
"Requested BANK() of non-label symbol \"%s\"",
fileSymbols[value].name.c_str()
);
isError = true;
value = 1;
}
@@ -249,9 +251,12 @@ static int32_t computeRPNExpr(Patch const &patch, std::vector<Symbol> const &fil
sect = sect_GetSection(name);
if (!sect) {
error(patch.src, patch.lineNo,
"Requested BANK() of section \"%s\", which was not found",
name);
error(
patch.src,
patch.lineNo,
"Requested BANK() of section \"%s\", which was not found",
name
);
isError = true;
value = 1;
} else {
@@ -261,8 +266,7 @@ static int32_t computeRPNExpr(Patch const &patch, std::vector<Symbol> const &fil
case RPN_BANK_SELF:
if (!patch.pcSection) {
error(patch.src, patch.lineNo,
"PC has no bank outside a section");
error(patch.src, patch.lineNo, "PC has no bank outside a section");
isError = true;
value = 1;
} else {
@@ -279,9 +283,12 @@ static int32_t computeRPNExpr(Patch const &patch, std::vector<Symbol> const &fil
sect = sect_GetSection(name);
if (!sect) {
error(patch.src, patch.lineNo,
"Requested SIZEOF() of section \"%s\", which was not found",
name);
error(
patch.src,
patch.lineNo,
"Requested SIZEOF() of section \"%s\", which was not found",
name
);
isError = true;
value = 1;
} else {
@@ -299,9 +306,12 @@ static int32_t computeRPNExpr(Patch const &patch, std::vector<Symbol> const &fil
assert(sect->offset == 0);
if (!sect) {
error(patch.src, patch.lineNo,
"Requested STARTOF() of section \"%s\", which was not found",
name);
error(
patch.src,
patch.lineNo,
"Requested STARTOF() of section \"%s\", which was not found",
name
);
isError = true;
value = 1;
} else {
@@ -312,8 +322,7 @@ static int32_t computeRPNExpr(Patch const &patch, std::vector<Symbol> const &fil
case RPN_SIZEOF_SECTTYPE:
value = getRPNByte(expression, size, patch.src, patch.lineNo);
if (value < 0 || value >= SECTTYPE_INVALID) {
error(patch.src, patch.lineNo,
"Requested SIZEOF() an invalid section type");
error(patch.src, patch.lineNo, "Requested SIZEOF() an invalid section type");
isError = true;
value = 0;
} else {
@@ -324,8 +333,7 @@ static int32_t computeRPNExpr(Patch const &patch, std::vector<Symbol> const &fil
case RPN_STARTOF_SECTTYPE:
value = getRPNByte(expression, size, patch.src, patch.lineNo);
if (value < 0 || value >= SECTTYPE_INVALID) {
error(patch.src, patch.lineNo,
"Requested STARTOF() an invalid section type");
error(patch.src, patch.lineNo, "Requested STARTOF() an invalid section type");
isError = true;
value = 0;
} else {
@@ -335,11 +343,8 @@ static int32_t computeRPNExpr(Patch const &patch, std::vector<Symbol> const &fil
case RPN_HRAM:
value = popRPN();
if (!isError && (value < 0
|| (value > 0xFF && value < 0xFF00)
|| value > 0xFFFF)) {
error(patch.src, patch.lineNo,
"Value %" PRId32 " is not in HRAM range", value);
if (!isError && (value < 0 || (value > 0xFF && value < 0xFF00) || value > 0xFFFF)) {
error(patch.src, patch.lineNo, "Value %" PRId32 " is not in HRAM range", value);
isError = true;
}
value &= 0xFF;
@@ -351,8 +356,7 @@ static int32_t computeRPNExpr(Patch const &patch, std::vector<Symbol> const &fil
// They can be easily checked with a bitmask
if (value & ~0x38) {
if (!isError)
error(patch.src, patch.lineNo,
"Value %" PRId32 " is not a RST vector", value);
error(patch.src, patch.lineNo, "Value %" PRId32 " is not a RST vector", value);
isError = true;
}
value |= 0xC7;
@@ -361,20 +365,17 @@ static int32_t computeRPNExpr(Patch const &patch, std::vector<Symbol> const &fil
case RPN_CONST:
value = 0;
for (uint8_t shift = 0; shift < 32; shift += 8)
value |= getRPNByte(expression, size,
patch.src, patch.lineNo) << shift;
value |= getRPNByte(expression, size, 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.src, patch.lineNo) << shift;
value |= getRPNByte(expression, size, patch.src, patch.lineNo) << shift;
if (value == -1) { // PC
if (!patch.pcSection) {
error(patch.src, patch.lineNo,
"PC has no value outside a section");
error(patch.src, patch.lineNo, "PC has no value outside a section");
value = 0;
isError = true;
} else {
@@ -384,16 +385,23 @@ static int32_t computeRPNExpr(Patch const &patch, std::vector<Symbol> const &fil
symbol = getSymbol(fileSymbols, value);
if (!symbol) {
error(patch.src, patch.lineNo,
"Unknown symbol \"%s\"", fileSymbols[value].name.c_str());
error(
patch.src,
patch.lineNo,
"Unknown symbol \"%s\"",
fileSymbols[value].name.c_str()
);
isError = true;
} else {
value = std::visit(Visitor{
[](int32_t val) -> int32_t { return val; },
[](Label label) -> int32_t {
return label.section->org + label.offset;
}
}, symbol->data);
value = std::visit(
Visitor{
[](int32_t val) -> int32_t { return val; },
[](Label label) -> int32_t {
return label.section->org + label.offset;
},
},
symbol->data
);
}
}
break;
@@ -403,8 +411,7 @@ static int32_t computeRPNExpr(Patch const &patch, std::vector<Symbol> const &fil
}
if (rpnStack.size() > 1)
error(patch.src, patch.lineNo,
"RPN stack has %zu entries on exit, not 1", rpnStack.size());
error(patch.src, patch.lineNo, "RPN stack has %zu entries on exit, not 1", rpnStack.size());
isError = false;
return popRPN();
@@ -412,8 +419,7 @@ static int32_t computeRPNExpr(Patch const &patch, std::vector<Symbol> const &fil
#undef popRPN
}
void patch_CheckAssertions(std::deque<Assertion> &assertions)
{
void patch_CheckAssertions(std::deque<Assertion> &assertions) {
verbosePrint("Checking assertions...\n");
for (Assertion &assert : assertions) {
@@ -423,24 +429,37 @@ void patch_CheckAssertions(std::deque<Assertion> &assertions)
if (!isError && !value) {
switch (type) {
case ASSERT_FATAL:
fatal(assert.patch.src, assert.patch.lineNo, "%s",
!assert.message.empty() ? assert.message.c_str()
: "assert failure");
fatal(
assert.patch.src,
assert.patch.lineNo,
"%s",
!assert.message.empty() ? assert.message.c_str() : "assert failure"
);
case ASSERT_ERROR:
error(assert.patch.src, assert.patch.lineNo, "%s",
!assert.message.empty() ? assert.message.c_str()
: "assert failure");
error(
assert.patch.src,
assert.patch.lineNo,
"%s",
!assert.message.empty() ? assert.message.c_str() : "assert failure"
);
break;
case ASSERT_WARN:
warning(assert.patch.src, assert.patch.lineNo, "%s",
!assert.message.empty() ? assert.message.c_str()
: "assert failure");
warning(
assert.patch.src,
assert.patch.lineNo,
"%s",
!assert.message.empty() ? assert.message.c_str() : "assert failure"
);
break;
}
} else if (isError && type == ASSERT_FATAL) {
fatal(assert.patch.src, assert.patch.lineNo,
"Failed to evaluate assertion%s%s",
!assert.message.empty() ? ": " : "", assert.message.c_str());
fatal(
assert.patch.src,
assert.patch.lineNo,
"Failed to evaluate assertion%s%s",
!assert.message.empty() ? ": " : "",
assert.message.c_str()
);
}
}
}
@@ -450,8 +469,7 @@ void patch_CheckAssertions(std::deque<Assertion> &assertions)
* @param section The section component to patch
* @param dataSection The section to patch
*/
static void applyFilePatches(Section &section, Section &dataSection)
{
static void applyFilePatches(Section &section, Section &dataSection) {
verbosePrint("Patching section \"%s\"...\n", section.name.c_str());
for (Patch &patch : section.patches) {
int32_t value = computeRPNExpr(patch, *section.fileSymbols);
@@ -465,9 +483,12 @@ static void applyFilePatches(Section &section, Section &dataSection)
int16_t jumpOffset = value - address;
if (!isError && (jumpOffset < -128 || jumpOffset > 127))
error(patch.src, patch.lineNo,
"jr target out of reach (expected -129 < %" PRId16 " < 128)",
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
@@ -476,17 +497,20 @@ static void applyFilePatches(Section &section, Section &dataSection)
int32_t min;
int32_t max;
} const types[PATCHTYPE_INVALID] = {
{ 1, -128, 255 }, // PATCHTYPE_BYTE
{ 2, -32768, 65536 }, // PATCHTYPE_WORD
{ 4, INT32_MIN, INT32_MAX }, // PATCHTYPE_LONG
{1, -128, 255 }, // PATCHTYPE_BYTE
{2, -32768, 65536 }, // PATCHTYPE_WORD
{4, INT32_MIN, INT32_MAX}, // PATCHTYPE_LONG
};
if (!isError && (value < types[patch.type].min
|| value > types[patch.type].max))
error(patch.src, patch.lineNo,
"Value %" PRId32 "%s is not %u-bit",
value, value < 0 ? " (maybe negative?)" : "",
types[patch.type].size * 8U);
if (!isError && (value < types[patch.type].min || value > types[patch.type].max))
error(
patch.src,
patch.lineNo,
"Value %" PRId32 "%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;
value >>= 8;
@@ -499,8 +523,7 @@ static void applyFilePatches(Section &section, Section &dataSection)
* Applies all of a section's patches, iterating over "components" of unionized sections
* @param section The section to patch
*/
static void applyPatches(Section &section)
{
static void applyPatches(Section &section) {
if (!sect_HasData(section.type))
return;
@@ -508,7 +531,6 @@ static void applyPatches(Section &section)
applyFilePatches(*component, section);
}
void patch_ApplyPatches()
{
void patch_ApplyPatches() {
sect_ForEach(applyPatches);
}

View File

@@ -48,7 +48,7 @@
struct Keyword {
std::string_view name;
yy::parser::symbol_type (* tokenGen)();
yy::parser::symbol_type (*tokenGen)();
};
}
@@ -79,35 +79,75 @@
%%
lines: %empty
| line lines
lines:
%empty
| line lines
;
line: INCLUDE string newline { includeFile(std::move($2)); } // Note: this additionally increments the line number!
| directive newline { incLineNo(); }
| newline { incLineNo(); }
| error newline { yyerrok; incLineNo(); } // Error recovery.
line:
INCLUDE string newline {
includeFile(std::move($2)); // Note: this additionally increments the line number!
}
| directive newline {
incLineNo();
}
| newline {
incLineNo();
}
// Error recovery.
| error newline {
yyerrok;
incLineNo();
}
;
directive: section_type { setSectionType($1); }
| section_type number { setSectionType($1, $2); }
| FLOATING { makeAddrFloating(); }
| ORG number { setAddr($2); }
| ALIGN number { alignTo($2, 0); }
| ALIGN number COMMA number { alignTo($2, $4); }
| DS number { pad($2); }
| string optional { placeSection($1, $2); }
directive:
section_type {
setSectionType($1);
}
| section_type number {
setSectionType($1, $2);
}
| FLOATING {
makeAddrFloating();
}
| ORG number {
setAddr($2);
}
| ALIGN number {
alignTo($2, 0);
}
| ALIGN number COMMA number {
alignTo($2, $4);
}
| DS number {
pad($2);
}
| string optional {
placeSection($1, $2);
}
;
optional: %empty { $$ = false; }
| OPTIONAL { $$ = true; }
optional:
%empty {
$$ = false;
}
| OPTIONAL {
$$ = true;
}
;
%%
#define scriptError(context, fmt, ...) \
::error(nullptr, 0, "%s(%" PRIu32 "): " fmt, \
context.path.c_str(), context.lineNo __VA_OPT__(,) __VA_ARGS__)
::error( \
nullptr, \
0, \
"%s(%" PRIu32 "): " fmt, \
context.path.c_str(), \
context.lineNo __VA_OPT__(, ) \
__VA_ARGS__ \
)
// Lexer.
@@ -134,8 +174,9 @@ static void includeFile(std::string &&path) {
if (!newContext.file.open(newContext.path, std::ios_base::in)) {
// The order is important: report the error, increment the line number, modify the stack!
scriptError(prevContext, "Failed to open included linker script \"%s\"",
newContext.path.c_str());
scriptError(
prevContext, "Failed to open included linker script \"%s\"", newContext.path.c_str()
);
++prevContext.lineNo;
lexerStack.pop_back();
} else {
@@ -291,9 +332,8 @@ try_again: // Can't use a `do {} while(0)` loop, otherwise compilers (wrongly) t
auto strUpperCmp = [](char cmp, char ref) {
// `locale::classic()` yields the "C" locale.
assert(!std::use_facet<std::ctype<char>>(std::locale::classic())
.is(std::ctype_base::lower, ref));
return std::use_facet<std::ctype<char>>(std::locale::classic())
.toupper(cmp) == ref;
.is(std::ctype_base::lower, ref));
return std::use_facet<std::ctype<char>>(std::locale::classic()).toupper(cmp) == ref;
};
ident.push_back(c);
@@ -351,8 +391,9 @@ static void setSectionType(SectionType type) {
auto const &context = lexerStack.back();
if (nbbanks(type) != 1) {
scriptError(context, "A bank number must be specified for %s",
sectionTypeInfo[type].name.c_str());
scriptError(
context, "A bank number must be specified for %s", sectionTypeInfo[type].name.c_str()
);
// Keep going with a default value for the bank index.
}
@@ -364,12 +405,16 @@ static void setSectionType(SectionType type, uint32_t bank) {
auto const &typeInfo = sectionTypeInfo[type];
if (bank < typeInfo.firstBank) {
scriptError(context, "%s bank %" PRIu32 " doesn't exist (the minimum is %" PRIu32 ")",
typeInfo.name.c_str(), bank, typeInfo.firstBank);
scriptError(
context, "%s bank %" PRIu32 " doesn't exist (the minimum is %" PRIu32 ")",
typeInfo.name.c_str(), bank, typeInfo.firstBank
);
bank = typeInfo.firstBank;
} else if (bank > typeInfo.lastBank) {
scriptError(context, "%s bank %" PRIu32 " doesn't exist (the maximum is %" PRIu32 ")",
typeInfo.name.c_str(), bank, typeInfo.lastBank);
scriptError(
context, "%s bank %" PRIu32 " doesn't exist (the maximum is %" PRIu32 ")",
typeInfo.name.c_str(), bank, typeInfo.lastBank
);
}
setActiveTypeAndIdx(type, bank - typeInfo.firstBank);
@@ -388,8 +433,10 @@ static void setAddr(uint32_t addr) {
if (addr < pc) {
scriptError(context, "Cannot decrease the current address (from $%04x to $%04x)", pc, addr);
} else if (addr > endaddr(activeType)) { // Allow "one past the end" sections.
scriptError(context, "Cannot set the current address to $%04" PRIx32 ": %s ends at $%04" PRIx16 "",
addr, typeInfo.name.c_str(), endaddr(activeType));
scriptError(
context, "Cannot set the current address to $%04" PRIx32 ": %s ends at $%04" PRIx16 "",
addr, typeInfo.name.c_str(), endaddr(activeType)
);
pc = endaddr(activeType);
} else {
pc = addr;
@@ -400,7 +447,9 @@ static void setAddr(uint32_t addr) {
static void makeAddrFloating() {
auto const &context = lexerStack.back();
if (activeType == SECTTYPE_INVALID) {
scriptError(context, "Cannot make the current address floating: no memory region is active");
scriptError(
context, "Cannot make the current address floating: no memory region is active"
);
return;
}
@@ -423,9 +472,12 @@ static void alignTo(uint32_t alignment, uint32_t alignOfs) {
uint32_t alignSize = 1u << alignment;
if (alignOfs >= alignSize) {
scriptError(context, "Cannot align: The alignment offset (%" PRIu32
") must be less than alignment size (%" PRIu32 ")",
alignOfs, alignSize);
scriptError(
context,
"Cannot align: The alignment offset (%" PRIu32
") must be less than alignment size (%" PRIu32 ")",
alignOfs, alignSize
);
return;
}
@@ -439,8 +491,9 @@ static void alignTo(uint32_t alignment, uint32_t alignOfs) {
auto &pc = curAddr[activeType][activeBankIdx];
if (alignment > 16) {
scriptError(context, "Cannot align: The alignment (%" PRIu32 ") must be less than 16",
alignment);
scriptError(
context, "Cannot align: The alignment (%" PRIu32 ") must be less than 16", alignment
);
return;
}
@@ -451,9 +504,12 @@ static void alignTo(uint32_t alignment, uint32_t alignOfs) {
uint32_t alignSize = 1u << alignment;
if (alignOfs >= alignSize) {
scriptError(context, "Cannot align: The alignment offset (%" PRIu32
") must be less than alignment size (%" PRIu32 ")",
alignOfs, alignSize);
scriptError(
context,
"Cannot align: The alignment offset (%" PRIu32
") must be less than alignment size (%" PRIu32 ")",
alignOfs, alignSize
);
return;
}
@@ -462,9 +518,12 @@ static void alignTo(uint32_t alignment, uint32_t alignOfs) {
}
if (uint16_t offset = pc - typeInfo.startAddr; length > typeInfo.size - offset) {
scriptError(context, "Cannot align: the next suitable address after $%04"
PRIx16 " is $%04" PRIx16 ", past $%04" PRIx16,
pc, (uint16_t)(pc + length), (uint16_t)(endaddr(activeType) + 1));
scriptError(
context,
"Cannot align: the next suitable address after $%04" PRIx16 " is $%04" PRIx16
", past $%04" PRIx16,
pc, (uint16_t)(pc + length), (uint16_t)(endaddr(activeType) + 1)
);
return;
}
@@ -488,8 +547,11 @@ static void pad(uint32_t length) {
assert(pc >= typeInfo.startAddr);
if (uint16_t offset = pc - typeInfo.startAddr; length + offset > typeInfo.size) {
scriptError(context, "Cannot increase the current address by %u bytes: only %u bytes to $%04" PRIx16,
length, typeInfo.size - offset, (uint16_t)(endaddr(activeType) + 1));
scriptError(
context,
"Cannot increase the current address by %u bytes: only %u bytes to $%04" PRIx16, length,
typeInfo.size - offset, (uint16_t)(endaddr(activeType) + 1)
);
} else {
pc += length;
}
@@ -498,8 +560,9 @@ static void pad(uint32_t length) {
static void placeSection(std::string const &name, bool isOptional) {
auto const &context = lexerStack.back();
if (activeType == SECTTYPE_INVALID) {
scriptError(context, "No memory region has been specified to place section \"%s\" in",
name.c_str());
scriptError(
context, "No memory region has been specified to place section \"%s\" in", name.c_str()
);
return;
}
@@ -520,14 +583,20 @@ static void placeSection(std::string const &name, bool isOptional) {
fragment->type = activeType;
}
} else if (section->type != activeType) {
scriptError(context, "\"%s\" is specified to be a %s section, but it is already a %s section",
name.c_str(), typeInfo.name.c_str(), sectionTypeInfo[section->type].name.c_str());
scriptError(
context, "\"%s\" is specified to be a %s section, but it is already a %s section",
name.c_str(), typeInfo.name.c_str(), sectionTypeInfo[section->type].name.c_str()
);
}
uint32_t bank = activeBankIdx + typeInfo.firstBank;
if (section->isBankFixed && bank != section->bank) {
scriptError(context, "The linker script places section \"%s\" in %s bank %" PRIu32 ", but it was already defined in bank %" PRIu32,
name.c_str(), sectionTypeInfo[section->type].name.c_str(), bank, section->bank);
scriptError(
context,
"The linker script places section \"%s\" in %s bank %" PRIu32
", but it was already defined in bank %" PRIu32,
name.c_str(), sectionTypeInfo[section->type].name.c_str(), bank, section->bank
);
}
section->isBankFixed = true;
section->bank = bank;
@@ -535,12 +604,22 @@ static void placeSection(std::string const &name, bool isOptional) {
if (!isPcFloating) {
uint16_t &org = curAddr[activeType][activeBankIdx];
if (section->isAddressFixed && org != section->org) {
scriptError(context, "The linker script assigns section \"%s\" to address $%04" PRIx16 ", but it was already at $%04" PRIx16,
name.c_str(), org, section->org);
scriptError(
context,
"The linker script assigns section \"%s\" to address $%04" PRIx16
", but it was already at $%04" PRIx16,
name.c_str(), org, section->org
);
} else if (section->isAlignFixed && (org & section->alignMask) != section->alignOfs) {
uint8_t alignment = std::countr_one(section->alignMask);
scriptError(context, "The linker script assigns section \"%s\" to address $%04" PRIx16 ", but that would be ALIGN[%" PRIu8 ", %" PRIu16 "] instead of the requested ALIGN[%" PRIu8 ", %" PRIu16 "]",
name.c_str(), org, alignment, (uint16_t)(org & section->alignMask), alignment, section->alignOfs);
scriptError(
context,
"The linker script assigns section \"%s\" to address $%04" PRIx16
", but that would be ALIGN[%" PRIu8 ", %" PRIu16
"] instead of the requested ALIGN[%" PRIu8 ", %" PRIu16 "]",
name.c_str(), org, alignment, (uint16_t)(org & section->alignMask), alignment,
section->alignOfs
);
}
section->isAddressFixed = true;
section->isAlignFixed = false; // This can't be set when the above is.
@@ -549,9 +628,12 @@ static void placeSection(std::string const &name, bool isOptional) {
uint16_t curOfs = org - typeInfo.startAddr;
if (section->size > typeInfo.size - curOfs) {
uint16_t overflowSize = section->size - (typeInfo.size - curOfs);
scriptError(context, "The linker script assigns section \"%s\" to address $%04" PRIx16 ", but then it would overflow %s by %" PRIx16 " byte%s",
name.c_str(), org, typeInfo.name.c_str(),
overflowSize, overflowSize == 1 ? "" : "s");
scriptError(
context,
"The linker script assigns section \"%s\" to address $%04" PRIx16
", but then it would overflow %s by %" PRIx16 " byte%s",
name.c_str(), org, typeInfo.name.c_str(), overflowSize, overflowSize == 1 ? "" : "s"
);
// Fill as much as possible without going out of bounds.
org = typeInfo.startAddr + typeInfo.size;
} else {

View File

@@ -1,5 +1,7 @@
/* SPDX-License-Identifier: MIT */
#include "link/sdas_obj.hpp"
#include <assert.h>
#include <ctype.h>
#include <errno.h>
@@ -12,20 +14,19 @@
#include <variant>
#include <vector>
#include "linkdefs.hpp"
#include "helpers.hpp"
#include "linkdefs.hpp"
#include "platform.hpp"
#include "link/assign.hpp"
#include "link/main.hpp"
#include "link/sdas_obj.hpp"
#include "link/section.hpp"
#include "link/symbol.hpp"
enum NumberType {
HEX = 16, // X
DEC = 10, // D
OCT = 8, // Q
OCT = 8, // Q
};
static void consumeLF(FileStackNode const &where, uint32_t lineNo, FILE *file) {
@@ -35,8 +36,8 @@ static void consumeLF(FileStackNode const &where, uint32_t lineNo, FILE *file) {
static char const *delim = " \f\n\r\t\v"; // Whitespace according to the C and POSIX locales
static int nextLine(std::vector<char> &lineBuf, uint32_t &lineNo,
FileStackNode const &where, FILE *file) {
static int
nextLine(std::vector<char> &lineBuf, uint32_t &lineNo, FileStackNode const &where, FILE *file) {
retry:
++lineNo;
int firstChar = getc(file);
@@ -91,7 +92,9 @@ static uint32_t readNumber(char const *str, char const *&endptr, enum NumberType
}
}
static uint32_t parseNumber(FileStackNode const &where, uint32_t lineNo, char const *str, enum NumberType base) {
static uint32_t parseNumber(
FileStackNode const &where, uint32_t lineNo, char const *str, enum NumberType base
) {
if (str[0] == '\0')
fatal(&where, lineNo, "Expected number, got empty string");
@@ -103,7 +106,8 @@ static uint32_t parseNumber(FileStackNode const &where, uint32_t lineNo, char co
return res;
}
static uint8_t parseByte(FileStackNode const &where, uint32_t lineNo, char const *str, enum NumberType base) {
static uint8_t
parseByte(FileStackNode const &where, uint32_t lineNo, char const *str, enum NumberType base) {
uint32_t num = parseNumber(where, lineNo, str, base);
if (num > UINT8_MAX)
@@ -113,49 +117,58 @@ static uint8_t parseByte(FileStackNode const &where, uint32_t lineNo, char const
enum AreaFlags {
AREA_TYPE = 2, // 0: Concatenate, 1: overlay
AREA_ISABS, // 0: Relative (???) address, 1: absolute address
AREA_PAGING, // Unsupported
AREA_ISABS, // 0: Relative (???) address, 1: absolute address
AREA_PAGING, // Unsupported
AREA_ALL_FLAGS = 1 << AREA_TYPE | 1 << AREA_ISABS | 1 << AREA_PAGING,
};
enum RelocFlags {
RELOC_SIZE, // 0: 16-bit, 1: 8-bit
RELOC_ISSYM, // 0: Area, 1: Symbol
RELOC_ISPCREL, // 0: Normal, 1: PC-relative
RELOC_EXPR16, // Only for 8-bit size; 0: 8-bit expr, 1: 16-bit expr
RELOC_SIGNED, // 0: signed, 1: unsigned
RELOC_ZPAGE, // Unsupported
RELOC_NPAGE, // Unsupported
RELOC_SIZE, // 0: 16-bit, 1: 8-bit
RELOC_ISSYM, // 0: Area, 1: Symbol
RELOC_ISPCREL, // 0: Normal, 1: PC-relative
RELOC_EXPR16, // Only for 8-bit size; 0: 8-bit expr, 1: 16-bit expr
RELOC_SIGNED, // 0: signed, 1: unsigned
RELOC_ZPAGE, // Unsupported
RELOC_NPAGE, // Unsupported
RELOC_WHICHBYTE, // 8-bit size with 16-bit expr only; 0: LOW(), 1: HIGH()
RELOC_EXPR24, // Only for 8-bit size; 0: follow RELOC_EXPR16, 1: 24-bit expr
RELOC_BANKBYTE, // 8-bit size with 24-bit expr only; 0: follow RELOC_WHICHBYTE, 1: BANK()
RELOC_EXPR24, // Only for 8-bit size; 0: follow RELOC_EXPR16, 1: 24-bit expr
RELOC_BANKBYTE, // 8-bit size with 24-bit expr only; 0: follow RELOC_WHICHBYTE, 1: BANK()
RELOC_ALL_FLAGS = 1 << RELOC_SIZE | 1 << RELOC_ISSYM | 1 << RELOC_ISPCREL | 1 << RELOC_EXPR16
| 1 << RELOC_SIGNED | 1 << RELOC_ZPAGE | 1 << RELOC_NPAGE | 1 << RELOC_WHICHBYTE
| 1 << RELOC_EXPR24 | 1 << RELOC_BANKBYTE,
| 1 << RELOC_SIGNED | 1 << RELOC_ZPAGE | 1 << RELOC_NPAGE
| 1 << RELOC_WHICHBYTE | 1 << RELOC_EXPR24 | 1 << RELOC_BANKBYTE,
};
void sdobj_ReadFile(FileStackNode const &where, FILE *file, std::vector<Symbol> &fileSymbols) {
std::vector<char> line(256);
char const *token;
#define getToken(ptr, ...) do { \
token = strtok((ptr), delim); \
if (!token) \
fatal(&where, lineNo, __VA_ARGS__); \
} while (0)
#define expectEol(...) do { \
token = strtok(nullptr, delim); \
if (token) \
fatal(&where, lineNo, __VA_ARGS__); \
} while (0)
#define expectToken(expected, lineType) do { \
getToken(nullptr, "'%c' line is too short", (lineType)); \
if (strcasecmp(token, (expected)) != 0) \
fatal(&where, lineNo, "Malformed '%c' line: expected \"%s\", got \"%s\"", \
(lineType), (expected), token); \
} while (0)
#define getToken(ptr, ...) \
do { \
token = strtok((ptr), delim); \
if (!token) \
fatal(&where, lineNo, __VA_ARGS__); \
} while (0)
#define expectEol(...) \
do { \
token = strtok(nullptr, delim); \
if (token) \
fatal(&where, lineNo, __VA_ARGS__); \
} while (0)
#define expectToken(expected, lineType) \
do { \
getToken(nullptr, "'%c' line is too short", (lineType)); \
if (strcasecmp(token, (expected)) != 0) \
fatal( \
&where, \
lineNo, \
"Malformed '%c' line: expected \"%s\", got \"%s\"", \
(lineType), \
(expected), \
token \
); \
} while (0)
uint32_t lineNo = 0;
int lineType = nextLine(line, lineNo, where, file);
@@ -175,8 +188,12 @@ void sdobj_ReadFile(FileStackNode const &where, FILE *file, std::vector<Symbol>
numberType = OCT;
break;
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]) {
@@ -237,9 +254,10 @@ void sdobj_ReadFile(FileStackNode const &where, FILE *file, std::vector<Symbol>
case 'A': {
if (fileSections.size() == expectedNbAreas)
warning(&where, lineNo, "Got more 'A' lines than the expected %" PRIu32,
expectedNbAreas);
Section *curSection = new(std::nothrow) Section();
warning(
&where, lineNo, "Got more 'A' lines than the expected %" PRIu32, expectedNbAreas
);
Section *curSection = new (std::nothrow) Section();
if (!curSection)
fatal(&where, lineNo, "Failed to alloc new area: %s", strerror(errno));
@@ -249,12 +267,11 @@ void sdobj_ReadFile(FileStackNode const &where, FILE *file, std::vector<Symbol>
// The following is required for fragment offsets to be reliably predicted
for (FileSection &entry : fileSections) {
if (!strcmp(token, entry.section->name.c_str()))
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
fileSections.push_back({ .section = curSection, .writeIndex = 0 });
fileSections.push_back({.section = curSection, .writeIndex = 0});
expectToken("size", 'A');
@@ -263,8 +280,12 @@ void sdobj_ReadFile(FileStackNode const &where, FILE *file, std::vector<Symbol>
uint32_t tmp = parseNumber(where, lineNo, token, numberType);
if (tmp > UINT16_MAX)
fatal(&where, lineNo, "Area \"%s\" is larger than the GB address space!?",
curSection->name.c_str());
fatal(
&where,
lineNo,
"Area \"%s\" is larger than the GB address space!?",
curSection->name.c_str()
);
curSection->size = tmp;
expectToken("flags", 'A');
@@ -276,7 +297,8 @@ void sdobj_ReadFile(FileStackNode const &where, FILE *file, std::vector<Symbol>
curSection->isAddressFixed = tmp & (1 << AREA_ISABS);
curSection->isBankFixed = curSection->isAddressFixed;
curSection->modifier = curSection->isAddressFixed || (tmp & (1 << AREA_TYPE))
? SECTION_NORMAL : SECTION_FRAGMENT;
? SECTION_NORMAL
: SECTION_FRAGMENT;
// If the section is absolute, its name might not be unique; thus, mangle the name
if (curSection->modifier == SECTION_NORMAL) {
curSection->name.append(where.name());
@@ -320,7 +342,7 @@ void sdobj_ReadFile(FileStackNode const &where, FILE *file, std::vector<Symbol>
} else {
curSection->type = SECTTYPE_INVALID; // This means "indeterminate"
}
curSection->isAlignFixed = false; // No such concept!
curSection->isAlignFixed = false; // No such concept!
curSection->fileSymbols = &fileSymbols; // IDs are instead per-section
curSection->nextu = nullptr;
break;
@@ -328,8 +350,12 @@ void sdobj_ReadFile(FileStackNode const &where, FILE *file, std::vector<Symbol>
case 'S': {
if (fileSymbols.size() == 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
);
Symbol &symbol = fileSymbols.emplace_back();
// Init other members
@@ -347,16 +373,11 @@ void sdobj_ReadFile(FileStackNode const &where, FILE *file, std::vector<Symbol>
// Symbols in sections are labels; their value is an offset
Section *section = fileSections.back().section;
if (section->isAddressFixed) {
assert(value >= section->org &&
value <= section->org + section->size);
assert(value >= section->org && value <= section->org + section->size);
value -= section->org;
}
symbol.data = Label{
// No need to set the `sectionID`, since we set the pointer
.sectionID = 0,
.offset = value,
.section = section
};
// No need to set the `sectionID`, since we set the pointer
symbol.data = Label{.sectionID = 0, .offset = value, .section = section};
} else {
// Symbols without sections are just constants
symbol.data = value;
@@ -377,23 +398,30 @@ void sdobj_ReadFile(FileStackNode const &where, FILE *file, std::vector<Symbol>
// The same symbol can only be defined twice if neither
// definition is in a floating section
auto visitor = Visitor{
[](int32_t value) -> std::tuple<Section *, int32_t> {
return {nullptr, value};
},
[](Label label) -> std::tuple<Section *, int32_t> {
return {label.section, label.offset};
}
[](int32_t value) -> std::tuple<Section *, int32_t> {
return {nullptr, value};
},
[](Label label) -> std::tuple<Section *, int32_t> {
return {label.section, label.offset};
},
};
auto [symbolSection, symbolValue] = std::visit(visitor, symbol.data);
auto [otherSection, otherValue] = std::visit(visitor, other->data);
if ((otherSection && !otherSection->isAddressFixed)
|| (symbolSection && !symbolSection->isAddressFixed)) {
sym_AddSymbol(symbol); // This will error out
} else if (otherValue != symbolValue) {
error(&where, lineNo,
"Definition of \"%s\" conflicts with definition in %s (%" PRId32 " != %" PRId32 ")",
symbol.name.c_str(), other->objFileName, symbolValue, otherValue);
error(
&where,
lineNo,
"Definition of \"%s\" conflicts with definition in %s (%" PRId32
" != %" PRId32 ")",
symbol.name.c_str(),
other->objFileName,
symbolValue,
otherValue
);
}
} else {
// Add a new definition
@@ -443,8 +471,13 @@ void sdobj_ReadFile(FileStackNode const &where, FILE *file, std::vector<Symbol>
getToken(nullptr, "'R' line is too short");
areaIdx |= (uint16_t)parseByte(where, lineNo, token, numberType) << 8;
if (areaIdx >= fileSections.size())
fatal(&where, lineNo, "'R' line references area #%" PRIu16 ", but there are only %zu (so far)",
areaIdx, fileSections.size());
fatal(
&where,
lineNo,
"'R' line references area #%" PRIu16 ", but there are only %zu (so far)",
areaIdx,
fileSections.size()
);
assert(!fileSections.empty()); // There should be at least one, from the above check
Section *section = fileSections[areaIdx].section;
uint16_t *writeIndex = &fileSections[areaIdx].writeIndex;
@@ -453,16 +486,29 @@ void sdobj_ReadFile(FileStackNode const &where, FILE *file, std::vector<Symbol>
if (section->isAddressFixed) {
if (addr < section->org)
fatal(&where, lineNo, "'T' line reports address $%04" PRIx16 " in \"%s\", which starts at $%04" PRIx16,
addr, section->name.c_str(), section->org);
fatal(
&where,
lineNo,
"'T' line reports address $%04" PRIx16
" in \"%s\", which starts at $%04" PRIx16,
addr,
section->name.c_str(),
section->org
);
addr -= section->org;
}
// Lines are emitted that violate this check but contain no "payload";
// ignore those. "Empty" lines shouldn't trigger allocation, either.
if (data.size() != ADDR_SIZE) {
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.empty()) {
assert(section->size != 0);
section->data.resize(section->size);
@@ -487,18 +533,29 @@ void sdobj_ReadFile(FileStackNode const &where, FILE *file, std::vector<Symbol>
if ((flags & 0xF0) == 0xF0) {
getToken(nullptr, "Incomplete relocation");
flags = (flags & 0x0F) | (uint16_t)parseByte(where, lineNo, token, numberType) << 4;
flags =
(flags & 0x0F) | (uint16_t)parseByte(where, lineNo, token, numberType) << 4;
}
getToken(nullptr, "Incomplete relocation");
uint8_t offset = parseByte(where, lineNo, token, numberType);
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 >= data.size())
fatal(&where, lineNo, "Relocation index is out of bounds (%" PRIu16 " >= %zu)",
offset, data.size());
fatal(
&where,
lineNo,
"Relocation index is out of bounds (%" PRIu16 " >= %zu)",
offset,
data.size()
);
getToken(nullptr, "Incomplete relocation");
uint16_t idx = parseByte(where, lineNo, token, numberType);
@@ -520,11 +577,17 @@ void sdobj_ReadFile(FileStackNode const &where, FILE *file, std::vector<Symbol>
patch.offset = offset - writtenOfs + *writeIndex;
if (section->patches.size() > 1) {
uint32_t prevOffset = section->patches[section->patches.size() - 2].offset;
if (prevOffset>= patch.offset)
fatal(&where, lineNo, "Relocs not sorted by offset are not supported (%" PRIu32 " >= %" PRIu32 ")",
prevOffset, patch.offset);
if (prevOffset >= patch.offset)
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.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;
@@ -533,8 +596,13 @@ void sdobj_ReadFile(FileStackNode const &where, FILE *file, std::vector<Symbol>
assert(offset < data.size());
if (data.size() - offset < nbBaseBytes)
fatal(&where, lineNo, "Reloc would patch out of bounds (%" PRIu8 " > %zu)",
nbBaseBytes, data.size() - offset);
fatal(
&where,
lineNo,
"Reloc would patch out of bounds (%" PRIu8 " > %zu)",
nbBaseBytes,
data.size() - offset
);
for (uint8_t i = 0; i < nbBaseBytes; ++i)
baseValue = baseValue | data[offset + i] << (8 * i);
@@ -542,8 +610,13 @@ void sdobj_ReadFile(FileStackNode const &where, FILE *file, std::vector<Symbol>
// Generate a RPN expression from the info and flags
if (flags & 1 << RELOC_ISSYM) {
if (idx >= fileSymbols.size())
fatal(&where, lineNo, "Reloc refers to symbol #%" PRIu16 " out of %zu",
idx, fileSymbols.size());
fatal(
&where,
lineNo,
"Reloc refers to symbol #%" PRIu16 " out of %zu",
idx,
fileSymbols.size()
);
Symbol const &sym = fileSymbols[idx];
// SDCC has a bunch of "magic symbols" that start with a
@@ -552,13 +625,18 @@ void sdobj_ReadFile(FileStackNode const &where, FILE *file, std::vector<Symbol>
if (sym.name.starts_with("b_")) {
// Look for the symbol being referenced, and use its index instead
for (idx = 0; idx < fileSymbols.size(); ++idx) {
if (sym.name.ends_with(fileSymbols[idx].name) &&
1 + sym.name.length() == fileSymbols[idx].name.length())
if (sym.name.ends_with(fileSymbols[idx].name)
&& 1 + sym.name.length() == fileSymbols[idx].name.length())
break;
}
if (idx == fileSymbols.size())
fatal(&where, lineNo, "\"%s\" is missing a reference to \"%s\"",
sym.name.c_str(), &sym.name.c_str()[1]);
fatal(
&where,
lineNo,
"\"%s\" is missing a reference to \"%s\"",
sym.name.c_str(),
&sym.name.c_str()[1]
);
patch.rpnExpression.resize(5);
patch.rpnExpression[0] = RPN_BANK_SYM;
patch.rpnExpression[1] = idx;
@@ -568,11 +646,19 @@ void sdobj_ReadFile(FileStackNode const &where, FILE *file, std::vector<Symbol>
} else if (sym.name.starts_with("l_")) {
patch.rpnExpression.resize(1 + sym.name.length() - 2 + 1);
patch.rpnExpression[0] = RPN_SIZEOF_SECT;
memcpy((char *)&patch.rpnExpression[1], &sym.name.c_str()[2], sym.name.length() - 2 + 1);
memcpy(
(char *)&patch.rpnExpression[1],
&sym.name.c_str()[2],
sym.name.length() - 2 + 1
);
} else if (sym.name.starts_with("s_")) {
patch.rpnExpression.resize(1 + sym.name.length() - 2 + 1);
patch.rpnExpression[0] = RPN_STARTOF_SECT;
memcpy((char *)&patch.rpnExpression[1], &sym.name.c_str()[2], sym.name.length() - 2 + 1);
memcpy(
(char *)&patch.rpnExpression[1],
&sym.name.c_str()[2],
sym.name.length() - 2 + 1
);
} else {
patch.rpnExpression.resize(5);
patch.rpnExpression[0] = RPN_SYM;
@@ -583,8 +669,13 @@ void sdobj_ReadFile(FileStackNode const &where, FILE *file, std::vector<Symbol>
}
} else {
if (idx >= fileSections.size())
fatal(&where, lineNo, "Reloc refers to area #%" PRIu16 " out of %zu",
idx, fileSections.size());
fatal(
&where,
lineNo,
"Reloc refers to area #%" PRIu16 " out of %zu",
idx,
fileSections.size()
);
// It gets funky. If the area is absolute, *actually*, we
// must not add its base address, as the assembler will
// already have added it in `baseValue`.
@@ -625,10 +716,18 @@ void sdobj_ReadFile(FileStackNode const &where, FILE *file, std::vector<Symbol>
// are present, so we must skip two of them
if (flags & 1 << RELOC_EXPR16) {
if (*writeIndex + (offset - writtenOfs) > section->size)
fatal(&where, lineNo, "'T' line writes past \"%s\"'s end (%u > %" PRIu16 ")",
section->name.c_str(), *writeIndex + (offset - writtenOfs), section->size);
fatal(
&where,
lineNo,
"'T' line writes past \"%s\"'s end (%u > %" PRIu16 ")",
section->name.c_str(),
*writeIndex + (offset - writtenOfs),
section->size
);
// 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;
writtenOfs = offset + 3; // Skip all three `baseValue` bytes, though
}
@@ -644,7 +743,9 @@ void sdobj_ReadFile(FileStackNode const &where, FILE *file, std::vector<Symbol>
patch.rpnExpression.push_back(16 >> 8);
patch.rpnExpression.push_back(16 >> 16);
patch.rpnExpression.push_back(16 >> 24);
patch.rpnExpression.push_back((flags & 1 << RELOC_SIGNED) ? RPN_SHR : RPN_USHR);
patch.rpnExpression.push_back(
(flags & 1 << RELOC_SIGNED) ? RPN_SHR : RPN_USHR
);
} else {
if (flags & 1 << RELOC_EXPR16 && flags & 1 << RELOC_WHICHBYTE) {
patch.rpnExpression.push_back(RPN_CONST);
@@ -652,7 +753,9 @@ void sdobj_ReadFile(FileStackNode const &where, FILE *file, std::vector<Symbol>
patch.rpnExpression.push_back(8 >> 8);
patch.rpnExpression.push_back(8 >> 16);
patch.rpnExpression.push_back(8 >> 24);
patch.rpnExpression.push_back((flags & 1 << RELOC_SIGNED) ? RPN_SHR : RPN_USHR);
patch.rpnExpression.push_back(
(flags & 1 << RELOC_SIGNED) ? RPN_SHR : RPN_USHR
);
}
patch.rpnExpression.push_back(RPN_CONST);
patch.rpnExpression.push_back(0xFF);
@@ -665,8 +768,12 @@ void sdobj_ReadFile(FileStackNode const &where, FILE *file, std::vector<Symbol>
assert(patch.type == PATCHTYPE_WORD);
fatal(&where, lineNo, "16-bit PC-relative relocations are not supported");
} 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)
);
}
}
@@ -674,8 +781,14 @@ void sdobj_ReadFile(FileStackNode const &where, FILE *file, std::vector<Symbol>
if (writtenOfs != data.size()) {
assert(data.size() > writtenOfs);
if (*writeIndex + (data.size() - writtenOfs) > section->size)
fatal(&where, lineNo, "'T' line writes past \"%s\"'s end (%zu > %" PRIu16 ")",
section->name.c_str(), *writeIndex + (data.size() - writtenOfs), section->size);
fatal(
&where,
lineNo,
"'T' line writes past \"%s\"'s end (%zu > %" PRIu16 ")",
section->name.c_str(),
*writeIndex + (data.size() - writtenOfs),
section->size
);
memcpy(&section->data[*writeIndex], &data[writtenOfs], data.size() - writtenOfs);
*writeIndex += data.size() - writtenOfs;
}
@@ -694,11 +807,21 @@ void sdobj_ReadFile(FileStackNode const &where, FILE *file, std::vector<Symbol>
if (!data.empty())
warning(&where, lineNo, "Last 'T' line had no 'R' line (ignored)");
if (fileSections.size() < expectedNbAreas)
warning(&where, lineNo, "Expected %" PRIu32 " 'A' lines, got only %zu", expectedNbAreas,
fileSections.size());
warning(
&where,
lineNo,
"Expected %" PRIu32 " 'A' lines, got only %zu",
expectedNbAreas,
fileSections.size()
);
if (fileSymbols.size() < expectedNbSymbols)
warning(&where, lineNo, "Expected %" PRIu32 " 'S' lines, got only %zu", expectedNbSymbols,
fileSymbols.size());
warning(
&where,
lineNo,
"Expected %" PRIu32 " 'S' lines, got only %zu",
expectedNbSymbols,
fileSymbols.size()
);
nbSectionsToAssign += fileSections.size();
@@ -707,8 +830,14 @@ void sdobj_ReadFile(FileStackNode const &where, FILE *file, std::vector<Symbol>
// RAM sections can have a size, but don't get any data (they shouldn't have any)
if (entry.writeIndex != section->size && entry.writeIndex != 0)
fatal(&where, lineNo, "\"%s\" was not fully written (%" PRIu16 " < %" PRIu16 ")",
section->name.c_str(), entry.writeIndex, section->size);
fatal(
&where,
lineNo,
"\"%s\" was not fully written (%" PRIu16 " < %" PRIu16 ")",
section->name.c_str(),
entry.writeIndex,
section->size
);
sect_AddSection(*section);

View File

@@ -1,39 +1,47 @@
/* SPDX-License-Identifier: MIT */
#include "link/section.hpp"
#include <assert.h>
#include <inttypes.h>
#include <map>
#include <stdlib.h>
#include <string>
#include <string.h>
#include "link/main.hpp"
#include "link/section.hpp"
#include <string>
#include "error.hpp"
#include "linkdefs.hpp"
#include "link/main.hpp"
std::map<std::string, Section *> sections;
void sect_ForEach(void (*callback)(Section &))
{
void sect_ForEach(void (*callback)(Section &)) {
for (auto &it : sections)
callback(*it.second);
}
static void checkSectUnionCompat(Section &target, Section &other)
{
static void checkSectUnionCompat(Section &target, Section &other) {
if (other.isAddressFixed) {
if (target.isAddressFixed) {
if (target.org != other.org)
errx("Section \"%s\" is defined with conflicting addresses $%04"
PRIx16 " and $%04" PRIx16, other.name.c_str(), target.org,
other.org);
errx(
"Section \"%s\" is defined with conflicting addresses $%04" PRIx16
" and $%04" PRIx16,
other.name.c_str(),
target.org,
other.org
);
} else if (target.isAlignFixed) {
if ((other.org - target.alignOfs) & target.alignMask)
errx("Section \"%s\" is defined with conflicting %d-byte alignment (offset %"
PRIu16 ") and address $%04" PRIx16, other.name.c_str(),
target.alignMask + 1, target.alignOfs, other.org);
errx(
"Section \"%s\" is defined with conflicting %d-byte alignment (offset %" PRIu16
") and address $%04" PRIx16,
other.name.c_str(),
target.alignMask + 1,
target.alignOfs,
other.org
);
}
target.isAddressFixed = true;
target.org = other.org;
@@ -41,17 +49,25 @@ static void checkSectUnionCompat(Section &target, Section &other)
} else if (other.isAlignFixed) {
if (target.isAddressFixed) {
if ((target.org - other.alignOfs) & other.alignMask)
errx("Section \"%s\" is defined with conflicting address $%04"
PRIx16 " and %d-byte alignment (offset %" PRIu16 ")",
other.name.c_str(), target.org, other.alignMask + 1,
other.alignOfs);
errx(
"Section \"%s\" is defined with conflicting address $%04" PRIx16
" and %d-byte alignment (offset %" PRIu16 ")",
other.name.c_str(),
target.org,
other.alignMask + 1,
other.alignOfs
);
} else if (target.isAlignFixed
&& (other.alignMask & target.alignOfs)
!= (target.alignMask & other.alignOfs)) {
errx("Section \"%s\" is defined with conflicting %d-byte alignment (offset %"
PRIu16 ") and %d-byte alignment (offset %" PRIu16 ")",
other.name.c_str(), target.alignMask + 1, target.alignOfs,
other.alignMask + 1, other.alignOfs);
&& (other.alignMask & target.alignOfs) != (target.alignMask & other.alignOfs)) {
errx(
"Section \"%s\" is defined with conflicting %d-byte alignment (offset %" PRIu16
") and %d-byte alignment (offset %" PRIu16 ")",
other.name.c_str(),
target.alignMask + 1,
target.alignOfs,
other.alignMask + 1,
other.alignOfs
);
} else if (!target.isAlignFixed || (other.alignMask > target.alignMask)) {
target.isAlignFixed = true;
target.alignMask = other.alignMask;
@@ -59,22 +75,30 @@ static void checkSectUnionCompat(Section &target, Section &other)
}
}
static void checkFragmentCompat(Section &target, Section &other)
{
static void checkFragmentCompat(Section &target, Section &other) {
if (other.isAddressFixed) {
uint16_t org = other.org - target.size;
if (target.isAddressFixed) {
if (target.org != org)
errx("Section \"%s\" is defined with conflicting addresses $%04"
PRIx16 " and $%04" PRIx16, other.name.c_str(), target.org,
other.org);
errx(
"Section \"%s\" is defined with conflicting addresses $%04" PRIx16
" and $%04" PRIx16,
other.name.c_str(),
target.org,
other.org
);
} else if (target.isAlignFixed) {
if ((org - target.alignOfs) & target.alignMask)
errx("Section \"%s\" is defined with conflicting %d-byte alignment (offset %"
PRIu16 ") and address $%04" PRIx16, other.name.c_str(),
target.alignMask + 1, target.alignOfs, other.org);
errx(
"Section \"%s\" is defined with conflicting %d-byte alignment (offset %" PRIu16
") and address $%04" PRIx16,
other.name.c_str(),
target.alignMask + 1,
target.alignOfs,
other.org
);
}
target.isAddressFixed = true;
target.org = org;
@@ -87,17 +111,25 @@ static void checkFragmentCompat(Section &target, Section &other)
if (target.isAddressFixed) {
if ((target.org - ofs) & other.alignMask)
errx("Section \"%s\" is defined with conflicting address $%04"
PRIx16 " and %d-byte alignment (offset %" PRIu16 ")",
other.name.c_str(), target.org, other.alignMask + 1,
other.alignOfs);
errx(
"Section \"%s\" is defined with conflicting address $%04" PRIx16
" and %d-byte alignment (offset %" PRIu16 ")",
other.name.c_str(),
target.org,
other.alignMask + 1,
other.alignOfs
);
} else if (target.isAlignFixed
&& (other.alignMask & target.alignOfs) != (target.alignMask & ofs)) {
errx("Section \"%s\" is defined with conflicting %d-byte alignment (offset %"
PRIu16 ") and %d-byte alignment (offset %" PRIu16 ")",
other.name.c_str(), target.alignMask + 1, target.alignOfs,
other.alignMask + 1, other.alignOfs);
} else if (target.isAlignFixed && (other.alignMask & target.alignOfs) != (target.alignMask & ofs)) {
errx(
"Section \"%s\" is defined with conflicting %d-byte alignment (offset %" PRIu16
") and %d-byte alignment (offset %" PRIu16 ")",
other.name.c_str(),
target.alignMask + 1,
target.alignOfs,
other.alignMask + 1,
other.alignOfs
);
} else if (!target.isAlignFixed || (other.alignMask > target.alignMask)) {
target.isAlignFixed = true;
@@ -107,22 +139,28 @@ static void checkFragmentCompat(Section &target, Section &other)
}
}
static void mergeSections(Section &target, Section &other, enum SectionModifier mod)
{
static void mergeSections(Section &target, Section &other, enum SectionModifier mod) {
// Common checks
if (target.type != other.type)
errx("Section \"%s\" is defined with conflicting types %s and %s",
other.name.c_str(), sectionTypeInfo[target.type].name.c_str(),
sectionTypeInfo[other.type].name.c_str());
errx(
"Section \"%s\" is defined with conflicting types %s and %s",
other.name.c_str(),
sectionTypeInfo[target.type].name.c_str(),
sectionTypeInfo[other.type].name.c_str()
);
if (other.isBankFixed) {
if (!target.isBankFixed) {
target.isBankFixed = true;
target.bank = other.bank;
} else if (target.bank != other.bank) {
errx("Section \"%s\" is defined with conflicting banks %" PRIu32 " and %"
PRIu32, other.name.c_str(), target.bank, other.bank);
errx(
"Section \"%s\" is defined with conflicting banks %" PRIu32 " and %" PRIu32,
other.name.c_str(),
target.bank,
other.bank
);
}
}
@@ -159,34 +197,38 @@ static void mergeSections(Section &target, Section &other, enum SectionModifier
target.nextu = &other;
}
void sect_AddSection(Section &section)
{
void sect_AddSection(Section &section) {
// Check if the section already exists
if (Section *other = sect_GetSection(section.name); other) {
if (section.modifier != other->modifier)
errx("Section \"%s\" defined as %s and %s", section.name.c_str(),
sectionModNames[section.modifier], sectionModNames[other->modifier]);
errx(
"Section \"%s\" defined as %s and %s",
section.name.c_str(),
sectionModNames[section.modifier],
sectionModNames[other->modifier]
);
else if (section.modifier == SECTION_NORMAL)
errx("Section name \"%s\" is already in use", section.name.c_str());
else
mergeSections(*other, section, section.modifier);
} else if (section.modifier == SECTION_UNION && sect_HasData(section.type)) {
errx("Section \"%s\" is of type %s, which cannot be unionized",
section.name.c_str(), sectionTypeInfo[section.type].name.c_str());
errx(
"Section \"%s\" is of type %s, which cannot be unionized",
section.name.c_str(),
sectionTypeInfo[section.type].name.c_str()
);
} else {
// If not, add it
sections[section.name] = &section;
}
}
Section *sect_GetSection(std::string const &name)
{
Section *sect_GetSection(std::string const &name) {
auto search = sections.find(name);
return search != sections.end() ? search->second : nullptr;
}
static void doSanityChecks(Section &section)
{
static void doSanityChecks(Section &section) {
// Sanity check the section's type
if (section.type < 0 || section.type >= SECTTYPE_INVALID) {
error(nullptr, 0, "Section \"%s\" has an invalid type", section.name.c_str());
@@ -195,21 +237,28 @@ static void doSanityChecks(Section &section)
if (is32kMode && section.type == SECTTYPE_ROMX) {
if (section.isBankFixed && section.bank != 1)
error(nullptr, 0, "%s: ROMX sections must be in bank 1 (if any) with option -t",
section.name.c_str());
error(
nullptr,
0,
"%s: ROMX sections must be in bank 1 (if any) with option -t",
section.name.c_str()
);
else
section.type = SECTTYPE_ROM0;
}
if (isWRAM0Mode && section.type == SECTTYPE_WRAMX) {
if (section.isBankFixed && section.bank != 1)
error(nullptr, 0, "%s: WRAMX sections must be in bank 1 with options -w or -d",
section.name.c_str());
error(
nullptr,
0,
"%s: WRAMX sections must be in bank 1 with options -w or -d",
section.name.c_str()
);
else
section.type = SECTTYPE_WRAM0;
}
if (isDmgMode && section.type == SECTTYPE_VRAM && section.bank == 1)
error(nullptr, 0, "%s: VRAM bank 1 can't be used with option -d",
section.name.c_str());
error(nullptr, 0, "%s: VRAM bank 1 can't be used with option -d", section.name.c_str());
// Check if alignment is reasonable, this is important to avoid UB
// An alignment of zero is equivalent to no alignment, basically
@@ -218,23 +267,42 @@ static void doSanityChecks(Section &section)
// Too large an alignment may not be satisfiable
if (section.isAlignFixed && (section.alignMask & sectionTypeInfo[section.type].startAddr))
error(nullptr, 0, "%s: %s sections cannot be aligned to $%04x bytes",
section.name.c_str(), sectionTypeInfo[section.type].name.c_str(),
section.alignMask + 1);
error(
nullptr,
0,
"%s: %s sections cannot be aligned to $%04x bytes",
section.name.c_str(),
sectionTypeInfo[section.type].name.c_str(),
section.alignMask + 1
);
uint32_t minbank = sectionTypeInfo[section.type].firstBank, maxbank = sectionTypeInfo[section.type].lastBank;
uint32_t minbank = sectionTypeInfo[section.type].firstBank,
maxbank = sectionTypeInfo[section.type].lastBank;
if (section.isBankFixed && section.bank < minbank && section.bank > maxbank)
error(nullptr, 0, minbank == maxbank
? "Cannot place section \"%s\" in bank %" PRIu32 ", it must be %" PRIu32
: "Cannot place section \"%s\" in bank %" PRIu32 ", it must be between %" PRIu32 " and %" PRIu32,
section.name.c_str(), section.bank, minbank, maxbank);
error(
nullptr,
0,
minbank == maxbank
? "Cannot place section \"%s\" in bank %" PRIu32 ", it must be %" PRIu32
: "Cannot place section \"%s\" in bank %" PRIu32 ", it must be between %" PRIu32
" and %" PRIu32,
section.name.c_str(),
section.bank,
minbank,
maxbank
);
// Check if section has a chance to be placed
if (section.size > sectionTypeInfo[section.type].size)
error(nullptr, 0, "Section \"%s\" is bigger than the max size for that type: $%"
PRIx16 " > $%" PRIx16,
section.name.c_str(), section.size, sectionTypeInfo[section.type].size);
error(
nullptr,
0,
"Section \"%s\" is bigger than the max size for that type: $%" PRIx16 " > $%" PRIx16,
section.name.c_str(),
section.size,
sectionTypeInfo[section.type].size
);
// Translate loose constraints to strong ones when they're equivalent
@@ -247,26 +315,41 @@ static void doSanityChecks(Section &section)
// It doesn't make sense to have both org and alignment set
if (section.isAlignFixed) {
if ((section.org & section.alignMask) != section.alignOfs)
error(nullptr, 0, "Section \"%s\"'s fixed address doesn't match its alignment",
section.name.c_str());
error(
nullptr,
0,
"Section \"%s\"'s fixed address doesn't match its alignment",
section.name.c_str()
);
section.isAlignFixed = false;
}
// Ensure the target address is valid
if (section.org < sectionTypeInfo[section.type].startAddr
|| section.org > endaddr(section.type))
error(nullptr, 0, "Section \"%s\"'s fixed address $%04" PRIx16 " is outside of range [$%04"
PRIx16 "; $%04" PRIx16 "]", section.name.c_str(), section.org,
sectionTypeInfo[section.type].startAddr, endaddr(section.type));
|| section.org > endaddr(section.type))
error(
nullptr,
0,
"Section \"%s\"'s fixed address $%04" PRIx16 " is outside of range [$%04" PRIx16
"; $%04" PRIx16 "]",
section.name.c_str(),
section.org,
sectionTypeInfo[section.type].startAddr,
endaddr(section.type)
);
if (section.org + section.size > endaddr(section.type) + 1)
error(nullptr, 0, "Section \"%s\"'s end address $%04x is greater than last address $%04x",
section.name.c_str(), section.org + section.size,
endaddr(section.type) + 1);
error(
nullptr,
0,
"Section \"%s\"'s end address $%04x is greater than last address $%04x",
section.name.c_str(),
section.org + section.size,
endaddr(section.type) + 1
);
}
}
void sect_DoSanityChecks()
{
void sect_DoSanityChecks() {
sect_ForEach(doSanityChecks);
}

View File

@@ -1,35 +1,33 @@
/* SPDX-License-Identifier: MIT */
#include "link/symbol.hpp"
#include <inttypes.h>
#include <map>
#include <stdlib.h>
#include <string>
#include <variant>
#include "link/object.hpp"
#include "link/section.hpp"
#include "link/symbol.hpp"
#include "link/main.hpp"
#include "error.hpp"
#include "helpers.hpp"
#include "link/main.hpp"
#include "link/object.hpp"
#include "link/section.hpp"
std::map<std::string, Symbol *> symbols;
Label &Symbol::label()
{
Label &Symbol::label() {
assert(std::holds_alternative<Label>(data));
return std::get<Label>(data);
}
Label const &Symbol::label() const
{
Label const &Symbol::label() const {
assert(std::holds_alternative<Label>(data));
return std::get<Label>(data);
}
void sym_AddSymbol(Symbol &symbol)
{
void sym_AddSymbol(Symbol &symbol) {
// Check if the symbol already exists
if (Symbol *other = sym_GetSymbol(symbol.name); other) {
fprintf(stderr, "error: \"%s\" both in %s from ", symbol.name.c_str(), symbol.objFileName);
@@ -44,8 +42,7 @@ void sym_AddSymbol(Symbol &symbol)
symbols[symbol.name] = &symbol;
}
Symbol *sym_GetSymbol(std::string const &name)
{
Symbol *sym_GetSymbol(std::string const &name) {
auto search = symbols.find(name);
return search != symbols.end() ? search->second : nullptr;
}