From 6611d58f5bbcbec77262d392e2923e1d680f6985 Mon Sep 17 00:00:00 2001 From: Fangrui Song Date: Thu, 7 Jul 2022 10:16:09 -0700 Subject: [PATCH] [ELF] Relax R_RISCV_ALIGN Alternative to D125036. Implement R_RISCV_ALIGN relaxation so that we can handle -mrelax object files (i.e. -mno-relax is no longer needed) and creates a framework for future relaxation. `relaxAux` is placed in a union with InputSectionBase::jumpInstrMod, storing auxiliary information for relaxation. In the first pass, `relaxAux` is allocated. The main data structure is `relocDeltas`: when referencing `relocations[i]`, the actual offset is `r_offset - (i ? relocDeltas[i-1] : 0)`. `relaxOnce` performs one relaxation pass. It computes `relocDeltas` for all text section. Then, adjust st_value/st_size for symbols relative to this section based on `SymbolAnchor`. `bytesDropped` is set so that `assignAddresses` knows that the size has changed. Run `relaxOnce` in the `finalizeAddressDependentContent` loop to wait for convergence of text sections and other address dependent sections (e.g. SHT_RELR). Note: extrating `relaxOnce` into a separate loop works for many cases but has issues in some linker script edge cases. After convergence, compute section contents: shrink the NOP sequence of each R_RISCV_ALIGN as appropriate. Instead of deleting bytes, we run a sequence of memcpy on the content delimitered by relocation locations. For R_RISCV_ALIGN let the next memcpy skip the desired number of bytes. Section content computation is parallelizable, but let's ensure the implementation is mature before optimizations. Technically we can save a copy if we interleave some code with `OutputSection::writeTo`, but let's not pollute the generic code (we don't have templated relocation resolving, so using conditions can impose overhead to non-RISCV.) Tested: `make ARCH=riscv CROSS_COMPILE=riscv64-linux-gnu- LLVM=1 defconfig all` built Linux kernel using -mrelax is bootable. FreeBSD RISCV64 system using -mrelax is bootable. bash/curl/firefox/libevent/vim/tmux using -mrelax works. Differential Revision: https://reviews.llvm.org/D127581 --- lld/ELF/Arch/RISCV.cpp | 237 ++++++++++++++++++++++++++++++++++- lld/ELF/InputSection.cpp | 4 + lld/ELF/InputSection.h | 29 +++-- lld/ELF/Relocations.cpp | 9 +- lld/ELF/Relocations.h | 13 +- lld/ELF/Target.h | 4 + lld/ELF/Writer.cpp | 13 +- lld/test/ELF/riscv-relax-align-rvc.s | 75 +++++++++++ lld/test/ELF/riscv-relax-align.s | 161 ++++++++++++++++++++++++ lld/test/ELF/riscv-reloc-align.s | 12 -- 10 files changed, 515 insertions(+), 42 deletions(-) create mode 100644 lld/test/ELF/riscv-relax-align-rvc.s create mode 100644 lld/test/ELF/riscv-relax-align.s delete mode 100644 lld/test/ELF/riscv-reloc-align.s diff --git a/lld/ELF/Arch/RISCV.cpp b/lld/ELF/Arch/RISCV.cpp index 7ba0214..7d2255f 100644 --- a/lld/ELF/Arch/RISCV.cpp +++ b/lld/ELF/Arch/RISCV.cpp @@ -7,9 +7,11 @@ //===----------------------------------------------------------------------===// #include "InputFiles.h" +#include "OutputSections.h" #include "Symbols.h" #include "SyntheticSections.h" #include "Target.h" +#include "llvm/Support/TimeProfiler.h" using namespace llvm; using namespace llvm::object; @@ -36,6 +38,7 @@ public: const uint8_t *loc) const override; void relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const override; + bool relaxOnce(int pass) const override; }; } // end anonymous namespace @@ -271,12 +274,7 @@ RelExpr RISCV::getRelExpr(const RelType type, const Symbol &s, case R_RISCV_TPREL_ADD: return R_NONE; case R_RISCV_ALIGN: - // Not just a hint; always padded to the worst-case number of NOPs, so may - // not currently be aligned, and without linker relaxation support we can't - // delete NOPs to realign. - errorOrWarn(getErrorLocation(loc) + "relocation R_RISCV_ALIGN requires " - "unimplemented linker relaxation; recompile with -mno-relax"); - return R_NONE; + return R_RELAX_HINT; default: error(getErrorLocation(loc) + "unknown relocation (" + Twine(type) + ") against symbol " + toString(s)); @@ -476,6 +474,233 @@ void RISCV::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const { } } +namespace { +struct SymbolAnchor { + uint64_t offset; + Defined *d; + bool end; // true for the anchor of st_value+st_size +}; +} // namespace + +struct elf::RISCVRelaxAux { + // This records symbol start and end offsets which will be adjusted according + // to the nearest relocDeltas element. + SmallVector anchors; + // For relocations[i], the actual offset is r_offset - (i ? relocDeltas[i-1] : + // 0). + std::unique_ptr relocDeltas; +}; + +static void initSymbolAnchors() { + SmallVector storage; + for (OutputSection *osec : outputSections) { + if (!(osec->flags & SHF_EXECINSTR)) + continue; + for (InputSection *sec : getInputSections(*osec, storage)) { + sec->relaxAux = make(); + if (sec->relocations.size()) + sec->relaxAux->relocDeltas = + std::make_unique(sec->relocations.size()); + } + } + // Store anchors (st_value and st_value+st_size) for symbols relative to text + // sections. + for (InputFile *file : ctx->objectFiles) + for (Symbol *sym : file->getSymbols()) { + auto *d = dyn_cast(sym); + if (!d || d->file != file) + continue; + if (auto *sec = dyn_cast_or_null(d->section)) + if (sec->flags & SHF_EXECINSTR && sec->relaxAux) { + // If sec is discarded, relaxAux will be nullptr. + sec->relaxAux->anchors.push_back({d->value, d, false}); + sec->relaxAux->anchors.push_back({d->value + d->size, d, true}); + } + } + // Sort anchors by offset so that we can find the closest relocation + // efficiently. For a zero size symbol, ensure that its start anchor precedes + // its end anchor. For two symbols with anchors at the same offset, their + // order does not matter. + for (OutputSection *osec : outputSections) { + if (!(osec->flags & SHF_EXECINSTR)) + continue; + for (InputSection *sec : getInputSections(*osec, storage)) { + llvm::sort(sec->relaxAux->anchors, [](auto &a, auto &b) { + return std::make_pair(a.offset, a.end) < + std::make_pair(b.offset, b.end); + }); + } + } +} + +static bool relax(InputSection &sec) { + const uint64_t secAddr = sec.getVA(); + auto &aux = *sec.relaxAux; + bool changed = false; + + // Restore original st_value for symbols relative to this section. + ArrayRef sa = makeArrayRef(aux.anchors); + uint32_t delta = 0; + for (auto it : llvm::enumerate(sec.relocations)) { + for (; sa.size() && sa[0].offset <= it.value().offset; sa = sa.slice(1)) + if (!sa[0].end) + sa[0].d->value += delta; + delta = aux.relocDeltas[it.index()]; + } + for (const SymbolAnchor &sa : sa) + if (!sa.end) + sa.d->value += delta; + sa = makeArrayRef(aux.anchors); + delta = 0; + + for (auto it : llvm::enumerate(sec.relocations)) { + Relocation &r = it.value(); + const size_t i = it.index(); + const uint64_t loc = secAddr + r.offset - delta; + uint32_t &cur = aux.relocDeltas[i], remove = 0; + switch (r.type) { + case R_RISCV_ALIGN: { + const uint64_t nextLoc = loc + r.addend; + const uint64_t align = PowerOf2Ceil(r.addend + 2); + // All bytes beyond the alignment boundary should be removed. + remove = nextLoc - ((loc + align - 1) & -align); + assert(static_cast(remove) >= 0 && + "R_RISCV_ALIGN needs expanding the content"); + break; + } + } + + // For all anchors whose offsets are <= r.offset, they are preceded by + // the previous relocation whose `relocDeltas` value equals `delta`. + // Decrease their st_value and update their st_size. + if (remove) { + for (; sa.size() && sa[0].offset <= r.offset; sa = sa.slice(1)) { + if (sa[0].end) + sa[0].d->size = sa[0].offset - delta - sa[0].d->value; + else + sa[0].d->value -= delta; + } + } + delta += remove; + if (delta != cur) { + cur = delta; + changed = true; + } + } + + for (const SymbolAnchor &a : sa) { + if (a.end) + a.d->size = a.offset - delta - a.d->value; + else + a.d->value -= delta; + } + // Inform assignAddresses that the size has changed. + if (!isUInt<16>(delta)) + fatal("section size decrease is too large"); + sec.bytesDropped = delta; + return changed; +} + +// When relaxing just R_RISCV_ALIGN, relocDeltas is usually changed only once in +// the absence of a linker script. For call and load/store R_RISCV_RELAX, code +// shrinkage may reduce displacement and make more relocations eligible for +// relaxation. Code shrinkage may increase displacement to a call/load/store +// target at a higher fixed address, invalidating an earlier relaxation. Any +// change in section sizes can have cascading effect and require another +// relaxation pass. +bool RISCV::relaxOnce(int pass) const { + llvm::TimeTraceScope timeScope("RISC-V relaxOnce"); + if (config->relocatable) + return false; + + if (pass == 0) + initSymbolAnchors(); + + SmallVector storage; + bool changed = false; + for (OutputSection *osec : outputSections) { + if (!(osec->flags & SHF_EXECINSTR)) + continue; + for (InputSection *sec : getInputSections(*osec, storage)) + changed |= relax(*sec); + } + return changed; +} + +void elf::riscvFinalizeRelax(int passes) { + llvm::TimeTraceScope timeScope("Finalize RISC-V relaxation"); + log("relaxation passes: " + Twine(passes)); + SmallVector storage; + for (OutputSection *osec : outputSections) { + if (!(osec->flags & SHF_EXECINSTR)) + continue; + for (InputSection *sec : getInputSections(*osec, storage)) { + RISCVRelaxAux &aux = *sec->relaxAux; + if (!aux.relocDeltas) + continue; + + auto &rels = sec->relocations; + ArrayRef old = sec->rawData; + size_t newSize = + old.size() - aux.relocDeltas[sec->relocations.size() - 1]; + uint8_t *p = context().bAlloc.Allocate(newSize); + uint64_t offset = 0; + int64_t delta = 0; + sec->rawData = makeArrayRef(p, newSize); + sec->bytesDropped = 0; + + // Update section content: remove NOPs for R_RISCV_ALIGN and rewrite + // instructions for relaxed relocations. + for (size_t i = 0, e = rels.size(); i != e; ++i) { + uint32_t remove = aux.relocDeltas[i] - delta; + delta = aux.relocDeltas[i]; + if (remove == 0) + continue; + + // Copy from last location to the current relocated location. + const Relocation &r = rels[i]; + uint64_t size = r.offset - offset; + memcpy(p, old.data() + offset, size); + p += size; + + // For R_RISCV_ALIGN, we will place `offset` in a location (among NOPs) + // to satisfy the alignment requirement. If `remove` is a multiple of 4, + // it is as if we have skipped some NOPs. Otherwise we are in the middle + // of a 4-byte NOP, and we need to rewrite the NOP sequence. + int64_t skip = 0; + if (r.type == R_RISCV_ALIGN) { + if (remove % 4 != 0) { + skip = r.addend - remove; + int64_t j = 0; + for (; j + 4 <= skip; j += 4) + write32le(p + j, 0x00000013); // nop + if (j != skip) { + assert(j + 2 == skip); + write16le(p + j, 0x0001); // c.nop + } + } + } + + p += skip; + offset = r.offset + skip + remove; + } + memcpy(p, old.data() + offset, old.size() - offset); + + // Substract the previous relocDeltas value from the relocation offset. + // For a pair of R_RISCV_CALL/R_RISCV_RELAX with the same offset, decrease + // their r_offset by the same delta. + delta = 0; + for (size_t i = 0, e = rels.size(); i != e;) { + uint64_t cur = rels[i].offset; + do { + rels[i].offset -= delta; + } while (++i != e && rels[i].offset == cur); + delta = aux.relocDeltas[i - 1]; + } + } + } +} + TargetInfo *elf::getRISCVTargetInfo() { static RISCV target; return ⌖ diff --git a/lld/ELF/InputSection.cpp b/lld/ELF/InputSection.cpp index 97fc18b..0dd17ae 100644 --- a/lld/ELF/InputSection.cpp +++ b/lld/ELF/InputSection.cpp @@ -622,6 +622,8 @@ uint64_t InputSectionBase::getRelocTargetVA(const InputFile *file, RelType type, return sym.getVA(a); case R_ADDEND: return a; + case R_RELAX_HINT: + return 0; case R_ARM_SBREL: return sym.getVA(a) - getARMStaticBase(sym); case R_GOT: @@ -987,6 +989,8 @@ void InputSectionBase::relocateAlloc(uint8_t *buf, uint8_t *bufEnd) { *rel.sym, rel.expr), bits); switch (rel.expr) { + case R_RELAX_HINT: + continue; case R_RELAX_GOT_PC: case R_RELAX_GOT_PC_NOPIC: target.relaxGot(bufLoc, rel, targetVA); diff --git a/lld/ELF/InputSection.h b/lld/ELF/InputSection.h index c7c8f45..d1b8897 100644 --- a/lld/ELF/InputSection.h +++ b/lld/ELF/InputSection.h @@ -10,7 +10,9 @@ #define LLD_ELF_INPUT_SECTION_H #include "Relocations.h" +#include "lld/Common/CommonLinkerContext.h" #include "lld/Common/LLVM.h" +#include "lld/Common/Memory.h" #include "llvm/ADT/CachedHashString.h" #include "llvm/ADT/DenseSet.h" #include "llvm/ADT/TinyPtrVector.h" @@ -97,6 +99,8 @@ protected: link(link), info(info) {} }; +struct RISCVRelaxAux; + // This corresponds to a section of an input file. class InputSectionBase : public SectionBase { public: @@ -129,11 +133,10 @@ public: return cast_or_null>(file); } - // If basic block sections are enabled, many code sections could end up with - // one or two jump instructions at the end that could be relaxed to a smaller - // instruction. The members below help trimming the trailing jump instruction - // and shrinking a section. - uint8_t bytesDropped = 0; + // Used by --optimize-bb-jumps and RISC-V linker relaxation temporarily to + // indicate the number of bytes which is not counted in the size. This should + // be reset to zero after uses. + uint16_t bytesDropped = 0; // Whether the section needs to be padded with a NOP filler due to // deleteFallThruJmpInsn. @@ -201,11 +204,17 @@ public: // This vector contains such "cooked" relocations. SmallVector relocations; - // These are modifiers to jump instructions that are necessary when basic - // block sections are enabled. Basic block sections creates opportunities to - // relax jump instructions at basic block boundaries after reordering the - // basic blocks. - JumpInstrMod *jumpInstrMod = nullptr; + union { + // These are modifiers to jump instructions that are necessary when basic + // block sections are enabled. Basic block sections creates opportunities + // to relax jump instructions at basic block boundaries after reordering the + // basic blocks. + JumpInstrMod *jumpInstrMod = nullptr; + + // Auxiliary information for RISC-V linker relaxation. RISC-V does not use + // jumpInstrMod. + RISCVRelaxAux *relaxAux; + }; // A function compiled with -fsplit-stack calling a function // compiled without -fsplit-stack needs its prologue adjusted. Find diff --git a/lld/ELF/Relocations.cpp b/lld/ELF/Relocations.cpp index 64381ae..e54e1eb 100644 --- a/lld/ELF/Relocations.cpp +++ b/lld/ELF/Relocations.cpp @@ -958,8 +958,8 @@ bool RelocationScanner::isStaticLinkTimeConstant(RelExpr e, RelType type, const Symbol &sym, uint64_t relOff) const { // These expressions always compute a constant - if (oneof(e)) @@ -2118,7 +2118,9 @@ bool ThunkCreator::normalizeExistingThunk(Relocation &rel, uint64_t src) { // made no changes. If the target requires range extension thunks, currently // ARM, then any future change in offset between caller and callee risks a // relocation out of range error. -bool ThunkCreator::createThunks(ArrayRef outputSections) { +bool ThunkCreator::createThunks(uint32_t pass, + ArrayRef outputSections) { + this->pass = pass; bool addressesChanged = false; if (pass == 0 && target->getThunkSectionSpacing()) @@ -2180,7 +2182,6 @@ bool ThunkCreator::createThunks(ArrayRef outputSections) { // Merge all created synthetic ThunkSections back into OutputSection mergeThunks(outputSections); - ++pass; return addressesChanged; } diff --git a/lld/ELF/Relocations.h b/lld/ELF/Relocations.h index da0f228..f70d255 100644 --- a/lld/ELF/Relocations.h +++ b/lld/ELF/Relocations.h @@ -46,6 +46,7 @@ enum RelExpr { R_PLT, R_PLT_PC, R_PLT_GOTPLT, + R_RELAX_HINT, R_RELAX_GOT_PC, R_RELAX_GOT_PC_NOPIC, R_RELAX_TLS_GD_TO_IE, @@ -139,12 +140,7 @@ class InputSectionDescription; class ThunkCreator { public: // Return true if Thunks have been added to OutputSections - bool createThunks(ArrayRef outputSections); - - // The number of completed passes of createThunks this permits us - // to do one time initialization on Pass 0 and put a limit on the - // number of times it can be called to prevent infinite loops. - uint32_t pass = 0; + bool createThunks(uint32_t pass, ArrayRef outputSections); private: void mergeThunks(ArrayRef outputSections); @@ -186,6 +182,11 @@ private: // so we need to make sure that there is only one of them. // The Mips LA25 Thunk is an example of an inline ThunkSection. llvm::DenseMap thunkedSections; + + // The number of completed passes of createThunks this permits us + // to do one time initialization on Pass 0 and put a limit on the + // number of times it can be called to prevent infinite loops. + uint32_t pass = 0; }; // Return a int64_t to make sure we get the sign extension out of the way as diff --git a/lld/ELF/Target.h b/lld/ELF/Target.h index 5e9bbd6..14b1f53 100644 --- a/lld/ELF/Target.h +++ b/lld/ELF/Target.h @@ -89,6 +89,9 @@ public: relocate(loc, Relocation{R_NONE, type, 0, 0, nullptr}, val); } + // Do a linker relaxation pass and return true if we changed something. + virtual bool relaxOnce(int pass) const { return false; } + virtual void applyJumpInstrMod(uint8_t *loc, JumpModType type, JumpModType val) const {} @@ -221,6 +224,7 @@ void writePrefixedInstruction(uint8_t *loc, uint64_t insn); void addPPC64SaveRestore(); uint64_t getPPC64TocBase(); uint64_t getAArch64Page(uint64_t expr); +void riscvFinalizeRelax(int passes); class AArch64Relaxer { bool safeToRelaxAdrpLdr = true; diff --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp index 8ac6fc7..a398a7f 100644 --- a/lld/ELF/Writer.cpp +++ b/lld/ELF/Writer.cpp @@ -1630,14 +1630,17 @@ template void Writer::finalizeAddressDependentContent() { if (config->emachine == EM_HEXAGON) hexagonTLSSymbolUpdate(outputSections); - int assignPasses = 0; + uint32_t pass = 0, assignPasses = 0; for (;;) { - bool changed = target->needsThunks && tc.createThunks(outputSections); + bool changed = target->needsThunks ? tc.createThunks(pass, outputSections) + : target->relaxOnce(pass); + ++pass; // With Thunk Size much smaller than branch range we expect to // converge quickly; if we get to 15 something has gone wrong. - if (changed && tc.pass >= 15) { - error("thunk creation not converged"); + if (changed && pass >= 15) { + error(target->needsThunks ? "thunk creation not converged" + : "relaxation not converged"); break; } @@ -1675,6 +1678,8 @@ template void Writer::finalizeAddressDependentContent() { } } } + if (!config->relocatable && config->emachine == EM_RISCV) + riscvFinalizeRelax(pass); if (config->relocatable) for (OutputSection *sec : outputSections) diff --git a/lld/test/ELF/riscv-relax-align-rvc.s b/lld/test/ELF/riscv-relax-align-rvc.s new file mode 100644 index 0000000..37758ed --- /dev/null +++ b/lld/test/ELF/riscv-relax-align-rvc.s @@ -0,0 +1,75 @@ +# REQUIRES: riscv + +# RUN: rm -rf %t && mkdir %t && cd %t + +# RUN: llvm-mc -filetype=obj -triple=riscv32 -mattr=+c,+relax %s -o 32.o +# RUN: ld.lld -Ttext=0x10000 32.o -o 32 +# RUN: llvm-objdump -td --no-show-raw-insn -M no-aliases 32 | FileCheck %s +## R_RISCV_ALIGN is handled regarldess of --no-relax. +# RUN: ld.lld -Ttext=0x10000 --no-relax 32.o -o 32.norelax +# RUN: llvm-objdump -td --no-show-raw-insn -M no-aliases 32.norelax | FileCheck %s + +# RUN: llvm-mc -filetype=obj -triple=riscv64 -mattr=+c,+relax %s -o 64.o +# RUN: ld.lld -Ttext=0x10000 64.o -o 64 +# RUN: llvm-objdump -td --no-show-raw-insn -M no-aliases 64 | FileCheck %s +# RUN: ld.lld -Ttext=0x10000 --no-relax 64.o -o 64.norelax +# RUN: llvm-objdump -td --no-show-raw-insn -M no-aliases 64.norelax | FileCheck %s + +# CHECK-DAG: 00010002 l .text {{0*}}1e a +# CHECK-DAG: 00010010 l .text {{0*}}22 b +# CHECK-DAG: 00010012 l .text {{0*}}1e c +# CHECK-DAG: 00010020 l .text {{0*}}16 d +# CHECK-DAG: 00010000 g .text {{0*}}36 _start + +# CHECK: <_start>: +# CHECK-NEXT: c.addi a0, 1 +# CHECK-EMPTY: +# CHECK-NEXT: : +# CHECK-NEXT: addi zero, zero, 0 +# CHECK-NEXT: addi zero, zero, 0 +# CHECK-NEXT: addi zero, zero, 0 +# CHECK-NEXT: c.nop +# CHECK-EMPTY: +# CHECK-NEXT: : +# CHECK-NEXT: 10010: c.addi a0, 2 +# CHECK-EMPTY: +# CHECK-NEXT: : +# CHECK-NEXT: c.addi a0, 3 +# CHECK-NEXT: addi zero, zero, 0 +# CHECK-NEXT: addi zero, zero, 0 +# CHECK-NEXT: addi zero, zero, 0 +# CHECK-EMPTY: +# CHECK-NEXT: : +# CHECK-NEXT: 10020: c.addi a0, 4 +# CHECK-NEXT: c.addi a0, 5 +# CHECK-NEXT: addi zero, zero, 0 +# CHECK-NEXT: addi zero, zero, 0 +# CHECK-NEXT: addi zero, zero, 0 +# CHECK-NEXT: 10030: c.addi a0, 6 +# CHECK-NEXT: c.addi a0, 7 +# CHECK-NEXT: c.addi a0, 8 +# CHECK-EMPTY: + +.global _start +_start: + c.addi a0, 1 +a: +.balign 16 +b: + c.addi a0, 2 +c: + c.addi a0, 3 +.balign 32 +.size a, . - a +d: + c.addi a0, 4 + c.addi a0, 5 +.balign 16 +.size c, . - c + c.addi a0, 6 +.size b, . - b + c.addi a0, 7 +.balign 4 + c.addi a0, 8 +.size d, . - d +.size _start, . - _start diff --git a/lld/test/ELF/riscv-relax-align.s b/lld/test/ELF/riscv-relax-align.s new file mode 100644 index 0000000..8a46d60 --- /dev/null +++ b/lld/test/ELF/riscv-relax-align.s @@ -0,0 +1,161 @@ +# REQUIRES: riscv +## Test that we can handle R_RISCV_ALIGN. + +# RUN: rm -rf %t && mkdir %t && cd %t + +# RUN: llvm-mc -filetype=obj -triple=riscv32 -mattr=+relax %s -o 32.o +# RUN: ld.lld -Ttext=0x10000 32.o -o 32 +# RUN: llvm-objdump -td --no-show-raw-insn -M no-aliases 32 | FileCheck %s +## R_RISCV_ALIGN is handled regarldess of --no-relax. +# RUN: ld.lld -Ttext=0x10000 --no-relax 32.o -o 32.norelax +# RUN: llvm-objdump -td --no-show-raw-insn -M no-aliases 32.norelax | FileCheck %s + +# RUN: llvm-mc -filetype=obj -triple=riscv64 -mattr=+relax %s -o 64.o +# RUN: ld.lld -Ttext=0x10000 64.o -o 64 +# RUN: llvm-objdump -td --no-show-raw-insn -M no-aliases 64 | FileCheck %s +# RUN: ld.lld -Ttext=0x10000 --no-relax 64.o -o 64.norelax +# RUN: llvm-objdump -td --no-show-raw-insn -M no-aliases 64.norelax | FileCheck %s + +# RUN: ld.lld -Ttext=0x10000 --gc-sections 64.o -o 64.gc +# RUN: llvm-objdump -td --no-show-raw-insn -M no-aliases 64.gc | FileCheck %s --check-prefix=GC + +## -r keeps section contents unchanged. +# RUN: ld.lld -r 64.o -o 64.r +# RUN: llvm-objdump -dr --no-show-raw-insn -M no-aliases 64.r | FileCheck %s --check-prefix=CHECKR + +# CHECK-DAG: 00010004 l .text {{0*}}1c a +# CHECK-DAG: 00010008 l .text {{0*}}28 b +# CHECK-DAG: 00010014 l .text {{0*}}20 c +# CHECK-DAG: 00010000 g .text {{0*}}38 _start + +# CHECK: <_start>: +# CHECK-NEXT: addi a0, a0, 1 +# CHECK-EMPTY: +# CHECK-NEXT: : +# CHECK-NEXT: addi a0, a0, 2 +# CHECK-EMPTY: +# CHECK-NEXT: : +# CHECK-NEXT: addi zero, zero, 0 +# CHECK-NEXT: addi zero, zero, 0 +# CHECK-NEXT: 10010: addi a0, a0, 3 +# CHECK-EMPTY: +# CHECK-NEXT: : +# CHECK-NEXT: addi a0, a0, 4 +# CHECK-NEXT: addi a0, a0, 5 +# CHECK-NEXT: addi zero, zero, 0 +# CHECK-NEXT: 10020: addi a0, a0, 6 +# CHECK-NEXT: addi a0, a0, 7 +# CHECK-NEXT: addi zero, zero, 0 +# CHECK-NEXT: addi zero, zero, 0 +# CHECK-NEXT: 10030: addi a0, a0, 8 +# CHECK-NEXT: addi a0, a0, 9 +# CHECK-EMPTY: +# CHECK: : +# CHECK-NEXT: addi a0, a0, 1 +# CHECK-EMPTY: +# CHECK-NEXT: : +# CHECK-NEXT: 10044: addi a0, a0, 2 +# CHECK-NEXT: addi zero, zero, 0 +# CHECK-NEXT: addi zero, zero, 0 +# CHECK-NEXT: addi zero, zero, 0 +# CHECK-NEXT: addi zero, zero, 0 +# CHECK-NEXT: addi zero, zero, 0 +# CHECK-NEXT: addi zero, zero, 0 +# CHECK-NEXT: 10060: addi a0, a0, 3 +# CHECK-EMPTY: + +## _start-0x10070 = 0x10000-0x10070 = -112 +# CHECK: <.L1>: +# CHECK-NEXT: 10070: auipc a0, 0 +# CHECK-NEXT: addi a0, a0, -112 +# CHECK-NEXT: addi zero, zero, 0 +# CHECK-NEXT: addi zero, zero, 0 +# CHECK-NEXT: auipc a0, 0 +# CHECK-NEXT: addi a0, a0, -112 +# CHECK-EMPTY: + +# GC-DAG: 00010004 l .text {{0*}}1c a +# GC-DAG: 00010008 l .text {{0*}}28 b +# GC-DAG: 00010014 l .text {{0*}}20 c +# GC-DAG: 00010000 g .text {{0*}}38 _start +# GC: <_start>: +# GC-NOT: : + +# CHECKR: <_start>: +# CHECKR-NEXT: addi a0, a0, 1 +# CHECKR-EMPTY: +# CHECKR-NEXT: : +# CHECKR-NEXT: addi a0, a0, 2 +# CHECKR-EMPTY: +# CHECKR-NEXT: : +# CHECKR-NEXT: addi zero, zero, 0 +# CHECKR-NEXT: 0000000000000008: R_RISCV_ALIGN *ABS*+0xc +# CHECKR-NEXT: addi zero, zero, 0 +# CHECKR-NEXT: addi zero, zero, 0 +# CHECKR-NEXT: addi a0, a0, 3 +# CHECKR-EMPTY: +# CHECKR-NEXT: : +# CHECKR-NEXT: addi a0, a0, 4 +# CHECKR-NEXT: addi a0, a0, 5 +# CHECKR-NEXT: addi zero, zero, 0 +# CHECKR-NEXT: 0000000000000020: R_RISCV_ALIGN *ABS*+0x1c +# CHECKR-NEXT: addi zero, zero, 0 +# CHECKR-NEXT: addi zero, zero, 0 +# CHECKR-NEXT: addi zero, zero, 0 +# CHECKR-NEXT: addi zero, zero, 0 +# CHECKR-NEXT: addi zero, zero, 0 +# CHECKR-NEXT: addi zero, zero, 0 +# CHECKR-NEXT: addi a0, a0, 6 +# CHECKR-NEXT: addi a0, a0, 7 +# CHECKR-NEXT: addi zero, zero, 0 +# CHECKR-NEXT: 0000000000000044: R_RISCV_ALIGN *ABS*+0xc +# CHECKR-NEXT: addi zero, zero, 0 +# CHECKR-NEXT: addi zero, zero, 0 +# CHECKR-NEXT: addi a0, a0, 8 +# CHECKR-NEXT: addi a0, a0, 9 + +.global _start +_start: + addi a0, a0, 1 +a: + addi a0, a0, 2 +b: +.balign 16 + addi a0, a0, 3 +c: + addi a0, a0, 4 + addi a0, a0, 5 +.balign 32 +.size a, . - a + addi a0, a0, 6 + addi a0, a0, 7 +.balign 16 +.size b, . - b + addi a0, a0, 8 +.size c, . - c + addi a0, a0, 9 +.size _start, . - _start + +## Test another text section. +.section .text2,"ax",@progbits +d: +e: +.balign 8 + addi a0, a0, 1 +f: + addi a0, a0, 2 +.balign 32 +.size d, . - d + addi a0, a0, 3 +.size e, . - e +.size f, . - f + +## Test that matching HI20 can be found despite deleted bytes. +.section .pcrel,"ax",@progbits +.L1: + auipc a0, %pcrel_hi(_start) + addi a0, a0, %pcrel_lo(.L1) +.balign 16 +.L2: + auipc a0, %pcrel_hi(_start) + addi a0, a0, %pcrel_lo(.L1) diff --git a/lld/test/ELF/riscv-reloc-align.s b/lld/test/ELF/riscv-reloc-align.s deleted file mode 100644 index 5103066..0000000 --- a/lld/test/ELF/riscv-reloc-align.s +++ /dev/null @@ -1,12 +0,0 @@ -# REQUIRES: riscv - -# RUN: llvm-mc -filetype=obj -triple=riscv32 -mattr=+relax %s -o %t.o -# RUN: not ld.lld %t.o -o /dev/null 2>&1 | FileCheck %s - -# CHECK: relocation R_RISCV_ALIGN requires unimplemented linker relaxation - -.global _start -_start: - nop - .balign 8 - nop -- 2.7.4