diff --git a/include/link/section.h b/include/link/section.h index fe13867d..f8b28047 100644 --- a/include/link/section.h +++ b/include/link/section.h @@ -37,6 +37,7 @@ struct Section { char *name; uint16_t size; enum SectionType type; + bool isUnion; bool isAddressFixed; uint16_t org; bool isBankFixed; @@ -50,6 +51,7 @@ struct Section { struct Symbol **fileSymbols; uint32_t nbSymbols; struct Symbol const **symbols; + struct Section *nextu; /* The next "component" of this unionized sect */ }; /* diff --git a/include/linkdefs.h b/include/linkdefs.h index 75fb2b45..76d89d57 100644 --- a/include/linkdefs.h +++ b/include/linkdefs.h @@ -14,7 +14,7 @@ #define RGBDS_OBJECT_VERSION_STRING "RGB%1hhu" #define RGBDS_OBJECT_VERSION_NUMBER (uint8_t)9 -#define RGBDS_OBJECT_REV 2 +#define RGBDS_OBJECT_REV 3 enum AssertionType { ASSERT_WARN, diff --git a/src/asm/output.c b/src/asm/output.c index 3c742aa0..449d95f4 100644 --- a/src/asm/output.c +++ b/src/asm/output.c @@ -189,7 +189,7 @@ static void writesection(struct Section *pSect, FILE *f) fputlong(pSect->size, f); - fputc(pSect->nType, f); + fputc(pSect->nType | pSect->isUnion << 7, f); fputlong(pSect->nOrg, f); fputlong(pSect->nBank, f); diff --git a/src/link/object.c b/src/link/object.c index d42565d9..5685983c 100644 --- a/src/link/object.c +++ b/src/link/object.c @@ -245,6 +245,7 @@ static void readSection(FILE *file, struct Section *section, char const *fileName) { int32_t tmp; + uint8_t type; tryReadstr(section->name, file, "%s: Cannot read section name: %s", fileName); @@ -254,8 +255,10 @@ static void readSection(FILE *file, struct Section *section, errx(1, "\"%s\"'s section size (%d) is invalid", section->name, tmp); section->size = tmp; - tryGetc(section->type, file, "%s: Cannot read \"%s\"'s type: %s", + tryGetc(type, file, "%s: Cannot read \"%s\"'s type: %s", fileName, section->name); + section->type = type & 0x7F; + section->isUnion = type >> 7; tryReadlong(tmp, file, "%s: Cannot read \"%s\"'s org: %s", fileName, section->name); section->isAddressFixed = tmp >= 0; @@ -358,6 +361,14 @@ static void readAssertion(FILE *file, struct Assertion *assert, fileName); } +static inline struct Section *getMainSection(struct Section *section) +{ + if (section->isUnion) + section = sect_GetSection(section->name); + + return section; +} + /** * Reads an object file of any supported format * @param fileName The filename to report for errors @@ -448,6 +459,7 @@ void obj_ReadFile(char const *fileName) if (!section) err(1, "%s: Couldn't create new section", fileName); + section->nextu = NULL; readSection(file, section, fileName); section->fileSymbols = fileSymbols; @@ -472,9 +484,10 @@ void obj_ReadFile(char const *fileName) if (sectionID == -1) { fileSymbols[i]->section = NULL; } else { - fileSymbols[i]->section = fileSections[sectionID]; /* Give the section a pointer to the symbol as well */ linkSymToSect(fileSymbols[i], fileSections[sectionID]); + fileSymbols[i]->section = + getMainSection(fileSections[sectionID]); } } diff --git a/src/link/patch.c b/src/link/patch.c index 0998dda0..38ae524e 100644 --- a/src/link/patch.c +++ b/src/link/patch.c @@ -117,6 +117,24 @@ static uint32_t getRPNByte(uint8_t const **expression, int32_t *size, return *(*expression)++; } +static struct Symbol const *getSymbol(struct Symbol ** const symbolList, + uint32_t index, char const *fileName) +{ + struct Symbol const *symbol = symbolList[index]; + + /* If the symbol is defined elsewhere... */ + if (symbol->type == SYMTYPE_IMPORT) { + struct Symbol const *symbolDefinition = + sym_GetSymbol(symbol->name); + if (!symbolDefinition) + errx(1, "%s: Unknown symbol \"%s\"", fileName, + symbol->name); + symbol = symbolDefinition; + } + + return symbol; +} + /** * Compute a patch's value from its RPN string. * @param patch The patch to compute the value of @@ -238,19 +256,8 @@ static int32_t computeRPNExpr(struct Patch const *patch, value |= getRPNByte(&expression, &size, patch->fileName) << shift; - symbol = section->fileSymbols[value]; - - /* If the symbol is defined elsewhere... */ - if (symbol->type == SYMTYPE_IMPORT) { - struct Symbol const *symbolDefinition = - sym_GetSymbol(symbol->name); - if (!symbolDefinition) - errx(1, "%s: Unknown symbol \"%s\"", - patch->fileName, symbol->name); - symbol = symbolDefinition; - } - - value = symbol->section->bank; + value = getSymbol(section->fileSymbols, value, + patch->fileName)->section->bank; break; case RPN_BANK_SECT: @@ -305,17 +312,8 @@ static int32_t computeRPNExpr(struct Patch const *patch, value |= getRPNByte(&expression, &size, patch->fileName) << shift; - symbol = section->fileSymbols[value]; - - /* If the symbol is defined elsewhere... */ - if (symbol->type == SYMTYPE_IMPORT) { - struct Symbol const *symbolDefinition = - sym_GetSymbol(symbol->name); - if (!symbolDefinition) - errx(1, "%s: Unknown symbol \"%s\"", - patch->fileName, symbol->name); - symbol = symbolDefinition; - } + symbol = getSymbol(section->fileSymbols, value, + patch->fileName); if (!strcmp(symbol->name, "@")) { value = section->org + patch->offset; @@ -387,10 +385,8 @@ void patch_CheckAssertions(struct Assertion *assert) * @param section The section to patch * @param arg Ignored callback arg */ -static void applyPatches(struct Section *section, void *arg) +static void applyFilePatches(struct Section *section) { - (void)arg; - if (!sect_HasData(section->type)) return; @@ -435,6 +431,22 @@ static void applyPatches(struct Section *section, void *arg) } } +/** + * Applies all of a section's patches, iterating over "components" of + * unionized sections + * @param section The section to patch + * @param arg Ignored callback arg + */ +static void applyPatches(struct Section *section, void *arg) +{ + (void)arg; + + do { + applyFilePatches(section); + section = section->nextu; + } while (section); +} + void patch_ApplyPatches(void) { initRPNStack(); diff --git a/src/link/section.c b/src/link/section.c index d1c35640..d52afee1 100644 --- a/src/link/section.c +++ b/src/link/section.c @@ -36,17 +36,76 @@ void sect_ForEach(void (*callback)(struct Section *, void *), void *arg) hash_ForEach(sections, forEach, &callbackArg); } +static void mergeSections(struct Section *target, struct Section *other) +{ + if (other->isAddressFixed) { + if (target->isAddressFixed) { + if (target->org != other->org) + errx(1, "Section \"%s\" is defined with conflicting addresses $%x and $%x", + other->name, target->org, other->org); + } else if (target->isAlignFixed) { + if (other->org & target->alignMask) + errx(1, "Section \"%s\" is defined with conflicting %u-byte alignment and address $%x", + other->name, target->alignMask + 1, + other->org); + } + target->isAddressFixed = true; + target->org = other->org; + } else if (other->isAlignFixed) { + if (target->isAddressFixed) { + if (target->org & other->alignMask) + errx(1, "Section \"%s\" is defined with conflicting address $%x and %u-byte alignment", + other->name, target->org, + other->alignMask + 1); + } else if (!target->isAlignFixed + || other->alignMask > target->alignMask) { + target->isAlignFixed = true; + target->alignMask = other->alignMask; + } + } + + if (other->isBankFixed) { + if (!target->isBankFixed) { + target->isBankFixed = true; + target->bank = other->bank; + } else if (target->bank != other->bank) { + errx(1, "Section \"%s\" is defined with conflicting banks %u and %u", + other->name, target->bank, other->bank); + } + } + + if (other->size > target->size) + target->size = other->size; + + target->nextu = other; +} + void sect_AddSection(struct Section *section) { /* Check if the section already exists */ - if (hash_GetElement(sections, section->name)) - errx(1, "Section name \"%s\" is already in use", section->name); + struct Section *other = hash_GetElement(sections, section->name); - /* If not, add it */ - bool collided = hash_AddElement(sections, section->name, section); + if (other) { + if (other->isUnion && section->isUnion) { + mergeSections(other, section); + } else if (section->isUnion || other->isUnion) { + errx(1, "Section \"%s\" defined as both unionized and not", + section->name); + } else { + errx(1, "Section name \"%s\" is already in use", + section->name); + } + } else if (section->isUnion && sect_HasData(section->type)) { + errx(1, "Section \"%s\" is of type %s, which cannot be unionized", + section->name, typeNames[section->type]); + } else { + /* If not, add it */ + bool collided = hash_AddElement(sections, section->name, + section); - if (beVerbose && collided) - warnx("Section hashmap collision occurred!"); + if (beVerbose && collided) + warnx("Section hashmap collision occurred!"); + } } struct Section *sect_GetSection(char const *name) diff --git a/test/link/section-union/a.asm b/test/link/section-union/a.asm new file mode 100644 index 00000000..55f9c90a --- /dev/null +++ b/test/link/section-union/a.asm @@ -0,0 +1,21 @@ +SECTION UNION "a", WRAM0 + ds 10 +a1: + +SECTION UNION "b", WRAMX,ALIGN[4] +banked:: + ds 10 +b1: + +SECTION UNION "c", HRAM[$FFC0] + ds 5 +c1:: + + +SECTION "output 1", ROM0 + dw a1,a2 ; $C00A, $C02A + dw b1,b2 ; $DABA, $DAB2 + dw c1,c2 ; $FFC5, $FFC5 + +SECTION "output 3", ROM0 + db BANK(banked) diff --git a/test/link/section-union/b.asm b/test/link/section-union/b.asm new file mode 100644 index 00000000..21828c1a --- /dev/null +++ b/test/link/section-union/b.asm @@ -0,0 +1,18 @@ +SECTION UNION "a", WRAM0 +a1: ; This is here to check that the two `a1` don't conflict + ds 42 +a2:: + +SECTION UNION "b", WRAMX[$DAB0] + ds 2 +b2:: + +SECTION UNION "c", HRAM[$FFC0] +b1: ; Same but in different sections now + ds 5 +c2:: + +SECTION "output 2", ROM0 + dw a1,a2 + dw b1,b2 + dw c1,c2 diff --git a/test/link/section-union/ref.out.bin b/test/link/section-union/ref.out.bin new file mode 100644 index 00000000..4ec95224 Binary files /dev/null and b/test/link/section-union/ref.out.bin differ diff --git a/test/link/section-union/script.link b/test/link/section-union/script.link new file mode 100644 index 00000000..1bb79ddc --- /dev/null +++ b/test/link/section-union/script.link @@ -0,0 +1,9 @@ +ROM0 + "output 1" + "output 2" + "output 3" +WRAM0 + "a" ; $C000 +WRAMX 1 + ORG $DAB0 + "b" diff --git a/test/link/test.sh b/test/link/test.sh index 8ec56be5..68d33600 100755 --- a/test/link/test.sh +++ b/test/link/test.sh @@ -71,12 +71,20 @@ for i in *.asm; do fi done -# This test does its own thing +# These tests do their own thing + $RGBASM -o $otemp high-low/a.asm $RGBLINK -o $gbtemp $otemp $RGBASM -o $otemp high-low/b.asm $RGBLINK -o $gbtemp2 $otemp -i="high-low.asm" tryDiff $gbtemp $gbtemp2 +i="high-low.asm" tryCmp $gbtemp $gbtemp2 +rc=$(($? || $rc)) + +$RGBASM -o $otemp section-union/a.asm +$RGBASM -o $gbtemp2 section-union/b.asm +$RGBLINK -o $gbtemp -l section-union/script.link $otemp $gbtemp2 +dd if=$gbtemp count=1 bs=$(printf %s $(wc -c < section-union/ref.out.bin)) > $otemp 2>/dev/null +i="section-union.asm" tryCmp section-union/ref.out.bin $otemp rc=$(($? || $rc)) rm -f $otemp $gbtemp $gbtemp2 $outtemp