diff options
Diffstat (limited to 'lld')
32 files changed, 1080 insertions, 97 deletions
diff --git a/lld/COFF/SymbolTable.cpp b/lld/COFF/SymbolTable.cpp index e11d2c6..0a88807 100644 --- a/lld/COFF/SymbolTable.cpp +++ b/lld/COFF/SymbolTable.cpp @@ -1399,6 +1399,7 @@ void SymbolTable::resolveAlternateNames() { auto toUndef = dyn_cast<Undefined>(toSym); if (toUndef && (!toUndef->weakAlias || toUndef->isAntiDep)) continue; + toSym->isUsedInRegularObj = true; if (toSym->isLazy()) forceLazy(toSym); u->setWeakAlias(toSym); diff --git a/lld/ELF/Arch/AArch64.cpp b/lld/ELF/Arch/AArch64.cpp index 1812f2a..27e77e9 100644 --- a/lld/ELF/Arch/AArch64.cpp +++ b/lld/ELF/Arch/AArch64.cpp @@ -154,6 +154,12 @@ RelExpr AArch64::getRelExpr(RelType type, const Symbol &s, case R_AARCH64_MOVW_UABS_G2_NC: case R_AARCH64_MOVW_UABS_G3: return R_ABS; + case R_AARCH64_PATCHINST: + if (!isAbsolute(s)) + Err(ctx) << getErrorLoc(ctx, loc) + << "R_AARCH64_PATCHINST relocation against non-absolute symbol " + << &s; + return R_ABS; case R_AARCH64_AUTH_ABS64: return RE_AARCH64_AUTH; case R_AARCH64_TLSDESC_ADR_PAGE21: @@ -506,6 +512,12 @@ void AArch64::relocate(uint8_t *loc, const Relocation &rel, checkIntUInt(ctx, loc, val, 32, rel); write32(ctx, loc, val); break; + case R_AARCH64_PATCHINST: + if (!rel.sym->isUndefined()) { + checkUInt(ctx, loc, val, 32, rel); + write32le(loc, val); + } + break; case R_AARCH64_PLT32: case R_AARCH64_GOTPCREL32: checkInt(ctx, loc, val, 32, rel); diff --git a/lld/ELF/Arch/LoongArch.cpp b/lld/ELF/Arch/LoongArch.cpp index 8802c8c..db2c71c 100644 --- a/lld/ELF/Arch/LoongArch.cpp +++ b/lld/ELF/Arch/LoongArch.cpp @@ -39,6 +39,7 @@ public: void relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const override; bool relaxOnce(int pass) const override; + bool synthesizeAlign(uint64_t &dot, InputSection *sec) override; RelExpr adjustTlsExpr(RelType type, RelExpr expr) const override; void relocateAlloc(InputSectionBase &sec, uint8_t *buf) const override; void finalizeRelax(int passes) const override; @@ -48,6 +49,19 @@ private: void tlsdescToLe(uint8_t *loc, const Relocation &rel, uint64_t val) const; bool tryGotToPCRel(uint8_t *loc, const Relocation &rHi20, const Relocation &rLo12, uint64_t secAddr) const; + template <class ELFT, class RelTy> + bool synthesizeAlignForInput(uint64_t &dot, InputSection *sec, + Relocs<RelTy> rels); + template <class ELFT, class RelTy> + void finalizeSynthesizeAligns(uint64_t &dot, InputSection *sec, + Relocs<RelTy> rels); + template <class ELFT> + bool synthesizeAlignAux(uint64_t &dot, InputSection *sec); + + // The following two variables are used by synthesized ALIGN relocations. + InputSection *baseSec = nullptr; + // r_offset and r_addend pairs. + SmallVector<std::pair<uint64_t, uint64_t>, 0> synthesizedAligns; }; } // end anonymous namespace @@ -766,6 +780,117 @@ void LoongArch::relocate(uint8_t *loc, const Relocation &rel, } } +// If the section alignment is > 4, advance `dot` to insert NOPs and synthesize +// an ALIGN relocation. Otherwise, return false to use default handling. +template <class ELFT, class RelTy> +bool LoongArch::synthesizeAlignForInput(uint64_t &dot, InputSection *sec, + Relocs<RelTy> rels) { + if (!baseSec) { + // Record the first input section with RELAX relocations. We will synthesize + // ALIGN relocations here. + for (auto rel : rels) { + if (rel.getType(false) == R_LARCH_RELAX) { + baseSec = sec; + break; + } + } + } else if (sec->addralign > 4) { + // If the alignment is > 4 and the section does not start with an ALIGN + // relocation, synthesize one. + bool hasAlignRel = llvm::any_of(rels, [](const RelTy &rel) { + return rel.r_offset == 0 && rel.getType(false) == R_LARCH_ALIGN; + }); + if (!hasAlignRel) { + synthesizedAligns.emplace_back(dot - baseSec->getVA(), + sec->addralign - 4); + dot += sec->addralign - 4; + return true; + } + } + return false; +} + +// Finalize the relocation section by appending synthesized ALIGN relocations +// after processing all input sections. +template <class ELFT, class RelTy> +void LoongArch::finalizeSynthesizeAligns(uint64_t &dot, InputSection *sec, + Relocs<RelTy> rels) { + auto *f = cast<ObjFile<ELFT>>(baseSec->file); + auto shdr = f->template getELFShdrs<ELFT>()[baseSec->relSecIdx]; + // Create a copy of InputSection. + sec = make<InputSection>(*f, shdr, baseSec->name); + auto *baseRelSec = cast<InputSection>(f->getSections()[baseSec->relSecIdx]); + *sec = *baseRelSec; + baseSec = nullptr; + + // Allocate buffer for original and synthesized relocations in RELA format. + // If CREL is used, OutputSection::finalizeNonAllocCrel will convert RELA to + // CREL. + auto newSize = rels.size() + synthesizedAligns.size(); + auto *relas = makeThreadLocalN<typename ELFT::Rela>(newSize); + sec->size = newSize * sizeof(typename ELFT::Rela); + sec->content_ = reinterpret_cast<uint8_t *>(relas); + sec->type = SHT_RELA; + // Copy original relocations to the new buffer, potentially converting CREL to + // RELA. + for (auto [i, r] : llvm::enumerate(rels)) { + relas[i].r_offset = r.r_offset; + relas[i].setSymbolAndType(r.getSymbol(0), r.getType(0), false); + if constexpr (RelTy::HasAddend) + relas[i].r_addend = r.r_addend; + } + // Append synthesized ALIGN relocations to the buffer. + for (auto [i, r] : llvm::enumerate(synthesizedAligns)) { + auto &rela = relas[rels.size() + i]; + rela.r_offset = r.first; + rela.setSymbolAndType(0, R_LARCH_ALIGN, false); + rela.r_addend = r.second; + } + synthesizedAligns.clear(); + // Replace the old relocation section with the new one in the output section. + // addOrphanSections ensures that the output relocation section is processed + // after osec. + for (SectionCommand *cmd : sec->getParent()->commands) { + auto *isd = dyn_cast<InputSectionDescription>(cmd); + if (!isd) + continue; + for (auto *&isec : isd->sections) + if (isec == baseRelSec) + isec = sec; + } +} + +template <class ELFT> +bool LoongArch::synthesizeAlignAux(uint64_t &dot, InputSection *sec) { + bool ret = false; + if (sec) { + invokeOnRelocs(*sec, ret = synthesizeAlignForInput<ELFT>, dot, sec); + } else if (baseSec) { + invokeOnRelocs(*baseSec, finalizeSynthesizeAligns<ELFT>, dot, sec); + } + return ret; +} + +// Without linker relaxation enabled for a particular relocatable file or +// section, the assembler will not generate R_LARCH_ALIGN relocations for +// alignment directives. This becomes problematic in a two-stage linking +// process: ld -r a.o b.o -o ab.o; ld ab.o -o ab. This function synthesizes an +// R_LARCH_ALIGN relocation at section start when needed. +// +// When called with an input section (`sec` is not null): If the section +// alignment is > 4, advance `dot` to insert NOPs and synthesize an ALIGN +// relocation. +// +// When called after all input sections are processed (`sec` is null): The +// output relocation section is updated with all the newly synthesized ALIGN +// relocations. +bool LoongArch::synthesizeAlign(uint64_t &dot, InputSection *sec) { + assert(ctx.arg.relocatable); + if (ctx.arg.is64) + return synthesizeAlignAux<ELF64LE>(dot, sec); + return synthesizeAlignAux<ELF32LE>(dot, sec); +} + static bool relaxable(ArrayRef<Relocation> relocs, size_t i) { return i + 1 < relocs.size() && relocs[i + 1].type == R_LARCH_RELAX; } @@ -1396,9 +1521,6 @@ void LoongArch::relocateAlloc(InputSectionBase &sec, uint8_t *buf) const { // change in section sizes can have cascading effect and require another // relaxation pass. bool LoongArch::relaxOnce(int pass) const { - if (ctx.arg.relocatable) - return false; - if (pass == 0) initSymbolAnchors(ctx); diff --git a/lld/ELF/Arch/RISCV.cpp b/lld/ELF/Arch/RISCV.cpp index 72d8315..85ddf1b 100644 --- a/lld/ELF/Arch/RISCV.cpp +++ b/lld/ELF/Arch/RISCV.cpp @@ -45,7 +45,21 @@ public: uint64_t val) const override; void relocateAlloc(InputSectionBase &sec, uint8_t *buf) const override; bool relaxOnce(int pass) const override; + template <class ELFT, class RelTy> + bool synthesizeAlignForInput(uint64_t &dot, InputSection *sec, + Relocs<RelTy> rels); + template <class ELFT, class RelTy> + void finalizeSynthesizeAligns(uint64_t &dot, InputSection *sec, + Relocs<RelTy> rels); + template <class ELFT> + bool synthesizeAlignAux(uint64_t &dot, InputSection *sec); + bool synthesizeAlign(uint64_t &dot, InputSection *sec) override; void finalizeRelax(int passes) const override; + + // The following two variables are used by synthesized ALIGN relocations. + InputSection *baseSec = nullptr; + // r_offset and r_addend pairs. + SmallVector<std::pair<uint64_t, uint64_t>, 0> synthesizedAligns; }; } // end anonymous namespace @@ -942,9 +956,6 @@ static bool relax(Ctx &ctx, int pass, InputSection &sec) { // relaxation pass. bool RISCV::relaxOnce(int pass) const { llvm::TimeTraceScope timeScope("RISC-V relaxOnce"); - if (ctx.arg.relocatable) - return false; - if (pass == 0) initSymbolAnchors(ctx); @@ -959,6 +970,117 @@ bool RISCV::relaxOnce(int pass) const { return changed; } +// If the section alignment is >= 4, advance `dot` to insert NOPs and synthesize +// an ALIGN relocation. Otherwise, return false to use default handling. +template <class ELFT, class RelTy> +bool RISCV::synthesizeAlignForInput(uint64_t &dot, InputSection *sec, + Relocs<RelTy> rels) { + if (!baseSec) { + // Record the first input section with RELAX relocations. We will synthesize + // ALIGN relocations here. + for (auto rel : rels) { + if (rel.getType(false) == R_RISCV_RELAX) { + baseSec = sec; + break; + } + } + } else if (sec->addralign >= 4) { + // If the alignment is >= 4 and the section does not start with an ALIGN + // relocation, synthesize one. + bool hasAlignRel = llvm::any_of(rels, [](const RelTy &rel) { + return rel.r_offset == 0 && rel.getType(false) == R_RISCV_ALIGN; + }); + if (!hasAlignRel) { + synthesizedAligns.emplace_back(dot - baseSec->getVA(), + sec->addralign - 2); + dot += sec->addralign - 2; + return true; + } + } + return false; +} + +// Finalize the relocation section by appending synthesized ALIGN relocations +// after processing all input sections. +template <class ELFT, class RelTy> +void RISCV::finalizeSynthesizeAligns(uint64_t &dot, InputSection *sec, + Relocs<RelTy> rels) { + auto *f = cast<ObjFile<ELFT>>(baseSec->file); + auto shdr = f->template getELFShdrs<ELFT>()[baseSec->relSecIdx]; + // Create a copy of InputSection. + sec = make<InputSection>(*f, shdr, baseSec->name); + auto *baseRelSec = cast<InputSection>(f->getSections()[baseSec->relSecIdx]); + *sec = *baseRelSec; + baseSec = nullptr; + + // Allocate buffer for original and synthesized relocations in RELA format. + // If CREL is used, OutputSection::finalizeNonAllocCrel will convert RELA to + // CREL. + auto newSize = rels.size() + synthesizedAligns.size(); + auto *relas = makeThreadLocalN<typename ELFT::Rela>(newSize); + sec->size = newSize * sizeof(typename ELFT::Rela); + sec->content_ = reinterpret_cast<uint8_t *>(relas); + sec->type = SHT_RELA; + // Copy original relocations to the new buffer, potentially converting CREL to + // RELA. + for (auto [i, r] : llvm::enumerate(rels)) { + relas[i].r_offset = r.r_offset; + relas[i].setSymbolAndType(r.getSymbol(0), r.getType(0), false); + if constexpr (RelTy::HasAddend) + relas[i].r_addend = r.r_addend; + } + // Append synthesized ALIGN relocations to the buffer. + for (auto [i, r] : llvm::enumerate(synthesizedAligns)) { + auto &rela = relas[rels.size() + i]; + rela.r_offset = r.first; + rela.setSymbolAndType(0, R_RISCV_ALIGN, false); + rela.r_addend = r.second; + } + synthesizedAligns.clear(); + // Replace the old relocation section with the new one in the output section. + // addOrphanSections ensures that the output relocation section is processed + // after osec. + for (SectionCommand *cmd : sec->getParent()->commands) { + auto *isd = dyn_cast<InputSectionDescription>(cmd); + if (!isd) + continue; + for (auto *&isec : isd->sections) + if (isec == baseRelSec) + isec = sec; + } +} + +template <class ELFT> +bool RISCV::synthesizeAlignAux(uint64_t &dot, InputSection *sec) { + bool ret = false; + if (sec) { + invokeOnRelocs(*sec, ret = synthesizeAlignForInput<ELFT>, dot, sec); + } else if (baseSec) { + invokeOnRelocs(*baseSec, finalizeSynthesizeAligns<ELFT>, dot, sec); + } + return ret; +} + +// Without linker relaxation enabled for a particular relocatable file or +// section, the assembler will not generate R_RISCV_ALIGN relocations for +// alignment directives. This becomes problematic in a two-stage linking +// process: ld -r a.o b.o -o ab.o; ld ab.o -o ab. This function synthesizes an +// R_RISCV_ALIGN relocation at section start when needed. +// +// When called with an input section (`sec` is not null): If the section +// alignment is >= 4, advance `dot` to insert NOPs and synthesize an ALIGN +// relocation. +// +// When called after all input sections are processed (`sec` is null): The +// output relocation section is updated with all the newly synthesized ALIGN +// relocations. +bool RISCV::synthesizeAlign(uint64_t &dot, InputSection *sec) { + assert(ctx.arg.relocatable); + if (ctx.arg.is64) + return synthesizeAlignAux<ELF64LE>(dot, sec); + return synthesizeAlignAux<ELF32LE>(dot, sec); +} + void RISCV::finalizeRelax(int passes) const { llvm::TimeTraceScope timeScope("Finalize RISC-V relaxation"); Log(ctx) << "relaxation passes: " << passes; diff --git a/lld/ELF/BPSectionOrderer.cpp b/lld/ELF/BPSectionOrderer.cpp index 0615204..38f15b9 100644 --- a/lld/ELF/BPSectionOrderer.cpp +++ b/lld/ELF/BPSectionOrderer.cpp @@ -76,10 +76,12 @@ DenseMap<const InputSectionBase *, int> elf::runBalancedPartitioning( if (!d) return; auto *sec = dyn_cast_or_null<InputSection>(d->section); - // Skip empty, discarded, ICF folded sections, .bss. Skipping ICF folded - // sections reduces duplicate detection work in BPSectionOrderer. - if (!sec || sec->size == 0 || !sec->isLive() || sec->repl != sec || - !sec->content().data() || !orderer.secToSym.try_emplace(sec, d).second) + // Skip section symbols. Skip empty, discarded, ICF folded sections, .bss. + // Skipping ICF folded sections reduces duplicate detection work in + // BPSectionOrderer. + if (sym.isSection() || !sec || sec->size == 0 || !sec->isLive() || + sec->repl != sec || !sec->content().data() || + !orderer.secToSym.try_emplace(sec, d).second) return; rootSymbolToSectionIdxs[CachedHashStringRef( lld::utils::getRootSymbol(sym.getName()))] diff --git a/lld/ELF/LinkerScript.cpp b/lld/ELF/LinkerScript.cpp index a5d08f4..921128d 100644 --- a/lld/ELF/LinkerScript.cpp +++ b/lld/ELF/LinkerScript.cpp @@ -1230,6 +1230,9 @@ bool LinkerScript::assignOffsets(OutputSection *sec) { if (sec->firstInOverlay) state->overlaySize = 0; + bool synthesizeAlign = + ctx.arg.relocatable && ctx.arg.relax && (sec->flags & SHF_EXECINSTR) && + (ctx.arg.emachine == EM_LOONGARCH || ctx.arg.emachine == EM_RISCV); // We visited SectionsCommands from processSectionCommands to // layout sections. Now, we visit SectionsCommands again to fix // section offsets. @@ -1260,7 +1263,10 @@ bool LinkerScript::assignOffsets(OutputSection *sec) { if (isa<PotentialSpillSection>(isec)) continue; const uint64_t pos = dot; - dot = alignToPowerOf2(dot, isec->addralign); + // If synthesized ALIGN may be needed, call maybeSynthesizeAlign and + // disable the default handling if the return value is true. + if (!(synthesizeAlign && ctx.target->synthesizeAlign(dot, isec))) + dot = alignToPowerOf2(dot, isec->addralign); isec->outSecOff = dot - sec->addr; dot += isec->getSize(); @@ -1276,6 +1282,12 @@ bool LinkerScript::assignOffsets(OutputSection *sec) { if (ctx.in.relroPadding && sec == ctx.in.relroPadding->getParent()) expandOutputSection(alignToPowerOf2(dot, ctx.arg.commonPageSize) - dot); + if (synthesizeAlign) { + const uint64_t pos = dot; + ctx.target->synthesizeAlign(dot, nullptr); + expandOutputSection(dot - pos); + } + // Non-SHF_ALLOC sections do not affect the addresses of other OutputSections // as they are not part of the process image. if (!(sec->flags & SHF_ALLOC)) { diff --git a/lld/ELF/OutputSections.cpp b/lld/ELF/OutputSections.cpp index 1020dd9..c9c63b3 100644 --- a/lld/ELF/OutputSections.cpp +++ b/lld/ELF/OutputSections.cpp @@ -889,9 +889,19 @@ void OutputSection::sortInitFini() { std::array<uint8_t, 4> OutputSection::getFiller(Ctx &ctx) { if (filler) return *filler; - if (flags & SHF_EXECINSTR) - return ctx.target->trapInstr; - return {0, 0, 0, 0}; + if (!(flags & SHF_EXECINSTR)) + return {0, 0, 0, 0}; + if (ctx.arg.relocatable && ctx.arg.emachine == EM_RISCV) { + // See RISCV::maybeSynthesizeAlign: Synthesized NOP bytes and ALIGN + // relocations might be needed between two input sections. Use a NOP for the + // filler. + if (ctx.arg.eflags & EF_RISCV_RVC) + return {1, 0, 1, 0}; + return {0x13, 0, 0, 0}; + } + if (ctx.arg.relocatable && ctx.arg.emachine == EM_LOONGARCH) + return {0, 0, 0x40, 0x03}; + return ctx.target->trapInstr; } void OutputSection::checkDynRelAddends(Ctx &ctx) { diff --git a/lld/ELF/Relocations.cpp b/lld/ELF/Relocations.cpp index 666863b..6f55bac 100644 --- a/lld/ELF/Relocations.cpp +++ b/lld/ELF/Relocations.cpp @@ -176,7 +176,7 @@ static RelType getMipsPairType(RelType type, bool isLocal) { // True if non-preemptable symbol always has the same value regardless of where // the DSO is loaded. -static bool isAbsolute(const Symbol &sym) { +bool elf::isAbsolute(const Symbol &sym) { if (sym.isUndefined()) return true; if (const auto *dr = dyn_cast<Defined>(&sym)) @@ -273,12 +273,12 @@ template <class ELFT> static bool isReadOnly(SharedSymbol &ss) { // them are copied by a copy relocation, all of them need to be copied. // Otherwise, they would refer to different places at runtime. template <class ELFT> -static SmallSet<SharedSymbol *, 4> getSymbolsAt(Ctx &ctx, SharedSymbol &ss) { +static SmallPtrSet<SharedSymbol *, 4> getSymbolsAt(Ctx &ctx, SharedSymbol &ss) { using Elf_Sym = typename ELFT::Sym; const auto &file = cast<SharedFile>(*ss.file); - SmallSet<SharedSymbol *, 4> ret; + SmallPtrSet<SharedSymbol *, 4> ret; for (const Elf_Sym &s : file.template getGlobalELFSyms<ELFT>()) { if (s.st_shndx == SHN_UNDEF || s.st_shndx == SHN_ABS || s.getType() == STT_TLS || s.st_value != ss.value) diff --git a/lld/ELF/Relocations.h b/lld/ELF/Relocations.h index c1c4860..7ea03c3 100644 --- a/lld/ELF/Relocations.h +++ b/lld/ELF/Relocations.h @@ -165,6 +165,8 @@ void addGotEntry(Ctx &ctx, Symbol &sym); void hexagonTLSSymbolUpdate(Ctx &ctx); bool hexagonNeedsTLSSymbol(ArrayRef<OutputSection *> outputSections); +bool isAbsolute(const Symbol &sym); + class ThunkSection; class Thunk; class InputSectionDescription; diff --git a/lld/ELF/Target.h b/lld/ELF/Target.h index fdc0c20..e121fa1 100644 --- a/lld/ELF/Target.h +++ b/lld/ELF/Target.h @@ -96,6 +96,9 @@ public: // Do a linker relaxation pass and return true if we changed something. virtual bool relaxOnce(int pass) const { return false; } + virtual bool synthesizeAlign(uint64_t &dot, InputSection *sec) { + return false; + } // Do finalize relaxation after collecting relaxation infos. virtual void finalizeRelax(int passes) const {} diff --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp index 2b0e097..4fa8039 100644 --- a/lld/ELF/Writer.cpp +++ b/lld/ELF/Writer.cpp @@ -1541,8 +1541,10 @@ template <class ELFT> void Writer<ELFT>::finalizeAddressDependentContent() { if (ctx.arg.randomizeSectionPadding) randomizeSectionPadding(ctx); + // Iterate until a fixed point is reached, skipping relocatable links since + // the final addresses are unavailable. uint32_t pass = 0, assignPasses = 0; - for (;;) { + while (!ctx.arg.relocatable) { bool changed = ctx.target->needsThunks ? tc.createThunks(pass, ctx.outputSections) : ctx.target->relaxOnce(pass); diff --git a/lld/MachO/Config.h b/lld/MachO/Config.h index a01e60e..19dba79 100644 --- a/lld/MachO/Config.h +++ b/lld/MachO/Config.h @@ -186,6 +186,7 @@ struct Configuration { bool interposable = false; bool errorForArchMismatch = false; bool ignoreAutoLink = false; + int readWorkers = 0; // ld64 allows invalid auto link options as long as the link succeeds. LLD // does not, but there are cases in the wild where the invalid linker options // exist. This allows users to ignore the specific invalid options in the case diff --git a/lld/MachO/Driver.cpp b/lld/MachO/Driver.cpp index 9eb391c..bcba759 100644 --- a/lld/MachO/Driver.cpp +++ b/lld/MachO/Driver.cpp @@ -44,8 +44,10 @@ #include "llvm/Support/FileSystem.h" #include "llvm/Support/Parallel.h" #include "llvm/Support/Path.h" +#include "llvm/Support/Process.h" #include "llvm/Support/TarWriter.h" #include "llvm/Support/TargetSelect.h" +#include "llvm/Support/Threading.h" #include "llvm/Support/TimeProfiler.h" #include "llvm/TargetParser/Host.h" #include "llvm/TextAPI/Architecture.h" @@ -282,11 +284,117 @@ static void saveThinArchiveToRepro(ArchiveFile const *file) { ": Archive::children failed: " + toString(std::move(e))); } -static InputFile *addFile(StringRef path, LoadType loadType, - bool isLazy = false, bool isExplicit = true, - bool isBundleLoader = false, - bool isForceHidden = false) { - std::optional<MemoryBufferRef> buffer = readFile(path); +struct DeferredFile { + StringRef path; + bool isLazy; + MemoryBufferRef buffer; +}; +using DeferredFiles = std::vector<DeferredFile>; + +class SerialBackgroundQueue { + std::deque<std::function<void()>> queue; + std::thread *running; + std::mutex mutex; + +public: + void queueWork(std::function<void()> work) { + mutex.lock(); + if (running && queue.empty()) { + mutex.unlock(); + running->join(); + mutex.lock(); + delete running; + running = nullptr; + } + + if (work) { + queue.emplace_back(std::move(work)); + if (!running) + running = new std::thread([&]() { + while (true) { + mutex.lock(); + if (queue.empty()) { + mutex.unlock(); + break; + } + auto work = std::move(queue.front()); + mutex.unlock(); + work(); + mutex.lock(); + queue.pop_front(); + mutex.unlock(); + } + }); + } + mutex.unlock(); + } +}; + +// Most input files have been mapped but not yet paged in. +// This code forces the page-ins on multiple threads so +// the process is not stalled waiting on disk buffer i/o. +void multiThreadedPageInBackground(DeferredFiles &deferred) { + static const size_t pageSize = Process::getPageSizeEstimate(); + static const size_t largeArchive = 10 * 1024 * 1024; +#ifndef NDEBUG + using namespace std::chrono; + std::atomic_int numDeferedFilesTouched = 0; + static std::atomic_uint64_t totalBytes = 0; + auto t0 = high_resolution_clock::now(); +#endif + + auto preloadDeferredFile = [&](const DeferredFile &deferredFile) { + const StringRef &buff = deferredFile.buffer.getBuffer(); + if (buff.size() > largeArchive) + return; +#ifndef NDEBUG + totalBytes += buff.size(); + numDeferedFilesTouched += 1; +#endif + + // Reference all file's mmap'd pages to load them into memory. + for (const char *page = buff.data(), *end = page + buff.size(); page < end; + page += pageSize) + LLVM_ATTRIBUTE_UNUSED volatile char t = *page; + }; +#if LLVM_ENABLE_THREADS + { // Create scope for waiting for the taskGroup + std::atomic_size_t index = 0; + llvm::parallel::TaskGroup taskGroup; + for (int w = 0; w < config->readWorkers; w++) + taskGroup.spawn([&index, &preloadDeferredFile, &deferred]() { + while (true) { + size_t localIndex = index.fetch_add(1); + if (localIndex >= deferred.size()) + break; + preloadDeferredFile(deferred[localIndex]); + } + }); + } +#endif +#ifndef NDEBUG + auto dt = high_resolution_clock::now() - t0; + if (Process::GetEnv("LLD_MULTI_THREAD_PAGE")) + llvm::dbgs() << "multiThreadedPageIn " << totalBytes << "/" + << numDeferedFilesTouched << "/" << deferred.size() << "/" + << duration_cast<milliseconds>(dt).count() / 1000. << "\n"; +#endif +} + +static void multiThreadedPageIn(const DeferredFiles &deferred) { + static SerialBackgroundQueue pageInQueue; + pageInQueue.queueWork([=]() { + DeferredFiles files = deferred; + multiThreadedPageInBackground(files); + }); +} + +static InputFile *processFile(std::optional<MemoryBufferRef> buffer, + DeferredFiles *archiveContents, StringRef path, + LoadType loadType, bool isLazy = false, + bool isExplicit = true, + bool isBundleLoader = false, + bool isForceHidden = false) { if (!buffer) return nullptr; MemoryBufferRef mbref = *buffer; @@ -379,6 +487,8 @@ static InputFile *addFile(StringRef path, LoadType loadType, continue; } + if (archiveContents) + archiveContents->push_back({path, isLazy, *mb}); if (!hasObjCSection(*mb)) continue; if (Error e = file->fetch(c, "-ObjC")) @@ -390,7 +500,8 @@ static InputFile *addFile(StringRef path, LoadType loadType, ": Archive::children failed: " + toString(std::move(e))); } } - file->addLazySymbols(); + if (!archiveContents || archiveContents->empty()) + file->addLazySymbols(); loadedArchives[path] = ArchiveFileInfo{file, isCommandLineLoad}; newFile = file; break; @@ -441,6 +552,24 @@ static InputFile *addFile(StringRef path, LoadType loadType, return newFile; } +static InputFile *addFile(StringRef path, LoadType loadType, + bool isLazy = false, bool isExplicit = true, + bool isBundleLoader = false, + bool isForceHidden = false) { + return processFile(readFile(path), nullptr, path, loadType, isLazy, + isExplicit, isBundleLoader, isForceHidden); +} + +static void deferFile(StringRef path, bool isLazy, DeferredFiles &deferred) { + std::optional<MemoryBufferRef> buffer = readFile(path); + if (!buffer) + return; + if (config->readWorkers) + deferred.push_back({path, isLazy, *buffer}); + else + processFile(buffer, nullptr, path, LoadType::CommandLine, isLazy); +} + static std::vector<StringRef> missingAutolinkWarnings; static void addLibrary(StringRef name, bool isNeeded, bool isWeak, bool isReexport, bool isHidden, bool isExplicit, @@ -564,13 +693,14 @@ void macho::resolveLCLinkerOptions() { } } -static void addFileList(StringRef path, bool isLazy) { +static void addFileList(StringRef path, bool isLazy, + DeferredFiles &deferredFiles) { std::optional<MemoryBufferRef> buffer = readFile(path); if (!buffer) return; MemoryBufferRef mbref = *buffer; for (StringRef path : args::getLines(mbref)) - addFile(rerootPath(path), LoadType::CommandLine, isLazy); + deferFile(rerootPath(path), isLazy, deferredFiles); } // We expect sub-library names of the form "libfoo", which will match a dylib @@ -1222,6 +1352,8 @@ static void createFiles(const InputArgList &args) { bool isLazy = false; // If we've processed an opening --start-lib, without a matching --end-lib bool inLib = false; + DeferredFiles deferredFiles; + for (const Arg *arg : args) { const Option &opt = arg->getOption(); warnIfDeprecatedOption(opt); @@ -1229,7 +1361,7 @@ static void createFiles(const InputArgList &args) { switch (opt.getID()) { case OPT_INPUT: - addFile(rerootPath(arg->getValue()), LoadType::CommandLine, isLazy); + deferFile(rerootPath(arg->getValue()), isLazy, deferredFiles); break; case OPT_needed_library: if (auto *dylibFile = dyn_cast_or_null<DylibFile>( @@ -1249,7 +1381,7 @@ static void createFiles(const InputArgList &args) { dylibFile->forceWeakImport = true; break; case OPT_filelist: - addFileList(arg->getValue(), isLazy); + addFileList(arg->getValue(), isLazy, deferredFiles); break; case OPT_force_load: addFile(rerootPath(arg->getValue()), LoadType::CommandLineForce); @@ -1295,6 +1427,24 @@ static void createFiles(const InputArgList &args) { break; } } + + if (config->readWorkers) { + multiThreadedPageIn(deferredFiles); + + DeferredFiles archiveContents; + std::vector<ArchiveFile *> archives; + for (auto &file : deferredFiles) { + auto inputFile = processFile(file.buffer, &archiveContents, file.path, + LoadType::CommandLine, file.isLazy); + if (ArchiveFile *archive = dyn_cast<ArchiveFile>(inputFile)) + archives.push_back(archive); + } + + if (!archiveContents.empty()) + multiThreadedPageIn(archiveContents); + for (auto *archive : archives) + archive->addLazySymbols(); + } } static void gatherInputSections() { @@ -1635,27 +1785,21 @@ bool link(ArrayRef<const char *> argsArr, llvm::raw_ostream &stdoutOS, config->osoPrefix = args.getLastArgValue(OPT_oso_prefix); if (!config->osoPrefix.empty()) { - // Expand special characters, such as ".", "..", or "~", if present. - // Note: LD64 only expands "." and not other special characters. - // That seems silly to imitate so we will not try to follow it, but rather - // just use real_path() to do it. - // The max path length is 4096, in theory. However that seems quite long // and seems unlikely that any one would want to strip everything from the // path. Hence we've picked a reasonably large number here. SmallString<1024> expanded; - if (!fs::real_path(config->osoPrefix, expanded, - /*expand_tilde=*/true)) { - // Note: LD64 expands "." to be `<current_dir>/` - // (ie., it has a slash suffix) whereas real_path() doesn't. - // So we have to append '/' to be consistent. - StringRef sep = sys::path::get_separator(); - // real_path removes trailing slashes as part of the normalization, but - // these are meaningful for our text based stripping - if (config->osoPrefix == "." || config->osoPrefix.ends_with(sep)) - expanded += sep; - config->osoPrefix = saver().save(expanded.str()); + // Expand "." into the current working directory. + if (config->osoPrefix == "." && !fs::current_path(expanded)) { + // Note: LD64 expands "." to be `<current_dir>/ + // (ie., it has a slash suffix) whereas current_path() doesn't. + // So we have to append '/' to be consistent because this is + // meaningful for our text based stripping. + expanded += sys::path::get_separator(); + } else { + expanded = config->osoPrefix; } + config->osoPrefix = saver().save(expanded.str()); } bool pie = args.hasFlag(OPT_pie, OPT_no_pie, true); @@ -1687,6 +1831,14 @@ bool link(ArrayRef<const char *> argsArr, llvm::raw_ostream &stdoutOS, } } + if (auto *arg = args.getLastArg(OPT_read_workers)) { + StringRef v(arg->getValue()); + unsigned threads = 0; + if (!llvm::to_integer(v, threads, 0) || threads < 0) + error(arg->getSpelling() + ": expected a positive integer, but got '" + + arg->getValue() + "'"); + config->readWorkers = threads; + } if (auto *arg = args.getLastArg(OPT_threads_eq)) { StringRef v(arg->getValue()); unsigned threads = 0; diff --git a/lld/MachO/ExportTrie.cpp b/lld/MachO/ExportTrie.cpp index 303eda4..34478f4 100644 --- a/lld/MachO/ExportTrie.cpp +++ b/lld/MachO/ExportTrie.cpp @@ -41,6 +41,7 @@ #include "llvm/BinaryFormat/MachO.h" #include "llvm/Support/LEB128.h" #include <optional> +#include <unordered_set> using namespace llvm; using namespace lld; @@ -296,13 +297,19 @@ namespace { // Parse a serialized trie and invoke a callback for each entry. class TrieParser { public: - TrieParser(const uint8_t *buf, size_t size, const TrieEntryCallback &callback) - : start(buf), end(start + size), callback(callback) {} + TrieParser(const std::string &fileName, const uint8_t *buf, size_t size, + const TrieEntryCallback &callback) + : fileName(fileName), start(buf), end(start + size), callback(callback) {} - void parse(const uint8_t *buf, const Twine &cumulativeString); + void parse(const uint8_t *buf, const Twine &cumulativeString, + std::unordered_set<size_t> &visited); - void parse() { parse(start, ""); } + void parse() { + std::unordered_set<size_t> visited; + parse(start, "", visited); + } + const std::string fileName; const uint8_t *start; const uint8_t *end; const TrieEntryCallback &callback; @@ -310,9 +317,13 @@ public: } // namespace -void TrieParser::parse(const uint8_t *buf, const Twine &cumulativeString) { +void TrieParser::parse(const uint8_t *buf, const Twine &cumulativeString, + std::unordered_set<size_t> &visited) { if (buf >= end) - fatal("Node offset points outside export section"); + fatal(fileName + ": export trie node offset points outside export section"); + + size_t currentOffset = buf - start; + visited.insert(currentOffset); unsigned ulebSize; uint64_t terminalSize = decodeULEB128(buf, &ulebSize); @@ -331,14 +342,18 @@ void TrieParser::parse(const uint8_t *buf, const Twine &cumulativeString) { buf += substring.size() + 1; offset = decodeULEB128(buf, &ulebSize); buf += ulebSize; - parse(start + offset, cumulativeString + substring); + if (visited.find(offset) != visited.end()) + fatal(fileName + ": export trie child node infinite loop"); + parse(start + offset, cumulativeString + substring, visited); } + + visited.erase(currentOffset); } -void macho::parseTrie(const uint8_t *buf, size_t size, - const TrieEntryCallback &callback) { +void macho::parseTrie(const std::string &fileName, const uint8_t *buf, + size_t size, const TrieEntryCallback &callback) { if (size == 0) return; - TrieParser(buf, size, callback).parse(); + TrieParser(fileName, buf, size, callback).parse(); } diff --git a/lld/MachO/ExportTrie.h b/lld/MachO/ExportTrie.h index aa7e3b0..fa73fc4 100644 --- a/lld/MachO/ExportTrie.h +++ b/lld/MachO/ExportTrie.h @@ -41,7 +41,8 @@ private: using TrieEntryCallback = llvm::function_ref<void(const llvm::Twine & /*name*/, uint64_t /*flags*/)>; -void parseTrie(const uint8_t *buf, size_t size, const TrieEntryCallback &); +void parseTrie(const std::string &fileName, const uint8_t *buf, size_t size, + const TrieEntryCallback &); } // namespace lld::macho diff --git a/lld/MachO/InputFiles.cpp b/lld/MachO/InputFiles.cpp index 3b3023a..442fc60 100644 --- a/lld/MachO/InputFiles.cpp +++ b/lld/MachO/InputFiles.cpp @@ -1789,12 +1789,13 @@ void DylibFile::parseExportedSymbols(uint32_t offset, uint32_t size) { auto *buf = reinterpret_cast<const uint8_t *>(mb.getBufferStart()); std::vector<TrieEntry> entries; // Find all the $ld$* symbols to process first. - parseTrie(buf + offset, size, [&](const Twine &name, uint64_t flags) { - StringRef savedName = saver().save(name); - if (handleLDSymbol(savedName)) - return; - entries.push_back({savedName, flags}); - }); + parseTrie(toString(this), buf + offset, size, + [&](const Twine &name, uint64_t flags) { + StringRef savedName = saver().save(name); + if (handleLDSymbol(savedName)) + return; + entries.push_back({savedName, flags}); + }); // Process the "normal" symbols. for (TrieEntry &entry : entries) { diff --git a/lld/MachO/Options.td b/lld/MachO/Options.td index 4f0602f..8ae50f3 100644 --- a/lld/MachO/Options.td +++ b/lld/MachO/Options.td @@ -396,6 +396,9 @@ def dead_strip : Flag<["-"], "dead_strip">, def interposable : Flag<["-"], "interposable">, HelpText<"Indirects access to all exported symbols in an image">, Group<grp_opts>; +def read_workers : Joined<["--"], "read-workers=">, + HelpText<"Approximate number of workers to use to eagerly preload input files content into memory. Use 0 to disable this feature. Default is disabled.">, + Group<grp_lld>; def order_file : Separate<["-"], "order_file">, MetaVarName<"<file>">, HelpText<"Layout functions and data according to specification in <file>">, diff --git a/lld/docs/ReleaseNotes.rst b/lld/docs/ReleaseNotes.rst index 6f60efd..cc1628f 100644 --- a/lld/docs/ReleaseNotes.rst +++ b/lld/docs/ReleaseNotes.rst @@ -1,3 +1,6 @@ +.. If you want to modify sections/contents permanently, you should modify both + ReleaseNotes.rst and ReleaseNotesTemplate.txt. + =========================== lld |release| Release Notes =========================== diff --git a/lld/docs/ReleaseNotesTemplate.txt b/lld/docs/ReleaseNotesTemplate.txt new file mode 100644 index 0000000..cc1628f --- /dev/null +++ b/lld/docs/ReleaseNotesTemplate.txt @@ -0,0 +1,48 @@ +.. If you want to modify sections/contents permanently, you should modify both + ReleaseNotes.rst and ReleaseNotesTemplate.txt. + +=========================== +lld |release| Release Notes +=========================== + +.. contents:: + :local: + +.. only:: PreRelease + + .. warning:: + These are in-progress notes for the upcoming LLVM |release| release. + Release notes for previous releases can be found on + `the Download Page <https://releases.llvm.org/download.html>`_. + +Introduction +============ + +This document contains the release notes for the lld linker, release |release|. +Here we describe the status of lld, including major improvements +from the previous release. All lld releases may be downloaded +from the `LLVM releases web site <https://llvm.org/releases/>`_. + +Non-comprehensive list of changes in this release +================================================= + +ELF Improvements +---------------- + +Breaking changes +---------------- + +COFF Improvements +----------------- + +MinGW Improvements +------------------ + +MachO Improvements +------------------ + +WebAssembly Improvements +------------------------ + +Fixes +##### diff --git a/lld/test/COFF/alternatename-lto.ll b/lld/test/COFF/alternatename-lto.ll new file mode 100644 index 0000000..c3666cd --- /dev/null +++ b/lld/test/COFF/alternatename-lto.ll @@ -0,0 +1,25 @@ +; REQUIRES: x86 +; RUN: mkdir -p %t.dir +; RUN: llvm-as -o %t.obj %s +; RUN: lld-link -out:%t.dll -dll -noentry %t.obj -export:test + +target datalayout = "e-m:w-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-windows-msvc19.33.0" + +$alt = comdat any + +@alt = weak_odr dso_local global i32 0, comdat, align 4 +@ext = external dso_local global i32, align 4 + +; Function Attrs: noinline nounwind optnone uwtable +define dso_local i32 @test() #0 { +entry: + %0 = load i32, ptr @ext, align 4 + ret i32 %0 +} + +attributes #0 = { noinline nounwind optnone uwtable } + +!llvm.linker.options = !{!0} + +!0 = !{!"/alternatename:ext=alt"} diff --git a/lld/test/ELF/aarch64-patchinst.s b/lld/test/ELF/aarch64-patchinst.s new file mode 100644 index 0000000..f91e4f9 --- /dev/null +++ b/lld/test/ELF/aarch64-patchinst.s @@ -0,0 +1,88 @@ +# REQUIRES: aarch64 +# RUN: rm -rf %t && split-file %s %t +# RUN: llvm-mc -filetype=obj -triple=aarch64 %t/use.s -o %t/use-le.o +# RUN: llvm-mc -filetype=obj -triple=aarch64 %t/def.s -o %t/def-le.o +# RUN: llvm-mc -filetype=obj -triple=aarch64 %t/rel.s -o %t/rel-le.o + +## Deactivation symbol used without being defined: instruction emitted as usual. +# RUN: ld.lld -o %t/undef-le %t/use-le.o --emit-relocs +# RUN: llvm-objdump -r %t/undef-le | FileCheck --check-prefix=RELOC %s +# RUN: llvm-objdump -d %t/undef-le | FileCheck --check-prefix=UNDEF %s +# RUN: ld.lld -pie -o %t/undef-le %t/use-le.o --emit-relocs +# RUN: llvm-objdump -r %t/undef-le | FileCheck --check-prefix=RELOC %s +# RUN: llvm-objdump -d %t/undef-le | FileCheck --check-prefix=UNDEF %s + +## Deactivation symbol defined: instructions overwritten with NOPs. +# RUN: ld.lld -o %t/def-le %t/use-le.o %t/def-le.o --emit-relocs +# RUN: llvm-objdump -r %t/def-le | FileCheck --check-prefix=RELOC %s +# RUN: llvm-objdump -d %t/def-le | FileCheck --check-prefix=DEF %s +# RUN: ld.lld -pie -o %t/def-le %t/use-le.o %t/def-le.o --emit-relocs +# RUN: llvm-objdump -r %t/def-le | FileCheck --check-prefix=RELOC %s +# RUN: llvm-objdump -d %t/def-le | FileCheck --check-prefix=DEF %s + +## Relocation pointing to a non-SHN_UNDEF non-SHN_ABS symbol is an error. +# RUN: not ld.lld -o %t/rel-le %t/use-le.o %t/rel-le.o 2>&1 | FileCheck --check-prefix=ERROR %s +# RUN: not ld.lld -pie -o %t/rel-le %t/use-le.o %t/rel-le.o 2>&1 | FileCheck --check-prefix=ERROR %s + +## Behavior unchanged by endianness: relocation always written as little endian. +# RUN: llvm-mc -filetype=obj -triple=aarch64_be %t/use.s -o %t/use-be.o +# RUN: llvm-mc -filetype=obj -triple=aarch64_be %t/def.s -o %t/def-be.o +# RUN: llvm-mc -filetype=obj -triple=aarch64_be %t/rel.s -o %t/rel-be.o +# RUN: ld.lld -o %t/undef-be %t/use-be.o --emit-relocs +# RUN: llvm-objdump -r %t/undef-be | FileCheck --check-prefix=RELOC %s +# RUN: llvm-objdump -d %t/undef-be | FileCheck --check-prefix=UNDEF %s +# RUN: ld.lld -pie -o %t/undef-be %t/use-be.o --emit-relocs +# RUN: llvm-objdump -r %t/undef-be | FileCheck --check-prefix=RELOC %s +# RUN: llvm-objdump -d %t/undef-be | FileCheck --check-prefix=UNDEF %s +# RUN: ld.lld -o %t/def-be %t/use-be.o %t/def-be.o --emit-relocs +# RUN: llvm-objdump -r %t/def-be | FileCheck --check-prefix=RELOC %s +# RUN: llvm-objdump -d %t/def-be | FileCheck --check-prefix=DEF %s +# RUN: ld.lld -pie -o %t/def-be %t/use-be.o %t/def-be.o --emit-relocs +# RUN: llvm-objdump -r %t/def-be | FileCheck --check-prefix=RELOC %s +# RUN: llvm-objdump -d %t/def-be | FileCheck --check-prefix=DEF %s +# RUN: not ld.lld -o %t/rel-be %t/use-be.o %t/rel-be.o 2>&1 | FileCheck --check-prefix=ERROR %s +# RUN: not ld.lld -pie -o %t/rel-be %t/use-be.o %t/rel-be.o 2>&1 | FileCheck --check-prefix=ERROR %s + +# RELOC: R_AARCH64_JUMP26 +# RELOC-NEXT: R_AARCH64_PATCHINST ds +# RELOC-NEXT: R_AARCH64_PATCHINST ds +# RELOC-NEXT: R_AARCH64_PATCHINST ds0+0xd503201f + +#--- use.s +.weak ds +.weak ds0 +# This instruction has a single relocation: the DS relocation. +# UNDEF: add x0, x1, x2 +# DEF: nop +# ERROR: R_AARCH64_PATCHINST relocation against non-absolute symbol ds +.reloc ., R_AARCH64_PATCHINST, ds +add x0, x1, x2 +# This instruction has two relocations: the DS relocation and the JUMP26 to f1. +# Make sure that the DS relocation takes precedence. +.reloc ., R_AARCH64_PATCHINST, ds +# UNDEF: b {{.*}} <f1> +# DEF: nop +# ERROR: R_AARCH64_PATCHINST relocation against non-absolute symbol ds +b f1 +# Alternative representation: instruction opcode stored in addend. +# UNDEF: add x3, x4, x5 +# DEF: nop +# ERROR: R_AARCH64_PATCHINST relocation against non-absolute symbol ds0 +.reloc ., R_AARCH64_PATCHINST, ds0 + 0xd503201f +add x3, x4, x5 + +.section .text.f1,"ax",@progbits +f1: +ret + +#--- def.s +.globl ds +ds = 0xd503201f +.globl ds0 +ds0 = 0 + +#--- rel.s +.globl ds +ds: +.globl ds0 +ds0: diff --git a/lld/test/ELF/bp-section-orderer.s b/lld/test/ELF/bp-section-orderer.s index 438d7c2..9a83c70 100644 --- a/lld/test/ELF/bp-section-orderer.s +++ b/lld/test/ELF/bp-section-orderer.s @@ -21,38 +21,38 @@ # RUN: llvm-profdata merge a.proftext -o a.profdata # RUN: ld.lld a.o --irpgo-profile=a.profdata --bp-startup-sort=function --verbose-bp-section-orderer --icf=all --gc-sections 2>&1 | FileCheck %s --check-prefix=STARTUP-FUNC-ORDER -# STARTUP-FUNC-ORDER: Ordered 3 sections ([[#]] bytes) using balanced partitioning -# STARTUP-FUNC-ORDER: Total area under the page fault curve: 3. +# STARTUP-FUNC-ORDER: Ordered 4 sections ([[#]] bytes) using balanced partitioning +# STARTUP-FUNC-ORDER: Total area under the page fault curve: 4. # RUN: ld.lld -o out.s a.o --irpgo-profile=a.profdata --bp-startup-sort=function # RUN: llvm-nm -jn out.s | tr '\n' , | FileCheck %s --check-prefix=STARTUP -# STARTUP: s5,s4,s3,s2,s1,A,B,C,F,E,D,merged1,merged2,_start,d4,d3,d2,d1,g1,{{$}} +# STARTUP: s5,s4,s3,s2,s1,A,B,C,L1,F,E,D,merged1,merged2,G,_start,d4,d3,d2,d1,g1,{{$}} # RUN: ld.lld -o out.os a.o --irpgo-profile=a.profdata --bp-startup-sort=function --symbol-ordering-file a.txt # RUN: llvm-nm -jn out.os | tr '\n' , | FileCheck %s --check-prefix=ORDER-STARTUP -# ORDER-STARTUP: s2,s1,s5,s4,s3,A,F,E,D,B,C,merged1,merged2,_start,d3,d2,d4,d1,g1,{{$}} +# ORDER-STARTUP: s2,s1,s5,s4,s3,A,F,E,D,B,C,L1,merged1,merged2,G,_start,d3,d2,d4,d1,g1,{{$}} # RUN: ld.lld -o out.cf a.o --verbose-bp-section-orderer --bp-compression-sort=function 2>&1 | FileCheck %s --check-prefix=BP-COMPRESSION-FUNC # RUN: ld.lld -o out.cf.icf a.o --verbose-bp-section-orderer --bp-compression-sort=function --icf=all --gc-sections 2>&1 | FileCheck %s --check-prefix=BP-COMPRESSION-ICF-FUNC # RUN: llvm-nm -jn out.cf | tr '\n' , | FileCheck %s --check-prefix=CFUNC -# CFUNC: s5,s4,s3,s2,s1,A,F,merged1,merged2,C,E,D,B,_start,d4,d3,d2,d1,g1,{{$}} +# CFUNC: s5,s4,s3,s2,s1,C,E,D,B,G,F,merged1,merged2,A,_start,L1,d4,d3,d2,d1,g1,{{$}} # RUN: ld.lld -o out.cd a.o --verbose-bp-section-orderer --bp-compression-sort=data 2>&1 | FileCheck %s --check-prefix=BP-COMPRESSION-DATA # RUN: llvm-nm -jn out.cd | tr '\n' , | FileCheck %s --check-prefix=CDATA -# CDATA: s5,s3,s4,s2,s1,F,C,E,D,B,A,merged1,merged2,_start,d4,d1,d3,d2,g1,{{$}} +# CDATA: s5,s3,s4,s2,s1,F,C,E,D,B,A,merged1,merged2,L1,G,_start,d4,d1,d3,d2,g1,{{$}} # RUN: ld.lld -o out.cb a.o --verbose-bp-section-orderer --bp-compression-sort=both 2>&1 | FileCheck %s --check-prefix=BP-COMPRESSION-BOTH # RUN: llvm-nm -jn out.cb | tr '\n' , | FileCheck %s --check-prefix=CBOTH -# CBOTH: s5,s3,s4,s2,s1,A,F,merged1,merged2,C,E,D,B,_start,d4,d1,d3,d2,g1,{{$}} +# CBOTH: s5,s3,s4,s2,s1,C,E,D,B,G,F,merged1,merged2,A,_start,L1,d4,d1,d3,d2,g1,{{$}} # RUN: ld.lld -o out.cbs a.o --verbose-bp-section-orderer --bp-compression-sort=both --irpgo-profile=a.profdata --bp-startup-sort=function 2>&1 | FileCheck %s --check-prefix=BP-COMPRESSION-BOTH # RUN: llvm-nm -jn out.cbs | tr '\n' , | FileCheck %s --check-prefix=CBOTH-STARTUP -# CBOTH-STARTUP: s5,s3,s4,s2,s1,A,B,C,F,E,D,merged1,merged2,_start,d4,d1,d3,d2,g1,{{$}} +# CBOTH-STARTUP: s5,s3,s4,s2,s1,A,B,C,L1,F,E,D,merged1,merged2,G,_start,d4,d1,d3,d2,g1,{{$}} -# BP-COMPRESSION-FUNC: Ordered 9 sections ([[#]] bytes) using balanced partitioning -# BP-COMPRESSION-ICF-FUNC: Ordered 8 sections ([[#]] bytes) using balanced partitioning +# BP-COMPRESSION-FUNC: Ordered 11 sections ([[#]] bytes) using balanced partitioning +# BP-COMPRESSION-ICF-FUNC: Ordered 9 sections ([[#]] bytes) using balanced partitioning # BP-COMPRESSION-DATA: Ordered 9 sections ([[#]] bytes) using balanced partitioning -# BP-COMPRESSION-BOTH: Ordered 18 sections ([[#]] bytes) using balanced partitioning +# BP-COMPRESSION-BOTH: Ordered 20 sections ([[#]] bytes) using balanced partitioning #--- a.proftext :ir @@ -63,7 +63,7 @@ 1 # Weight 1 -A, B, C +A, B, C, L1 A # Func Hash: @@ -97,6 +97,14 @@ D # Counter Values: 1 +L1 +# Func Hash: +5555 +# Num Counters: +1 +# Counter Values: +1 + #--- a.txt A F @@ -137,6 +145,9 @@ void A() {} RETAIN int merged1(int a) { return F(a + 101); } int merged2(int a) { return F(a + 101); } +RETAIN static int L1(int a) { return a + 103; } +int G(int a) { return L1(a); } + int _start() { return 0; } #--- gen @@ -148,7 +159,7 @@ clang --target=aarch64-linux-gnu -O0 -ffunction-sections -fdata-sections -fno-as .p2align 2 .type F,@function F: // @F -// %bb.0: // %entry +// %bb.0: sub sp, sp, #32 stp x29, x30, [sp, #16] // 16-byte Folded Spill add x29, sp, #16 @@ -167,7 +178,7 @@ F: // @F .p2align 2 .type C,@function C: // @C -// %bb.0: // %entry +// %bb.0: sub sp, sp, #32 stp x29, x30, [sp, #16] // 16-byte Folded Spill add x29, sp, #16 @@ -186,7 +197,7 @@ C: // @C .p2align 2 .type E,@function E: // @E -// %bb.0: // %entry +// %bb.0: sub sp, sp, #32 stp x29, x30, [sp, #16] // 16-byte Folded Spill add x29, sp, #16 @@ -205,7 +216,7 @@ E: // @E .p2align 2 .type D,@function D: // @D -// %bb.0: // %entry +// %bb.0: sub sp, sp, #32 stp x29, x30, [sp, #16] // 16-byte Folded Spill add x29, sp, #16 @@ -224,7 +235,7 @@ D: // @D .p2align 2 .type B,@function B: // @B -// %bb.0: // %entry +// %bb.0: sub sp, sp, #32 stp x29, x30, [sp, #16] // 16-byte Folded Spill add x29, sp, #16 @@ -243,7 +254,7 @@ B: // @B .p2align 2 .type A,@function A: // @A -// %bb.0: // %entry +// %bb.0: ret .Lfunc_end5: .size A, .Lfunc_end5-A @@ -253,7 +264,7 @@ A: // @A .p2align 2 .type merged1,@function merged1: // @merged1 -// %bb.0: // %entry +// %bb.0: sub sp, sp, #32 stp x29, x30, [sp, #16] // 16-byte Folded Spill add x29, sp, #16 @@ -272,7 +283,7 @@ merged1: // @merged1 .p2align 2 .type merged2,@function merged2: // @merged2 -// %bb.0: // %entry +// %bb.0: sub sp, sp, #32 stp x29, x30, [sp, #16] // 16-byte Folded Spill add x29, sp, #16 @@ -286,16 +297,48 @@ merged2: // @merged2 .Lfunc_end7: .size merged2, .Lfunc_end7-merged2 // -- End function + .section .text.L1,"axR",@progbits + .p2align 2 // -- Begin function L1 + .type L1,@function +L1: // @L1 +// %bb.0: + sub sp, sp, #16 + str w0, [sp, #12] + ldr w8, [sp, #12] + add w0, w8, #103 + add sp, sp, #16 + ret +.Lfunc_end8: + .size L1, .Lfunc_end8-L1 + // -- End function + .section .text.G,"ax",@progbits + .globl G // -- Begin function G + .p2align 2 + .type G,@function +G: // @G +// %bb.0: + sub sp, sp, #32 + stp x29, x30, [sp, #16] // 16-byte Folded Spill + add x29, sp, #16 + stur w0, [x29, #-4] + ldur w0, [x29, #-4] + bl L1 + ldp x29, x30, [sp, #16] // 16-byte Folded Reload + add sp, sp, #32 + ret +.Lfunc_end9: + .size G, .Lfunc_end9-G + // -- End function .section .text._start,"ax",@progbits .globl _start // -- Begin function _start .p2align 2 .type _start,@function _start: // @_start -// %bb.0: // %entry +// %bb.0: mov w0, wzr ret -.Lfunc_end8: - .size _start, .Lfunc_end8-_start +.Lfunc_end10: + .size _start, .Lfunc_end10-_start // -- End function .type s5,@object // @s5 .section .rodata.s5,"a",@progbits @@ -395,3 +438,4 @@ g1: .addrsig_sym B .addrsig_sym A .addrsig_sym merged1 + .addrsig_sym L1 diff --git a/lld/test/ELF/loongarch-call36.s b/lld/test/ELF/loongarch-call36.s index b593fdf..5cc0f2f 100644 --- a/lld/test/ELF/loongarch-call36.s +++ b/lld/test/ELF/loongarch-call36.s @@ -8,14 +8,14 @@ ## hi20 = target - pc + (1 << 17) >> 18 = 0x60020 - 0x20010 + 0x20000 >> 18 = 1 ## lo18 = target - pc & (1 << 18) - 1 = 0x60020 - 0x20010 & 0x3ffff = 16 # EXE1: 20010: pcaddu18i $t0, 1 -# EXE1-NEXT: 20014: jirl $zero, $t0, 16 +# EXE1-NEXT: 20014: jirl $zero, $t0, 16 <foo> # RUN: ld.lld %t/a.o --section-start=.text=0x20010 --section-start=.sec.foo=0x40020 -o %t/exe2 # RUN: llvm-objdump --no-show-raw-insn -d %t/exe2 | FileCheck --match-full-lines %s --check-prefix=EXE2 ## hi20 = target - pc + (1 << 17) >> 18 = 0x40020 - 0x20010 + 0x20000 >> 18 = 1 ## lo18 = target - pc & (1 << 18) - 1 = 0x40020 - 0x20010 & 0x3ffff = -131056 # EXE2: 20010: pcaddu18i $t0, 1 -# EXE2-NEXT: 20014: jirl $zero, $t0, -131056 +# EXE2-NEXT: 20014: jirl $zero, $t0, -131056 <foo> # RUN: ld.lld %t/a.o -shared -T %t/a.t -o %t/a.so # RUN: llvm-readelf -x .got.plt %t/a.so | FileCheck --check-prefix=GOTPLT %s @@ -34,7 +34,7 @@ ## hi20 = foo@plt - pc + (1 << 17) >> 18 = 0x1234520 - 0x1274670 + 0x20000 >> 18 = -1 ## lo18 = foo@plt - pc & (1 << 18) - 1 = 0x1234520 - 0x1274670 & 0x3ffff = -336 # SO-NEXT: pcaddu18i $t0, -1{{$}} -# SO-NEXT: jirl $zero, $t0, -336{{$}} +# SO-NEXT: jirl $zero, $t0, -336 <.plt+0x20>{{$}} # GOTPLT: section '.got.plt': # GOTPLT-NEXT: 0x01274730 00000000 00000000 00000000 00000000 diff --git a/lld/test/ELF/loongarch-relocatable-align.s b/lld/test/ELF/loongarch-relocatable-align.s new file mode 100644 index 0000000..747a58e --- /dev/null +++ b/lld/test/ELF/loongarch-relocatable-align.s @@ -0,0 +1,147 @@ +# REQUIRES: loongarch + +## Test LA64. +# RUN: rm -rf %t && split-file %s %t && cd %t +# RUN: llvm-mc -filetype=obj -triple=loongarch64 -mattr=+relax a.s -o a.o +# RUN: llvm-mc -filetype=obj -triple=loongarch64 -mattr=+relax --defsym ELF64=1 b.s -o b.o +# RUN: llvm-mc -filetype=obj -triple=loongarch64 -mattr=+relax b1.s -o b1.o +# RUN: llvm-mc -filetype=obj -triple=loongarch64 -mattr=+relax c.s -o c.o +# RUN: llvm-mc -filetype=obj -triple=loongarch64 d.s -o d.o + +## No RELAX. Don't synthesize ALIGN. +# RUN: ld.lld -r b.o d.o -o bd.ro +# RUN: llvm-readelf -r bd.ro | FileCheck %s --check-prefix=NOREL + +# NOREL: no relocations + +# RUN: ld.lld -r b.o b.o a.o b.o b1.o c.o d.o -o out.ro +# RUN: llvm-objdump -dr --no-show-raw-insn out.ro | FileCheck %s +# RUN: llvm-readelf -r out.ro | FileCheck %s --check-prefix=CHECK-REL + +# CHECK: <b0>: +# CHECK-NEXT: 0: addi.d $a0, $a1, 1 +# CHECK-NEXT: 4: nop +# CHECK-EMPTY: +# CHECK-NEXT: <b0>: +# CHECK-NEXT: 8: addi.d $a0, $a1, 1 +# CHECK-EMPTY: +# CHECK-NEXT: <_start>: +# CHECK-NEXT: c: pcalau12i $a0, 0 +# CHECK-NEXT: 000000000000000c: R_LARCH_PCALA_HI20 .Ltext1_start +# CHECK-NEXT: 000000000000000c: R_LARCH_RELAX *ABS* +# CHECK-NEXT: 10: addi.d $a0, $a0, 0 +# CHECK-NEXT: 0000000000000010: R_LARCH_PCALA_LO12 .Ltext1_start +# CHECK-NEXT: 0000000000000010: R_LARCH_RELAX *ABS* +# CHECK-NEXT: 14: nop +# CHECK-NEXT: 0000000000000014: R_LARCH_ALIGN *ABS*+0x4 +# CHECK-EMPTY: +# CHECK-NEXT: <b0>: +# CHECK-NEXT: 18: addi.d $a0, $a1, 1 +# CHECK-NEXT: 1c: nop +# CHECK-NEXT: 20: nop +# CHECK-NEXT: 0000000000000020: R_LARCH_ALIGN *ABS*+0x4 +# CHECK-NEXT: 24: nop +# CHECK-EMPTY: +# CHECK-NEXT: <b1>: +# CHECK-NEXT: 28: addi.d $a0, $a1, 3 +# CHECK-EMPTY: +# CHECK-NEXT: <c0>: +# CHECK-NEXT: 2c: addi.d $a0, $a1, 4 +# CHECK-NEXT: 30: nop +# CHECK-NEXT: 0000000000000030: R_LARCH_ALIGN *ABS*+0x4 +# CHECK-EMPTY: +# CHECK-NEXT: <d0>: +# CHECK-NEXT: 34: addi.d $a0, $a1, 5 + +# CHECK-REL: Relocation section '.rela.text' at offset {{.*}} contains 7 entries: +# CHECK-REL: Relocation section '.rela.text1' at offset {{.*}} contains 5 entries: + +## Test LA32. +# RUN: llvm-mc -filetype=obj -triple=loongarch32 -mattr=+relax a.s -o a.32.o +# RUN: llvm-mc -filetype=obj -triple=loongarch32 -mattr=+relax b.s -o b.32.o +# RUN: ld.lld -r a.32.o b.32.o -o out.32.ro +# RUN: ld.lld -Ttext=0x10000 out.32.ro -o out32 +# RUN: llvm-objdump -dr --no-show-raw-insn out32 | FileCheck %s --check-prefix=CHECK32 + +# CHECK32: <_start>: +# CHECK32-NEXT: 10000: pcaddi $a0, 4 +# CHECK32-NEXT: 10004: nop +# CHECK32-EMPTY: +# CHECK32-NEXT: <b0>: +# CHECK32-NEXT: 10008: addi.w $a0, $a1, 1 +# CHECK32: <.Ltext1_start>: +# CHECK32-NEXT: 10010: pcaddi $a1, 0 +# CHECK32-NEXT: 10014: nop +# CHECK32-NEXT: 10018: addi.w $a0, $a1, 2 + +## Test CREL. +# RUN: llvm-mc -filetype=obj -triple=loongarch64 -mattr=+relax --crel a.s -o acrel.o +# RUN: ld.lld -r acrel.o b.o -o out.crel.ro +# RUN: llvm-objdump -dr --no-show-raw-insn out.crel.ro | FileCheck %s --check-prefix=CHECKC + +# CHECKC: <_start>: +# CHECKC-NEXT: 0: pcalau12i $a0, 0 +# CHECKC-NEXT: 0000000000000000: R_LARCH_PCALA_HI20 .Ltext1_start +# CHECKC-NEXT: 0000000000000000: R_LARCH_RELAX *ABS* +# CHECKC-NEXT: 4: addi.d $a0, $a0, 0 +# CHECKC-NEXT: 0000000000000004: R_LARCH_PCALA_LO12 .Ltext1_start +# CHECKC-NEXT: 0000000000000004: R_LARCH_RELAX *ABS* +# CHECKC-NEXT: 8: nop +# CHECKC-NEXT: 0000000000000008: R_LARCH_ALIGN *ABS*+0x4 +# CHECKC-EMPTY: +# CHECKC-NEXT: <b0>: +# CHECKC-NEXT: c: addi.d $a0, $a1, 1 + +#--- a.s +.globl _start +_start: + la.pcrel $a0, .Ltext1_start + +.section .text1,"ax" +.Ltext1_start: + la.pcrel $a1, .Ltext1_start + +#--- b.s +.macro addi dst, src1, src2 +.ifdef ELF64 + addi.d \dst, \src1, \src2 +.else + addi.w \dst, \src1, \src2 +.endif +.endm + +## Needs synthesized ALIGN. +.option push +.option norelax +.balign 8 +b0: + addi $a0, $a1, 1 + +.section .text1,"ax" +.balign 8 + addi $a0, $a1, 2 + +.option pop + +#--- b1.s +# Starts with an ALIGN relocation, don't need synthesized ALIGN. +.option push +.option norelax + .reloc ., R_LARCH_ALIGN, 4 + nop +.balign 8 +b1: + addi.d $a0, $a1, 3 +.option pop + +#--- c.s +## Alignment == 4, don't need synthesized ALIGN. +.balign 4 +c0: + addi.d $a0, $a1, 4 + +#--- d.s +## Needs synthesized ALIGN. +.balign 8 +d0: + addi.d $a0, $a1, 5 diff --git a/lld/test/ELF/riscv-relax-align.s b/lld/test/ELF/riscv-relax-align.s index 3efc774f..b3db2c94 100644 --- a/lld/test/ELF/riscv-relax-align.s +++ b/lld/test/ELF/riscv-relax-align.s @@ -10,7 +10,7 @@ # 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: llvm-mc -filetype=obj -triple=riscv64 -mattr=+relax %s -riscv-align-rvc=0 -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 @@ -29,7 +29,7 @@ # CHECK-DAG: 00010000 g .text {{0*}}38 _start # CHECK: <_start>: -# CHECK-NEXT: addi a0, a0, 0x1 +# CHECK-NEXT: lui a0, 0x10 # CHECK-EMPTY: # CHECK-NEXT: <a>: # CHECK-NEXT: addi a0, a0, 0x2 @@ -82,7 +82,9 @@ # GC-NOT: <d>: # CHECKR: <_start>: -# CHECKR-NEXT: addi a0, a0, 0x1 +# CHECKR-NEXT: lui a0, 0x0 +# CHECKR-NEXT: 0000000000000000: R_RISCV_HI20 _start +# CHECKR-NEXT: 0000000000000000: R_RISCV_RELAX *ABS* # CHECKR-EMPTY: # CHECKR-NEXT: <a>: # CHECKR-NEXT: addi a0, a0, 0x2 @@ -116,7 +118,7 @@ .global _start _start: - addi a0, a0, 0x1 + lui a0, %hi(_start) a: addi a0, a0, 0x2 b: diff --git a/lld/test/ELF/riscv-relax-emit-relocs.s b/lld/test/ELF/riscv-relax-emit-relocs.s index 26ea0d1..358a288 100644 --- a/lld/test/ELF/riscv-relax-emit-relocs.s +++ b/lld/test/ELF/riscv-relax-emit-relocs.s @@ -3,11 +3,11 @@ # RUN: rm -rf %t && mkdir %t && cd %t -# RUN: llvm-mc -filetype=obj -triple=riscv32 -mattr=+relax %s -o 32.o +# RUN: llvm-mc -filetype=obj -triple=riscv32 -mattr=+relax %s -o 32.o -riscv-align-rvc=0 # RUN: ld.lld -Ttext=0x10000 --emit-relocs 32.o -o 32 # RUN: llvm-objdump -dr --no-show-raw-insn -M no-aliases 32 | FileCheck %s -# RUN: llvm-mc -filetype=obj -triple=riscv64 -mattr=+relax %s -o 64.o +# RUN: llvm-mc -filetype=obj -triple=riscv64 -mattr=+relax %s -o 64.o -riscv-align-rvc=0 # RUN: ld.lld -Ttext=0x10000 --emit-relocs 64.o -o 64 # RUN: llvm-objdump -dr --no-show-raw-insn -M no-aliases 64 | FileCheck %s diff --git a/lld/test/ELF/riscv-relocatable-align.s b/lld/test/ELF/riscv-relocatable-align.s new file mode 100644 index 0000000..9cd59e9 --- /dev/null +++ b/lld/test/ELF/riscv-relocatable-align.s @@ -0,0 +1,140 @@ +# REQUIRES: riscv +# RUN: rm -rf %t && split-file %s %t && cd %t +# RUN: llvm-mc -filetype=obj -triple=riscv64 -mattr=+c,+relax a.s -o ac.o +# RUN: llvm-mc -filetype=obj -triple=riscv64 -mattr=+c,+relax b.s -o bc.o +# RUN: llvm-mc -filetype=obj -triple=riscv64 -mattr=+c,+relax b1.s -o b1c.o +# RUN: llvm-mc -filetype=obj -triple=riscv64 -mattr=+c,+relax c.s -o cc.o +# RUN: llvm-mc -filetype=obj -triple=riscv64 -mattr=+c d.s -o dc.o + +## No RELAX. Don't synthesize ALIGN. +# RUN: ld.lld -r bc.o dc.o -o bd.ro + +# NOREL: no relocations + +# RUN: ld.lld -r bc.o bc.o ac.o bc.o b1c.o cc.o dc.o -o out.ro +# RUN: llvm-objdump -dr -M no-aliases out.ro | FileCheck %s +# RUN: llvm-readelf -r out.ro | FileCheck %s --check-prefix=CHECK-REL + +# RUN: llvm-mc -filetype=obj -triple=riscv32 -mattr=+relax a.s -o a.o +# RUN: llvm-mc -filetype=obj -triple=riscv32 -mattr=+relax b.s -o b.o +# RUN: llvm-mc -filetype=obj -triple=riscv32 -mattr=+relax d.s -o d.o +# RUN: ld.lld -r a.o b.o d.o -o out0.ro +# RUN: ld.lld -Ttext=0x10000 out0.ro -o out0 +# RUN: llvm-objdump -dr -M no-aliases out0 | FileCheck %s --check-prefix=CHECK1 + +# CHECK: <b0>: +# CHECK-NEXT: 0: 00158513 addi a0, a1, 0x1 +# CHECK-NEXT: 4: 0001 c.nop +# CHECK-NEXT: 6: 0001 c.nop +# CHECK-EMPTY: +# CHECK-NEXT: <b0>: +# CHECK-NEXT: 8: 00158513 addi a0, a1, 0x1 +# CHECK-EMPTY: +# CHECK-NEXT: <_start>: +# CHECK-NEXT: c: 00000097 auipc ra, 0x0 +# CHECK-NEXT: 000000000000000c: R_RISCV_CALL_PLT foo +# CHECK-NEXT: 000000000000000c: R_RISCV_RELAX *ABS* +# CHECK-NEXT: 10: 000080e7 jalr ra, 0x0(ra) <_start> +# CHECK-NEXT: 14: 0001 c.nop +# CHECK-NEXT: 0000000000000014: R_RISCV_ALIGN *ABS*+0x6 +# CHECK-NEXT: 16: 0001 c.nop +# CHECK-NEXT: 18: 0001 c.nop +# CHECK-EMPTY: +# CHECK-NEXT: <b0>: +# CHECK-NEXT: 1a: 00158513 addi a0, a1, 0x1 +# CHECK-NEXT: 1e: 0001 c.nop +# CHECK-NEXT: 20: 0001 c.nop +# CHECK-NEXT: 0000000000000020: R_RISCV_ALIGN *ABS*+0x6 +# CHECK-NEXT: 22: 0001 c.nop +# CHECK-NEXT: 24: 00000013 addi zero, zero, 0x0 +# CHECK-EMPTY: +# CHECK-NEXT: <b0>: +# CHECK-NEXT: 28: 00158513 addi a0, a1, 0x1 +# CHECK-EMPTY: +# CHECK-NEXT: <c0>: +# CHECK-NEXT: 2c: 00000097 auipc ra, 0x0 +# CHECK-NEXT: 000000000000002c: R_RISCV_CALL_PLT foo +# CHECK-NEXT: 000000000000002c: R_RISCV_RELAX *ABS* +# CHECK-NEXT: 30: 000080e7 jalr ra, 0x0(ra) <c0> +# CHECK-NEXT: 34: 0001 c.nop +# CHECK-NEXT: 0000000000000034: R_RISCV_ALIGN *ABS*+0x2 +# CHECK-EMPTY: +# CHECK-NEXT: <d0>: +# CHECK-NEXT: 36: 00258513 addi a0, a1, 0x2 + +# CHECK-REL: Relocation section '.rela.text' at offset {{.*}} contains 7 entries: +# CHECK-REL: Relocation section '.rela.text1' at offset {{.*}} contains 3 entries: + +# CHECK1: <_start>: +# CHECK1-NEXT: 010000ef jal ra, 0x10010 <foo> +# CHECK1-NEXT: 00000013 addi zero, zero, 0x0 +# CHECK1-EMPTY: +# CHECK1-NEXT: <b0>: +# CHECK1-NEXT: 00158513 addi a0, a1, 0x1 +# CHECK1-EMPTY: +# CHECK1-NEXT: <d0>: +# CHECK1-NEXT: 00258513 addi a0, a1, 0x2 + +## Test CREL. +# RUN: llvm-mc -filetype=obj -triple=riscv64 -mattr=+c,+relax --crel a.s -o acrel.o +# RUN: ld.lld -r acrel.o bc.o -o out1.ro +# RUN: llvm-objdump -dr -M no-aliases out1.ro | FileCheck %s --check-prefix=CHECK2 + +# CHECK2: <_start>: +# CHECK2-NEXT: 0: 00000097 auipc ra, 0x0 +# CHECK2-NEXT: 0000000000000000: R_RISCV_CALL_PLT foo +# CHECK2-NEXT: 0000000000000000: R_RISCV_RELAX *ABS* +# CHECK2-NEXT: 4: 000080e7 jalr ra, 0x0(ra) <_start> +# CHECK2-NEXT: 8: 0001 c.nop +# CHECK2-NEXT: 0000000000000008: R_RISCV_ALIGN *ABS*+0x6 +# CHECK2-NEXT: a: 0001 c.nop +# CHECK2-NEXT: c: 0001 c.nop +# CHECK2-EMPTY: +# CHECK2-NEXT: <b0>: +# CHECK2-NEXT: e: 00158513 addi a0, a1, 0x1 + +#--- a.s +.globl _start +_start: + call foo + +.section .text1,"ax" +.globl foo +foo: + call foo + +#--- b.s +## Needs synthesized ALIGN +.option push +.option norelax +.balign 8 +b0: + addi a0, a1, 1 + +.section .text1,"ax" +.balign 8 + addi a0, a1, 1 + +.option pop + +#--- b1.s +.option push +.option norelax + .reloc ., R_RISCV_ALIGN, 6 + addi x0, x0, 0 + c.nop +.balign 8 +b0: + addi a0, a1, 1 +.option pop + +#--- c.s +.balign 2 +c0: + call foo + +#--- d.s +## Needs synthesized ALIGN +.balign 4 +d0: + addi a0, a1, 2 diff --git a/lld/test/MachO/invalid/Inputs/macho-trie-node-loop b/lld/test/MachO/invalid/Inputs/macho-trie-node-loop Binary files differnew file mode 100755 index 0000000..b94dfa2 --- /dev/null +++ b/lld/test/MachO/invalid/Inputs/macho-trie-node-loop diff --git a/lld/test/MachO/invalid/export-trie-node-loop.s b/lld/test/MachO/invalid/export-trie-node-loop.s new file mode 100644 index 0000000..fe99159 --- /dev/null +++ b/lld/test/MachO/invalid/export-trie-node-loop.s @@ -0,0 +1,9 @@ +# REQUIRES: x86 +# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %s -o %t.o +# RUN: not %lld -o %t %t.o %S/Inputs/macho-trie-node-loop 2>&1 | FileCheck %s +# CHECK: error: +# CHECK-SAME: /Inputs/macho-trie-node-loop: export trie child node infinite loop + +.globl _main +_main: + ret diff --git a/lld/test/MachO/stabs.s b/lld/test/MachO/stabs.s index 968656d..e32b9fc 100644 --- a/lld/test/MachO/stabs.s +++ b/lld/test/MachO/stabs.s @@ -63,9 +63,18 @@ # RUN: dsymutil -s %t/test-rel | grep 'N_OSO' | FileCheck %s -D#TEST_TIME=0x10 -D#FOO_TIME=0x20 --check-prefix=REL-PATH-NO-SLASH # RUN: cd %t && ZERO_AR_DATE=0 %lld -lSystem test.o foo.o no-debug.o -oso_prefix "." -o %t/test-rel-dot # RUN: dsymutil -s %t/test-rel-dot | grep 'N_OSO' | FileCheck %s -D#TEST_TIME=0x10 -D#FOO_TIME=0x20 --check-prefix=REL-DOT -## Set HOME to %t (for ~ to expand to) -# RUN: cd %t && env HOME=%t ZERO_AR_DATE=0 %lld -lSystem test.o foo.o no-debug.o -oso_prefix "~" -o %t/test-rel-tilde -# RUN: dsymutil -s %t/test-rel-tilde | grep 'N_OSO' | FileCheck %s -D#TEST_TIME=0x10 -D#FOO_TIME=0x20 --check-prefix=REL-PATH +# RUN: cd %t && ZERO_AR_DATE=0 %lld -lSystem ./test.o ./foo.o ./no-debug.o -oso_prefix "." -o %t/test-rel-dot +# RUN: dsymutil -s %t/test-rel-dot | grep 'N_OSO' | FileCheck %s -D#TEST_TIME=0x10 -D#FOO_TIME=0x20 --check-prefix=REL-DOT-EXPLICIT + +## Check that symlinks are not expanded when -oso_prefix . is used. +# RUN: mkdir -p %t/private/var/folders/tmp && ln -s private/var %t/var +# RUN: cp %t/test.o %t/foo.o %t/no-debug.o %t/private/var/folders/tmp +# RUN: env TZ=GMT touch -t "197001010000.16" %t/private/var/folders/tmp/test.o +# RUN: env TZ=GMT touch -t "197001010000.32" %t/private/var/folders/tmp/foo.o +# RUN: cd %t/var/folders/tmp && ZERO_AR_DATE=0 %lld -lSystem test.o foo.o no-debug.o -oso_prefix "." -o test-rel-symlink +# RUN: dsymutil -s %t/private/var/folders/tmp/test-rel-symlink | grep 'N_OSO' | FileCheck %s -D#TEST_TIME=0x10 -D#FOO_TIME=0x20 --check-prefix=REL-DOT +# RUN: cd %t/var/folders/tmp && ZERO_AR_DATE=0 %lld -lSystem ./test.o ./foo.o ./no-debug.o -oso_prefix "." -o test-rel-symlink +# RUN: dsymutil -s %t/private/var/folders/tmp/test-rel-symlink | grep 'N_OSO' | FileCheck %s -D#TEST_TIME=0x10 -D#FOO_TIME=0x20 --check-prefix=REL-DOT-EXPLICIT ## Check that we don't emit DWARF or stabs when -S is used # RUN: %lld -lSystem test.o foo.o no-debug.o -S -o %t/test-no-debug @@ -91,6 +100,7 @@ # REL-PATH: (N_OSO ) 03 0001 [[#%.16x,TEST_TIME]] '/test.o' # REL-PATH-NO-SLASH: (N_OSO ) 03 0001 [[#%.16x,TEST_TIME]] 'test.o' # REL-DOT: (N_OSO ) 03 0001 [[#%.16x,TEST_TIME]] 'test.o' +# REL-DOT-EXPLICIT: (N_OSO ) 03 0001 [[#%.16x,TEST_TIME]] './test.o' # CHECK-NEXT: (N_STSYM ) [[#%.2d,MORE_DATA_ID + 1]] 0000 [[#%.16x,STATIC:]] '_static_var' # CHECK-NEXT: (N_FUN ) [[#%.2d,TEXT_ID + 1]] 0000 [[#%.16x,MAIN:]] '_main' # CHECK-NEXT: (N_FUN ) 00 0000 0000000000000006{{$}} diff --git a/lld/test/wasm/libsearch.s b/lld/test/wasm/libsearch.s index 2333651..20f1e9b 100644 --- a/lld/test/wasm/libsearch.s +++ b/lld/test/wasm/libsearch.s @@ -93,6 +93,11 @@ // RUN: wasm-ld -pie --experimental-pic --emit-relocs --no-gc-sections -o %t3 %t.o -L%t.dir -Bstatic -call_shared -lls // RUN: llvm-readobj --symbols %t3 | FileCheck --check-prefix=DYNAMIC %s +/// -r implies -Bstatic and has precedence over -Bdynamic. +// RUN: wasm-ld -r -Bdynamic %t.o -L%t.dir -lls -o %t3.ro +// RUN: llvm-readobj -s -h %t3.ro | FileCheck --check-prefix=RELOCATABLE %s +// RELOCATABLE: Name: _static + .globl _start, _bar _start: .functype _start () -> () diff --git a/lld/wasm/Driver.cpp b/lld/wasm/Driver.cpp index 1c5d21c0..b57d774 100644 --- a/lld/wasm/Driver.cpp +++ b/lld/wasm/Driver.cpp @@ -406,7 +406,8 @@ void LinkerDriver::createFiles(opt::InputArgList &args) { ctx.arg.isStatic = true; break; case OPT_Bdynamic: - ctx.arg.isStatic = false; + if (!ctx.arg.relocatable) + ctx.arg.isStatic = false; break; case OPT_whole_archive: inWholeArchive = true; |