mirror of
https://github.com/gbdev/rgbds.git
synced 2026-01-11 02:51:51 +00:00
Run clang-format on everything (#1332)
This commit is contained in:
@@ -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 §ion, MemoryLocation const &location)
|
||||
{
|
||||
static void assignSection(Section §ion, MemoryLocation const &location) {
|
||||
// Propagate the assigned location to all UNIONs/FRAGMENTs
|
||||
// so `jr` patches in them will have the correct offset
|
||||
for (Section *next = §ion; next != nullptr; next = next->nextu) {
|
||||
@@ -77,9 +75,9 @@ static void assignSection(Section §ion, 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 §ion, FreeSpace const &freeSpace,
|
||||
MemoryLocation const &location)
|
||||
{
|
||||
static bool isLocationSuitable(
|
||||
Section const §ion, FreeSpace const &freeSpace, MemoryLocation const &location
|
||||
) {
|
||||
if (section.isAddressFixed && section.org != location.address)
|
||||
return false;
|
||||
|
||||
@@ -99,8 +97,7 @@ static bool isLocationSuitable(Section const §ion, 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 §ion, MemoryLocation &location)
|
||||
{
|
||||
static ssize_t getPlacement(Section const §ion, MemoryLocation &location) {
|
||||
SectionTypeInfo const &typeInfo = sectionTypeInfo[section.type];
|
||||
|
||||
static uint16_t curScrambleROM = 0;
|
||||
@@ -166,16 +163,16 @@ static ssize_t getPlacement(Section const §ion, 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 §ion, MemoryLocation &location)
|
||||
* sections of decreasing size.
|
||||
* @param section The section to place
|
||||
*/
|
||||
static void placeSection(Section §ion)
|
||||
{
|
||||
static void placeSection(Section §ion) {
|
||||
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 §ion)
|
||||
// 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 §ion)
|
||||
} 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 §ion)
|
||||
|
||||
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 §ion)
|
||||
{
|
||||
static void categorizeSection(Section §ion) {
|
||||
uint8_t constraints = 0;
|
||||
|
||||
if (section.isBankFixed)
|
||||
@@ -337,8 +356,7 @@ static void categorizeSection(Section §ion)
|
||||
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);
|
||||
|
||||
|
||||
@@ -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 §ion)
|
||||
{
|
||||
static void freeSection(Section §ion) {
|
||||
Section *next = §ion;
|
||||
|
||||
for (Section *nextu; next; next = nextu) {
|
||||
@@ -352,13 +355,11 @@ static void freeSection(Section §ion)
|
||||
};
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -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 §Name,
|
||||
uint32_t i, std::vector<FileStackNode> const &fileNodes)
|
||||
{
|
||||
static void readPatch(
|
||||
FILE *file,
|
||||
Patch &patch,
|
||||
char const *fileName,
|
||||
std::string const §Name,
|
||||
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 §ion, char const *fileName,
|
||||
std::vector<FileStackNode> const &fileNodes)
|
||||
{
|
||||
static void readSection(
|
||||
FILE *file, Section §ion, char const *fileName, std::vector<FileStackNode> const &fileNodes
|
||||
) {
|
||||
int32_t tmp;
|
||||
uint8_t byte;
|
||||
|
||||
@@ -277,8 +351,9 @@ static void readSection(FILE *file, Section §ion, 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 §ion, 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 §ion, 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 §ion, char const *fileName,
|
||||
* @param symbol The symbol to link
|
||||
* @param section The section to link
|
||||
*/
|
||||
static void linkSymToSect(Symbol &symbol, Section §ion)
|
||||
{
|
||||
static void linkSymToSect(Symbol &symbol, Section §ion) {
|
||||
uint32_t a = 0, b = section.symbols.size();
|
||||
int32_t symbolOffset = symbol.label().offset;
|
||||
|
||||
@@ -361,9 +455,13 @@ static void linkSymToSect(Symbol &symbol, Section §ion)
|
||||
* @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 §ion)
|
||||
{
|
||||
static Section *getMainSection(Section §ion) {
|
||||
return section.modifier != SECTION_NORMAL ? sect_GetSection(section.name) : §ion;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -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 §ion)
|
||||
{
|
||||
void out_AddSection(Section const §ion) {
|
||||
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 §ion)
|
||||
bankSections.insert(pos, §ion);
|
||||
}
|
||||
|
||||
Section const *out_OverlappingSection(Section const §ion)
|
||||
{
|
||||
Section const *out_OverlappingSection(Section const §ion) {
|
||||
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 §ion)
|
||||
* 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() ? §ions[SECTTYPE_ROM0][0].sections : nullptr,
|
||||
sectionTypeInfo[SECTTYPE_ROM0].startAddr, sectionTypeInfo[SECTTYPE_ROM0].size);
|
||||
writeBank(
|
||||
!sections[SECTTYPE_ROM0].empty() ? §ions[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(§ions[SECTTYPE_ROMX][i].sections,
|
||||
sectionTypeInfo[SECTTYPE_ROMX].startAddr, sectionTypeInfo[SECTTYPE_ROMX].size);
|
||||
for (uint32_t i = 0; i < sections[SECTTYPE_ROMX].size(); i++)
|
||||
writeBank(
|
||||
§ions[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 §List, 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 §List, 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 §List, 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 §List, 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 §List, 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();
|
||||
|
||||
@@ -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 §ion, Section &dataSection)
|
||||
{
|
||||
static void applyFilePatches(Section §ion, 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 §ion, 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 §ion, 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 §ion, 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 §ion)
|
||||
{
|
||||
static void applyPatches(Section §ion) {
|
||||
if (!sect_HasData(section.type))
|
||||
return;
|
||||
|
||||
@@ -508,7 +531,6 @@ static void applyPatches(Section §ion)
|
||||
applyFilePatches(*component, section);
|
||||
}
|
||||
|
||||
void patch_ApplyPatches()
|
||||
{
|
||||
void patch_ApplyPatches() {
|
||||
sect_ForEach(applyPatches);
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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(§ion->data[*writeIndex], &data[writtenOfs], offset - writtenOfs + 1);
|
||||
memcpy(
|
||||
§ion->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(§ion->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);
|
||||
|
||||
|
||||
@@ -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 §ion)
|
||||
{
|
||||
void sect_AddSection(Section §ion) {
|
||||
// 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] = §ion;
|
||||
}
|
||||
}
|
||||
|
||||
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 §ion)
|
||||
{
|
||||
static void doSanityChecks(Section §ion) {
|
||||
// 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 §ion)
|
||||
|
||||
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 §ion)
|
||||
|
||||
// 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 §ion)
|
||||
// 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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user