Report locations for RGBLINK errors with conflicting objects (#1494)

This requires updating the object file format to record the
fstack context for sections themselves, not just for patches.
This commit is contained in:
Sylvie
2024-09-10 13:23:48 -04:00
committed by GitHub
parent 8cd0e66297
commit 1dcc000572
26 changed files with 173 additions and 79 deletions

View File

@@ -46,6 +46,8 @@ struct Section {
bool isAlignFixed; bool isAlignFixed;
uint16_t alignMask; uint16_t alignMask;
uint16_t alignOfs; uint16_t alignOfs;
FileStackNode const *src;
int32_t lineNo;
std::vector<uint8_t> data; // Array of size `size`, or 0 if `type` does not have data std::vector<uint8_t> data; // Array of size `size`, or 0 if `type` does not have data
std::vector<Patch> patches; std::vector<Patch> patches;
// Extra info computed during linking // Extra info computed during linking

View File

@@ -25,7 +25,6 @@ struct Symbol {
// Info contained in the object files // Info contained in the object files
std::string name; std::string name;
ExportLevel type; ExportLevel type;
char const *objFileName;
FileStackNode const *src; FileStackNode const *src;
int32_t lineNo; int32_t lineNo;
Either< Either<

View File

@@ -145,6 +145,10 @@ If the symbol belongs to a section, this is the offset within that symbol's sect
.Bl -tag -width Ds -compact .Bl -tag -width Ds -compact
.It Cm STRING Ar Name .It Cm STRING Ar Name
The section's name. The section's name.
.It Cm LONG Ar NodeID
Context in which the section was defined.
.It Cm LONG Ar LineNo
Line number in the context at which the section was defined.
.It Cm LONG Ar Size .It Cm LONG Ar Size
The section's size, in bytes. The section's size, in bytes.
.It Cm BYTE Ar Type .It Cm BYTE Ar Type

View File

@@ -85,8 +85,13 @@ static void writePatch(Patch const &patch, FILE *file) {
} }
static void writeSection(Section const &sect, FILE *file) { static void writeSection(Section const &sect, FILE *file) {
assume(sect.src->ID != (uint32_t)-1);
putString(sect.name, file); putString(sect.name, file);
putLong(sect.src->ID, file);
putLong(sect.fileLine, file);
putLong(sect.size, file); putLong(sect.size, file);
bool isUnion = sect.modifier == SECTION_UNION; bool isUnion = sect.modifier == SECTION_UNION;

View File

@@ -288,6 +288,8 @@ static Section *createSection(
sect.align = alignment; sect.align = alignment;
sect.alignOfs = alignOffset; sect.alignOfs = alignOffset;
out_RegisterNode(sect.src);
// It is only needed to allocate memory for ROM sections. // It is only needed to allocate memory for ROM sections.
if (sect_HasData(type)) if (sect_HasData(type))
sect.data.resize(sectionTypeInfo[type].size); sect.data.resize(sectionTypeInfo[type].size);

View File

@@ -193,7 +193,6 @@ static void readSymbol(
); );
// If the symbol is defined in this file, read its definition // If the symbol is defined in this file, read its definition
if (symbol.type != SYMTYPE_IMPORT) { if (symbol.type != SYMTYPE_IMPORT) {
symbol.objFileName = fileName;
uint32_t nodeID; uint32_t nodeID;
tryReadLong( tryReadLong(
nodeID, file, "%s: Cannot read \"%s\"'s node ID: %s", fileName, symbol.name.c_str() nodeID, file, "%s: Cannot read \"%s\"'s node ID: %s", fileName, symbol.name.c_str()
@@ -346,6 +345,18 @@ static void readSection(
uint8_t byte; uint8_t byte;
tryReadString(section.name, file, "%s: Cannot read section name: %s", fileName); tryReadString(section.name, file, "%s: Cannot read section name: %s", fileName);
uint32_t nodeID;
tryReadLong(
nodeID, file, "%s: Cannot read \"%s\"'s node ID: %s", fileName, section.name.c_str()
);
section.src = &fileNodes[nodeID];
tryReadLong(
section.lineNo,
file,
"%s: Cannot read \"%s\"'s line number: %s",
fileName,
section.name.c_str()
);
tryReadLong(tmp, file, "%s: Cannot read \"%s\"'s' size: %s", fileName, section.name.c_str()); tryReadLong(tmp, file, "%s: Cannot read \"%s\"'s' size: %s", fileName, section.name.c_str());
if (tmp < 0 || tmp > UINT16_MAX) if (tmp < 0 || tmp > UINT16_MAX)
errx("\"%s\"'s section size (%" PRId32 ") is invalid", section.name.c_str(), tmp); errx("\"%s\"'s section size (%" PRId32 ") is invalid", section.name.c_str(), tmp);

View File

@@ -256,6 +256,9 @@ void sdobj_ReadFile(FileStackNode const &where, FILE *file, std::vector<Symbol>
); );
std::unique_ptr<Section> curSection = std::make_unique<Section>(); std::unique_ptr<Section> curSection = std::make_unique<Section>();
curSection->src = &where;
curSection->lineNo = lineNo;
getToken(line.data(), "'A' line is too short"); getToken(line.data(), "'A' line is too short");
assume(strlen(token) != 0); // This should be impossible, tokens are non-empty assume(strlen(token) != 0); // This should be impossible, tokens are non-empty
// The following is required for fragment offsets to be reliably predicted // The following is required for fragment offsets to be reliably predicted
@@ -355,7 +358,6 @@ void sdobj_ReadFile(FileStackNode const &where, FILE *file, std::vector<Symbol>
Symbol &symbol = fileSymbols.emplace_back(); Symbol &symbol = fileSymbols.emplace_back();
// Init other members // Init other members
symbol.objFileName = where.name().c_str();
symbol.src = &where; symbol.src = &where;
symbol.lineNo = lineNo; symbol.lineNo = lineNo;
@@ -408,16 +410,17 @@ void sdobj_ReadFile(FileStackNode const &where, FILE *file, std::vector<Symbol>
|| (symbolSection && !symbolSection->isAddressFixed)) { || (symbolSection && !symbolSection->isAddressFixed)) {
sym_AddSymbol(symbol); // This will error out sym_AddSymbol(symbol); // This will error out
} else if (otherValue != symbolValue) { } else if (otherValue != symbolValue) {
error( fprintf(
&where, stderr,
lineNo, "error: \"%s\" is defined as %" PRId32 " at ",
"Definition of \"%s\" conflicts with definition in %s (%" PRId32
" != %" PRId32 ")",
symbol.name.c_str(), symbol.name.c_str(),
other->objFileName, symbolValue
symbolValue,
otherValue
); );
symbol.src->dump(symbol.lineNo);
fprintf(stderr, ", but as %" PRId32 " at ", otherValue);
other->src->dump(other->lineNo);
putc('\n', stderr);
exit(1);
} }
} else { } else {
// Add a new definition // Add a new definition

View File

@@ -21,24 +21,32 @@ void sect_ForEach(void (*callback)(Section &)) {
static void checkAgainstFixedAddress(Section const &target, Section const &other, uint16_t org) { static void checkAgainstFixedAddress(Section const &target, Section const &other, uint16_t org) {
if (target.isAddressFixed) { if (target.isAddressFixed) {
if (target.org != org) { if (target.org != org) {
errx( fprintf(
"Section \"%s\" is defined with conflicting addresses $%04" PRIx16 stderr,
" and $%04" PRIx16, "error: Section \"%s\" is defined with address $%04" PRIx16 " at ",
other.name.c_str(), target.name.c_str(),
target.org, target.org
other.org
); );
target.src->dump(target.lineNo);
fprintf(stderr, ", but with address $%04" PRIx16 " at ", other.org);
other.src->dump(other.lineNo);
putc('\n', stderr);
exit(1);
} }
} else if (target.isAlignFixed) { } else if (target.isAlignFixed) {
if ((org - target.alignOfs) & target.alignMask) { if ((org - target.alignOfs) & target.alignMask) {
errx( fprintf(
"Section \"%s\" is defined with conflicting %d-byte alignment (offset %" PRIu16 stderr,
") and address $%04" PRIx16, "error: Section \"%s\" is defined with %d-byte alignment (offset %" PRIu16 ") at ",
other.name.c_str(), target.name.c_str(),
target.alignMask + 1, target.alignMask + 1,
target.alignOfs, target.alignOfs
other.org
); );
target.src->dump(target.lineNo);
fprintf(stderr, ", but with address $%04" PRIx16 " at ", other.org);
other.src->dump(other.lineNo);
putc('\n', stderr);
exit(1);
} }
} }
} }
@@ -46,27 +54,43 @@ static void checkAgainstFixedAddress(Section const &target, Section const &other
static bool checkAgainstFixedAlign(Section const &target, Section const &other, int32_t ofs) { static bool checkAgainstFixedAlign(Section const &target, Section const &other, int32_t ofs) {
if (target.isAddressFixed) { if (target.isAddressFixed) {
if ((target.org - ofs) & other.alignMask) { if ((target.org - ofs) & other.alignMask) {
errx( fprintf(
"Section \"%s\" is defined with conflicting address $%04" PRIx16 stderr,
" and %d-byte alignment (offset %" PRIu16 ")", "error: Section \"%s\" is defined with address $%04" PRIx16 " at ",
other.name.c_str(), target.name.c_str(),
target.org, target.org
);
target.src->dump(target.lineNo);
fprintf(
stderr,
", but with %d-byte alignment (offset %" PRIu16 ") at ",
other.alignMask + 1, other.alignMask + 1,
other.alignOfs other.alignOfs
); );
other.src->dump(other.lineNo);
putc('\n', stderr);
exit(1);
} }
return false; return false;
} else if (target.isAlignFixed } else if (target.isAlignFixed
&& (other.alignMask & target.alignOfs) != (target.alignMask & ofs)) { && (other.alignMask & target.alignOfs) != (target.alignMask & ofs)) {
errx( fprintf(
"Section \"%s\" is defined with conflicting %d-byte alignment (offset %" PRIu16 stderr,
") and %d-byte alignment (offset %" PRIu16 ")", "error: Section \"%s\" is defined with %d-byte alignment (offset %" PRIu16 ") at ",
other.name.c_str(), target.name.c_str(),
target.alignMask + 1, target.alignMask + 1,
target.alignOfs, target.alignOfs
);
target.src->dump(target.lineNo);
fprintf(
stderr,
", but with %d-byte alignment (offset %" PRIu16 ") at ",
other.alignMask + 1, other.alignMask + 1,
other.alignOfs other.alignOfs
); );
other.src->dump(other.lineNo);
putc('\n', stderr);
exit(1);
} else { } else {
return !target.isAlignFixed || (other.alignMask > target.alignMask); return !target.isAlignFixed || (other.alignMask > target.alignMask);
} }
@@ -106,25 +130,36 @@ static void checkFragmentCompat(Section &target, Section &other) {
static void mergeSections(Section &target, std::unique_ptr<Section> &&other, SectionModifier mod) { static void mergeSections(Section &target, std::unique_ptr<Section> &&other, SectionModifier mod) {
// Common checks // Common checks
if (target.type != other->type) if (target.type != other->type) {
errx( fprintf(
"Section \"%s\" is defined with conflicting types %s and %s", stderr,
other->name.c_str(), "error: Section \"%s\" is defined with type %s at ",
sectionTypeInfo[target.type].name.c_str(), target.name.c_str(),
sectionTypeInfo[other->type].name.c_str() sectionTypeInfo[target.type].name.c_str()
); );
target.src->dump(target.lineNo);
fprintf(stderr, ", but with type %s at ", sectionTypeInfo[other->type].name.c_str());
other->src->dump(other->lineNo);
putc('\n', stderr);
exit(1);
}
if (other->isBankFixed) { if (other->isBankFixed) {
if (!target.isBankFixed) { if (!target.isBankFixed) {
target.isBankFixed = true; target.isBankFixed = true;
target.bank = other->bank; target.bank = other->bank;
} else if (target.bank != other->bank) { } else if (target.bank != other->bank) {
errx( fprintf(
"Section \"%s\" is defined with conflicting banks %" PRIu32 " and %" PRIu32, stderr,
other->name.c_str(), "error: Section \"%s\" is defined with bank %" PRIu32 " at ",
target.bank, target.name.c_str(),
other->bank target.bank
); );
target.src->dump(target.lineNo);
fprintf(stderr, ", but with bank %" PRIu32 " at ", other->bank);
other->src->dump(other->lineNo);
putc('\n', stderr);
exit(1);
} }
} }
@@ -164,17 +199,28 @@ static void mergeSections(Section &target, std::unique_ptr<Section> &&other, Sec
void sect_AddSection(std::unique_ptr<Section> &&section) { void sect_AddSection(std::unique_ptr<Section> &&section) {
// Check if the section already exists // Check if the section already exists
if (Section *other = sect_GetSection(section->name); other) { if (Section *other = sect_GetSection(section->name); other) {
if (section->modifier != other->modifier) if (section->modifier != other->modifier) {
errx( fprintf(
"Section \"%s\" defined as %s and %s", stderr,
"error: Section \"%s\" is defined as %s at ",
section->name.c_str(), section->name.c_str(),
sectionModNames[section->modifier], sectionModNames[section->modifier]
sectionModNames[other->modifier]
); );
else if (section->modifier == SECTION_NORMAL) section->src->dump(section->lineNo);
errx("Section name \"%s\" is already in use", section->name.c_str()); fprintf(stderr, ", but as %s at ", sectionModNames[other->modifier]);
else other->src->dump(other->lineNo);
putc('\n', stderr);
exit(1);
} else if (section->modifier == SECTION_NORMAL) {
fprintf(stderr, "error: Section \"%s\" is defined at ", section->name.c_str());
section->src->dump(section->lineNo);
fputs(", but also at ", stderr);
other->src->dump(other->lineNo);
putc('\n', stderr);
exit(1);
} else {
mergeSections(*other, std::move(section), section->modifier); mergeSections(*other, std::move(section), section->modifier);
}
} else if (section->modifier == SECTION_UNION && sect_HasData(section->type)) { } else if (section->modifier == SECTION_UNION && sect_HasData(section->type)) {
errx( errx(
"Section \"%s\" is of type %s, which cannot be unionized", "Section \"%s\" is of type %s, which cannot be unionized",

View File

@@ -2,6 +2,7 @@
#include "link/symbol.hpp" #include "link/symbol.hpp"
#include <inttypes.h>
#include <stdlib.h> #include <stdlib.h>
#include <unordered_map> #include <unordered_map>
#include <vector> #include <vector>
@@ -33,9 +34,19 @@ void sym_AddSymbol(Symbol &symbol) {
// Check if the symbol already exists with a different value // Check if the symbol already exists with a different value
if (other && !(symValue && otherValue && *symValue == *otherValue)) { if (other && !(symValue && otherValue && *symValue == *otherValue)) {
fprintf(stderr, "error: \"%s\" both in %s from ", symbol.name.c_str(), symbol.objFileName); fprintf(stderr, "error: \"%s\" is defined as ", symbol.name.c_str());
if (symValue)
fprintf(stderr, "%" PRId32, *symValue);
else
fputs("a label", stderr);
fputs(" at ", stderr);
symbol.src->dump(symbol.lineNo); symbol.src->dump(symbol.lineNo);
fprintf(stderr, " and in %s from ", other->objFileName); fputs(", but as ", stderr);
if (otherValue)
fprintf(stderr, "%" PRId32, *otherValue);
else
fputs("another label", stderr);
fputs(" at ", stderr);
other->src->dump(other->lineNo); other->src->dump(other->lineNo);
putc('\n', stderr); putc('\n', stderr);
exit(1); exit(1);

View File

@@ -1 +1 @@
error: Section "Frag" is defined with conflicting 2-byte alignment (offset 1) and 4-byte alignment (offset 3) error: Section "Frag" is defined with 2-byte alignment (offset 1) at fragment-align/base-bad/a.asm(2), but with 4-byte alignment (offset 3) at fragment-align/base-bad/b.asm(2)

View File

@@ -1 +1 @@
error: Section "Frag" is defined with conflicting address $0000 and 4-byte alignment (offset 2) error: Section "Frag" is defined with address $0000 at fragment-align/org-bad/a.asm(2), but with 4-byte alignment (offset 2) at fragment-align/org-bad/b.asm(2)

View File

@@ -1 +1 @@
error: Section "Frag" is defined with conflicting 4-byte alignment (offset 1) and address $0006 error: Section "Frag" is defined with 4-byte alignment (offset 1) at fragment-align/org-rev-bad/a.asm(6), but with address $0006 at fragment-align/org-rev-bad/b.asm(2)

View File

@@ -0,0 +1,2 @@
SECTION "Same", ROM0
db 1

View File

@@ -0,0 +1,2 @@
SECTION "Same", ROM0
db 2

View File

@@ -0,0 +1 @@
error: Section "Same" is defined at section-normal/same-name/b.asm(1), but also at section-normal/same-name/a.asm(1)

View File

@@ -1,4 +1,4 @@
error: Section "conflicting alignment" is defined with conflicting 4-byte alignment (offset 0) and address $cafe error: Section "conflicting alignment" is defined with 4-byte alignment (offset 0) at section-union/align-conflict.asm(7), but with address $cafe at section-union/align-conflict.asm(7)
--- ---
error: <stdin>(18): error: <stdin>(18):
Section already declared as aligned to 4 bytes (offset 0) Section already declared as aligned to 4 bytes (offset 0)

View File

@@ -1,4 +1,4 @@
error: Section "conflicting alignment" is defined with conflicting 8-byte alignment (offset 7) and 16-byte alignment (offset 14) error: Section "conflicting alignment" is defined with 8-byte alignment (offset 7) at section-union/align-ofs-conflict.asm(7), but with 16-byte alignment (offset 14) at section-union/align-ofs-conflict.asm(7)
--- ---
error: <stdin>(18): error: <stdin>(18):
Section already declared with incompatible 8-byte alignment (offset 7) Section already declared with incompatible 8-byte alignment (offset 7)

View File

@@ -1,4 +1,4 @@
error: Section "conflicting types" is defined with conflicting types HRAM and WRAM0 error: Section "conflicting types" is defined with type HRAM at section-union/bad-types.asm(7), but with type WRAM0 at section-union/bad-types.asm(7)
--- ---
error: <stdin>(18): error: <stdin>(18):
Section already exists but with type HRAM Section already exists but with type HRAM

View File

@@ -1,4 +1,4 @@
error: Section "conflicting banks" is defined with conflicting banks 4 and 1 error: Section "conflicting banks" is defined with bank 4 at section-union/bank-conflict.asm(5), but with bank 1 at section-union/bank-conflict.asm(5)
--- ---
error: <stdin>(14): error: <stdin>(14):
Section already declared with different bank 4 Section already declared with different bank 4

View File

@@ -1,4 +1,4 @@
error: Section "conflicting alignment" is defined with conflicting 8-byte alignment (offset 7) and 8-byte alignment (offset 6) error: Section "conflicting alignment" is defined with 8-byte alignment (offset 7) at section-union/different-ofs.asm(7), but with 8-byte alignment (offset 6) at section-union/different-ofs.asm(7)
--- ---
error: <stdin>(18): error: <stdin>(18):
Section already declared with incompatible 8-byte alignment (offset 7) Section already declared with incompatible 8-byte alignment (offset 7)

View File

@@ -1,4 +1,4 @@
error: Section "conflicting address" is defined with conflicting addresses $beef and $babe error: Section "conflicting address" is defined with address $beef at section-union/org-conflict.asm(7), but with address $babe at section-union/org-conflict.asm(7)
--- ---
error: <stdin>(16): error: <stdin>(16):
Section already declared as fixed at different address $beef Section already declared as fixed at different address $beef

View File

@@ -1 +1 @@
error: "Same" both in section-union/same-export/a.o from section-union/same-export/a.asm(2) and in section-union/same-export/b.o from section-union/same-export/b.asm(2) error: "Same" is defined as a label at section-union/same-export/a.asm(2), but as another label at section-union/same-export/b.asm(2)

View File

@@ -0,0 +1 @@
EXPORT DEF SAME EQU 1

View File

@@ -0,0 +1 @@
EXPORT DEF SAME EQU 2

View File

@@ -0,0 +1 @@
error: "SAME" is defined as 2 at symbols/conflict/b.asm(1), but as 1 at symbols/conflict/a.asm(1)

View File

@@ -86,17 +86,6 @@ evaluateTest () {
fi fi
} }
substPath () {
# Replace the file name with a different one to match changed output, escaping regex metacharacters
subst="$(printf '%s\n' "$1" | sed 's:[][\/.^$*]:\\&:g')"
sed -i'' -e "s|$subst|$2|g" "$3"
if which cygpath &>/dev/null; then
# Replace the MinGW path with a Windows path, escaping regex metacharacters
subst="$(cygpath -m "$1" | sed 's:[][\/.^$*]:\\&:g')"
sed -i'' -e "s|$subst|$2|g" "$3"
fi
}
for i in *.asm; do for i in *.asm; do
test=${i%.asm} test=${i%.asm}
startTest startTest
@@ -268,6 +257,15 @@ rgblinkQuiet -o "$gbtemp" "$otemp" "$gbtemp2"
tryCmpRom "$test"/ref.out.bin tryCmpRom "$test"/ref.out.bin
evaluateTest evaluateTest
test="section-normal/same-name"
startTest
"$RGBASM" -o "$otemp" "$test"/a.asm
"$RGBASM" -o "$gbtemp" "$test"/b.asm
continueTest
rgblinkQuiet "$otemp" "$gbtemp" 2>"$outtemp"
tryDiff "$test"/out.err "$outtemp"
evaluateTest
test="section-union/good" test="section-union/good"
startTest startTest
"$RGBASM" -o "$otemp" "$test"/a.asm "$RGBASM" -o "$otemp" "$test"/a.asm
@@ -283,8 +281,6 @@ startTest
"$RGBASM" -o "$gbtemp2" "$test"/b.asm "$RGBASM" -o "$gbtemp2" "$test"/b.asm
continueTest continueTest
rgblinkQuiet "$gbtemp2" "$otemp" 2>"$outtemp" rgblinkQuiet "$gbtemp2" "$otemp" 2>"$outtemp"
substPath "$otemp" "$test/a.o" "$outtemp"
substPath "$gbtemp2" "$test/b.o" "$outtemp"
tryDiff "$test"/out.err "$outtemp" tryDiff "$test"/out.err "$outtemp"
evaluateTest evaluateTest
@@ -294,8 +290,6 @@ startTest
"$RGBASM" -o "$gbtemp2" "$test"/b.asm "$RGBASM" -o "$gbtemp2" "$test"/b.asm
continueTest continueTest
rgblinkQuiet -o "$gbtemp" "$gbtemp2" "$otemp" 2>"$outtemp" rgblinkQuiet -o "$gbtemp" "$gbtemp2" "$otemp" 2>"$outtemp"
substPath "$otemp" "$test/a.o" "$outtemp"
substPath "$gbtemp2" "$test/b.o" "$outtemp"
tryDiff "$test"/out.err "$outtemp" tryDiff "$test"/out.err "$outtemp"
tryCmpRom "$test"/ref.out.bin tryCmpRom "$test"/ref.out.bin
evaluateTest evaluateTest
@@ -317,6 +311,15 @@ for i in section-union/*.asm; do
evaluateTest evaluateTest
done done
test="symbols/conflict"
startTest
"$RGBASM" -o "$otemp" "$test"/a.asm
"$RGBASM" -o "$gbtemp" "$test"/b.asm
continueTest
rgblinkQuiet "$otemp" "$gbtemp" 2>"$outtemp"
tryDiff "$test"/out.err "$outtemp"
evaluateTest
test="symbols/good" test="symbols/good"
startTest startTest
"$RGBASM" -o "$otemp" "$test"/a.asm "$RGBASM" -o "$otemp" "$test"/a.asm