diff --git a/lld/ELF/Arch/RISCV.cpp b/lld/ELF/Arch/RISCV.cpp --- a/lld/ELF/Arch/RISCV.cpp +++ b/lld/ELF/Arch/RISCV.cpp @@ -41,6 +41,7 @@ RelType getDynRel(RelType type) const override; RelExpr getRelExpr(RelType type, const Symbol &s, const uint8_t *loc) const override; + void relocateAlloc(InputSectionBase &sec, uint8_t *buf) const override; void relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const override; bool relaxOnce(int pass) const override; @@ -95,6 +96,17 @@ (extractBits(imm, 4, 0) << 7); } +static void checkULEB128LengthNoExtend(uint8_t *loc, const RelType type, + unsigned oldLength, unsigned newLength) { + if (newLength <= oldLength) + return; + + ErrorPlace errPlace = getErrorPlace(loc); + error(errPlace.loc + " ULEB128 value has extended the length by " + + toString(type) + "relocation from " + Twine(oldLength) + " byte to " + + Twine(newLength) + " byte"); +} + RISCV::RISCV() { copyRel = R_RISCV_COPY; pltRel = R_RISCV_JUMP_SLOT; @@ -270,6 +282,8 @@ case R_RISCV_SUB16: case R_RISCV_SUB32: case R_RISCV_SUB64: + case R_RISCV_SET_ULEB128: + case R_RISCV_SUB_ULEB128: return R_RISCV_ADD; case R_RISCV_JAL: case R_RISCV_BRANCH: @@ -306,6 +320,60 @@ } } +void RISCV::relocateAlloc(InputSectionBase &sec, uint8_t *buf) const { + const unsigned bits = config->is64 ? 64 : 32; + uint64_t secAddr = sec.getOutputSection()->addr; + if (auto *s = dyn_cast(&sec)) + secAddr += s->outSecOff; + + uint8_t *lastULEB128SetLoc = nullptr; + uint64_t lastULEB128SetVal = 0; + for (const Relocation &rel : sec.relocs()) { + uint8_t *loc = buf + rel.offset; + const uint64_t val = SignExtend64( + sec.getRelocTargetVA(sec.file, rel.type, rel.addend, + secAddr + rel.offset, *rel.sym, rel.expr), + bits); + + if (rel.expr == R_RELAX_HINT) + continue; + + switch (rel.type) { + case R_RISCV_SET_ULEB128: { + if (lastULEB128SetLoc != nullptr) { + ErrorPlace errPlace = getErrorPlace(loc); + error(errPlace.loc + + " More than one R_RISCV_SET_ULEB128 in same location"); + } + lastULEB128SetLoc = loc; + lastULEB128SetVal = val; + break; + } + case R_RISCV_SUB_ULEB128: { + if (loc != lastULEB128SetLoc) { + ErrorPlace errPlace = getErrorPlace(loc); + error(errPlace.loc + + " R_RISCV_SUB_ULEB128 must come after R_RISCV_SET_ULEB128"); + break; + } + unsigned oldLength; + decodeULEB128(loc, &oldLength); + uint64_t newVal = lastULEB128SetVal - val; + unsigned newLength = encodeULEB128(newVal, loc, /*PadTo*/ oldLength); + checkULEB128LengthNoExtend(loc, rel.type, oldLength, newLength); + lastULEB128SetLoc = nullptr; + break; + } + default: + relocate(loc, rel, val); + } + } + if (lastULEB128SetLoc != nullptr) { + ErrorPlace errPlace = getErrorPlace(lastULEB128SetLoc); + error(errPlace.loc + " Orphan R_RISCV_SET_ULEB128"); + } +} + void RISCV::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const { const unsigned bits = config->wordsize * 8;