aboutsummaryrefslogtreecommitdiff
path: root/lld
diff options
context:
space:
mode:
Diffstat (limited to 'lld')
-rw-r--r--lld/COFF/SymbolTable.cpp1
-rw-r--r--lld/ELF/Arch/AArch64.cpp12
-rw-r--r--lld/ELF/Arch/LoongArch.cpp128
-rw-r--r--lld/ELF/Arch/RISCV.cpp128
-rw-r--r--lld/ELF/BPSectionOrderer.cpp10
-rw-r--r--lld/ELF/LinkerScript.cpp14
-rw-r--r--lld/ELF/OutputSections.cpp16
-rw-r--r--lld/ELF/Relocations.cpp6
-rw-r--r--lld/ELF/Relocations.h2
-rw-r--r--lld/ELF/Target.h3
-rw-r--r--lld/ELF/Writer.cpp4
-rw-r--r--lld/MachO/Config.h1
-rw-r--r--lld/MachO/Driver.cpp204
-rw-r--r--lld/MachO/ExportTrie.cpp35
-rw-r--r--lld/MachO/ExportTrie.h3
-rw-r--r--lld/MachO/InputFiles.cpp13
-rw-r--r--lld/MachO/Options.td3
-rw-r--r--lld/docs/ReleaseNotes.rst3
-rw-r--r--lld/docs/ReleaseNotesTemplate.txt48
-rw-r--r--lld/test/COFF/alternatename-lto.ll25
-rw-r--r--lld/test/ELF/aarch64-patchinst.s88
-rw-r--r--lld/test/ELF/bp-section-orderer.s90
-rw-r--r--lld/test/ELF/loongarch-call36.s6
-rw-r--r--lld/test/ELF/loongarch-relocatable-align.s147
-rw-r--r--lld/test/ELF/riscv-relax-align.s10
-rw-r--r--lld/test/ELF/riscv-relax-emit-relocs.s4
-rw-r--r--lld/test/ELF/riscv-relocatable-align.s140
-rwxr-xr-xlld/test/MachO/invalid/Inputs/macho-trie-node-loopbin0 -> 8752 bytes
-rw-r--r--lld/test/MachO/invalid/export-trie-node-loop.s9
-rw-r--r--lld/test/MachO/stabs.s16
-rw-r--r--lld/test/wasm/libsearch.s5
-rw-r--r--lld/wasm/Driver.cpp3
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
new file mode 100755
index 0000000..b94dfa2
--- /dev/null
+++ b/lld/test/MachO/invalid/Inputs/macho-trie-node-loop
Binary files differ
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;