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; @@ -101,6 +102,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; @@ -276,6 +288,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: @@ -313,6 +327,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; diff --git a/lld/test/ELF/riscv-relax-uleb128.s b/lld/test/ELF/riscv-relax-uleb128.s new file mode 100644 --- /dev/null +++ b/lld/test/ELF/riscv-relax-uleb128.s @@ -0,0 +1,77 @@ +# REQUIRES: riscv +## Relax R_RISCV_SET_ULEB128 and R_RISCV_SUB_ULEB128. + +# RUN: rm -rf %t && split-file %s %t && cd %t + +## RELAX +# RUN: llvm-mc -filetype=obj -triple=riscv32 -riscv-enable-uleb128 -mattr=+c,+relax a.s -o a.32c.o +# RUN: ld.lld -T lds a.32c.o -o 32c +# RUN: llvm-objdump -td -j .text -j .data -M no-aliases 32c | FileCheck %s --check-prefixes=RELAX,RELAX32 + +# RUN: llvm-mc -filetype=obj -triple=riscv64 -riscv-enable-uleb128 -mattr=+c,+relax a.s -o a.64c.o +# RUN: ld.lld -T lds a.64c.o -o 64c +# RUN: llvm-objdump -td -j .text -j .data -M no-aliases 64c | FileCheck %s --check-prefixes=RELAX,RELAX64 + +## --no-relax disables relaxation. +# RUN: ld.lld -T lds a.64c.o --no-relax -o 64c.norelax +# RUN: llvm-objdump -td -j .text -j .data -M no-aliases 64c.norelax | FileCheck %s --check-prefixes=NORELAX + +# RELAX32: 00010002 l .text 00000000 a +# RELAX32: 00010002 l .text 00000000 .Lend_start +# RELAX32: 00010000 g .text 00000002 _start + +# RELAX64: 0000000000010004 l .text 0000000000000000 a +# RELAX64: 0000000000010004 l .text 0000000000000000 .Lend_start +# RELAX64: 0000000000010000 g .text 0000000000000004 _start + +# RELAX-LABEL: <_start>: +# RELAX32-NEXT: 10000: 09 20 c.jal 0x10002 +# RELAX64-NEXT: 10000: ef 00 40 00 jal ra, 0x10004 +# RELAX-EMPTY: +# RELAX-NEXT: : +# RELAX32-NEXT: 10002: 82 80 c.jr ra +# RELAX64-NEXT: 10004: 82 80 c.jr ra +# RELAX-EMPTY: + +# RELAX-LABEL: <.data>: +# RELAX32: 20000: 82 80 +# RELAX32-NEXT: 20002: 80 80 +# RELAX32-NEXT: 20004: 00 +# RELAX64: 20000: 84 80 +# RELAX64-NEXT: 20002: 80 80 +# RELAX64-NEXT: 20004: 80 80 +# RELAX64-NEXT: 20006: 80 80 +# RELAX64-NEXT: 20008: 80 00 +# RELAX-EMPTY: + +# NORELAX-LABEL: <_start>: +# NORELAX: 10000: 97 00 00 00 auipc ra, 0 +# NORELAX-NEXT: e7 80 80 00 jalr ra, 8(ra) +# NORELAX-EMPTY: + +# NORELAX-LABEL: <.data>: +# NORELAX: 20000: 88 80 +# NORELAX-NEXT: 20002: 80 80 +# NORELAX-NEXT: 20004: 80 80 +# NORELAX-NEXT: 20006: 80 80 +# NORELAX-NEXT: 20008: 80 00 +# NORELAX-EMPTY: + + +#--- a.s +.global _start +_start: + call a # rv32c: c.jal; rv64c: jal +.Lend_start: +.size _start, . - _start + +a: + ret +.data +.uleb128 .Lend_start-_start + +#--- lds +SECTIONS { + .text 0x10000 : { *(.text) } + .data 0x20000 : { *(.data) } +}