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 @@ -25,6 +25,7 @@ public: RISCV(); uint32_t calcEFlags() const override; + bool inBranchRange(RelType type, uint64_t src, uint64_t dst) const override; void writeGotHeader(uint8_t *buf) const override; void writeGotPlt(uint8_t *buf, const Symbol &s) const override; void writePltHeader(uint8_t *buf) const override; @@ -35,6 +36,14 @@ const uint8_t *loc) const override; void relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const override; + void relax(InputSection &inputSection, Relocation &rel, + std::vector> + &bytesToDelete) const override; + void relaxSection(InputSection &inputSection) const override; + void + relaxRelocationCall(InputSection &inputSection, Relocation &rel, + std::vector> + &bytesToDelete) const; }; } // end anonymous namespace @@ -44,6 +53,9 @@ enum Op { ADDI = 0x13, AUIPC = 0x17, + CJ = 0xa001, + CJAL = 0x2001, + JAL = 0x6F, JALR = 0x67, LD = 0x3003, LW = 0x2003, @@ -52,6 +64,7 @@ }; enum Reg { + X_ZERO = 0, X_RA = 1, X_T0 = 5, X_T1 = 6, @@ -62,9 +75,34 @@ static uint32_t hi20(uint32_t val) { return (val + 0x800) >> 12; } static uint32_t lo12(uint32_t val) { return val & 4095; } +// Extract bits V[Begin:End], where range is inclusive, and Begin must be < 63. +static uint32_t extractBits(uint64_t v, uint32_t begin, uint32_t end) { + return (v & ((1ULL << (begin + 1)) - 1)) >> end; +} + +static uint32_t encodeImmJType(const int32_t input) { + return (extractBits(input, 20, 20) << 31) | + (extractBits(input, 10, 1) << 21) | + (extractBits(input, 11, 11) << 20) | + (extractBits(input, 19, 12) << 12); +} + +static uint16_t encodeImmCJType(const int16_t input) { + return (extractBits(input, 11, 11) << 12) | (extractBits(input, 4, 4) << 11) | + (extractBits(input, 9, 8) << 9) | (extractBits(input, 10, 10) << 8) | + (extractBits(input, 6, 6) << 7) | (extractBits(input, 7, 7) << 6) | + (extractBits(input, 3, 1) << 3) | (extractBits(input, 5, 5) << 2); +} + +static uint16_t cjtype(uint16_t op, uint16_t offset) { + return op | encodeImmCJType(offset); +} static uint32_t itype(uint32_t op, uint32_t rd, uint32_t rs1, uint32_t imm) { return op | (rd << 7) | (rs1 << 15) | (imm << 20); } +static uint32_t jtype(uint8_t op, uint8_t rd, uint32_t imm) { + return op | (rd << 7) | encodeImmJType(imm); +} static uint32_t rtype(uint32_t op, uint32_t rd, uint32_t rs1, uint32_t rs2) { return op | (rd << 7) | (rs1 << 15) | (rs2 << 20); } @@ -238,6 +276,7 @@ case R_RISCV_TPREL_LO12_S: return R_TLS; case R_RISCV_RELAX: + return R_ABS; case R_RISCV_TPREL_ADD: return R_NONE; case R_RISCV_ALIGN: @@ -254,9 +293,35 @@ } } -// Extract bits V[Begin:End], where range is inclusive, and Begin must be < 63. -static uint32_t extractBits(uint64_t v, uint32_t begin, uint32_t end) { - return (v & ((1ULL << (begin + 1)) - 1)) >> end; +void RISCV::relaxSection(InputSection &inputSection) const { + std::vector> bytesToDelete; + const auto end = std::end(inputSection.relocations); + for (auto it = std::begin(inputSection.relocations); it != end; ++it) { + // Check if each relocation is paired with an R_RISCV_RELAX before + // relaxation. + if (it + 1 == end || (it + 1)->type != R_RISCV_RELAX || + it->offset != (it + 1)->offset) + continue; + + relax(inputSection, *it, bytesToDelete); + + // Skip over the R_RISCV_RELAX. + ++it; + } + inputSection.deleteBytes(bytesToDelete); +} + +void RISCV::relax(InputSection &inputSection, Relocation &rel, + std::vector> + &bytesToDelete) const { + switch (rel.type) { + // auipc + jalr pair + case R_RISCV_CALL: + case R_RISCV_CALL_PLT: { + relaxRelocationCall(inputSection, rel, bytesToDelete); + return; + } + } } void RISCV::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const { @@ -288,18 +353,8 @@ case R_RISCV_RVC_JUMP: { checkInt(loc, static_cast(val) >> 1, 11, rel); checkAlignment(loc, val, 2, rel); - uint16_t insn = read16le(loc) & 0xE003; - uint16_t imm11 = extractBits(val, 11, 11) << 12; - uint16_t imm4 = extractBits(val, 4, 4) << 11; - uint16_t imm9_8 = extractBits(val, 9, 8) << 9; - uint16_t imm10 = extractBits(val, 10, 10) << 8; - uint16_t imm6 = extractBits(val, 6, 6) << 7; - uint16_t imm7 = extractBits(val, 7, 7) << 6; - uint16_t imm3_1 = extractBits(val, 3, 1) << 3; - uint16_t imm5 = extractBits(val, 5, 5) << 2; - insn |= imm11 | imm4 | imm9_8 | imm10 | imm6 | imm7 | imm3_1 | imm5; - write16le(loc, insn); + write16le(loc, cjtype(read16le(loc) & 0xE003, val)); return; } @@ -320,14 +375,11 @@ checkInt(loc, static_cast(val) >> 1, 20, rel); checkAlignment(loc, val, 2, rel); - uint32_t insn = read32le(loc) & 0xFFF; - uint32_t imm20 = extractBits(val, 20, 20) << 31; - uint32_t imm10_1 = extractBits(val, 10, 1) << 21; - uint32_t imm11 = extractBits(val, 11, 11) << 20; - uint32_t imm19_12 = extractBits(val, 19, 12) << 12; - insn |= imm20 | imm10_1 | imm11 | imm19_12; + const auto insn = read32le(loc); + const uint8_t opcode = extractBits(insn, 6, 0); + const uint8_t reg = extractBits(insn, 11, 7); - write32le(loc, insn); + write32le(loc, jtype(opcode, reg, val)); return; } @@ -451,5 +503,86 @@ return ⌖ } +enum RISCVImmediateSize { IType = 12, JType = 21, UType = 21, CJType = 11 }; + +bool RISCV::inBranchRange(RelType type, uint64_t src, uint64_t dst) const { + // Compute the number of bits that are available to store the branch range + const uint8_t range = [&type]() -> uint8_t { + switch (type) { + case R_RISCV_RVC_JUMP: + return RISCVImmediateSize::CJType; + case R_RISCV_JAL: + return RISCVImmediateSize::JType; + default: + llvm_unreachable( + "unknown relocation type, cannot calculate inBranchBrange"); + } + }(); + + // Convert the range from number of bits it can be stored in to an explicit + // maximum distance away in bits + const uint64_t distance = 1 << range; + + bool withinUpperRange = true; + bool withinLowerRange = true; + + // If src + distance doesn't cause an integer overflow then check if + // it's within the upper range. + if (distance < std::numeric_limits::max() - src) + withinUpperRange = dst <= src + distance; + + // If src - distance doesn't cause an integer underflow then check if + // it's within the lower range. + if (src >= distance) + withinLowerRange = dst >= src - distance; + + return withinUpperRange && withinLowerRange; +} + +void RISCV::relaxRelocationCall( + InputSection &inputSection, Relocation &rel, + std::vector> &bytesToDelete) + const { + // The address to be jumped to. + const uint64_t targetLoc = rel.sym->getVA(); + // The address we are jumping from. + const uint64_t loc = inputSection.getVA() + rel.offset; + + const auto jalr = inputSection.data()[rel.offset + 4]; + const uint8_t rd = extractBits(jalr, 11, 7); + + // C.JAL is RV32 only but C.J is not. + const bool RVC_enabled = (config->eflags & EF_RISCV_RVC) && + (rd == 0 || (!config->is64 && rd == X_RA)); + + // Convert to C.J or C.JAL. + if (RVC_enabled && inBranchRange(R_RISCV_RVC_JUMP, loc, targetLoc)) { + const uint16_t compressedInsn = rd == 0 ? CJ : CJAL; + + // Write over the auipc instruction + inputSection.edit(rel.offset, cjtype(compressedInsn, 0)); + bytesToDelete.emplace_back(rel.offset + 2, 6); + rel.type = R_RISCV_RVC_JUMP; + + // Convert to JAL. + } else if (inBranchRange(R_RISCV_JAL, loc, targetLoc)) { + // Write over the auipc instruction + inputSection.edit(rel.offset, jtype(JAL, X_RA, 0)); + bytesToDelete.emplace_back(rel.offset + 4, 4); + rel.type = R_RISCV_JAL; + + // Convert to x0 offset JALR. + // As this is zero offset, the exact address goes into + // the immediate, meaning as long as it fits we don't + // need to check if inBranchRange. + } else if (isInt(targetLoc)) { + // Write over the auipc instruction + inputSection.edit(rel.offset, itype(JALR, X_RA, X_ZERO, 0)); + bytesToDelete.emplace_back(rel.offset + 4, 4); + rel.type = R_RISCV_LO12_I; + rel.expr = R_ABS; // This is x0 offset, no longer PC relative. + } + return; +} } // namespace elf } // namespace lld diff --git a/lld/ELF/Config.h b/lld/ELF/Config.h --- a/lld/ELF/Config.h +++ b/lld/ELF/Config.h @@ -186,6 +186,7 @@ bool pie; bool printGcSections; bool printIcfSections; + bool relax; bool relocatable; bool relrPackDynRelocs; bool saveTemps; diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp --- a/lld/ELF/Driver.cpp +++ b/lld/ELF/Driver.cpp @@ -960,6 +960,7 @@ config->printSymbolOrder = args.getLastArgValue(OPT_print_symbol_order); config->rpath = getRpath(args); + config->relax = args.hasArg(OPT_relax); config->relocatable = args.hasArg(OPT_relocatable); config->saveTemps = args.hasArg(OPT_save_temps); if (args.hasArg(OPT_shuffle_sections)) diff --git a/lld/ELF/InputSection.h b/lld/ELF/InputSection.h --- a/lld/ELF/InputSection.h +++ b/lld/ELF/InputSection.h @@ -154,6 +154,13 @@ return rawData; } + MutableArrayRef mutableData() const { + if (!copiedData) + makeMutableDataCopy(); + return llvm::makeMutableArrayRef(const_cast(rawData.data()), + rawData.size()); + } + uint64_t getOffsetInFile() const; // Input sections are part of an output section. Special sections @@ -186,6 +193,19 @@ // Returns the size of this section (even if this is a common or BSS.) size_t getSize() const; + // Edits the rawData, by writing newData over the existing data, at the + // position given by loc. + template void edit(uint64_t loc, T newData) const { + if (loc + sizeof(T) > getSize()) + llvm_unreachable("Cannot edit - out of range\n"); + auto mutData = mutableData(); + memcpy(&mutData[loc], &newData, sizeof(T)); + } + + void + deleteBytes(const std::vector> + &ranges); + InputSection *getLinkOrderDep() const; // Get the function symbol that encloses this offset from within the @@ -248,6 +268,13 @@ // compressed in the first place, or because we ended up uncompressing it). // Since the feature is not used often, this is usually -1. mutable int64_t uncompressedSize = -1; + + void makeMutableDataCopy() const; + + // This field stores whether we have made a mutable copy of the data, either + // because we have uncompressed it or because during relaxation we have had + // to rewrite the contents. + mutable bool copiedData = false; }; // SectionPiece represents a piece of splittable section contents. diff --git a/lld/ELF/InputSection.cpp b/lld/ELF/InputSection.cpp --- a/lld/ELF/InputSection.cpp +++ b/lld/ELF/InputSection.cpp @@ -142,6 +142,75 @@ return rawData.size() - bytesDropped; } +void InputSectionBase::deleteBytes( + const std::vector> &ranges) { + uint64_t totalBytesDeleted = 0; + const auto mutData = mutableData(); + uint8_t *buf = mutData.data(); + const auto size = getSize(); + // for (const auto &range : ranges) { + const auto end = std::end(ranges); + for (auto it = std::begin(ranges); it != end; ++it) { + const auto loc = it->first; + const auto numberOfBytes = it->second; + + // Don't try and delete bytes that are out of bounds. + if (loc + numberOfBytes > getSize()) + llvm_unreachable(loc + " Cannot delete - out of range\n"); + + // Remove the bytes from rawData. + const auto destinationAddress = buf + loc - totalBytesDeleted; + const auto sourceAddress = buf + loc + numberOfBytes; + const auto nextOffset = it + 1 == end ? size : (it + 1)->first; + memmove(destinationAddress, sourceAddress, + nextOffset - loc - numberOfBytes); + totalBytesDeleted += numberOfBytes; + } + rawData = makeArrayRef(buf, getSize() - totalBytesDeleted); + + // Adjust all relocations in this section. + for (Relocation &relocation : relocations) { + uint64_t distanceToMove = 0; + // Count up the total distance to shift each relocation before shifting. + for (const auto &range : ranges) { + if (range.first < relocation.offset) + distanceToMove += range.second; + } + relocation.offset -= distanceToMove; + } + + // Adjust symbols in this section. + // for (const auto &symbol : file->getSymbols()) { + for (const auto &symbol : in.symTab->getSymbols()) { + auto *definedSymbol = dyn_cast(symbol.sym); + if (!definedSymbol || definedSymbol->section != this) + continue; + + const auto symbolStart = definedSymbol->value; + const auto symbolEnd = symbolStart + definedSymbol->size; + + for (const auto &range : ranges) { + const auto loc = range.first; + const auto numberOfBytes = range.second; + if (symbolStart <= loc && loc < symbolEnd) + definedSymbol->size -= numberOfBytes; + + if (symbolStart >= loc + numberOfBytes) + definedSymbol->value -= numberOfBytes; + } + } + + auto outputSection = getOutputSection(); + + // Close the gaps between sections by adjusting their addresses. + for (const auto otherSection : outputSections) { + if (otherSection->addr > outputSection->addr) + otherSection->addr -= totalBytesDeleted; + } + + outputSection->size -= totalBytesDeleted; +} + void InputSectionBase::uncompress() const { size_t size = uncompressedSize; char *uncompressedBuf; @@ -1210,6 +1279,21 @@ other->markDead(); } +void InputSectionBase::makeMutableDataCopy() const { + static std::mutex mu; + std::lock_guard lock(mu); + + ArrayRef oldRef = data(); + // In case the above just uncompressed + if (copiedData) + return; + + size_t size = oldRef.size(); + uint8_t *newData = bAlloc.Allocate(size); + memcpy(newData, oldRef.data(), size); + rawData = makeArrayRef(newData, size); +} + template EhInputSection::EhInputSection(ObjFile &f, const typename ELFT::Shdr &header, diff --git a/lld/ELF/Options.td b/lld/ELF/Options.td --- a/lld/ELF/Options.td +++ b/lld/ELF/Options.td @@ -318,6 +318,8 @@ defm rpath: Eq<"rpath", "Add a DT_RUNPATH to the output">; +def relax: F<"relax">, HelpText<"Enable linker relaxation">; + def relocatable: F<"relocatable">, HelpText<"Create relocatable object file">; defm retain_symbols_file: diff --git a/lld/ELF/Target.h b/lld/ELF/Target.h --- a/lld/ELF/Target.h +++ b/lld/ELF/Target.h @@ -91,6 +91,17 @@ virtual void applyJumpInstrMod(uint8_t *loc, JumpModType type, JumpModType val) const {} + virtual void relaxSection(InputSection &inputSection) const { + return; + }; + + virtual void + relax(InputSection &inputSection, Relocation &rel, + std::vector> + &bytesToDelete) const { + return; + }; + virtual ~TargetInfo(); // This deletes a jump insn at the end of the section if it is a fall thru to diff --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp --- a/lld/ELF/Writer.cpp +++ b/lld/ELF/Writer.cpp @@ -72,6 +72,7 @@ void assignFileOffsetsBinary(); void setPhdrs(Partition &part); void checkSections(); + void relaxSections(); void fixSectionAlignments(); void openFile(); void writeTrapInstr(); @@ -598,6 +599,9 @@ for (Partition &part : partitions) removeEmptyPTLoad(part.phdrs); + if (config->relax) + relaxSections(); + if (!config->oFormatBinary) assignFileOffsets(); else @@ -2721,6 +2725,12 @@ checkOverlap("load address", lmas, false); } +template void Writer::relaxSections() { + for (InputSectionBase *inputSection : inputSections) { + target->relaxSection(cast(*inputSection)); + } +} + // The entry point address is chosen in the following ways. // // 1. the '-e' entry command-line option; diff --git a/lld/test/ELF/riscv-relax-call-2.s b/lld/test/ELF/riscv-relax-call-2.s new file mode 100644 --- /dev/null +++ b/lld/test/ELF/riscv-relax-call-2.s @@ -0,0 +1,71 @@ +# REQUIRES: riscv + +# RUN: llvm-mc -filetype=obj -triple=riscv32-unknown-elf -mattr=+relax %s -o %t.rv32.o +# RUN: llvm-mc -filetype=obj -triple=riscv64-unknown-elf -mattr=+relax %s -o %t.rv64.o + +# RUN: ld.lld %t.rv32.o --defsym foo=8 -relax -o %t.rv32 +# RUN: ld.lld %t.rv64.o --defsym foo=8 -relax -o %t.rv64 + +# RUN: llvm-objdump -d %t.rv32 | FileCheck --check-prefix=32 %s +# RUN: llvm-objdump -d %t.rv64 | FileCheck --check-prefix=64 %s +# RUN: llvm-readelf --symbols %t.rv32 | FileCheck --check-prefix=Symbols32 %s +# RUN: llvm-readelf --symbols %t.rv64 | FileCheck --check-prefix=Symbols64 %s +# RUN: llvm-readelf --sections %t.rv32 | FileCheck --check-prefix=Sections32 %s +# RUN: llvm-readelf --sections %t.rv64 | FileCheck --check-prefix=Sections64 %s +# RUN: llvm-readelf --segments %t.rv32 | FileCheck --check-prefix=Segments32 %s +# RUN: llvm-readelf --segments %t.rv64 | FileCheck --check-prefix=Segments64 %s + +# 32: 110b4: ef e0 5e f5 jal -69804 +# 32: 110b8: ef e0 1e f5 jal -69808 +# 32: 110bc: ef e0 de f4 jal -69812 + +# 64: 11120: ef e0 9e ee jal -69912 +# 64: 11124: ef e0 5e ee jal -69916 +# 64: 11128: ef e0 1e ee jal -69920 + +# Check the symbols have been adjusted correctly + +# Symbols32: Num: Value Size Type Bind Vis Ndx Name +# Symbols32: 1: 000110b4 12 NOTYPE GLOBAL DEFAULT 1 _start +# Symbols32: 2: 00000008 0 NOTYPE GLOBAL DEFAULT ABS foo + +# Symbols64: Num: Value Size Type Bind Vis Ndx Name +# Symbols64: 1: 0000000000011120 12 NOTYPE GLOBAL DEFAULT 1 _start +# Symbols64: 2: 0000000000000008 0 NOTYPE GLOBAL DEFAULT ABS foo + +# Check the sections have been adjusted correctly + +# Sections32: Section Headers: +# Sections32: [Nr] Name Type Address Off Size ES Flg Lk Inf Al +# Sections32: [ 0] NULL 00000000 000000 000000 00 0 0 0 +# Sections32: [ 1] .text PROGBITS 000110b4 0000b4 00000c 00 AX 0 0 4 +# Sections32: [ 2] .comment PROGBITS 00000000 0000c0 000008 01 MS 0 0 1 +# Sections32: [ 3] .symtab SYMTAB 00000000 0000c8 000030 10 5 1 4 +# Sections32: [ 4] .shstrtab STRTAB 00000000 0000f8 00002a 00 0 0 1 +# Sections32: [ 5] .strtab STRTAB 00000000 000122 00000c 00 0 0 1 + +#Sections64: Section Headers: +#Sections64: [Nr] Name Type Address Off Size ES Flg Lk Inf Al +#Sections64: [ 0] NULL 0000000000000000 000000 000000 00 0 0 0 +#Sections64: [ 1] .text PROGBITS 0000000000011120 000120 00000c 00 AX 0 0 4 +#Sections64: [ 2] .comment PROGBITS 0000000000000000 00012c 000008 01 MS 0 0 1 +#Sections64: [ 3] .symtab SYMTAB 0000000000000000 000138 000048 18 5 1 8 +#Sections64: [ 4] .shstrtab STRTAB 0000000000000000 000180 00002a 00 0 0 1 +#Sections64: [ 5] .strtab STRTAB 0000000000000000 0001aa 00000c 00 0 0 1 + +# Check the segments have been adjusted correctly + +# Segments32: Program Headers: +# Segments32: Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align +# Segments32: LOAD 0x0000b4 0x000110b4 0x000110b4 0x0000c 0x0000c R E 0x1000 + +# Segments64: Program Headers: +# Segments64: Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align +# Segments64: LOAD 0x000120 0x0000000000011120 0x0000000000011120 0x00000c 0x00000c R E 0x1000 + +.global _start +_start: + call foo + call foo + call foo + .size _start, . - _start diff --git a/lld/test/ELF/riscv-relax-call-3.s b/lld/test/ELF/riscv-relax-call-3.s new file mode 100644 --- /dev/null +++ b/lld/test/ELF/riscv-relax-call-3.s @@ -0,0 +1,93 @@ +# REQUIRES: riscv + +# RUN: llvm-mc -filetype=obj -triple=riscv32-unknown-elf -mattr=+relax %s -o %t.rv32.o +# RUN: llvm-mc -filetype=obj -triple=riscv64-unknown-elf -mattr=+relax %s -o %t.rv64.o + +# RUN: ld.lld %t.rv32.o -relax -o %t.rv32 +# RUN: ld.lld %t.rv64.o -relax -o %t.rv64 + +# RUN: llvm-objdump -d %t.rv32 | FileCheck --check-prefix=32 %s +# RUN: llvm-objdump -d %t.rv64 | FileCheck --check-prefix=64 %s +# RUN: llvm-readelf --symbols %t.rv32 | FileCheck --check-prefix=Symbols32 %s +# RUN: llvm-readelf --symbols %t.rv64 | FileCheck --check-prefix=Symbols64 %s +# RUN: llvm-readelf --sections %t.rv32 | FileCheck --check-prefix=Sections32 %s +# RUN: llvm-readelf --sections %t.rv64 | FileCheck --check-prefix=Sections64 %s +# RUN: llvm-readelf --segments %t.rv32 | FileCheck --check-prefix=Segments32 %s +# RUN: llvm-readelf --segments %t.rv64 | FileCheck --check-prefix=Segments64 %s + +# This tests jumping over another relocation that will later be relaxed. + +# 32: 000110b4 <_start>: +# 32: 110b4: ef 00 80 00 jal 8 + +# 32: 000110b8 : +# 32: 110b8: ef f0 df ff jal -4 <_start> + +# 32: 000110bc : +# 32: 110bc: ef f0 9f ff jal -8 <_start> + + +# 64: 0000000000011120 <_start>: +# 64: 11120: ef 00 80 00 jal 8 + +# 64: 0000000000011124 : +# 64: 11124: ef f0 df ff jal -4 <_start> + +# 64: 0000000000011128 : +# 64: 11128: ef f0 9f ff jal -8 <_start> + +# Check the symbols have been adjusted correctly + +# Symbols32: Num: Value Size Type Bind Vis Ndx Name +# Symbols32: 1: 000110bc 4 NOTYPE LOCAL DEFAULT 1 bar +# Symbols32: 2: 000110b8 4 NOTYPE LOCAL DEFAULT 1 foo +# Symbols32: 3: 000110b4 4 NOTYPE GLOBAL DEFAULT 1 _start + +# Symbols64: Num: Value Size Type Bind Vis Ndx Name +# Symbols64: 1: 0000000000011128 4 NOTYPE LOCAL DEFAULT 1 bar +# Symbols64: 2: 0000000000011124 4 NOTYPE LOCAL DEFAULT 1 foo +# Symbols64: 3: 0000000000011120 4 NOTYPE GLOBAL DEFAULT 1 _start + +# Check the sections have been adjusted correctly + +# Sections32: Section Headers: +# Sections32: [Nr] Name Type Address Off Size ES Flg Lk Inf Al +# Sections32: [ 0] NULL 00000000 000000 000000 00 0 0 0 +# Sections32: [ 1] .text PROGBITS 000110b4 0000b4 00000c 00 AX 0 0 4 +# Sections32: [ 2] .comment PROGBITS 00000000 0000c0 000008 01 MS 0 0 1 +# Sections32: [ 3] .symtab SYMTAB 00000000 0000c8 000040 10 5 3 4 +# Sections32: [ 4] .shstrtab STRTAB 00000000 000108 00002a 00 0 0 1 +# Sections32: [ 5] .strtab STRTAB 00000000 000132 000010 00 0 0 1 + +# Sections64: Section Headers: +# Sections64: [Nr] Name Type Address Off Size ES Flg Lk Inf Al +# Sections64: [ 0] NULL 0000000000000000 000000 000000 00 0 0 0 +# Sections64: [ 1] .text PROGBITS 0000000000011120 000120 00000c 00 AX 0 0 4 +# Sections64: [ 2] .comment PROGBITS 0000000000000000 00012c 000008 01 MS 0 0 1 +# Sections64: [ 3] .symtab SYMTAB 0000000000000000 000138 000060 18 5 3 8 +# Sections64: [ 4] .shstrtab STRTAB 0000000000000000 000198 00002a 00 0 0 1 +# Sections64: [ 5] .strtab STRTAB 0000000000000000 0001c2 000010 00 0 0 1 + +# Check the segments have been adjusted correctly + +# Segments32: Program Headers: +# Segments32: Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align +# Segments32: LOAD 0x0000b4 0x000110b4 0x000110b4 0x0000c 0x0000c R E 0x1000 + +# Segments64: Program Headers: +# Segments64: Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align +# Segments64: LOAD 0x000120 0x0000000000011120 0x0000000000011120 0x00000c 0x00000c R E 0x1000 + + +.global _start +_start: + call bar + .size _start, . - _start + +foo: + call _start + .size foo, . - foo + +bar: + call _start + .size bar, . - bar diff --git a/lld/test/ELF/riscv-relax-call-4.s b/lld/test/ELF/riscv-relax-call-4.s new file mode 100644 --- /dev/null +++ b/lld/test/ELF/riscv-relax-call-4.s @@ -0,0 +1,94 @@ +# REQUIRES: riscv + +# RUN: llvm-mc -filetype=obj -triple=riscv32-unknown-elf -mattr=+relax %s -o %t.rv32.o +# RUN: llvm-mc -filetype=obj -triple=riscv64-unknown-elf -mattr=+relax %s -o %t.rv64.o + +# RUN: ld.lld %t.rv32.o -relax -o %t.rv32 +# RUN: ld.lld %t.rv64.o -relax -o %t.rv64 + +# RUN: llvm-objdump -d %t.rv32 | FileCheck --check-prefix=32 %s +# RUN: llvm-objdump -d %t.rv64 | FileCheck --check-prefix=64 %s +# RUN: llvm-readelf --symbols %t.rv32 | FileCheck --check-prefix=Symbols32 %s +# RUN: llvm-readelf --symbols %t.rv64 | FileCheck --check-prefix=Symbols64 %s +# RUN: llvm-readelf --sections %t.rv32 | FileCheck --check-prefix=Sections32 %s +# RUN: llvm-readelf --sections %t.rv64 | FileCheck --check-prefix=Sections64 %s +# RUN: llvm-readelf --segments %t.rv32 | FileCheck --check-prefix=Segments32 %s +# RUN: llvm-readelf --segments %t.rv64 | FileCheck --check-prefix=Segments64 %s + +# This tests many succesive calls to the same location that will all be relaxed + +# 32: 000110b4 <_start>: +# 32: 110b4: ef 00 80 01 jal 24 +# 32: 110b8: ef 00 40 01 jal 20 +# 32: 110bc: ef 00 00 01 jal 16 +# 32: 110c0: ef 00 c0 00 jal 12 +# 32: 110c4: ef 00 80 00 jal 8 +# 32: 110c8: ef 00 40 00 jal 4 + +# 32: 000110cc : +# 32: 110cc: ef f0 9f fe jal -24 <_start> + +# 64: 0000000000011120 <_start>: +# 64: 11120: ef 00 80 01 jal 24 +# 64: 11124: ef 00 40 01 jal 20 +# 64: 11128: ef 00 00 01 jal 16 +# 64: 1112c: ef 00 c0 00 jal 12 +# 64: 11130: ef 00 80 00 jal 8 +# 64: 11134: ef 00 40 00 jal 4 + +# 64: 0000000000011138 : +# 64: 11138: ef f0 9f fe jal -24 <_start> + +# Check the symbols have been adjusted correctly + +# Symbols32: Num: Value Size Type Bind Vis Ndx Name +# Symbols32: 1: 000110cc 4 NOTYPE LOCAL DEFAULT 1 foo +# Symbols32: 2: 000110b4 24 NOTYPE GLOBAL DEFAULT 1 _start + +# Symbols64: Num: Value Size Type Bind Vis Ndx Name +# Symbols64: 1: 0000000000011138 4 NOTYPE LOCAL DEFAULT 1 foo +# Symbols64: 2: 0000000000011120 24 NOTYPE GLOBAL DEFAULT 1 _start + +# Check the sections have been adjusted correctly + +# Sections32: Section Headers: +# Sections32: [Nr] Name Type Address Off Size ES Flg Lk Inf Al +# Sections32: [ 0] NULL 00000000 000000 000000 00 0 0 0 +# Sections32: [ 1] .text PROGBITS 000110b4 0000b4 00001c 00 AX 0 0 4 +# Sections32: [ 2] .comment PROGBITS 00000000 0000d0 000008 01 MS 0 0 1 +# Sections32: [ 3] .symtab SYMTAB 00000000 0000d8 000030 10 5 2 4 +# Sections32: [ 4] .shstrtab STRTAB 00000000 000108 00002a 00 0 0 1 +# Sections32: [ 5] .strtab STRTAB 00000000 000132 00000c 00 0 0 1 + +# Sections64: Section Headers: +# Sections64: [Nr] Name Type Address Off Size ES Flg Lk Inf Al +# Sections64: [ 0] NULL 0000000000000000 000000 000000 00 0 0 0 +# Sections64: [ 1] .text PROGBITS 0000000000011120 000120 00001c 00 AX 0 0 4 +# Sections64: [ 2] .comment PROGBITS 0000000000000000 00013c 000008 01 MS 0 0 1 +# Sections64: [ 3] .symtab SYMTAB 0000000000000000 000148 000048 18 5 2 8 +# Sections64: [ 4] .shstrtab STRTAB 0000000000000000 000190 00002a 00 0 0 1 +# Sections64: [ 5] .strtab STRTAB 0000000000000000 0001ba 00000c 00 0 0 1 + +# Check the segments have been adjusted correctly + +# Segments32: Program Headers: +# Segments32: Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align +# Segments32: LOAD 0x0000b4 0x000110b4 0x000110b4 0x0001c 0x0001c R E 0x1000 + +# Segments64: Program Headers: +# Segments64: Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align +# Segments64: LOAD 0x000120 0x0000000000011120 0x0000000000011120 0x00001c 0x00001c R E 0x1000 + +.global _start +_start: + call foo + call foo + call foo + call foo + call foo + call foo + .size _start, . - _start + +foo: + call _start + .size foo, . - foo diff --git a/lld/test/ELF/riscv-relax-call-5.s b/lld/test/ELF/riscv-relax-call-5.s new file mode 100644 --- /dev/null +++ b/lld/test/ELF/riscv-relax-call-5.s @@ -0,0 +1,109 @@ +# REQUIRES: riscv + +# RUN: llvm-mc -filetype=obj -triple=riscv32-unknown-elf -mattr=+relax %s -o %t.rv32.o +# RUN: llvm-mc -filetype=obj -triple=riscv64-unknown-elf -mattr=+relax %s -o %t.rv64.o + +# RUN: ld.lld %t.rv32.o -relax -o %t.rv32 +# RUN: ld.lld %t.rv64.o -relax -o %t.rv64 + +# RUN: llvm-objdump -d %t.rv32 | FileCheck --check-prefix=32 %s +# RUN: llvm-objdump -d %t.rv64 | FileCheck --check-prefix=64 %s +# RUN: llvm-readelf --symbols %t.rv32 | FileCheck --check-prefix=Symbols32 %s +# RUN: llvm-readelf --symbols %t.rv64 | FileCheck --check-prefix=Symbols64 %s +# RUN: llvm-readelf --sections %t.rv32 | FileCheck --check-prefix=Sections32 %s +# RUN: llvm-readelf --sections %t.rv64 | FileCheck --check-prefix=Sections64 %s +# RUN: llvm-readelf --segments %t.rv32 | FileCheck --check-prefix=Segments32 %s +# RUN: llvm-readelf --segments %t.rv64 | FileCheck --check-prefix=Segments64 %s + +# 32: 000110b4 <_start>: +# 32: 110b4: ef 00 00 00 jal 0 <_start> +# 32: 110b8: ef 00 80 00 jal 8 +# 32: 110bc: ef 00 00 01 jal 16 + +# 32: 000110c0 : +# 32: 110c0: ef f0 5f ff jal -12 <_start> +# 32: 110c4: ef f0 df ff jal -4 +# 32: 110c8: ef 00 40 00 jal 4 + +# 32: 000110cc : +# 32: 110cc: ef f0 9f fe jal -24 <_start> +# 32: 110d0: ef f0 1f ff jal -16 +# 32: 110d4: ef f0 9f ff jal -8 + +# 64: 0000000000011120 <_start>: +# 64: 11120: ef 00 00 00 jal 0 <_start> +# 64: 11124: ef 00 80 00 jal 8 +# 64: 11128: ef 00 00 01 jal 16 + +# 64: 000000000001112c : +# 64: 1112c: ef f0 5f ff jal -12 <_start> +# 64: 11130: ef f0 df ff jal -4 +# 64: 11134: ef 00 40 00 jal 4 + +# 64: 0000000000011138 : +# 64: 11138: ef f0 9f fe jal -24 <_start> +# 64: 1113c: ef f0 1f ff jal -16 +# 64: 11140: ef f0 9f ff jal -8 + +# Check the symbols have been adjusted correctly + +# Symbols32: Num: Value Size Type Bind Vis Ndx Name +# Symbols32: 1: 000110cc 12 NOTYPE LOCAL DEFAULT 1 bar +# Symbols32: 2: 000110c0 12 NOTYPE LOCAL DEFAULT 1 foo +# Symbols32: 3: 000110b4 12 NOTYPE GLOBAL DEFAULT 1 _start + +# Symbols64: Num: Value Size Type Bind Vis Ndx Name +# Symbols64: 1: 0000000000011138 12 NOTYPE LOCAL DEFAULT 1 bar +# Symbols64: 2: 000000000001112c 12 NOTYPE LOCAL DEFAULT 1 foo +# Symbols64: 3: 0000000000011120 12 NOTYPE GLOBAL DEFAULT 1 _start + +# Check the sections have been adjusted correctly + +# Sections32: Section Headers: +# Sections32: [Nr] Name Type Address Off Size ES Flg Lk Inf Al +# Sections32: [ 0] NULL 00000000 000000 000000 00 0 0 0 +# Sections32: [ 1] .text PROGBITS 000110b4 0000b4 000024 00 AX 0 0 4 +# Sections32: [ 2] .comment PROGBITS 00000000 0000d8 000008 01 MS 0 0 1 +# Sections32: [ 3] .symtab SYMTAB 00000000 0000e0 000040 10 5 3 4 +# Sections32: [ 4] .shstrtab STRTAB 00000000 000120 00002a 00 0 0 1 +# Sections32: [ 5] .strtab STRTAB 00000000 00014a 000010 00 0 0 1 + +# Sections64: Section Headers: +# Sections64: [Nr] Name Type Address Off Size ES Flg Lk Inf Al +# Sections64: [ 0] NULL 0000000000000000 000000 000000 00 0 0 0 +# Sections64: [ 1] .text PROGBITS 0000000000011120 000120 000024 00 AX 0 0 4 +# Sections64: [ 2] .comment PROGBITS 0000000000000000 000144 000008 01 MS 0 0 1 +# Sections64: [ 3] .symtab SYMTAB 0000000000000000 000150 000060 18 5 3 8 +# Sections64: [ 4] .shstrtab STRTAB 0000000000000000 0001b0 00002a 00 0 0 1 +# Sections64: [ 5] .strtab STRTAB 0000000000000000 0001da 000010 00 0 0 1 + +# Check the segments have been adjusted correctly + +# Segments32: Program Headers: +# Segments32: Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align +# Segments32: LOAD 0x0000b4 0x000110b4 0x000110b4 0x00024 0x00024 R E 0x1000 + +# Segments64: Program Headers: +# Segments64: Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align +# Segments64: LOAD 0x000120 0x0000000000011120 0x0000000000011120 0x000024 0x000024 R E 0x1000 + +# This tests many successive calls to the same location that will all be relaxed + +.global _start +_start: + call _start + call foo + call bar + .size _start, . - _start + +foo: + call _start + call foo + call bar + .size foo, . - foo + +bar: + call _start + call foo + call bar + .size bar, . - bar diff --git a/lld/test/ELF/riscv-relax-call-6.s b/lld/test/ELF/riscv-relax-call-6.s new file mode 100644 --- /dev/null +++ b/lld/test/ELF/riscv-relax-call-6.s @@ -0,0 +1,190 @@ +# REQUIRES: riscv + +# RUN: llvm-mc -filetype=obj -triple=riscv32-unknown-elf -mattr=+relax %s -o %t.rv32.o +# RUN: llvm-mc -filetype=obj -triple=riscv64-unknown-elf -mattr=+relax %s -o %t.rv64.o + +# RUN: ld.lld %t.rv32.o --defsym foo=8 -relax -o %t.rv32 +# RUN: ld.lld %t.rv64.o --defsym foo=8 -relax -o %t.rv64 + +# RUN: llvm-objdump -d %t.rv32 | FileCheck --check-prefix=32 %s +# RUN: llvm-objdump -d %t.rv64 | FileCheck --check-prefix=64 %s +# RUN: llvm-readelf --symbols %t.rv32 | FileCheck --check-prefix=Symbols32 %s +# RUN: llvm-readelf --symbols %t.rv64 | FileCheck --check-prefix=Symbols64 %s +# RUN: llvm-readelf --sections %t.rv32 | FileCheck --check-prefix=Sections32 %s +# RUN: llvm-readelf --sections %t.rv64 | FileCheck --check-prefix=Sections64 %s +# RUN: llvm-readelf --segments %t.rv32 | FileCheck --check-prefix=Segments32 %s +# RUN: llvm-readelf --segments %t.rv64 | FileCheck --check-prefix=Segments64 %s + +# 32: 000110b4 <_start>: +# 32: 110b4: ef e0 5e f5 jal -69804 +# 32: 110b8: 13 00 00 00 nop +# 32: 110bc: 13 00 00 00 nop +# 32: 110c0: 13 00 00 00 nop +# 32: 110c4: ef 00 00 04 jal 64 +# 32: 110c8: 13 00 00 00 nop +# 32: 110cc: 13 00 00 00 nop +# 32: 110d0: 13 00 00 00 nop +# 32: 110d4: ef 00 00 03 jal 48 +# 32: 110d8: 13 00 00 00 nop +# 32: 110dc: 13 00 00 00 nop +# 32: 110e0: 13 00 00 00 nop +# 32: 110e4: ef 00 00 02 jal 32 +# 32: 110e8: 13 00 00 00 nop +# 32: 110ec: 13 00 00 00 nop +# 32: 110f0: 13 00 00 00 nop +# 32: 110f4: ef 00 00 01 jal 16 +# 32: 110f8: 13 00 00 00 nop +# 32: 110fc: 13 00 00 00 nop +# 32: 11100: 13 00 00 00 nop + +# 32: 00011104 : +# 32: 11104: 13 00 00 00 nop +# 32: 11108: 13 00 00 00 nop +# 32: 1110c: 13 00 00 00 nop +# 32: 11110: ef e0 9e ef jal -69896 +# 32: 11114: 13 00 00 00 nop +# 32: 11118: 13 00 00 00 nop +# 32: 1111c: 13 00 00 00 nop +# 32: 11120: ef e0 9e ee jal -69912 +# 32: 11124: 13 00 00 00 nop +# 32: 11128: 13 00 00 00 nop +# 32: 1112c: 13 00 00 00 nop +# 32: 11130: ef e0 9e ed jal -69928 +# 32: 11134: 13 00 00 00 nop +# 32: 11138: 13 00 00 00 nop +# 32: 1113c: 13 00 00 00 nop +# 32: 11140: ef e0 9e ec jal -69944 +# 32: 11144: 13 00 00 00 nop +# 32: 11148: 13 00 00 00 nop +# 32: 1114c: 13 00 00 00 nop + +# 64: 0000000000011120 <_start>: +# 64: 11120: ef e0 9e ee jal -69912 +# 64: 11124: 13 00 00 00 nop +# 64: 11128: 13 00 00 00 nop +# 64: 1112c: 13 00 00 00 nop +# 64: 11130: ef 00 00 04 jal 64 +# 64: 11134: 13 00 00 00 nop +# 64: 11138: 13 00 00 00 nop +# 64: 1113c: 13 00 00 00 nop +# 64: 11140: ef 00 00 03 jal 48 +# 64: 11144: 13 00 00 00 nop +# 64: 11148: 13 00 00 00 nop +# 64: 1114c: 13 00 00 00 nop +# 64: 11150: ef 00 00 02 jal 32 +# 64: 11154: 13 00 00 00 nop +# 64: 11158: 13 00 00 00 nop +# 64: 1115c: 13 00 00 00 nop +# 64: 11160: ef 00 00 01 jal 16 +# 64: 11164: 13 00 00 00 nop +# 64: 11168: 13 00 00 00 nop +# 64: 1116c: 13 00 00 00 nop + +# 64: 0000000000011170 : +# 64: 11170: 13 00 00 00 nop +# 64: 11174: 13 00 00 00 nop +# 64: 11178: 13 00 00 00 nop +# 64: 1117c: ef e0 de e8 jal -70004 +# 64: 11180: 13 00 00 00 nop +# 64: 11184: 13 00 00 00 nop +# 64: 11188: 13 00 00 00 nop +# 64: 1118c: ef e0 de e7 jal -70020 +# 64: 11190: 13 00 00 00 nop +# 64: 11194: 13 00 00 00 nop +# 64: 11198: 13 00 00 00 nop +# 64: 1119c: ef e0 de e6 jal -70036 +# 64: 111a0: 13 00 00 00 nop +# 64: 111a4: 13 00 00 00 nop +# 64: 111a8: 13 00 00 00 nop +# 64: 111ac: ef e0 de e5 jal -70052 +# 64: 111b0: 13 00 00 00 nop +# 64: 111b4: 13 00 00 00 nop +# 64: 111b8: 13 00 00 00 nop + +# Check the symbols have been adjusted correctly + +# Symbols32: Num: Value Size Type Bind Vis Ndx Name +# Symbols32: 1: 00011104 76 NOTYPE LOCAL DEFAULT 1 bar +# Symbols32: 2: 000110b4 80 NOTYPE GLOBAL DEFAULT 1 _start +# Symbols32: 3: 00000008 0 NOTYPE GLOBAL DEFAULT ABS foo + +# Symbols64: Num: Value Size Type Bind Vis Ndx Name +# Symbols64: 1: 0000000000011170 76 NOTYPE LOCAL DEFAULT 1 bar +# Symbols64: 2: 0000000000011120 80 NOTYPE GLOBAL DEFAULT 1 _start +# Symbols64: 3: 0000000000000008 0 NOTYPE GLOBAL DEFAULT ABS foo + +# Check the sections have been adjusted correctly + +# Sections32: Section Headers: +# Sections32: [Nr] Name Type Address Off Size ES Flg Lk Inf Al +# Sections32: [ 0] NULL 00000000 000000 000000 00 0 0 0 +# Sections32: [ 1] .text PROGBITS 000110b4 0000b4 00009c 00 AX 0 0 4 +# Sections32: [ 2] .comment PROGBITS 00000000 000150 000008 01 MS 0 0 1 +# Sections32: [ 3] .symtab SYMTAB 00000000 000158 000040 10 5 2 4 +# Sections32: [ 4] .shstrtab STRTAB 00000000 000198 00002a 00 0 0 1 +# Sections32: [ 5] .strtab STRTAB 00000000 0001c2 000010 00 0 0 1 + +# Sections64: Section Headers: +# Sections64: [Nr] Name Type Address Off Size ES Flg Lk Inf Al +# Sections64: [ 0] NULL 0000000000000000 000000 000000 00 0 0 0 +# Sections64: [ 1] .text PROGBITS 0000000000011120 000120 00009c 00 AX 0 0 4 +# Sections64: [ 2] .comment PROGBITS 0000000000000000 0001bc 000008 01 MS 0 0 1 +# Sections64: [ 3] .symtab SYMTAB 0000000000000000 0001c8 000060 18 5 2 8 +# Sections64: [ 4] .shstrtab STRTAB 0000000000000000 000228 00002a 00 0 0 1 +# Sections64: [ 5] .strtab STRTAB 0000000000000000 000252 000010 00 0 0 1 + +# Check the segments have been adjusted correctly + +# Segments32: Program Headers: +# Segments32: Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align +# Segments32: LOAD 0x0000b4 0x000110b4 0x000110b4 0x0009c 0x0009c R E 0x1000 + +# Segments64: Program Headers: +# Segments64: Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align +# Segments64: LOAD 0x000120 0x0000000000011120 0x0000000000011120 0x00009c 0x00009c R E 0x1000 + +.global _start +_start: + call foo + nop + nop + nop + call bar + nop + nop + nop + call bar + nop + nop + nop + call bar + nop + nop + nop + call bar + nop + nop + nop + .size _start, . - _start + +bar: + nop + nop + nop + call foo + nop + nop + nop + call foo + nop + nop + nop + call foo + nop + nop + nop + call foo + nop + nop + nop + .size bar, . - bar diff --git a/lld/test/ELF/riscv-relax-call-multiple-sections.yaml b/lld/test/ELF/riscv-relax-call-multiple-sections.yaml new file mode 100644 --- /dev/null +++ b/lld/test/ELF/riscv-relax-call-multiple-sections.yaml @@ -0,0 +1,100 @@ +# REQUIRES: riscv + +# RUN: yaml2obj %s -o %t.rv32.o + +# RUN: ld.lld %t.rv32.o -relax -o %t.rv32 + +# RUN: llvm-objdump -d %t.rv32 | FileCheck --check-prefix=32 %s +# RUN: llvm-readelf --symbols %t.rv32 | FileCheck --check-prefix=Symbols32 %s +# RUN: llvm-readelf --sections %t.rv32 | FileCheck --check-prefix=Sections32 %s +# RUN: llvm-readelf --segments %t.rv32 | FileCheck --check-prefix=Segments32 %s + +# This tests relaxing calls to versioned symbols using the .symver directive. + +# 32: 000110b4 <_start>: +# 32: 110b4: ef 00 40 00 jal 4 + +# 32: Disassembly of section .other: + +# 32: 000110b8 : +# 32: 110b8: ef f0 df ff jal -4 <_start> + +# Check the symbols have been adjusted correctly + +# Symbols32: Num: Value Size Type Bind Vis Ndx Name +# Symbols32: 1: 000110b4 4 NOTYPE GLOBAL DEFAULT 1 _start +# Symbols32: 2: 000110b8 4 NOTYPE GLOBAL DEFAULT 2 foo + +# Check the sections have been adjusted correctly + +# Sections32: Section Headers: +# Sections32: [Nr] Name Type Address Off Size ES Flg Lk Inf Al +# Sections32: [ 0] NULL 00000000 000000 000000 00 0 0 0 +# Sections32: [ 1] .text PROGBITS 000110b4 0000b4 000004 00 AX 0 0 4 +# Sections32: [ 2] .other PROGBITS 000110b8 0000b8 000004 00 AX 0 0 1 +# Sections32: [ 3] .comment PROGBITS 00000000 0000bc 000008 01 MS 0 0 1 +# Sections32: [ 4] .symtab SYMTAB 00000000 0000c4 000030 10 6 1 4 +# Sections32: [ 5] .shstrtab STRTAB 00000000 0000f4 000031 00 0 0 1 +# Sections32: [ 6] .strtab STRTAB 00000000 000125 00000c 00 0 0 1 + +# Check the segments have been adjusted correctly + +# Segments32: Program Headers: +# Segments32: Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align +# Segments32: LOAD 0x0000b4 0x000110b4 0x000110b4 0x00008 0x00008 R E 0x1000 + +# Used to test relaxation function calls (R_RISCV_CALL) that jump into a +# different section. + +--- !ELF +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_RISCV +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x0000000000000004 + Content: '97000000E7800000' + - Name: .rela.text + Type: SHT_RELA + Link: .symtab + AddressAlign: 0x0000000000000004 + EntSize: 0x000000000000000C + Info: .text + Relocations: + - Offset: 0x0000000000000000 + Symbol: foo + Type: R_RISCV_CALL + - Offset: 0x0000000000000000 + Type: R_RISCV_RELAX + - Name: .other + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + Address: 0x0000000000000008 + AddressAlign: 0x0000000000000000 + Content: '97000000E7800000' + - Name: .rela.other + Type: SHT_RELA + Link: .symtab + AddressAlign: 0x0000000000000004 + EntSize: 0x000000000000000C + Info: .other + Relocations: + - Offset: 0x0000000000000000 + Symbol: _start + Type: R_RISCV_CALL + - Offset: 0x0000000000000000 + Type: R_RISCV_RELAX +Symbols: + - Name: _start + Section: .text + Binding: STB_GLOBAL + Size: 0x0000000000000008 + - Name: foo + Section: .other + Binding: STB_GLOBAL + Size: 0x0000000000000008 +... diff --git a/lld/test/ELF/riscv-relax-call-symbol-versions.s b/lld/test/ELF/riscv-relax-call-symbol-versions.s new file mode 100644 --- /dev/null +++ b/lld/test/ELF/riscv-relax-call-symbol-versions.s @@ -0,0 +1,106 @@ +# REQUIRES: riscv + +# RUN: llvm-mc -filetype=obj -triple=riscv32-unknown-elf -mattr=+relax %s -o %t.rv32.o +# RUN: llvm-mc -filetype=obj -triple=riscv64-unknown-elf -mattr=+relax %s -o %t.rv64.o + +# RUN: ld.lld %t.rv32.o -relax -o %t.rv32 +# RUN: ld.lld %t.rv64.o -relax -o %t.rv64 + +# RUN: llvm-objdump -d %t.rv32 | FileCheck --check-prefix=32 %s +# RUN: llvm-objdump -d %t.rv64 | FileCheck --check-prefix=64 %s +# RUN: llvm-readelf --symbols %t.rv32 | FileCheck --check-prefix=Symbols32 %s +# RUN: llvm-readelf --symbols %t.rv64 | FileCheck --check-prefix=Symbols64 %s +# RUN: llvm-readelf --sections %t.rv32 | FileCheck --check-prefix=Sections32 %s +# RUN: llvm-readelf --sections %t.rv64 | FileCheck --check-prefix=Sections64 %s +# RUN: llvm-readelf --segments %t.rv32 | FileCheck --check-prefix=Segments32 %s +# RUN: llvm-readelf --segments %t.rv64 | FileCheck --check-prefix=Segments64 %s + +# This tests relaxing calls to versioned symbols using the .symver directive. + +# 32: 000110b4 <_start>: +# 32: 110b4: ef 00 80 00 jal 8 +# 32: 110b8: ef 00 c0 00 jal 12 + +# 32: 000110bc : +# 32: 110bc: ef 00 00 00 jal 0 +# 32: 110c0: ef 00 40 00 jal 4 + +# 32: 000110c4 : +# 32: 110c4: ef f0 9f ff jal -8 +# 32: 110c8: ef f0 df ff jal -4 + +# 64: 0000000000011120 <_start>: +# 64: 11120: ef 00 80 00 jal 8 +# 64: 11124: ef 00 c0 00 jal 12 + +# 64: 0000000000011128 : +# 64: 11128: ef 00 00 00 jal 0 +# 64: 1112c: ef 00 40 00 jal 4 + +# 64: 0000000000011130 : +# 64: 11130: ef f0 9f ff jal -8 +# 64: 11134: ef f0 df ff jal -4 + +# Check the symbols have been adjusted correctly + +# Symbols32: Num: Value Size Type Bind Vis Ndx Name +# Symbols32: 1: 000110bc 8 NOTYPE LOCAL DEFAULT 1 bar +# Symbols32: 2: 000110bc 8 NOTYPE LOCAL DEFAULT 1 foo@VER1 +# Symbols32: 3: 000110c4 8 NOTYPE LOCAL DEFAULT 1 foo@VER2 +# Symbols32: 4: 000110c4 8 NOTYPE LOCAL DEFAULT 1 sym +# Symbols32: 5: 000110b4 8 NOTYPE GLOBAL DEFAULT 1 _start + +# Symbols64: Num: Value Size Type Bind Vis Ndx Name +# Symbols64: 1: 0000000000011128 8 NOTYPE LOCAL DEFAULT 1 bar +# Symbols64: 2: 0000000000011128 8 NOTYPE LOCAL DEFAULT 1 foo@VER1 +# Symbols64: 3: 0000000000011130 8 NOTYPE LOCAL DEFAULT 1 foo@VER2 +# Symbols64: 4: 0000000000011130 8 NOTYPE LOCAL DEFAULT 1 sym +# Symbols64: 5: 0000000000011120 8 NOTYPE GLOBAL DEFAULT 1 _start + +# Check the sections have been adjusted correctly + +# Sections32: Section Headers: +# Sections32: [Nr] Name Type Address Off Size ES Flg Lk Inf Al +# Sections32: [ 0] NULL 00000000 000000 000000 00 0 0 0 +# Sections32: [ 1] .text PROGBITS 000110b4 0000b4 000018 00 AX 0 0 4 +# Sections32: [ 2] .comment PROGBITS 00000000 0000cc 000008 01 MS 0 0 1 +# Sections32: [ 3] .symtab SYMTAB 00000000 0000d4 000060 10 5 5 4 +# Sections32: [ 4] .shstrtab STRTAB 00000000 000134 00002a 00 0 0 1 +# Sections32: [ 5] .strtab STRTAB 00000000 00015e 000022 00 0 0 1 + +# Sections64: Section Headers: +# Sections64: [Nr] Name Type Address Off Size ES Flg Lk Inf Al +# Sections64: [ 0] NULL 0000000000000000 000000 000000 00 0 0 0 +# Sections64: [ 1] .text PROGBITS 0000000000011120 000120 000018 00 AX 0 0 4 +# Sections64: [ 2] .comment PROGBITS 0000000000000000 000138 000008 01 MS 0 0 1 +# Sections64: [ 3] .symtab SYMTAB 0000000000000000 000140 000090 18 5 5 8 +# Sections64: [ 4] .shstrtab STRTAB 0000000000000000 0001d0 00002a 00 0 0 1 +# Sections64: [ 5] .strtab STRTAB 0000000000000000 0001fa 000022 00 0 0 1 + +# Check the segments have been adjusted correctly + +# Segments32: Program Headers: +# Segments32: Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align +# Segments32: LOAD 0x0000b4 0x000110b4 0x000110b4 0x00018 0x00018 R E 0x1000 + +# Segments64: Program Headers: +# Segments64: Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align +# Segments64: LOAD 0x000120 0x0000000000011120 0x0000000000011120 0x000018 0x000018 R E 0x1000 + +.global _start +_start: + call foo@VER1 + call foo@VER2 + .size _start, . - _start + +bar: + call foo@VER1 + call foo@VER2 + .size bar, . - bar + .symver bar, foo@VER1 + +sym: + call foo@VER1 + call foo@VER2 + .size sym, . - sym + .symver sym, foo@VER2 diff --git a/lld/test/ELF/riscv-relax-call-wrapped-symbols.s b/lld/test/ELF/riscv-relax-call-wrapped-symbols.s new file mode 100644 --- /dev/null +++ b/lld/test/ELF/riscv-relax-call-wrapped-symbols.s @@ -0,0 +1,128 @@ +# REQUIRES: riscv + +# RUN: llvm-mc -filetype=obj -triple=riscv32-unknown-elf -mattr=+relax %s -o %t.rv32.o +# RUN: llvm-mc -filetype=obj -triple=riscv64-unknown-elf -mattr=+relax %s -o %t.rv64.o + +# RUN: ld.lld %t.rv32.o -relax -o %t.rv32 --wrap foo +# RUN: ld.lld %t.rv64.o -relax -o %t.rv64 --wrap foo + +# RUN: llvm-objdump -d %t.rv32 | FileCheck --check-prefix=32 %s +# RUN: llvm-objdump -d %t.rv64 | FileCheck --check-prefix=64 %s +# RUN: llvm-readelf --symbols %t.rv32 | FileCheck --check-prefix=Symbols32 %s +# RUN: llvm-readelf --symbols %t.rv64 | FileCheck --check-prefix=Symbols64 %s +# RUN: llvm-readelf --sections %t.rv32 | FileCheck --check-prefix=Sections32 %s +# RUN: llvm-readelf --sections %t.rv64 | FileCheck --check-prefix=Sections64 %s +# RUN: llvm-readelf --segments %t.rv32 | FileCheck --check-prefix=Segments32 %s +# RUN: llvm-readelf --segments %t.rv64 | FileCheck --check-prefix=Segments64 %s + +# This tests relaxing relocations jumping between wrapped symbols. + +# 32: 000110b4 <_start>: +# 32: 110b4: ef 00 c0 00 jal 12 +# 32: 110b8: ef 00 40 01 jal 20 <__wrap_foo> +# 32: 110bc: ef 00 c0 01 jal 28 <__real_foo> + +# 32: 000110c0 : +# 32: 110c0: ef 00 00 00 jal 0 +# 32: 110c4: ef 00 80 00 jal 8 <__wrap_foo> +# 32: 110c8: ef 00 00 01 jal 16 <__real_foo> + +# 32: 000110cc <__wrap_foo>: +# 32: 110cc: ef f0 5f ff jal -12 +# 32: 110d0: ef f0 df ff jal -4 <__wrap_foo> +# 32: 110d4: ef 00 40 00 jal 4 <__real_foo> + +# 32: 000110d8 <__real_foo>: +# 32: 110d8: ef f0 9f fe jal -24 +# 32: 110dc: ef f0 1f ff jal -16 <__wrap_foo> +# 32: 110e0: ef f0 9f ff jal -8 <__real_foo> + + +# 64: 0000000000011120 <_start>: +# 64: 11120: ef 00 c0 00 jal 12 +# 64: 11124: ef 00 40 01 jal 20 <__wrap_foo> +# 64: 11128: ef 00 c0 01 jal 28 <__real_foo> + +# 64: 000000000001112c : +# 64: 1112c: ef 00 00 00 jal 0 +# 64: 11130: ef 00 80 00 jal 8 <__wrap_foo> +# 64: 11134: ef 00 00 01 jal 16 <__real_foo> + +# 64: 0000000000011138 <__wrap_foo>: +# 64: 11138: ef f0 5f ff jal -12 +# 64: 1113c: ef f0 df ff jal -4 <__wrap_foo> +# 64: 11140: ef 00 40 00 jal 4 <__real_foo> + +# 64: 0000000000011144 <__real_foo>: +# 64: 11144: ef f0 9f fe jal -24 +# 64: 11148: ef f0 1f ff jal -16 <__wrap_foo> +# 64: 1114c: ef f0 9f ff jal -8 <__real_foo> + +# Check the symbols have been adjusted correctly + +# Symbols32: Num: Value Size Type Bind Vis Ndx Name +# Symbols32: 1: 000110d8 12 NOTYPE LOCAL DEFAULT 1 __real_foo +# Symbols32: 2: 000110cc 12 NOTYPE LOCAL DEFAULT 1 __wrap_foo +# Symbols32: 3: 000110c0 12 NOTYPE LOCAL DEFAULT 1 foo +# Symbols32: 4: 000110b4 12 NOTYPE GLOBAL DEFAULT 1 _start + +# Symbols64: Num: Value Size Type Bind Vis Ndx Name +# Symbols64: 1: 0000000000011144 12 NOTYPE LOCAL DEFAULT 1 __real_foo +# Symbols64: 2: 0000000000011138 12 NOTYPE LOCAL DEFAULT 1 __wrap_foo +# Symbols64: 3: 000000000001112c 12 NOTYPE LOCAL DEFAULT 1 foo +# Symbols64: 4: 0000000000011120 12 NOTYPE GLOBAL DEFAULT 1 _start + +# Check the sections have been adjusted correctly + +# Sections32: Section Headers: +# Sections32: [Nr] Name Type Address Off Size ES Flg Lk Inf Al +# Sections32: [ 0] NULL 00000000 000000 000000 00 0 0 0 +# Sections32: [ 1] .text PROGBITS 000110b4 0000b4 000030 00 AX 0 0 4 +# Sections32: [ 2] .comment PROGBITS 00000000 0000e4 000008 01 MS 0 0 1 +# Sections32: [ 3] .symtab SYMTAB 00000000 0000ec 000050 10 5 4 4 +# Sections32: [ 4] .shstrtab STRTAB 00000000 00013c 00002a 00 0 0 1 +# Sections32: [ 5] .strtab STRTAB 00000000 000166 000022 00 0 0 1 + +# Sections64: Section Headers: +# Sections64: [Nr] Name Type Address Off Size ES Flg Lk Inf Al +# Sections64: [ 0] NULL 0000000000000000 000000 000000 00 0 0 0 +# Sections64: [ 1] .text PROGBITS 0000000000011120 000120 000030 00 AX 0 0 4 +# Sections64: [ 2] .comment PROGBITS 0000000000000000 000150 000008 01 MS 0 0 1 +# Sections64: [ 3] .symtab SYMTAB 0000000000000000 000158 000078 18 5 4 8 +# Sections64: [ 4] .shstrtab STRTAB 0000000000000000 0001d0 00002a 00 0 0 1 +# Sections64: [ 5] .strtab STRTAB 0000000000000000 0001fa 000022 00 0 0 1 + +# Check the segments have been adjusted correctly + +# Segments32: Program Headers: +# Segments32: Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align +# Segments32: LOAD 0x0000b4 0x000110b4 0x000110b4 0x00030 0x00030 R E 0x1000 + +# Segments64: Program Headers: +# Segments64: Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align +# Segments64: LOAD 0x000120 0x0000000000011120 0x0000000000011120 0x000030 0x000030 R E 0x1000 + +.global _start +_start: + call foo + call __wrap_foo + call __real_foo + .size _start, . - _start + +foo: + call foo + call __wrap_foo + call __real_foo + .size foo, . - foo + +__wrap_foo: + call foo + call __wrap_foo + call __real_foo + .size __wrap_foo, . - __wrap_foo + +__real_foo: + call foo + call __wrap_foo + call __real_foo + .size __real_foo, . - __real_foo diff --git a/lld/test/ELF/riscv-relax-call.s b/lld/test/ELF/riscv-relax-call.s new file mode 100644 --- /dev/null +++ b/lld/test/ELF/riscv-relax-call.s @@ -0,0 +1,99 @@ +# REQUIRES: riscv + +# RUN: llvm-mc -filetype=obj -triple=riscv32-unknown-elf -mattr=+relax %s -o %t.rv32.o +# RUN: llvm-mc -filetype=obj -triple=riscv64-unknown-elf -mattr=+relax %s -o %t.rv64.o + +# RUN: ld.lld %t.rv32.o --defsym foo=8 -relax -o %t.rv32 +# RUN: ld.lld %t.rv64.o --defsym foo=8 -relax -o %t.rv64 + +# RUN: llvm-objdump -d %t.rv32 | FileCheck --check-prefix=32 %s +# RUN: llvm-objdump -d %t.rv64 | FileCheck --check-prefix=64 %s +# RUN: llvm-readelf --symbols %t.rv32 | FileCheck --check-prefix=Symbols32 %s +# RUN: llvm-readelf --symbols %t.rv64 | FileCheck --check-prefix=Symbols64 %s +# RUN: llvm-readelf --sections %t.rv32 | FileCheck --check-prefix=Sections32 %s +# RUN: llvm-readelf --sections %t.rv64 | FileCheck --check-prefix=Sections64 %s +# RUN: llvm-readelf --segments %t.rv32 | FileCheck --check-prefix=Segments32 %s +# RUN: llvm-readelf --segments %t.rv64 | FileCheck --check-prefix=Segments64 %s + + +# 32: 000110b4 <_start>: +# 32: 110b4: ef e0 5e f5 jal -69804 +# 32: 110b8: ef 00 00 01 jal 16 +# 32: 110bc: ef 00 c0 00 jal 12 +# 32: 110c0: ef 00 80 00 jal 8 +# 32: 110c4: ef 00 40 00 jal 4 + +# 32: 000110c8 : +# 32: 110c8: ef e0 1e f4 jal -69824 +# 32: 110cc: ef e0 de f3 jal -69828 +# 32: 110d0: ef e0 9e f3 jal -69832 +# 32: 110d4: ef e0 5e f3 jal -69836 + +# 64: 0000000000011120 <_start>: +# 64: 11120: ef e0 9e ee jal -69912 +# 64: 11124: ef 00 00 01 jal 16 +# 64: 11128: ef 00 c0 00 jal 12 +# 64: 1112c: ef 00 80 00 jal 8 +# 64: 11130: ef 00 40 00 jal 4 + +# 64: 0000000000011134 : +# 64: 11134: ef e0 5e ed jal -69932 +# 64: 11138: ef e0 1e ed jal -69936 +# 64: 1113c: ef e0 de ec jal -69940 +# 64: 11140: ef e0 9e ec jal -69944 + +# Check the symbols have been adjusted correctly + +# Symbols32: Num: Value Size Type Bind Vis Ndx Name +# Symbols32: 1: 000110c8 16 NOTYPE LOCAL DEFAULT 1 bar +# Symbols32: 2: 000110b4 20 NOTYPE GLOBAL DEFAULT 1 _start + +# Symbols64: Num: Value Size Type Bind Vis Ndx Name +# Symbols64: 1: 0000000000011134 16 NOTYPE LOCAL DEFAULT 1 bar +# Symbols64: 2: 0000000000011120 20 NOTYPE GLOBAL DEFAULT 1 _start + +# Check the sections have been adjusted correctly + +# Sections32: Section Headers: +# Sections32: [Nr] Name Type Address Off Size ES Flg Lk Inf Al +# Sections32: [ 0] NULL 00000000 000000 000000 00 0 0 0 +# Sections32: [ 1] .text PROGBITS 000110b4 0000b4 000024 00 AX 0 0 4 +# Sections32: [ 2] .comment PROGBITS 00000000 0000d8 000008 01 MS 0 0 1 +# Sections32: [ 3] .symtab SYMTAB 00000000 0000e0 000040 10 5 2 4 +# Sections32: [ 4] .shstrtab STRTAB 00000000 000120 00002a 00 0 0 1 +# Sections32: [ 5] .strtab STRTAB 00000000 00014a 000010 00 0 0 1 + +# Sections64: Section Headers: +# Sections64: [Nr] Name Type Address Off Size ES Flg Lk Inf Al +# Sections64: [ 0] NULL 0000000000000000 000000 000000 00 0 0 0 +# Sections64: [ 1] .text PROGBITS 0000000000011120 000120 000024 00 AX 0 0 4 +# Sections64: [ 2] .comment PROGBITS 0000000000000000 000144 000008 01 MS 0 0 1 +# Sections64: [ 3] .symtab SYMTAB 0000000000000000 000150 000060 18 5 2 8 +# Sections64: [ 4] .shstrtab STRTAB 0000000000000000 0001b0 00002a 00 0 0 1 +# Sections64: [ 5] .strtab STRTAB 0000000000000000 0001da 000010 00 0 0 1 + +# Check the segments have been adjusted correctly + +# Segments32: Program Headers: +# Segments32: Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align +# Segments32: LOAD 0x0000b4 0x000110b4 0x000110b4 0x00024 0x00024 R E 0x1000 + +# Segments64: Program Headers: +# Segments64: Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align +# Segments64: LOAD 0x000000 0x0000000000010000 0x0000000000010000 0x000120 0x000120 R 0x1000 + +.global _start +_start: + call foo + call bar + call bar + call bar + call bar + .size _start, . - _start + +bar: + call foo + call foo + call foo + call foo + .size bar, . - bar