diff options
Diffstat (limited to 'lld/ELF')
-rw-r--r-- | lld/ELF/Arch/LoongArch.cpp | 126 | ||||
-rw-r--r-- | lld/ELF/Arch/Mips.cpp | 4 | ||||
-rw-r--r-- | lld/ELF/Config.h | 3 | ||||
-rw-r--r-- | lld/ELF/Driver.cpp | 3 | ||||
-rw-r--r-- | lld/ELF/InputFiles.cpp | 131 | ||||
-rw-r--r-- | lld/ELF/InputSection.cpp | 5 | ||||
-rw-r--r-- | lld/ELF/Relocations.cpp | 29 | ||||
-rw-r--r-- | lld/ELF/Relocations.h | 1 | ||||
-rw-r--r-- | lld/ELF/SyntheticSections.cpp | 56 | ||||
-rw-r--r-- | lld/ELF/SyntheticSections.h | 71 | ||||
-rw-r--r-- | lld/ELF/Target.cpp | 3 | ||||
-rw-r--r-- | lld/ELF/Target.h | 35 | ||||
-rw-r--r-- | lld/ELF/Writer.cpp | 20 |
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()); |