diff --git a/lld/ELF/Arch/RISCV.cpp b/lld/ELF/Arch/RISCV.cpp index 72d83159ad8ac..33d3fdcc60ff8 100644 --- a/lld/ELF/Arch/RISCV.cpp +++ b/lld/ELF/Arch/RISCV.cpp @@ -27,7 +27,7 @@ using namespace lld::elf; namespace { -class RISCV final : public TargetInfo { +class RISCV : public TargetInfo { public: RISCV(Ctx &); uint32_t calcEFlags() const override; @@ -1065,6 +1065,66 @@ void RISCV::finalizeRelax(int passes) const { } } +namespace { + +class RISCVCfiLpUnlabeledPLT final : public RISCV { +public: + RISCVCfiLpUnlabeledPLT(Ctx &ctx); + void writePltHeader(uint8_t *buf) const override; + void writePlt(uint8_t *buf, const Symbol &sym, + uint64_t pltEntryAddr) const override; +}; + +} // namespace + +RISCVCfiLpUnlabeledPLT::RISCVCfiLpUnlabeledPLT(Ctx &ctx) : RISCV(ctx) { + pltHeaderSize = 48; +} + +void RISCVCfiLpUnlabeledPLT::writePltHeader(uint8_t *buf) const { + // lpad 0 + // 1: auipc t2, %pcrel_hi(.got.plt) + // sub t1, t1, t3 + // l[wd] t3, %pcrel_lo(1b)(t2); t3 = _dl_runtime_resolve + // addi t1, t1, -pltHeaderSize-16; t1 = &.plt[i] - &.plt[0] + // addi t0, t2, %pcrel_lo(1b) + // srli t1, t1, (rv64?1:2); t1 = &.got.plt[i] - &.got.plt[0] + // l[wd] t0, Wordsize(t0); t0 = link_map + // jr t3 + // nop + // nop + // nop + const uint32_t offset = + ctx.in.gotPlt->getVA() - (ctx.in.plt->getVA() + 4 /* offset for lpad */); + const uint32_t load = ctx.arg.is64 ? LD : LW; + write32le(buf + 0, utype(AUIPC, 0, 0)); // lpad 0 + write32le(buf + 4, utype(AUIPC, X_T2, hi20(offset))); + write32le(buf + 8, rtype(SUB, X_T1, X_T1, X_T3)); + write32le(buf + 12, itype(load, X_T3, X_T2, lo12(offset))); + write32le(buf + 16, itype(ADDI, X_T1, X_T1, -ctx.target->pltHeaderSize - 16)); + write32le(buf + 20, itype(ADDI, X_T0, X_T2, lo12(offset))); + write32le(buf + 24, itype(SRLI, X_T1, X_T1, ctx.arg.is64 ? 1 : 2)); + write32le(buf + 28, itype(load, X_T0, X_T0, ctx.arg.wordsize)); + write32le(buf + 32, itype(JALR, 0, X_T3, 0)); + write32le(buf + 36, itype(ADDI, 0, 0, 0)); // nop + write32le(buf + 40, itype(ADDI, 0, 0, 0)); // nop + write32le(buf + 44, itype(ADDI, 0, 0, 0)); // nop +} + +void RISCVCfiLpUnlabeledPLT::writePlt(uint8_t *buf, const Symbol &sym, + uint64_t pltEntryAddr) const { + // lpad 0 + // 1: auipc t3, %pcrel_hi(f@.got.plt) + // l[wd] t3, %pcrel_lo(1b)(t3) + // jalr t1, t3 + const uint32_t offset = + sym.getGotPltVA(ctx) - (pltEntryAddr + 4 /* offset for lpad */); + write32le(buf + 0, utype(AUIPC, 0, 0)); // lpad 0 + write32le(buf + 4, utype(AUIPC, X_T3, hi20(offset))); + write32le(buf + 8, itype(ctx.arg.is64 ? LD : LW, X_T3, X_T3, lo12(offset))); + write32le(buf + 12, itype(JALR, X_T1, X_T3, 0)); +} + namespace { // Representation of the merged .riscv.attributes input sections. The psABI // specifies merge policy for attributes. E.g. if we link an object without an @@ -1357,4 +1417,12 @@ void elf::mergeRISCVAttributesSections(Ctx &ctx) { mergeAttributesSection(ctx, sections)); } -void elf::setRISCVTargetInfo(Ctx &ctx) { ctx.target.reset(new RISCV(ctx)); } +void elf::setRISCVTargetInfo(Ctx &ctx) { + RISCV *target; + if (ctx.arg.andFeatures & GNU_PROPERTY_RISCV_FEATURE_1_CFI_LP_UNLABELED) + target = new RISCVCfiLpUnlabeledPLT(ctx); + else + target = new RISCV(ctx); + + ctx.target.reset(target); +} diff --git a/lld/test/ELF/riscv-plt-cfi-lp-unlabeled.s b/lld/test/ELF/riscv-plt-cfi-lp-unlabeled.s new file mode 100644 index 0000000000000..4a80081ecc258 --- /dev/null +++ b/lld/test/ELF/riscv-plt-cfi-lp-unlabeled.s @@ -0,0 +1,135 @@ +# REQUIRES: riscv +# RUN: rm -rf %t && split-file %s %t && cd %t + +# RUN: llvm-mc -filetype=obj -triple=riscv32 rv32-foo.s -o foo32.o +# RUN: ld.lld -shared foo32.o -soname=libfoo32.so -z zicfilp-unlabeled-report=error --fatal-warnings -o libfoo32.so +# RUN: llvm-mc -filetype=obj -triple=riscv32 rv32-start.s -o start32.o +# RUN: ld.lld start32.o libfoo32.so -z zicfilp-unlabeled-report=error --fatal-warnings -o out32 +# RUN: llvm-readelf -S out32 | FileCheck --check-prefix=SEC32 %s +# RUN: llvm-objdump -d --no-show-raw-insn --mattr=+experimental-zicfilp out32 | FileCheck --check-prefixes=DIS,DIS32 %s + +# RUN: llvm-mc -filetype=obj -triple=riscv64 rv64-foo.s -o foo64.o +# RUN: ld.lld -shared foo64.o -soname=libfoo64.so -z zicfilp-unlabeled-report=error --fatal-warnings -o libfoo64.so +# RUN: llvm-mc -filetype=obj -triple=riscv64 rv64-start.s -o start64.o +# RUN: ld.lld start64.o libfoo64.so -z zicfilp-unlabeled-report=error --fatal-warnings -o out64 +# RUN: llvm-readelf -S out64 | FileCheck --check-prefix=SEC64 %s +# RUN: llvm-objdump -d --no-show-raw-insn --mattr=+experimental-zicfilp out64 | FileCheck --check-prefixes=DIS,DIS64 %s + +# SEC32: .plt PROGBITS {{0*}}00011210 +# SEC32: .got.plt PROGBITS {{0*}}000132b8 + +# SEC64: .plt PROGBITS {{0*}}00011330 +# SEC64: .got.plt PROGBITS {{0*}}00013440 + +# DIS: Disassembly of section .plt: +# DIS: <.plt>: +# DIS-NEXT: lpad 0x0 +# DIS-NEXT: auipc t2, 0x2 +# DIS-NEXT: sub t1, t1, t3 +# DIS32-NEXT: lw t3, 0xa4(t2) +# DIS64-NEXT: ld t3, 0x10c(t2) +# DIS-NEXT: addi t1, t1, -0x40 +# DIS32-NEXT: addi t0, t2, 0xa4 +# DIS64-NEXT: addi t0, t2, 0x10c +# DIS32-NEXT: srli t1, t1, 0x2 +# DIS64-NEXT: srli t1, t1, 0x1 +# DIS32-NEXT: lw t0, 0x4(t0) +# DIS64-NEXT: ld t0, 0x8(t0) +# DIS-NEXT: jr t3 +# DIS-NEXT: nop +# DIS-NEXT: nop +# DIS-NEXT: nop + +# DIS: lpad 0x0 +# DIS-NEXT: auipc t3, 0x2 +# DIS32-NEXT: lw t3, 0x7c(t3) +# DIS64-NEXT: ld t3, 0xec(t3) +# DIS-NEXT: jalr t1, t3 + +#--- rv32-start.s + +.section ".note.gnu.property", "a" +.balign 4 +.4byte 4 +.4byte (ndesc_end - ndesc_begin) +.4byte 0x5 // NT_GNU_PROPERTY_TYPE_0 +.asciz "GNU" +ndesc_begin: +.balign 4 +.4byte 0xc0000000 // GNU_PROPERTY_RISCV_FEATURE_1_AND +.4byte 4 +.4byte 1 // GNU_PROPERTY_RISCV_FEATURE_1_CFI_LP_UNLABELED +.balign 4 +ndesc_end: + +.text +.global _start, foo + +_start: + call foo@plt + +#--- rv32-foo.s + +.section ".note.gnu.property", "a" +.balign 4 +.4byte 4 +.4byte (ndesc_end - ndesc_begin) +.4byte 0x5 // NT_GNU_PROPERTY_TYPE_0 +.asciz "GNU" +ndesc_begin: +.balign 4 +.4byte 0xc0000000 // GNU_PROPERTY_RISCV_FEATURE_1_AND +.4byte 4 +.4byte 1 // GNU_PROPERTY_RISCV_FEATURE_1_CFI_LP_UNLABELED +.balign 4 +ndesc_end: + +.text +.global foo +.type foo, @function +foo: + ret + +#--- rv64-start.s + +.section ".note.gnu.property", "a" +.balign 8 +.4byte 4 +.4byte (ndesc_end - ndesc_begin) +.4byte 0x5 // NT_GNU_PROPERTY_TYPE_0 +.asciz "GNU" +ndesc_begin: +.balign 8 +.4byte 0xc0000000 // GNU_PROPERTY_RISCV_FEATURE_1_AND +.4byte 4 +.4byte 1 // GNU_PROPERTY_RISCV_FEATURE_1_CFI_LP_UNLABELED +.balign 8 +ndesc_end: + +.text +.global _start, foo + +_start: + call foo@plt + +#--- rv64-foo.s + +.section ".note.gnu.property", "a" +.balign 8 +.4byte 4 +.4byte (ndesc_end - ndesc_begin) +.4byte 0x5 // NT_GNU_PROPERTY_TYPE_0 +.asciz "GNU" +ndesc_begin: +.balign 8 +.4byte 0xc0000000 // GNU_PROPERTY_RISCV_FEATURE_1_AND +.4byte 4 +.4byte 1 // GNU_PROPERTY_RISCV_FEATURE_1_CFI_LP_UNLABELED +.balign 8 +ndesc_end: + +.text +.global foo +.type foo, @function +foo: + ret