aboutsummaryrefslogtreecommitdiff
path: root/lld/ELF
diff options
context:
space:
mode:
Diffstat (limited to 'lld/ELF')
-rw-r--r--lld/ELF/Arch/LoongArch.cpp126
-rw-r--r--lld/ELF/Arch/Mips.cpp4
-rw-r--r--lld/ELF/Config.h3
-rw-r--r--lld/ELF/Driver.cpp3
-rw-r--r--lld/ELF/InputFiles.cpp131
-rw-r--r--lld/ELF/InputSection.cpp5
-rw-r--r--lld/ELF/Relocations.cpp29
-rw-r--r--lld/ELF/Relocations.h1
-rw-r--r--lld/ELF/SyntheticSections.cpp56
-rw-r--r--lld/ELF/SyntheticSections.h71
-rw-r--r--lld/ELF/Target.cpp3
-rw-r--r--lld/ELF/Target.h35
-rw-r--r--lld/ELF/Writer.cpp20
13 files changed, 346 insertions, 141 deletions
diff --git a/lld/ELF/Arch/LoongArch.cpp b/lld/ELF/Arch/LoongArch.cpp
index 15dcddb..8802c8c 100644
--- a/lld/ELF/Arch/LoongArch.cpp
+++ b/lld/ELF/Arch/LoongArch.cpp
@@ -46,6 +46,8 @@ public:
private:
void tlsdescToIe(uint8_t *loc, const Relocation &rel, uint64_t val) const;
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;
};
} // end anonymous namespace
@@ -809,10 +811,13 @@ static void relaxPCHi20Lo12(Ctx &ctx, const InputSection &sec, size_t i,
// address.
// Meanwhile skip undefined, preemptible and STT_GNU_IFUNC symbols, because
// these symbols may be resolve in runtime.
+ // Moreover, relaxation can only occur if the addends of both relocations are
+ // zero for GOT references.
if (rHi20.type == R_LARCH_GOT_PC_HI20 &&
- (!rHi20.sym->isDefined() || rHi20.sym->isPreemptible ||
- rHi20.sym->isGnuIFunc() ||
- (ctx.arg.isPic && !cast<Defined>(*rHi20.sym).section)))
+ (!rHi20.sym || rHi20.sym != rLo12.sym || !rHi20.sym->isDefined() ||
+ rHi20.sym->isPreemptible || rHi20.sym->isGnuIFunc() ||
+ (ctx.arg.isPic && !cast<Defined>(*rHi20.sym).section) ||
+ rHi20.addend != 0 || rLo12.addend != 0))
return;
uint64_t dest = 0;
@@ -1152,6 +1157,78 @@ void LoongArch::tlsdescToLe(uint8_t *loc, const Relocation &rel,
}
}
+// Try GOT indirection to PC relative optimization.
+// From:
+// * pcalau12i $a0, %got_pc_hi20(sym_got)
+// * ld.w/d $a0, $a0, %got_pc_lo12(sym_got)
+// To:
+// * pcalau12i $a0, %pc_hi20(sym)
+// * addi.w/d $a0, $a0, %pc_lo12(sym)
+//
+// Note: Althouth the optimization has been performed, the GOT entries still
+// exists, similarly to AArch64. Eliminating the entries will increase code
+// complexity.
+bool LoongArch::tryGotToPCRel(uint8_t *loc, const Relocation &rHi20,
+ const Relocation &rLo12, uint64_t secAddr) const {
+ // Check if the relocations apply to consecutive instructions.
+ if (rHi20.offset + 4 != rLo12.offset)
+ return false;
+
+ // Check if the relocations reference the same symbol and skip undefined,
+ // preemptible and STT_GNU_IFUNC symbols.
+ if (!rHi20.sym || rHi20.sym != rLo12.sym || !rHi20.sym->isDefined() ||
+ rHi20.sym->isPreemptible || rHi20.sym->isGnuIFunc())
+ return false;
+
+ // GOT references to absolute symbols can't be relaxed to use PCALAU12I/ADDI
+ // in position-independent code because these instructions produce a relative
+ // address.
+ if ((ctx.arg.isPic && !cast<Defined>(*rHi20.sym).section))
+ return false;
+
+ // Check if the addends of the both relocations are zero.
+ if (rHi20.addend != 0 || rLo12.addend != 0)
+ return false;
+
+ const uint32_t currInsn = read32le(loc);
+ const uint32_t nextInsn = read32le(loc + 4);
+ const uint32_t ldOpcode = ctx.arg.is64 ? LD_D : LD_W;
+ // Check if the first instruction is PCALAU12I and the second instruction is
+ // LD.
+ if ((currInsn & 0xfe000000) != PCALAU12I ||
+ (nextInsn & 0xffc00000) != ldOpcode)
+ return false;
+
+ // Check if use the same register.
+ if (getD5(currInsn) != getJ5(nextInsn) || getJ5(nextInsn) != getD5(nextInsn))
+ return false;
+
+ Symbol &sym = *rHi20.sym;
+ uint64_t symLocal = sym.getVA(ctx);
+ const int64_t displace = symLocal - getLoongArchPage(secAddr + rHi20.offset);
+ // Check if the symbol address is in
+ // [(PC & ~0xfff) - 2GiB - 0x800, (PC & ~0xfff) + 2GiB - 0x800).
+ const int64_t underflow = -0x80000000LL - 0x800;
+ const int64_t overflow = 0x80000000LL - 0x800;
+ if (!(displace >= underflow && displace < overflow))
+ return false;
+
+ Relocation newRHi20 = {RE_LOONGARCH_PAGE_PC, R_LARCH_PCALA_HI20, rHi20.offset,
+ rHi20.addend, &sym};
+ Relocation newRLo12 = {R_ABS, R_LARCH_PCALA_LO12, rLo12.offset, rLo12.addend,
+ &sym};
+ uint64_t pageDelta =
+ getLoongArchPageDelta(symLocal, secAddr + rHi20.offset, rHi20.type);
+ // pcalau12i $a0, %pc_hi20
+ write32le(loc, insn(PCALAU12I, getD5(currInsn), 0, 0));
+ relocate(loc, newRHi20, pageDelta);
+ // addi.w/d $a0, $a0, %pc_lo12
+ write32le(loc + 4, insn(ctx.arg.is64 ? ADDI_D : ADDI_W, getD5(nextInsn),
+ getJ5(nextInsn), 0));
+ relocate(loc + 4, newRLo12, SignExtend64(symLocal, 64));
+ return true;
+}
+
// During TLSDESC GD_TO_IE, the converted code sequence always includes an
// instruction related to the Lo12 relocation (ld.[wd]). To obtain correct val
// in `getRelocTargetVA`, expr of this instruction should be adjusted to
@@ -1169,6 +1246,30 @@ RelExpr LoongArch::adjustTlsExpr(RelType type, RelExpr expr) const {
return expr;
}
+static bool pairForGotRels(ArrayRef<Relocation> relocs) {
+ // Check if R_LARCH_GOT_PC_HI20 and R_LARCH_GOT_PC_LO12 always appear in
+ // pairs.
+ size_t i = 0;
+ const size_t size = relocs.size();
+ for (; i != size; ++i) {
+ if (relocs[i].type == R_LARCH_GOT_PC_HI20) {
+ if (i + 1 < size && relocs[i + 1].type == R_LARCH_GOT_PC_LO12) {
+ ++i;
+ continue;
+ }
+ if (relaxable(relocs, i) && i + 2 < size &&
+ relocs[i + 2].type == R_LARCH_GOT_PC_LO12) {
+ i += 2;
+ continue;
+ }
+ break;
+ } else if (relocs[i].type == R_LARCH_GOT_PC_LO12) {
+ break;
+ }
+ }
+ return i == size;
+}
+
void LoongArch::relocateAlloc(InputSectionBase &sec, uint8_t *buf) const {
const unsigned bits = ctx.arg.is64 ? 64 : 32;
uint64_t secAddr = sec.getOutputSection()->addr;
@@ -1178,6 +1279,7 @@ void LoongArch::relocateAlloc(InputSectionBase &sec, uint8_t *buf) const {
secAddr += ehIn->getParent()->outSecOff;
bool isExtreme = false, isRelax = false;
const MutableArrayRef<Relocation> relocs = sec.relocs();
+ const bool isPairForGotRels = pairForGotRels(relocs);
for (size_t i = 0, size = relocs.size(); i != size; ++i) {
Relocation &rel = relocs[i];
uint8_t *loc = buf + rel.offset;
@@ -1261,6 +1363,24 @@ void LoongArch::relocateAlloc(InputSectionBase &sec, uint8_t *buf) const {
tlsdescToLe(loc, rel, val);
}
continue;
+ case RE_LOONGARCH_GOT_PAGE_PC:
+ // In LoongArch, we try GOT indirection to PC relative optimization in
+ // normal or medium code model, whether or not with R_LARCH_RELAX
+ // relocation. Moreover, if the original code sequence can be relaxed to a
+ // single instruction `pcaddi`, the first instruction will be removed and
+ // it will not reach here.
+ if (isPairForGotRels && rel.type == R_LARCH_GOT_PC_HI20) {
+ bool isRelax = relaxable(relocs, i);
+ const Relocation lo12Rel = isRelax ? relocs[i + 2] : relocs[i + 1];
+ if (lo12Rel.type == R_LARCH_GOT_PC_LO12 &&
+ tryGotToPCRel(loc, rel, lo12Rel, secAddr)) {
+ // isRelax: skip relocations R_LARCH_RELAX, R_LARCH_GOT_PC_LO12
+ // !isRelax: skip relocation R_LARCH_GOT_PC_LO12
+ i += isRelax ? 2 : 1;
+ continue;
+ }
+ }
+ break;
default:
break;
}
diff --git a/lld/ELF/Arch/Mips.cpp b/lld/ELF/Arch/Mips.cpp
index 91c7f15..f88b021c 100644
--- a/lld/ELF/Arch/Mips.cpp
+++ b/lld/ELF/Arch/Mips.cpp
@@ -40,6 +40,10 @@ public:
};
} // namespace
+uint64_t elf::getMipsPageAddr(uint64_t addr) {
+ return (addr + 0x8000) & ~0xffff;
+}
+
template <class ELFT> MIPS<ELFT>::MIPS(Ctx &ctx) : TargetInfo(ctx) {
gotPltHeaderEntriesNum = 2;
defaultMaxPageSize = 65536;
diff --git a/lld/ELF/Config.h b/lld/ELF/Config.h
index d9639b0..a83a4c1 100644
--- a/lld/ELF/Config.h
+++ b/lld/ELF/Config.h
@@ -404,6 +404,7 @@ struct Config {
bool zIfuncNoplt;
bool zInitfirst;
bool zInterpose;
+ bool zKeepDataSectionPrefix;
bool zKeepTextSectionPrefix;
bool zLrodataAfterBss;
bool zNoBtCfi;
@@ -701,6 +702,8 @@ struct Ctx : CommonLinkerContext {
std::unique_ptr<llvm::TarWriter> tar;
// InputFile for linker created symbols with no source location.
InputFile *internalFile = nullptr;
+ // Dummy Undefined for relocations without a symbol.
+ Undefined *dummySym = nullptr;
// True if symbols can be exported (isExported) or preemptible.
bool hasDynsym = false;
// True if SHT_LLVM_SYMPART is used.
diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp
index 21d228e..6c2f318 100644
--- a/lld/ELF/Driver.cpp
+++ b/lld/ELF/Driver.cpp
@@ -1625,6 +1625,8 @@ static void readConfigs(Ctx &ctx, opt::InputArgList &args) {
ctx.arg.zIfuncNoplt = hasZOption(args, "ifunc-noplt");
ctx.arg.zInitfirst = hasZOption(args, "initfirst");
ctx.arg.zInterpose = hasZOption(args, "interpose");
+ ctx.arg.zKeepDataSectionPrefix = getZFlag(
+ args, "keep-data-section-prefix", "nokeep-data-section-prefix", false);
ctx.arg.zKeepTextSectionPrefix = getZFlag(
args, "keep-text-section-prefix", "nokeep-text-section-prefix", false);
ctx.arg.zLrodataAfterBss =
@@ -3138,6 +3140,7 @@ template <class ELFT> void LinkerDriver::link(opt::InputArgList &args) {
ctx.symtab->insert(arg->getValue())->traced = true;
ctx.internalFile = createInternalFile(ctx, "<internal>");
+ ctx.dummySym = make<Undefined>(ctx.internalFile, "", STB_LOCAL, 0, 0);
// Handle -u/--undefined before input files. If both a.a and b.so define foo,
// -u foo a.a b.so will extract a.a.
diff --git a/lld/ELF/InputFiles.cpp b/lld/ELF/InputFiles.cpp
index 71e72e7..a5921fe 100644
--- a/lld/ELF/InputFiles.cpp
+++ b/lld/ELF/InputFiles.cpp
@@ -20,7 +20,9 @@
#include "llvm/ADT/CachedHashString.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/LTO/LTO.h"
+#include "llvm/Object/Archive.h"
#include "llvm/Object/IRObjectFile.h"
+#include "llvm/Support/AArch64AttributeParser.h"
#include "llvm/Support/ARMAttributeParser.h"
#include "llvm/Support/ARMBuildAttributes.h"
#include "llvm/Support/Endian.h"
@@ -537,6 +539,41 @@ uint32_t ObjFile<ELFT>::getSectionIndex(const Elf_Sym &sym) const {
this);
}
+template <class ELFT>
+static void
+handleAArch64BAAndGnuProperties(ObjFile<ELFT> *file, Ctx &ctx,
+ const AArch64BuildAttrSubsections &baInfo) {
+ if (file->aarch64PauthAbiCoreInfo) {
+ // Check for data mismatch.
+ if (file->aarch64PauthAbiCoreInfo) {
+ if (baInfo.Pauth.TagPlatform != file->aarch64PauthAbiCoreInfo->platform ||
+ baInfo.Pauth.TagSchema != file->aarch64PauthAbiCoreInfo->version)
+ Err(ctx) << file
+ << " GNU properties and build attributes have conflicting "
+ "AArch64 PAuth data";
+ }
+ if (baInfo.AndFeatures != file->andFeatures)
+ Err(ctx) << file
+ << " GNU properties and build attributes have conflicting "
+ "AArch64 PAuth data";
+ } else {
+ // When BuildAttributes are missing, PauthABI value defaults to (TagPlatform
+ // = 0, TagSchema = 0). GNU properties do not write PAuthAbiCoreInfo if GNU
+ // property is not present. To match this behaviour, we only write
+ // PAuthAbiCoreInfo when there is at least one non-zero value. The
+ // specification reserves TagPlatform = 0, TagSchema = 1 values to match the
+ // 'Invalid' GNU property section with platform = 0, version = 0.
+ if (baInfo.Pauth.TagPlatform || baInfo.Pauth.TagSchema) {
+ if (baInfo.Pauth.TagPlatform == 0 && baInfo.Pauth.TagSchema == 1)
+ file->aarch64PauthAbiCoreInfo = {0, 0};
+ else
+ file->aarch64PauthAbiCoreInfo = {baInfo.Pauth.TagPlatform,
+ baInfo.Pauth.TagSchema};
+ }
+ file->andFeatures = baInfo.AndFeatures;
+ }
+}
+
template <class ELFT> void ObjFile<ELFT>::parse(bool ignoreComdats) {
object::ELFFile<ELFT> obj = this->getObj();
// Read a section table. justSymbols is usually false.
@@ -554,6 +591,7 @@ template <class ELFT> void ObjFile<ELFT>::parse(bool ignoreComdats) {
sections.resize(size);
for (size_t i = 0; i != size; ++i) {
const Elf_Shdr &sec = objSections[i];
+
if (LLVM_LIKELY(sec.sh_type == SHT_PROGBITS))
continue;
if (LLVM_LIKELY(sec.sh_type == SHT_GROUP)) {
@@ -637,13 +675,6 @@ template <class ELFT> void ObjFile<ELFT>::parse(bool ignoreComdats) {
}
break;
case EM_AARCH64:
- // FIXME: BuildAttributes have been implemented in llvm, but not yet in
- // lld. Remove the section so that it does not accumulate in the output
- // file. When support is implemented we expect not to output a build
- // attributes section in files of type ET_EXEC or ET_SHARED, but ld -r
- // ouptut will need a single merged attributes section.
- if (sec.sh_type == SHT_AARCH64_ATTRIBUTES)
- sections[i] = &InputSection::discarded;
// Producing a static binary with MTE globals is not currently supported,
// remove all SHT_AARCH64_MEMTAG_GLOBALS_STATIC sections as they're unused
// medatada, and we don't want them to end up in the output file for
@@ -744,6 +775,8 @@ void ObjFile<ELFT>::initializeSections(bool ignoreComdats,
StringRef shstrtab = CHECK2(obj.getSectionStringTable(objSections), this);
uint64_t size = objSections.size();
SmallVector<ArrayRef<Elf_Word>, 0> selectedGroups;
+ AArch64BuildAttrSubsections aarch64BAsubSections;
+ bool hasAArch64BuildAttributes = false;
for (size_t i = 0; i != size; ++i) {
if (this->sections[i] == &InputSection::discarded)
continue;
@@ -775,6 +808,26 @@ void ObjFile<ELFT>::initializeSections(bool ignoreComdats,
continue;
}
+ // Processor-specific types that do not use the following switch statement.
+ //
+ // Extract Build Attributes section contents into aarch64BAsubSections.
+ // Input objects may contain both build Build Attributes and GNU
+ // properties. We delay processing Build Attributes until we have finished
+ // reading all sections so that we can check that these are consistent.
+ if (type == SHT_AARCH64_ATTRIBUTES && ctx.arg.emachine == EM_AARCH64) {
+ ArrayRef<uint8_t> contents = check(obj.getSectionContents(sec));
+ AArch64AttributeParser attributes;
+ if (Error e = attributes.parse(contents, ELFT::Endianness)) {
+ StringRef name = check(obj.getSectionName(sec, shstrtab));
+ InputSection isec(*this, sec, name);
+ Warn(ctx) << &isec << ": " << std::move(e);
+ } else {
+ aarch64BAsubSections = extractBuildAttributesSubsections(attributes);
+ hasAArch64BuildAttributes = true;
+ }
+ this->sections[i] = &InputSection::discarded;
+ continue;
+ }
switch (type) {
case SHT_GROUP: {
if (!ctx.arg.relocatable)
@@ -912,6 +965,12 @@ void ObjFile<ELFT>::initializeSections(bool ignoreComdats,
<< linkSec;
}
+ // Handle AArch64 Build Attributes and GNU properties:
+ // - Err on mismatched values.
+ // - Store missing values as GNU properties.
+ if (hasAArch64BuildAttributes)
+ handleAArch64BAAndGnuProperties<ELFT>(this, ctx, aarch64BAsubSections);
+
for (ArrayRef<Elf_Word> entries : selectedGroups)
handleSectionGroup<ELFT>(this->sections, entries);
}
@@ -1753,6 +1812,39 @@ static uint8_t getOsAbi(const Triple &t) {
}
}
+// For DTLTO, bitcode member names must be valid paths to files on disk.
+// For thin archives, resolve `memberPath` relative to the archive's location.
+// Returns true if adjusted; false otherwise. Non-thin archives are unsupported.
+static bool dtltoAdjustMemberPathIfThinArchive(Ctx &ctx, StringRef archivePath,
+ std::string &memberPath) {
+ assert(!archivePath.empty());
+
+ if (ctx.arg.dtltoDistributor.empty())
+ return false;
+
+ // Read the archive header to determine if it's a thin archive.
+ auto bufferOrErr =
+ MemoryBuffer::getFileSlice(archivePath, sizeof(ThinArchiveMagic) - 1, 0);
+ if (std::error_code ec = bufferOrErr.getError()) {
+ ErrAlways(ctx) << "cannot open " << archivePath << ": " << ec.message();
+ return false;
+ }
+
+ if (!bufferOrErr->get()->getBuffer().starts_with(ThinArchiveMagic))
+ return false;
+
+ SmallString<128> resolvedPath;
+ if (path::is_relative(memberPath)) {
+ resolvedPath = path::parent_path(archivePath);
+ path::append(resolvedPath, memberPath);
+ } else
+ resolvedPath = memberPath;
+
+ path::remove_dots(resolvedPath, /*remove_dot_dot=*/true);
+ memberPath = resolvedPath.str();
+ return true;
+}
+
BitcodeFile::BitcodeFile(Ctx &ctx, MemoryBufferRef mb, StringRef archiveName,
uint64_t offsetInArchive, bool lazy)
: InputFile(ctx, BitcodeKind, mb) {
@@ -1763,17 +1855,22 @@ BitcodeFile::BitcodeFile(Ctx &ctx, MemoryBufferRef mb, StringRef archiveName,
if (ctx.arg.thinLTOIndexOnly)
path = replaceThinLTOSuffix(ctx, mb.getBufferIdentifier());
- // ThinLTO assumes that all MemoryBufferRefs given to it have a unique
- // name. If two archives define two members with the same name, this
- // causes a collision which result in only one of the objects being taken
- // into consideration at LTO time (which very likely causes undefined
- // symbols later in the link stage). So we append file offset to make
- // filename unique.
StringSaver &ss = ctx.saver;
- StringRef name = archiveName.empty()
- ? ss.save(path)
- : ss.save(archiveName + "(" + path::filename(path) +
- " at " + utostr(offsetInArchive) + ")");
+ StringRef name;
+ if (archiveName.empty() ||
+ dtltoAdjustMemberPathIfThinArchive(ctx, archiveName, path)) {
+ name = ss.save(path);
+ } else {
+ // ThinLTO assumes that all MemoryBufferRefs given to it have a unique
+ // name. If two archives define two members with the same name, this
+ // causes a collision which result in only one of the objects being taken
+ // into consideration at LTO time (which very likely causes undefined
+ // symbols later in the link stage). So we append file offset to make
+ // filename unique.
+ name = ss.save(archiveName + "(" + path::filename(path) + " at " +
+ utostr(offsetInArchive) + ")");
+ }
+
MemoryBufferRef mbref(mb.getBuffer(), name);
obj = CHECK2(lto::InputFile::create(mbref), this);
diff --git a/lld/ELF/InputSection.cpp b/lld/ELF/InputSection.cpp
index 68e3feb..784ff7c 100644
--- a/lld/ELF/InputSection.cpp
+++ b/lld/ELF/InputSection.cpp
@@ -861,6 +861,11 @@ uint64_t InputSectionBase::getRelocTargetVA(Ctx &ctx, const Relocation &r,
return ctx.in.mipsGot->getVA() +
ctx.in.mipsGot->getPageEntryOffset(file, *r.sym, a) -
ctx.in.mipsGot->getGp(file);
+ case RE_MIPS_OSEC_LOCAL_PAGE:
+ // This is used by the MIPS multi-GOT implementation. It relocates
+ // addresses of 64kb pages that lie inside the output section that sym is
+ // a representative for.
+ return getMipsPageAddr(r.sym->getOutputSection()->addr) + a;
case RE_MIPS_GOT_OFF:
case RE_MIPS_GOT_OFF32:
// In case of MIPS if a GOT relocation has non-zero addend this addend
diff --git a/lld/ELF/Relocations.cpp b/lld/ELF/Relocations.cpp
index bd22fe2..32ac28d6 100644
--- a/lld/ELF/Relocations.cpp
+++ b/lld/ELF/Relocations.cpp
@@ -885,10 +885,12 @@ static void addPltEntry(Ctx &ctx, PltSection &plt, GotPltSection &gotPlt,
RelocationBaseSection &rel, RelType type, Symbol &sym) {
plt.addEntry(sym);
gotPlt.addEntry(sym);
- rel.addReloc({type, &gotPlt, sym.getGotPltOffset(ctx),
- sym.isPreemptible ? DynamicReloc::AgainstSymbol
- : DynamicReloc::AddendOnlyWithTargetVA,
- sym, 0, R_ABS});
+ if (sym.isPreemptible)
+ rel.addReloc(
+ {type, &gotPlt, sym.getGotPltOffset(ctx), true, sym, 0, R_ADDEND});
+ else
+ rel.addReloc(
+ {type, &gotPlt, sym.getGotPltOffset(ctx), false, sym, 0, R_ABS});
}
void elf::addGotEntry(Ctx &ctx, Symbol &sym) {
@@ -897,9 +899,8 @@ void elf::addGotEntry(Ctx &ctx, Symbol &sym) {
// If preemptible, emit a GLOB_DAT relocation.
if (sym.isPreemptible) {
- ctx.mainPart->relaDyn->addReloc({ctx.target->gotRel, ctx.in.got.get(), off,
- DynamicReloc::AgainstSymbol, sym, 0,
- R_ABS});
+ ctx.mainPart->relaDyn->addReloc(
+ {ctx.target->gotRel, ctx.in.got.get(), off, true, sym, 0, R_ADDEND});
return;
}
@@ -920,15 +921,13 @@ static void addGotAuthEntry(Ctx &ctx, Symbol &sym) {
// If preemptible, emit a GLOB_DAT relocation.
if (sym.isPreemptible) {
ctx.mainPart->relaDyn->addReloc({R_AARCH64_AUTH_GLOB_DAT, ctx.in.got.get(),
- off, DynamicReloc::AgainstSymbol, sym, 0,
- R_ABS});
+ off, true, sym, 0, R_ADDEND});
return;
}
// Signed GOT requires dynamic relocation.
ctx.in.got->getPartition(ctx).relaDyn->addReloc(
- {R_AARCH64_AUTH_RELATIVE, ctx.in.got.get(), off,
- DynamicReloc::AddendOnlyWithTargetVA, sym, 0, R_ABS});
+ {R_AARCH64_AUTH_RELATIVE, ctx.in.got.get(), off, false, sym, 0, R_ABS});
}
static void addTpOffsetGotEntry(Ctx &ctx, Symbol &sym) {
@@ -1159,9 +1158,8 @@ void RelocationScanner::processAux(RelExpr expr, RelType type, uint64_t offset,
sec->addReloc({expr, type, offset, addend, &sym});
part.relrAuthDyn->relocs.push_back({sec, sec->relocs().size() - 1});
} else {
- part.relaDyn->addReloc({R_AARCH64_AUTH_RELATIVE, sec, offset,
- DynamicReloc::AddendOnlyWithTargetVA, sym,
- addend, R_ABS});
+ part.relaDyn->addReloc({R_AARCH64_AUTH_RELATIVE, sec, offset, false,
+ sym, addend, R_ABS});
}
return;
}
@@ -1948,13 +1946,12 @@ void elf::postScanRelocations(Ctx &ctx) {
GotSection *got = ctx.in.got.get();
if (ctx.needsTlsLd.load(std::memory_order_relaxed) && got->addTlsIndex()) {
- static Undefined dummy(ctx.internalFile, "", STB_LOCAL, 0, 0);
if (ctx.arg.shared)
ctx.mainPart->relaDyn->addReloc(
{ctx.target->tlsModuleIndexRel, got, got->getTlsIndexOff()});
else
got->addConstant({R_ADDEND, ctx.target->symbolicRel,
- got->getTlsIndexOff(), 1, &dummy});
+ got->getTlsIndexOff(), 1, ctx.dummySym});
}
assert(ctx.symAux.size() == 1);
diff --git a/lld/ELF/Relocations.h b/lld/ELF/Relocations.h
index 02ddf70..c1c4860 100644
--- a/lld/ELF/Relocations.h
+++ b/lld/ELF/Relocations.h
@@ -110,6 +110,7 @@ enum RelExpr {
RE_MIPS_GOT_LOCAL_PAGE,
RE_MIPS_GOT_OFF,
RE_MIPS_GOT_OFF32,
+ RE_MIPS_OSEC_LOCAL_PAGE,
RE_MIPS_TLSGD,
RE_MIPS_TLSLD,
RE_PPC32_PLTREL,
diff --git a/lld/ELF/SyntheticSections.cpp b/lld/ELF/SyntheticSections.cpp
index efec41a..0d87f9a 100644
--- a/lld/ELF/SyntheticSections.cpp
+++ b/lld/ELF/SyntheticSections.cpp
@@ -769,10 +769,6 @@ void GotSection::writeTo(uint8_t *buf) {
}
}
-static uint64_t getMipsPageAddr(uint64_t addr) {
- return (addr + 0x8000) & ~0xffff;
-}
-
static uint64_t getMipsPageCount(uint64_t size) {
return (size + 0xfffe) / 0xffff + 1;
}
@@ -786,7 +782,7 @@ void MipsGotSection::addEntry(InputFile &file, Symbol &sym, int64_t addend,
FileGot &g = getGot(file);
if (expr == RE_MIPS_GOT_LOCAL_PAGE) {
if (const OutputSection *os = sym.getOutputSection())
- g.pagesMap.insert({os, {}});
+ g.pagesMap.insert({os, {&sym}});
else
g.local16.insert({{nullptr, getMipsPageAddr(sym.getVA(ctx, addend))}, 0});
} else if (sym.isTls())
@@ -1066,8 +1062,7 @@ void MipsGotSection::build() {
// be allocated before us in the static TLS block.
if (s->isPreemptible || ctx.arg.shared)
ctx.mainPart->relaDyn->addReloc(
- {ctx.target->tlsGotRel, this, offset,
- DynamicReloc::AgainstSymbolWithTargetVA, *s, 0, R_ABS});
+ {ctx.target->tlsGotRel, this, offset, true, *s, 0, R_ABS});
}
for (std::pair<Symbol *, size_t> &p : got.dynTlsSymbols) {
Symbol *s = p.first;
@@ -1115,15 +1110,16 @@ void MipsGotSection::build() {
size_t pageCount = l.second.count;
for (size_t pi = 0; pi < pageCount; ++pi) {
uint64_t offset = (l.second.firstIndex + pi) * ctx.arg.wordsize;
- ctx.mainPart->relaDyn->addReloc({ctx.target->relativeRel, this, offset,
- l.first, int64_t(pi * 0x10000)});
+ ctx.mainPart->relaDyn->addReloc(
+ {ctx.target->relativeRel, this, offset, false, *l.second.repSym,
+ int64_t(pi * 0x10000), RE_MIPS_OSEC_LOCAL_PAGE});
}
}
for (const std::pair<GotEntry, size_t> &p : got.local16) {
uint64_t offset = p.second * ctx.arg.wordsize;
ctx.mainPart->relaDyn->addReloc({ctx.target->relativeRel, this, offset,
- DynamicReloc::AddendOnlyWithTargetVA,
- *p.first.first, p.first.second, R_ABS});
+ false, *p.first.first, p.first.second,
+ R_ABS});
}
}
}
@@ -1646,24 +1642,10 @@ uint64_t DynamicReloc::getOffset() const {
}
int64_t DynamicReloc::computeAddend(Ctx &ctx) const {
- switch (kind) {
- case AddendOnly:
- assert(sym == nullptr);
- return addend;
- case AgainstSymbol:
- assert(sym != nullptr);
- return addend;
- case AddendOnlyWithTargetVA:
- case AgainstSymbolWithTargetVA: {
- uint64_t ca = inputSec->getRelocTargetVA(
- ctx, Relocation{expr, type, 0, addend, sym}, getOffset());
- return ctx.arg.is64 ? ca : SignExtend64<32>(ca);
- }
- case MipsMultiGotPage:
- assert(sym == nullptr);
- return getMipsPageAddr(outputSec->addr) + addend;
- }
- llvm_unreachable("Unknown DynamicReloc::Kind enum");
+ assert(!isFinal && "addend already computed");
+ uint64_t ca = inputSec->getRelocTargetVA(
+ ctx, Relocation{expr, type, 0, addend, sym}, getOffset());
+ return ctx.arg.is64 ? ca : SignExtend64<32>(ca);
}
uint32_t DynamicReloc::getSymIndex(SymbolTableBaseSection *symTab) const {
@@ -1691,8 +1673,8 @@ RelocationBaseSection::RelocationBaseSection(Ctx &ctx, StringRef name,
void RelocationBaseSection::addSymbolReloc(
RelType dynType, InputSectionBase &isec, uint64_t offsetInSec, Symbol &sym,
int64_t addend, std::optional<RelType> addendRelType) {
- addReloc(DynamicReloc::AgainstSymbol, dynType, isec, offsetInSec, sym, addend,
- R_ADDEND, addendRelType ? *addendRelType : ctx.target->noneRel);
+ addReloc(true, dynType, isec, offsetInSec, sym, addend, R_ADDEND,
+ addendRelType ? *addendRelType : ctx.target->noneRel);
}
void RelocationBaseSection::addAddendOnlyRelocIfNonPreemptible(
@@ -1700,11 +1682,9 @@ void RelocationBaseSection::addAddendOnlyRelocIfNonPreemptible(
RelType addendRelType) {
// No need to write an addend to the section for preemptible symbols.
if (sym.isPreemptible)
- addReloc({dynType, &isec, offsetInSec, DynamicReloc::AgainstSymbol, sym, 0,
- R_ABS});
+ addReloc({dynType, &isec, offsetInSec, true, sym, 0, R_ADDEND});
else
- addReloc(DynamicReloc::AddendOnlyWithTargetVA, dynType, isec, offsetInSec,
- sym, 0, R_ABS, addendRelType);
+ addReloc(false, dynType, isec, offsetInSec, sym, 0, R_ABS, addendRelType);
}
void RelocationBaseSection::mergeRels() {
@@ -1744,17 +1724,17 @@ void RelocationBaseSection::finalizeContents() {
}
}
-void DynamicReloc::computeRaw(Ctx &ctx, SymbolTableBaseSection *symt) {
+void DynamicReloc::finalize(Ctx &ctx, SymbolTableBaseSection *symt) {
r_offset = getOffset();
r_sym = getSymIndex(symt);
addend = computeAddend(ctx);
- kind = AddendOnly; // Catch errors
+ isFinal = true; // Catch errors
}
void RelocationBaseSection::computeRels() {
SymbolTableBaseSection *symTab = getPartition(ctx).dynSymTab.get();
parallelForEach(relocs, [&ctx = ctx, symTab](DynamicReloc &rel) {
- rel.computeRaw(ctx, symTab);
+ rel.finalize(ctx, symTab);
});
auto irelative = std::stable_partition(
diff --git a/lld/ELF/SyntheticSections.h b/lld/ELF/SyntheticSections.h
index 5f01513..223dfe3 100644
--- a/lld/ELF/SyntheticSections.h
+++ b/lld/ELF/SyntheticSections.h
@@ -327,9 +327,11 @@ private:
size_t startIndex = 0;
struct PageBlock {
+ Symbol *repSym; // Representative symbol for the OutputSection
size_t firstIndex;
size_t count;
- PageBlock() : firstIndex(0), count(0) {}
+ PageBlock(Symbol *repSym = nullptr)
+ : repSym(repSym), firstIndex(0), count(0) {}
};
// Map output sections referenced by MIPS GOT relocations
@@ -418,61 +420,31 @@ private:
class DynamicReloc {
public:
- enum Kind {
- /// The resulting dynamic relocation does not reference a symbol (#sym must
- /// be nullptr) and uses #addend as the result of computeAddend(ctx).
- AddendOnly,
- /// The resulting dynamic relocation will not reference a symbol: #sym is
- /// only used to compute the addend with InputSection::getRelocTargetVA().
- /// Useful for various relative and TLS relocations (e.g. R_X86_64_TPOFF64).
- AddendOnlyWithTargetVA,
- /// The resulting dynamic relocation references symbol #sym from the dynamic
- /// symbol table and uses #addend as the value of computeAddend(ctx).
- AgainstSymbol,
- /// The resulting dynamic relocation references symbol #sym from the dynamic
- /// symbol table and uses InputSection::getRelocTargetVA() + #addend for the
- /// final addend. It can be used for relocations that write the symbol VA as
- // the addend (e.g. R_MIPS_TLS_TPREL64) but still reference the symbol.
- AgainstSymbolWithTargetVA,
- /// This is used by the MIPS multi-GOT implementation. It relocates
- /// addresses of 64kb pages that lie inside the output section.
- MipsMultiGotPage,
- };
- /// This constructor records a relocation against a symbol.
+ /// This constructor records a normal relocation.
DynamicReloc(RelType type, const InputSectionBase *inputSec,
- uint64_t offsetInSec, Kind kind, Symbol &sym, int64_t addend,
- RelExpr expr)
+ uint64_t offsetInSec, bool isAgainstSymbol, Symbol &sym,
+ int64_t addend, RelExpr expr)
: sym(&sym), inputSec(inputSec), offsetInSec(offsetInSec), type(type),
- addend(addend), kind(kind), expr(expr) {}
+ addend(addend), isAgainstSymbol(isAgainstSymbol), isFinal(false),
+ expr(expr) {}
/// This constructor records a relative relocation with no symbol.
DynamicReloc(RelType type, const InputSectionBase *inputSec,
uint64_t offsetInSec, int64_t addend = 0)
- : sym(nullptr), inputSec(inputSec), offsetInSec(offsetInSec), type(type),
- addend(addend), kind(AddendOnly), expr(R_ADDEND) {}
- /// This constructor records dynamic relocation settings used by the MIPS
- /// multi-GOT implementation.
- DynamicReloc(RelType type, const InputSectionBase *inputSec,
- uint64_t offsetInSec, const OutputSection *outputSec,
- int64_t addend)
- : sym(nullptr), outputSec(outputSec), inputSec(inputSec),
- offsetInSec(offsetInSec), type(type), addend(addend),
- kind(MipsMultiGotPage), expr(R_ADDEND) {}
+ : DynamicReloc(type, inputSec, offsetInSec, false,
+ *inputSec->getCtx().dummySym, addend, R_ADDEND) {}
uint64_t getOffset() const;
uint32_t getSymIndex(SymbolTableBaseSection *symTab) const;
- bool needsDynSymIndex() const {
- return kind == AgainstSymbol || kind == AgainstSymbolWithTargetVA;
- }
+ bool needsDynSymIndex() const { return isAgainstSymbol; }
/// Computes the addend of the dynamic relocation. Note that this is not the
/// same as the #addend member variable as it may also include the symbol
/// address/the address of the corresponding GOT entry/etc.
int64_t computeAddend(Ctx &) const;
- void computeRaw(Ctx &, SymbolTableBaseSection *symt);
+ void finalize(Ctx &, SymbolTableBaseSection *symt);
Symbol *sym;
- const OutputSection *outputSec = nullptr;
const InputSectionBase *inputSec;
uint64_t offsetInSec;
uint64_t r_offset;
@@ -483,7 +455,15 @@ public:
int64_t addend;
private:
- Kind kind;
+ /// Whether this was constructed with a Kind of AgainstSymbol.
+ LLVM_PREFERRED_TYPE(bool)
+ uint8_t isAgainstSymbol : 1;
+
+ /// The resulting dynamic relocation has already had its addend computed.
+ /// Calling computeAddend() is an error.
+ LLVM_PREFERRED_TYPE(bool)
+ uint8_t isFinal : 1;
+
// The kind of expression used to calculate the added (required e.g. for
// relative GOT relocations).
RelExpr expr;
@@ -528,8 +508,8 @@ public:
uint64_t offsetInSec, Symbol &sym, int64_t addend,
RelType addendRelType, RelExpr expr) {
assert(expr != R_ADDEND && "expected non-addend relocation expression");
- addReloc<shard>(DynamicReloc::AddendOnlyWithTargetVA, dynType, isec,
- offsetInSec, sym, addend, expr, addendRelType);
+ addReloc<shard>(false, dynType, isec, offsetInSec, sym, addend, expr,
+ addendRelType);
}
/// Add a dynamic relocation using the target address of \p sym as the addend
/// if \p sym is non-preemptible. Otherwise add a relocation against \p sym.
@@ -538,14 +518,15 @@ public:
uint64_t offsetInSec, Symbol &sym,
RelType addendRelType);
template <bool shard = false>
- void addReloc(DynamicReloc::Kind kind, RelType dynType, InputSectionBase &sec,
+ void addReloc(bool isAgainstSymbol, RelType dynType, InputSectionBase &sec,
uint64_t offsetInSec, Symbol &sym, int64_t addend, RelExpr expr,
RelType addendRelType) {
// Write the addends to the relocated address if required. We skip
// it if the written value would be zero.
if (ctx.arg.writeAddends && (expr != R_ADDEND || addend != 0))
sec.addReloc({expr, addendRelType, offsetInSec, addend, &sym});
- addReloc<shard>({dynType, &sec, offsetInSec, kind, sym, addend, expr});
+ addReloc<shard>(
+ {dynType, &sec, offsetInSec, isAgainstSymbol, sym, addend, expr});
}
bool isNeeded() const override {
return !relocs.empty() ||
diff --git a/lld/ELF/Target.cpp b/lld/ELF/Target.cpp
index ad7d57d..4946484 100644
--- a/lld/ELF/Target.cpp
+++ b/lld/ELF/Target.cpp
@@ -105,10 +105,9 @@ ErrorPlace elf::getErrorPlace(Ctx &ctx, const uint8_t *loc) {
if (isecLoc <= loc && loc < isecLoc + isec->getSize()) {
std::string objLoc = isec->getLocation(loc - isecLoc);
// Return object file location and source file location.
- Undefined dummy(ctx.internalFile, "", STB_LOCAL, 0, 0);
ELFSyncStream msg(ctx, DiagLevel::None);
if (isec->file)
- msg << isec->getSrcMsg(dummy, loc - isecLoc);
+ msg << isec->getSrcMsg(*ctx.dummySym, loc - isecLoc);
return {isec, objLoc + ": ", std::string(msg.str())};
}
}
diff --git a/lld/ELF/Target.h b/lld/ELF/Target.h
index 93f1592..fdc0c20 100644
--- a/lld/ELF/Target.h
+++ b/lld/ELF/Target.h
@@ -214,6 +214,7 @@ void processArmCmseSymbols(Ctx &);
template <class ELFT> uint32_t calcMipsEFlags(Ctx &);
uint8_t getMipsFpAbiFlag(Ctx &, InputFile *file, uint8_t oldFlag,
uint8_t newFlag);
+uint64_t getMipsPageAddr(uint64_t addr);
bool isMipsN32Abi(Ctx &, const InputFile &f);
bool isMicroMips(Ctx &);
bool isMipsR6(Ctx &);
@@ -338,21 +339,23 @@ inline uint64_t overwriteULEB128(uint8_t *bufLoc, uint64_t val) {
#pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments"
#endif
#define invokeELFT(f, ...) \
- switch (ctx.arg.ekind) { \
- case lld::elf::ELF32LEKind: \
- f<llvm::object::ELF32LE>(__VA_ARGS__); \
- break; \
- case lld::elf::ELF32BEKind: \
- f<llvm::object::ELF32BE>(__VA_ARGS__); \
- break; \
- case lld::elf::ELF64LEKind: \
- f<llvm::object::ELF64LE>(__VA_ARGS__); \
- break; \
- case lld::elf::ELF64BEKind: \
- f<llvm::object::ELF64BE>(__VA_ARGS__); \
- break; \
- default: \
- llvm_unreachable("unknown ctx.arg.ekind"); \
- }
+ do { \
+ switch (ctx.arg.ekind) { \
+ case lld::elf::ELF32LEKind: \
+ f<llvm::object::ELF32LE>(__VA_ARGS__); \
+ break; \
+ case lld::elf::ELF32BEKind: \
+ f<llvm::object::ELF32BE>(__VA_ARGS__); \
+ break; \
+ case lld::elf::ELF64LEKind: \
+ f<llvm::object::ELF64LE>(__VA_ARGS__); \
+ break; \
+ case lld::elf::ELF64BEKind: \
+ f<llvm::object::ELF64BE>(__VA_ARGS__); \
+ break; \
+ default: \
+ llvm_unreachable("unknown ctx.arg.ekind"); \
+ } \
+ } while (0)
#endif
diff --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp
index 15909da..2b0e097 100644
--- a/lld/ELF/Writer.cpp
+++ b/lld/ELF/Writer.cpp
@@ -553,6 +553,19 @@ template <class ELFT> void Writer<ELFT>::addSectionSymbols() {
}
}
+// Returns true if this is a variant of .data.rel.ro.
+static bool isRelRoDataSection(Ctx &ctx, StringRef secName) {
+ if (!secName.consume_front(".data.rel.ro"))
+ return false;
+ if (secName.empty())
+ return true;
+ // If -z keep-data-section-prefix is specified, additionally allow
+ // '.data.rel.ro.hot' and '.data.rel.ro.unlikely'.
+ if (ctx.arg.zKeepDataSectionPrefix)
+ return secName == ".hot" || secName == ".unlikely";
+ return false;
+}
+
// Today's loaders have a feature to make segments read-only after
// processing dynamic relocations to enhance security. PT_GNU_RELRO
// is defined for that.
@@ -629,7 +642,7 @@ static bool isRelroSection(Ctx &ctx, const OutputSection *sec) {
// magic section names.
StringRef s = sec->name;
- bool abiAgnostic = s == ".data.rel.ro" || s == ".bss.rel.ro" ||
+ bool abiAgnostic = isRelRoDataSection(ctx, s) || s == ".bss.rel.ro" ||
s == ".ctors" || s == ".dtors" || s == ".jcr" ||
s == ".eh_frame" || s == ".fini_array" ||
s == ".init_array" || s == ".preinit_array";
@@ -1573,9 +1586,8 @@ template <class ELFT> void Writer<ELFT>::finalizeAddressDependentContent() {
if (isInt<32>(reloc.sym->getVA(ctx, reloc.addend)))
return false;
part.relaDyn->addReloc({R_AARCH64_AUTH_RELATIVE, elem.inputSec,
- reloc.offset,
- DynamicReloc::AddendOnlyWithTargetVA,
- *reloc.sym, reloc.addend, R_ABS});
+ reloc.offset, false, *reloc.sym,
+ reloc.addend, R_ABS});
return true;
});
changed |= (it != part.relrAuthDyn->relocs.end());