From 26cc6eb96a90fb86352e0cc06c1e7ac9025ad109 Mon Sep 17 00:00:00 2001 From: Scott Duensing Date: Fri, 14 Aug 2026 18:01:21 -0500 Subject: [PATCH] Linker fixes. --- src/link816/link816.cpp | 42 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/src/link816/link816.cpp b/src/link816/link816.cpp index 954e754..b399cb4 100644 --- a/src/link816/link816.cpp +++ b/src/link816/link816.cpp @@ -548,9 +548,16 @@ static uint32_t relocWidth(uint8_t rtype) { } +unsigned long gDbgBareCalls = 0; +unsigned long gDbgTextCalls = 0; static void applyReloc(std::vector &buf, uint32_t off, uint32_t patchAddr, uint32_t target, uint8_t rtype, const std::string &symName) { + extern unsigned long gDbgBareCalls; + gDbgBareCalls++; + if (symName.find("gRnInPack") != std::string::npos) { + std::fprintf(stderr, "TRACE-BARE %s rtype=%u target=0x%X off=0x%X\n", symName.c_str(), rtype, target, off); + } int64_t pcrelDisp; switch (rtype) { case R_W65816_IMM8: @@ -766,6 +773,12 @@ static void applyTextReloc(std::vector &buf, uint32_t off, } const TextSeg &pseg = segments[patchSeg - 1]; uint32_t targetSeg = findSegContaining(target, segments); + gDbgTextCalls++; + // TEMP TRACE (RetroNet hunt): decisions for the mirror-pack refs. + if (symName.find("gRnInPack") != std::string::npos) { + std::fprintf(stderr, "TRACE %s rtype=%u patchSeg=%u targetSeg=%u target=0x%X rec=%d\n", + symName.c_str(), rtype, patchSeg, targetSeg, target, (int)gRecordSites); + } // Cross-segment FUNCTION POINTER (IMM16 &func to a function in seg2+): // clang forms the pointer as func's 16-bit offset + the $00BE seg1 bank, // which is wrong for a function in another bank. Redirect the IMM16 to @@ -792,6 +805,34 @@ static void applyTextReloc(std::vector &buf, uint32_t off, // per-seg list for seg 1, since rodata is in seg 1's bank by // design). if (targetSeg == 0) { + // BANK-0 DATA/BSS target (inside SEG1's payload) referenced with a LONG form from an + // overflow segment: the baked bank byte would be the LINK-TIME bank (0), but the Loader + // places seg1 in an arbitrary bank - every such read/write silently hits the wrong bank + // (measured live on the RetroNet IIgs client: cross-TU constant-index accesses compile to + // LDA long and returned garbage while DBR-relative abs16 to the same symbols worked). + // Emit a 3-byte cINTERSEG against seg 1 so the Loader writes the true 24-bit address. + // ...and the SAME hole exists for 16-bit forms: seg1 is only PAGE-aligned by the real + // GS/OS Loader (it does not honor ALIGN=$10000), so abs16 operands and bank-byte + // materializations referencing seg1 data are wrong by seg1's placement offset unless the + // Loader patches them. Emit 2-byte cINTERSEG records for those too; BANKSIZE=$10000 + // guarantees seg1 never straddles a bank, so offset16+DBR stays coherent. + if (gRecordSites && patchSeg >= 1 && !segments.empty() && + (rtype == R_W65816_IMM24 || rtype == R_W65816_DATA32 || + rtype == R_W65816_IMM16 || rtype == R_W65816_BANK16) && + (target & 0xFF0000u) == 0u && target != 0u) { + uint8_t cnt = (rtype == R_W65816_IMM24 || rtype == R_W65816_DATA32) ? 3 : 2; + for (uint8_t i = 0; i < cnt; ++i) + buf[off + i] = 0; + InterImm24Site s; + s.patchSeg = static_cast(patchSeg); + s.patchOff = static_cast(patchAddr - pseg.base); + s.targetSeg = 1; + s.targetOff = static_cast(target); + s.byteCnt = cnt; + s.bitShift = (rtype == R_W65816_BANK16) ? 16 : 0; + gInterImm24Sites.push_back(s); + return; + } applyReloc(buf, off, patchAddr, target, rtype, symName); // Also push into the per-seg list with the same gates as // recordCRelocSite (target in same bank as textBase and @@ -2570,6 +2611,7 @@ int main(int argc, char **argv) { L.bssBase, L.bssSize, outPath.c_str(), image.size()); if (L.segments.size() > 1) { + std::fprintf(stderr, " [dbg: textReloc=%lu bare=%lu]", gDbgTextCalls, gDbgBareCalls); std::fprintf(stderr, " + %zu extra segments", L.segments.size() - 1); for (size_t k = 1; k < L.segments.size(); ++k) {