diff --git a/include/link/section.hpp b/include/link/section.hpp index b33a9abe..09f7a4e3 100644 --- a/include/link/section.hpp +++ b/include/link/section.hpp @@ -46,6 +46,8 @@ struct Section { bool isAlignFixed; uint16_t alignMask; uint16_t alignOfs; + FileStackNode const *src; + int32_t lineNo; std::vector data; // Array of size `size`, or 0 if `type` does not have data std::vector patches; // Extra info computed during linking diff --git a/include/link/symbol.hpp b/include/link/symbol.hpp index daa5c0e8..f84d6974 100644 --- a/include/link/symbol.hpp +++ b/include/link/symbol.hpp @@ -25,7 +25,6 @@ struct Symbol { // Info contained in the object files std::string name; ExportLevel type; - char const *objFileName; FileStackNode const *src; int32_t lineNo; Either< diff --git a/man/rgbds.5 b/man/rgbds.5 index 17cd4251..b83dd4aa 100644 --- a/man/rgbds.5 +++ b/man/rgbds.5 @@ -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 .It Cm STRING Ar 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 The section's size, in bytes. .It Cm BYTE Ar Type diff --git a/src/asm/output.cpp b/src/asm/output.cpp index daae116e..f73a0f2a 100644 --- a/src/asm/output.cpp +++ b/src/asm/output.cpp @@ -85,8 +85,13 @@ static void writePatch(Patch const &patch, FILE *file) { } static void writeSection(Section const §, FILE *file) { + assume(sect.src->ID != (uint32_t)-1); + putString(sect.name, file); + putLong(sect.src->ID, file); + putLong(sect.fileLine, file); + putLong(sect.size, file); bool isUnion = sect.modifier == SECTION_UNION; diff --git a/src/asm/section.cpp b/src/asm/section.cpp index 93e7a425..edf9b7d9 100644 --- a/src/asm/section.cpp +++ b/src/asm/section.cpp @@ -288,6 +288,8 @@ static Section *createSection( sect.align = alignment; sect.alignOfs = alignOffset; + out_RegisterNode(sect.src); + // It is only needed to allocate memory for ROM sections. if (sect_HasData(type)) sect.data.resize(sectionTypeInfo[type].size); diff --git a/src/link/object.cpp b/src/link/object.cpp index 8bb4ceaf..227bd974 100644 --- a/src/link/object.cpp +++ b/src/link/object.cpp @@ -193,7 +193,6 @@ static void readSymbol( ); // 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() @@ -346,6 +345,18 @@ static void readSection( uint8_t byte; 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()); if (tmp < 0 || tmp > UINT16_MAX) errx("\"%s\"'s section size (%" PRId32 ") is invalid", section.name.c_str(), tmp); diff --git a/src/link/sdas_obj.cpp b/src/link/sdas_obj.cpp index 049a91d1..0697f965 100644 --- a/src/link/sdas_obj.cpp +++ b/src/link/sdas_obj.cpp @@ -256,6 +256,9 @@ void sdobj_ReadFile(FileStackNode const &where, FILE *file, std::vector ); std::unique_ptr
curSection = std::make_unique
(); + curSection->src = &where; + curSection->lineNo = lineNo; + getToken(line.data(), "'A' line is too short"); assume(strlen(token) != 0); // This should be impossible, tokens are non-empty // 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 = fileSymbols.emplace_back(); // Init other members - symbol.objFileName = where.name().c_str(); symbol.src = &where; symbol.lineNo = lineNo; @@ -408,16 +410,17 @@ void sdobj_ReadFile(FileStackNode const &where, FILE *file, std::vector || (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 ")", + fprintf( + stderr, + "error: \"%s\" is defined as %" PRId32 " at ", symbol.name.c_str(), - other->objFileName, - symbolValue, - otherValue + symbolValue ); + symbol.src->dump(symbol.lineNo); + fprintf(stderr, ", but as %" PRId32 " at ", otherValue); + other->src->dump(other->lineNo); + putc('\n', stderr); + exit(1); } } else { // Add a new definition diff --git a/src/link/section.cpp b/src/link/section.cpp index 639770ee..4715092e 100644 --- a/src/link/section.cpp +++ b/src/link/section.cpp @@ -21,24 +21,32 @@ void sect_ForEach(void (*callback)(Section &)) { static void checkAgainstFixedAddress(Section const &target, Section const &other, uint16_t org) { 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 + fprintf( + stderr, + "error: Section \"%s\" is defined with address $%04" PRIx16 " at ", + target.name.c_str(), + target.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) { 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(), + fprintf( + stderr, + "error: Section \"%s\" is defined with %d-byte alignment (offset %" PRIu16 ") at ", + target.name.c_str(), target.alignMask + 1, - target.alignOfs, - other.org + target.alignOfs ); + 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) { 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, + fprintf( + stderr, + "error: Section \"%s\" is defined with address $%04" PRIx16 " at ", + target.name.c_str(), + target.org + ); + target.src->dump(target.lineNo); + fprintf( + stderr, + ", but with %d-byte alignment (offset %" PRIu16 ") at ", other.alignMask + 1, other.alignOfs ); + other.src->dump(other.lineNo); + putc('\n', stderr); + exit(1); } return false; } 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(), + fprintf( + stderr, + "error: Section \"%s\" is defined with %d-byte alignment (offset %" PRIu16 ") at ", + target.name.c_str(), 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.alignOfs ); + other.src->dump(other.lineNo); + putc('\n', stderr); + exit(1); } else { 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
&&other, 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() + if (target.type != other->type) { + fprintf( + stderr, + "error: Section \"%s\" is defined with type %s at ", + target.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 (!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 + fprintf( + stderr, + "error: Section \"%s\" is defined with bank %" PRIu32 " at ", + target.name.c_str(), + 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
&&other, Sec void sect_AddSection(std::unique_ptr
&§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", + if (section->modifier != other->modifier) { + fprintf( + stderr, + "error: Section \"%s\" is defined as %s at ", section->name.c_str(), - sectionModNames[section->modifier], - sectionModNames[other->modifier] + sectionModNames[section->modifier] ); - else if (section->modifier == SECTION_NORMAL) - errx("Section name \"%s\" is already in use", section->name.c_str()); - else + section->src->dump(section->lineNo); + fprintf(stderr, ", but as %s at ", sectionModNames[other->modifier]); + 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); + } } else if (section->modifier == SECTION_UNION && sect_HasData(section->type)) { errx( "Section \"%s\" is of type %s, which cannot be unionized", diff --git a/src/link/symbol.cpp b/src/link/symbol.cpp index 293e91f2..d5a63911 100644 --- a/src/link/symbol.cpp +++ b/src/link/symbol.cpp @@ -2,6 +2,7 @@ #include "link/symbol.hpp" +#include #include #include #include @@ -33,9 +34,19 @@ void sym_AddSymbol(Symbol &symbol) { // Check if the symbol already exists with a different value 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); - 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); putc('\n', stderr); exit(1); diff --git a/test/link/fragment-align/base-bad/out.err b/test/link/fragment-align/base-bad/out.err index b7856ac4..8cd6eec2 100644 --- a/test/link/fragment-align/base-bad/out.err +++ b/test/link/fragment-align/base-bad/out.err @@ -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) diff --git a/test/link/fragment-align/org-bad/out.err b/test/link/fragment-align/org-bad/out.err index 5618077d..cc2d5364 100644 --- a/test/link/fragment-align/org-bad/out.err +++ b/test/link/fragment-align/org-bad/out.err @@ -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) diff --git a/test/link/fragment-align/org-rev-bad/out.err b/test/link/fragment-align/org-rev-bad/out.err index 3c284d29..04e186cb 100644 --- a/test/link/fragment-align/org-rev-bad/out.err +++ b/test/link/fragment-align/org-rev-bad/out.err @@ -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) diff --git a/test/link/section-normal/same-name/a.asm b/test/link/section-normal/same-name/a.asm new file mode 100644 index 00000000..915d0335 --- /dev/null +++ b/test/link/section-normal/same-name/a.asm @@ -0,0 +1,2 @@ +SECTION "Same", ROM0 +db 1 diff --git a/test/link/section-normal/same-name/b.asm b/test/link/section-normal/same-name/b.asm new file mode 100644 index 00000000..fcbdd18f --- /dev/null +++ b/test/link/section-normal/same-name/b.asm @@ -0,0 +1,2 @@ +SECTION "Same", ROM0 +db 2 diff --git a/test/link/section-normal/same-name/out.err b/test/link/section-normal/same-name/out.err new file mode 100644 index 00000000..f569b86e --- /dev/null +++ b/test/link/section-normal/same-name/out.err @@ -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) diff --git a/test/link/section-union/align-conflict.out b/test/link/section-union/align-conflict.out index d0cc366a..5d640dd5 100644 --- a/test/link/section-union/align-conflict.out +++ b/test/link/section-union/align-conflict.out @@ -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: (18): Section already declared as aligned to 4 bytes (offset 0) diff --git a/test/link/section-union/align-ofs-conflict.out b/test/link/section-union/align-ofs-conflict.out index a09960a8..3b5e7a3b 100644 --- a/test/link/section-union/align-ofs-conflict.out +++ b/test/link/section-union/align-ofs-conflict.out @@ -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: (18): Section already declared with incompatible 8-byte alignment (offset 7) diff --git a/test/link/section-union/bad-types.out b/test/link/section-union/bad-types.out index 6f3b0451..13daba79 100644 --- a/test/link/section-union/bad-types.out +++ b/test/link/section-union/bad-types.out @@ -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: (18): Section already exists but with type HRAM diff --git a/test/link/section-union/bank-conflict.out b/test/link/section-union/bank-conflict.out index 60587c53..81c49404 100644 --- a/test/link/section-union/bank-conflict.out +++ b/test/link/section-union/bank-conflict.out @@ -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: (14): Section already declared with different bank 4 diff --git a/test/link/section-union/different-ofs.out b/test/link/section-union/different-ofs.out index ef259e1a..b2aed633 100644 --- a/test/link/section-union/different-ofs.out +++ b/test/link/section-union/different-ofs.out @@ -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: (18): Section already declared with incompatible 8-byte alignment (offset 7) diff --git a/test/link/section-union/org-conflict.out b/test/link/section-union/org-conflict.out index 7e0e3767..4c544e11 100644 --- a/test/link/section-union/org-conflict.out +++ b/test/link/section-union/org-conflict.out @@ -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: (16): Section already declared as fixed at different address $beef diff --git a/test/link/section-union/same-export/out.err b/test/link/section-union/same-export/out.err index c6261ac7..318bc0eb 100644 --- a/test/link/section-union/same-export/out.err +++ b/test/link/section-union/same-export/out.err @@ -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) diff --git a/test/link/symbols/conflict/a.asm b/test/link/symbols/conflict/a.asm new file mode 100644 index 00000000..89c26918 --- /dev/null +++ b/test/link/symbols/conflict/a.asm @@ -0,0 +1 @@ +EXPORT DEF SAME EQU 1 diff --git a/test/link/symbols/conflict/b.asm b/test/link/symbols/conflict/b.asm new file mode 100644 index 00000000..7c0e2154 --- /dev/null +++ b/test/link/symbols/conflict/b.asm @@ -0,0 +1 @@ +EXPORT DEF SAME EQU 2 diff --git a/test/link/symbols/conflict/out.err b/test/link/symbols/conflict/out.err new file mode 100644 index 00000000..be3505bd --- /dev/null +++ b/test/link/symbols/conflict/out.err @@ -0,0 +1 @@ +error: "SAME" is defined as 2 at symbols/conflict/b.asm(1), but as 1 at symbols/conflict/a.asm(1) diff --git a/test/link/test.sh b/test/link/test.sh index 54b05569..3a7e06da 100755 --- a/test/link/test.sh +++ b/test/link/test.sh @@ -86,17 +86,6 @@ evaluateTest () { 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 test=${i%.asm} startTest @@ -268,6 +257,15 @@ rgblinkQuiet -o "$gbtemp" "$otemp" "$gbtemp2" tryCmpRom "$test"/ref.out.bin 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" startTest "$RGBASM" -o "$otemp" "$test"/a.asm @@ -283,8 +281,6 @@ startTest "$RGBASM" -o "$gbtemp2" "$test"/b.asm continueTest rgblinkQuiet "$gbtemp2" "$otemp" 2>"$outtemp" -substPath "$otemp" "$test/a.o" "$outtemp" -substPath "$gbtemp2" "$test/b.o" "$outtemp" tryDiff "$test"/out.err "$outtemp" evaluateTest @@ -294,8 +290,6 @@ startTest "$RGBASM" -o "$gbtemp2" "$test"/b.asm continueTest rgblinkQuiet -o "$gbtemp" "$gbtemp2" "$otemp" 2>"$outtemp" -substPath "$otemp" "$test/a.o" "$outtemp" -substPath "$gbtemp2" "$test/b.o" "$outtemp" tryDiff "$test"/out.err "$outtemp" tryCmpRom "$test"/ref.out.bin evaluateTest @@ -317,6 +311,15 @@ for i in section-union/*.asm; do evaluateTest 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" startTest "$RGBASM" -o "$otemp" "$test"/a.asm