[lld] [ELF] Move target-specific synthetic sections into Arch/ files (PR #184057)
Fangrui Song via llvm-commits
llvm-commits at lists.llvm.org
Mon Mar 2 09:56:15 PST 2026
https://github.com/MaskRay updated https://github.com/llvm/llvm-project/pull/184057
>From 0789278aa9cb846310504c1aeb421c28373f31e2 Mon Sep 17 00:00:00 2001
From: Fangrui Song <i at maskray.me>
Date: Sun, 1 Mar 2026 18:16:53 -0800
Subject: [PATCH 1/2] [ELF] Move target-specific synthetic sections into Arch/
files
Add a TargetInfo::initTargetSections hook, called from
createSyntheticSections, for targets to create target-specific synthetic
sections.
Move target-specific synthetic section classes from
SyntheticSections.h/.cpp into their respective Arch/ files. Remove the
target-specific prefix while moving classes.
Some synthetic sections (e.g. ARMExidxSyntheticSection,
PPC64LongBranchTargetSection) have their members referenced by other
generic files, therefore they are not moved.
---
lld/ELF/Arch/ARM.cpp | 46 ++++++-
lld/ELF/Arch/Mips.cpp | 179 ++++++++++++++++++++++++
lld/ELF/Arch/PPC.cpp | 41 ++++++
lld/ELF/Arch/PPC64.cpp | 7 +
lld/ELF/Arch/X86.cpp | 21 +++
lld/ELF/Arch/X86_64.cpp | 21 +++
lld/ELF/Config.h | 9 +-
lld/ELF/SyntheticSections.cpp | 253 +---------------------------------
lld/ELF/SyntheticSections.h | 120 ----------------
lld/ELF/Target.h | 1 +
10 files changed, 316 insertions(+), 382 deletions(-)
diff --git a/lld/ELF/Arch/ARM.cpp b/lld/ELF/Arch/ARM.cpp
index fc6eee24cf4ff..b4486f53a0042 100644
--- a/lld/ELF/Arch/ARM.cpp
+++ b/lld/ELF/Arch/ARM.cpp
@@ -25,11 +25,17 @@ using namespace lld;
using namespace lld::elf;
using namespace llvm::object;
+// Cortex-M Security Extensions. Prefix for functions that should be exported
+// for the non-secure world.
+constexpr char ACLESESYM_PREFIX[] = "__acle_se_";
+constexpr int ACLESESYM_SIZE = 8;
+
namespace {
class ARM final : public TargetInfo {
public:
ARM(Ctx &);
uint32_t calcEFlags() const override;
+ void initTargetSections() override;
RelExpr getRelExpr(RelType type, const Symbol &s,
const uint8_t *loc) const override;
RelType getDynRel(RelType type) const override;
@@ -64,6 +70,33 @@ class ARM final : public TargetInfo {
int group, bool check) const;
};
enum class CodeState { Data = 0, Thumb = 2, Arm = 4 };
+
+struct CmseSGVeneer {
+ CmseSGVeneer(Symbol *sym, Symbol *acleSeSym,
+ std::optional<uint64_t> addr = std::nullopt)
+ : sym(sym), acleSeSym(acleSeSym), entAddr{addr} {}
+ static const size_t size{ACLESESYM_SIZE};
+ std::optional<uint64_t> getAddr() const { return entAddr; };
+
+ Symbol *sym;
+ Symbol *acleSeSym;
+ uint64_t offset = 0;
+ const std::optional<uint64_t> entAddr;
+};
+
+struct ArmCmseSGSection : SyntheticSection {
+ ArmCmseSGSection(Ctx &ctx);
+ bool isNeeded() const override { return !entries.empty(); }
+ size_t getSize() const override;
+ void writeTo(uint8_t *buf) override;
+ void addSGVeneer(Symbol *sym, Symbol *ext_sym);
+ void addMappingSymbol();
+ void finalizeContents() override;
+ uint64_t impLibMaxAddr = 0;
+ SmallVector<std::pair<Symbol *, Symbol *>, 0> entries;
+ SmallVector<std::unique_ptr<CmseSGVeneer>, 0> sgVeneers;
+ uint64_t newEntries = 0;
+};
} // namespace
ARM::ARM(Ctx &ctx) : TargetInfo(ctx) {
@@ -108,6 +141,11 @@ uint32_t ARM::calcEFlags() const {
return EF_ARM_EABI_VER5 | abiFloatType | armBE8;
}
+void ARM::initTargetSections() {
+ ctx.in.armCmseSGSection = std::make_unique<ArmCmseSGSection>(ctx);
+ ctx.inputSections.push_back(ctx.in.armCmseSGSection.get());
+}
+
// Only needed to support relocations used by relocateNonAlloc and
// preprocessRelocs.
RelExpr ARM::getRelExpr(RelType type, const Symbol &s,
@@ -1444,20 +1482,20 @@ void ArmCmseSGSection::addSGVeneer(Symbol *acleSeSym, Symbol *sym) {
return;
// Only secure symbols with values equal to that of it's non-secure
// counterpart needs to be in the .gnu.sgstubs section.
- std::unique_ptr<ArmCmseSGVeneer> ss;
+ std::unique_ptr<CmseSGVeneer> ss;
auto it = ctx.symtab->cmseImportLib.find(sym->getName());
if (it != ctx.symtab->cmseImportLib.end()) {
Defined *impSym = it->second;
- ss = std::make_unique<ArmCmseSGVeneer>(sym, acleSeSym, impSym->value);
+ ss = std::make_unique<CmseSGVeneer>(sym, acleSeSym, impSym->value);
} else {
- ss = std::make_unique<ArmCmseSGVeneer>(sym, acleSeSym);
+ ss = std::make_unique<CmseSGVeneer>(sym, acleSeSym);
++newEntries;
}
sgVeneers.emplace_back(std::move(ss));
}
void ArmCmseSGSection::writeTo(uint8_t *buf) {
- for (std::unique_ptr<ArmCmseSGVeneer> &s : sgVeneers) {
+ for (std::unique_ptr<CmseSGVeneer> &s : sgVeneers) {
uint8_t *p = buf + s->offset;
write16(ctx, p + 0, 0xe97f); // SG
write16(ctx, p + 2, 0xe97f);
diff --git a/lld/ELF/Arch/Mips.cpp b/lld/ELF/Arch/Mips.cpp
index 1f8abbb98b1fd..1c319b1d760e4 100644
--- a/lld/ELF/Arch/Mips.cpp
+++ b/lld/ELF/Arch/Mips.cpp
@@ -24,6 +24,7 @@ template <class ELFT> class MIPS final : public TargetInfo {
public:
MIPS(Ctx &);
uint32_t calcEFlags() const override;
+ void initTargetSections() override;
RelExpr getRelExpr(RelType type, const Symbol &s,
const uint8_t *loc) const override;
int64_t getImplicitAddend(const uint8_t *buf, RelType type) const override;
@@ -42,6 +43,47 @@ template <class ELFT> class MIPS final : public TargetInfo {
uint64_t val) const override;
bool usesOnlyLowPageBits(RelType type) const override;
};
+
+struct RldMapSection : SyntheticSection {
+ RldMapSection(Ctx &ctx)
+ : SyntheticSection(ctx, ".rld_map", SHT_PROGBITS, SHF_ALLOC | SHF_WRITE,
+ ctx.arg.wordsize) {}
+ size_t getSize() const override { return ctx.arg.wordsize; }
+ void writeTo(uint8_t *buf) override {}
+};
+
+template <class ELFT> struct AbiFlagsSection : SyntheticSection {
+ using Elf_Mips_ABIFlags = llvm::object::Elf_Mips_ABIFlags<ELFT>;
+ AbiFlagsSection(Ctx &ctx);
+ bool isNeeded() const override { return needed; }
+ size_t getSize() const override { return sizeof(Elf_Mips_ABIFlags); }
+ void writeTo(uint8_t *buf) override { memcpy(buf, &flags, sizeof(flags)); }
+ Elf_Mips_ABIFlags flags = {};
+ bool needed = false;
+};
+
+template <class ELFT> struct OptionsSection : SyntheticSection {
+ using Elf_Mips_Options = llvm::object::Elf_Mips_Options<ELFT>;
+ using Elf_Mips_RegInfo = llvm::object::Elf_Mips_RegInfo<ELFT>;
+ OptionsSection(Ctx &ctx);
+ bool isNeeded() const override { return needed; }
+ size_t getSize() const override {
+ return sizeof(Elf_Mips_Options) + sizeof(Elf_Mips_RegInfo);
+ }
+ void writeTo(uint8_t *buf) override;
+ Elf_Mips_RegInfo reginfo = {};
+ bool needed = false;
+};
+
+template <class ELFT> struct ReginfoSection : SyntheticSection {
+ using Elf_Mips_RegInfo = llvm::object::Elf_Mips_RegInfo<ELFT>;
+ ReginfoSection(Ctx &ctx);
+ bool isNeeded() const override { return needed; }
+ size_t getSize() const override { return sizeof(Elf_Mips_RegInfo); }
+ void writeTo(uint8_t *buf) override;
+ Elf_Mips_RegInfo reginfo = {};
+ bool needed = false;
+};
} // namespace
uint64_t elf::getMipsPageAddr(uint64_t addr) {
@@ -79,6 +121,19 @@ template <class ELFT> uint32_t MIPS<ELFT>::calcEFlags() const {
return calcMipsEFlags<ELFT>(ctx);
}
+template <class ELFT> void MIPS<ELFT>::initTargetSections() {
+ if (!ctx.arg.shared && ctx.hasDynsym) {
+ ctx.in.mipsRldMap = std::make_unique<RldMapSection>(ctx);
+ ctx.inputSections.push_back(ctx.in.mipsRldMap.get());
+ }
+ ctx.in.mipsAbiFlags = std::make_unique<AbiFlagsSection<ELFT>>(ctx);
+ ctx.inputSections.push_back(ctx.in.mipsAbiFlags.get());
+ ctx.in.mipsOptions = std::make_unique<OptionsSection<ELFT>>(ctx);
+ ctx.inputSections.push_back(ctx.in.mipsOptions.get());
+ ctx.in.mipsReginfo = std::make_unique<ReginfoSection<ELFT>>(ctx);
+ ctx.inputSections.push_back(ctx.in.mipsReginfo.get());
+}
+
template <class ELFT>
RelExpr MIPS<ELFT>::getRelExpr(RelType type, const Symbol &s,
const uint8_t *loc) const {
@@ -906,6 +961,130 @@ template <class ELFT> bool elf::isMipsPIC(const Defined *sym) {
return cast<ObjFile<ELFT>>(file)->getObj().getHeader().e_flags & EF_MIPS_PIC;
}
+template <class ELFT>
+AbiFlagsSection<ELFT>::AbiFlagsSection(Ctx &ctx)
+ : SyntheticSection(ctx, ".MIPS.abiflags", SHT_MIPS_ABIFLAGS, SHF_ALLOC, 8) {
+ this->entsize = sizeof(Elf_Mips_ABIFlags);
+
+ for (InputSectionBase *sec : ctx.inputSections) {
+ if (sec->type != SHT_MIPS_ABIFLAGS)
+ continue;
+ sec->markDead();
+ needed = true;
+
+ const size_t size = sec->content().size();
+ // Older version of BFD (such as the default FreeBSD linker) concatenate
+ // .MIPS.abiflags instead of merging. To allow for this case (or potential
+ // zero padding) we ignore everything after the first Elf_Mips_ABIFlags
+ if (size < sizeof(Elf_Mips_ABIFlags)) {
+ Err(ctx) << sec->file << ": invalid size of .MIPS.abiflags section: got "
+ << size << " instead of " << sizeof(Elf_Mips_ABIFlags);
+ return;
+ }
+ auto *s =
+ reinterpret_cast<const Elf_Mips_ABIFlags *>(sec->content().data());
+ if (s->version != 0) {
+ Err(ctx) << sec->file << ": unexpected .MIPS.abiflags version "
+ << s->version;
+ return;
+ }
+
+ // LLD checks ISA compatibility in calcMipsEFlags(). Here we just
+ // select the highest number of ISA/Rev/Ext.
+ flags.isa_level = std::max(flags.isa_level, s->isa_level);
+ flags.isa_rev = std::max(flags.isa_rev, s->isa_rev);
+ flags.isa_ext = std::max(flags.isa_ext, s->isa_ext);
+ flags.gpr_size = std::max(flags.gpr_size, s->gpr_size);
+ flags.cpr1_size = std::max(flags.cpr1_size, s->cpr1_size);
+ flags.cpr2_size = std::max(flags.cpr2_size, s->cpr2_size);
+ flags.ases |= s->ases;
+ flags.flags1 |= s->flags1;
+ flags.flags2 |= s->flags2;
+ flags.fp_abi =
+ elf::getMipsFpAbiFlag(ctx, sec->file, flags.fp_abi, s->fp_abi);
+ }
+}
+
+template <class ELFT>
+OptionsSection<ELFT>::OptionsSection(Ctx &ctx)
+ : SyntheticSection(ctx, ".MIPS.options", SHT_MIPS_OPTIONS, SHF_ALLOC, 8) {
+ this->entsize = sizeof(Elf_Mips_Options) + sizeof(Elf_Mips_RegInfo);
+
+ // N64 ABI only.
+ if (!ELFT::Is64Bits)
+ return;
+
+ for (InputSectionBase *sec : ctx.inputSections) {
+ if (sec->type != SHT_MIPS_OPTIONS)
+ continue;
+ sec->markDead();
+ needed = true;
+
+ ArrayRef<uint8_t> d = sec->content();
+ while (!d.empty()) {
+ if (d.size() < sizeof(Elf_Mips_Options)) {
+ Err(ctx) << sec->file << ": invalid size of .MIPS.options section";
+ break;
+ }
+
+ auto *opt = reinterpret_cast<const Elf_Mips_Options *>(d.data());
+ if (opt->kind == ODK_REGINFO) {
+ reginfo.ri_gprmask |= opt->getRegInfo().ri_gprmask;
+ sec->getFile<ELFT>()->mipsGp0 = opt->getRegInfo().ri_gp_value;
+ break;
+ }
+
+ if (!opt->size) {
+ Err(ctx) << sec->file << ": zero option descriptor size";
+ break;
+ }
+ d = d.slice(opt->size);
+ }
+ }
+}
+
+template <class ELFT> void OptionsSection<ELFT>::writeTo(uint8_t *buf) {
+ auto *options = reinterpret_cast<Elf_Mips_Options *>(buf);
+ options->kind = ODK_REGINFO;
+ options->size = getSize();
+
+ if (!ctx.arg.relocatable)
+ reginfo.ri_gp_value = ctx.in.mipsGot->getGp();
+ memcpy(buf + sizeof(Elf_Mips_Options), ®info, sizeof(reginfo));
+}
+
+template <class ELFT>
+ReginfoSection<ELFT>::ReginfoSection(Ctx &ctx)
+ : SyntheticSection(ctx, ".reginfo", SHT_MIPS_REGINFO, SHF_ALLOC, 4) {
+ this->entsize = sizeof(Elf_Mips_RegInfo);
+
+ // Section should be alive for O32 and N32 ABIs only.
+ if (ELFT::Is64Bits)
+ return;
+
+ for (InputSectionBase *sec : ctx.inputSections) {
+ if (sec->type != SHT_MIPS_REGINFO)
+ continue;
+ sec->markDead();
+ needed = true;
+
+ if (sec->content().size() != sizeof(Elf_Mips_RegInfo)) {
+ Err(ctx) << sec->file << ": invalid size of .reginfo section";
+ return;
+ }
+
+ auto *r = reinterpret_cast<const Elf_Mips_RegInfo *>(sec->content().data());
+ reginfo.ri_gprmask |= r->ri_gprmask;
+ sec->getFile<ELFT>()->mipsGp0 = r->ri_gp_value;
+ }
+}
+
+template <class ELFT> void ReginfoSection<ELFT>::writeTo(uint8_t *buf) {
+ if (!ctx.arg.relocatable)
+ reginfo.ri_gp_value = ctx.in.mipsGot->getGp();
+ memcpy(buf, ®info, sizeof(reginfo));
+}
+
void elf::setMipsTargetInfo(Ctx &ctx) {
switch (ctx.arg.ekind) {
case ELF32LEKind: {
diff --git a/lld/ELF/Arch/PPC.cpp b/lld/ELF/Arch/PPC.cpp
index d517125d16a3d..e6290874bec2f 100644
--- a/lld/ELF/Arch/PPC.cpp
+++ b/lld/ELF/Arch/PPC.cpp
@@ -27,6 +27,7 @@ namespace {
class PPC final : public TargetInfo {
public:
PPC(Ctx &);
+ void initTargetSections() override;
RelExpr getRelExpr(RelType type, const Symbol &s,
const uint8_t *loc) const override;
RelType getDynRel(RelType type) const override;
@@ -52,12 +53,23 @@ class PPC final : public TargetInfo {
bool inBranchRange(RelType type, uint64_t src, uint64_t dst) const override;
void relocate(uint8_t *loc, const Relocation &rel,
uint64_t val) const override;
+
private:
void relaxTlsGdToIe(uint8_t *loc, const Relocation &rel, uint64_t val) const;
void relaxTlsGdToLe(uint8_t *loc, const Relocation &rel, uint64_t val) const;
void relaxTlsLdToLe(uint8_t *loc, const Relocation &rel, uint64_t val) const;
void relaxTlsIeToLe(uint8_t *loc, const Relocation &rel, uint64_t val) const;
};
+
+struct Got2Section : SyntheticSection {
+ Got2Section(Ctx &ctx)
+ : SyntheticSection(ctx, ".got2", SHT_PROGBITS, SHF_ALLOC | SHF_WRITE, 4) {
+ }
+ bool isNeeded() const override;
+ size_t getSize() const override { return 0; }
+ void writeTo(uint8_t *buf) override {}
+ void finalizeContents() override;
+};
} // namespace
static uint16_t lo(uint32_t v) { return v; }
@@ -180,6 +192,11 @@ PPC::PPC(Ctx &ctx) : TargetInfo(ctx) {
write32(ctx, trapInstr.data(), 0x7fe00008);
}
+void PPC::initTargetSections() {
+ ctx.in.ppc32Got2 = std::make_unique<Got2Section>(ctx);
+ ctx.inputSections.push_back(ctx.in.ppc32Got2.get());
+}
+
void PPC::writeIplt(uint8_t *buf, const Symbol &sym,
uint64_t /*pltEntryAddr*/) const {
// In -pie or -shared mode, assume r30 points to .got2+0x8000, and use a
@@ -601,3 +618,27 @@ void PPC::relaxTlsIeToLe(uint8_t *loc, const Relocation &rel,
}
void elf::setPPCTargetInfo(Ctx &ctx) { ctx.target.reset(new PPC(ctx)); }
+
+bool Got2Section::isNeeded() const {
+ for (SectionCommand *cmd : getParent()->commands)
+ if (auto *isd = dyn_cast<InputSectionDescription>(cmd))
+ for (InputSection *isec : isd->sections)
+ if (isec != this)
+ return true;
+ return false;
+}
+
+void Got2Section::finalizeContents() {
+ // PPC32 may create multiple GOT sections for -fPIC/-fPIE, one per file in
+ // .got2 . This function computes outSecOff of each .got2 to be used in
+ // PPC32PltCallStub::writeTo(). The purpose of this empty synthetic section is
+ // to collect input sections named ".got2".
+ for (SectionCommand *cmd : getParent()->commands)
+ if (auto *isd = dyn_cast<InputSectionDescription>(cmd)) {
+ for (InputSection *isec : isd->sections) {
+ // isec->file may be nullptr for MergeSyntheticSection.
+ if (isec != this && isec->file)
+ isec->file->ppc32Got2 = isec;
+ }
+ }
+}
diff --git a/lld/ELF/Arch/PPC64.cpp b/lld/ELF/Arch/PPC64.cpp
index 59dea59d16e68..32a1c04ed61a6 100644
--- a/lld/ELF/Arch/PPC64.cpp
+++ b/lld/ELF/Arch/PPC64.cpp
@@ -169,6 +169,7 @@ class PPC64 final : public TargetInfo {
public:
PPC64(Ctx &);
uint32_t calcEFlags() const override;
+ void initTargetSections() override;
RelExpr getRelExpr(RelType type, const Symbol &s,
const uint8_t *loc) const override;
RelType getDynRel(RelType type) const override;
@@ -969,6 +970,12 @@ void PPC64::relaxTlsIeToLe(uint8_t *loc, const Relocation &rel,
}
}
+void PPC64::initTargetSections() {
+ ctx.in.ppc64LongBranchTarget =
+ std::make_unique<PPC64LongBranchTargetSection>(ctx);
+ ctx.inputSections.push_back(ctx.in.ppc64LongBranchTarget.get());
+}
+
// Only needed to support relocations used by relocateNonAlloc and relocateEh.
RelExpr PPC64::getRelExpr(RelType type, const Symbol &s,
const uint8_t *loc) const {
diff --git a/lld/ELF/Arch/X86.cpp b/lld/ELF/Arch/X86.cpp
index 30d78e9ebd460..94d3b1696aea5 100644
--- a/lld/ELF/Arch/X86.cpp
+++ b/lld/ELF/Arch/X86.cpp
@@ -23,6 +23,7 @@ namespace {
class X86 : public TargetInfo {
public:
X86(Ctx &);
+ void initTargetSections() override;
RelExpr getRelExpr(RelType type, const Symbol &s,
const uint8_t *loc) const override;
int64_t getImplicitAddend(const uint8_t *buf, RelType type) const override;
@@ -46,6 +47,19 @@ class X86 : public TargetInfo {
void relaxTlsLdToLe(uint8_t *loc, const Relocation &rel, uint64_t val) const;
void relaxTlsIeToLe(uint8_t *loc, const Relocation &rel, uint64_t val) const;
};
+
+struct IBTPltSection : SyntheticSection {
+ IBTPltSection(Ctx &ctx)
+ : SyntheticSection(ctx, ".plt", SHT_PROGBITS, SHF_ALLOC | SHF_EXECINSTR,
+ 16) {}
+ bool isNeeded() const override { return ctx.in.plt->getNumEntries() > 0; }
+ size_t getSize() const override {
+ return 16 + ctx.in.plt->getNumEntries() * ctx.target->pltEntrySize;
+ }
+ void writeTo(uint8_t *buf) override {
+ ctx.target->writeIBTPlt(buf, ctx.in.plt->getNumEntries());
+ }
+};
} // namespace
X86::X86(Ctx &ctx) : TargetInfo(ctx) {
@@ -70,6 +84,13 @@ X86::X86(Ctx &ctx) : TargetInfo(ctx) {
defaultImageBase = 0x400000;
}
+void X86::initTargetSections() {
+ if (ctx.arg.andFeatures & GNU_PROPERTY_X86_FEATURE_1_IBT) {
+ ctx.in.ibtPlt = std::make_unique<IBTPltSection>(ctx);
+ ctx.inputSections.push_back(ctx.in.ibtPlt.get());
+ }
+}
+
// Only needed to support relocations used by relocateNonAlloc and relocateEh.
RelExpr X86::getRelExpr(RelType type, const Symbol &s,
const uint8_t *loc) const {
diff --git a/lld/ELF/Arch/X86_64.cpp b/lld/ELF/Arch/X86_64.cpp
index 2d12b5cd9df04..1bd8619cf601f 100644
--- a/lld/ELF/Arch/X86_64.cpp
+++ b/lld/ELF/Arch/X86_64.cpp
@@ -28,6 +28,7 @@ namespace {
class X86_64 : public TargetInfo {
public:
X86_64(Ctx &);
+ void initTargetSections() override;
RelExpr getRelExpr(RelType type, const Symbol &s,
const uint8_t *loc) const override;
RelType getDynRel(RelType type) const override;
@@ -61,6 +62,19 @@ class X86_64 : public TargetInfo {
void relaxTlsLdToLe(uint8_t *loc, const Relocation &rel, uint64_t val) const;
void relaxTlsIeToLe(uint8_t *loc, const Relocation &rel, uint64_t val) const;
};
+
+struct IBTPltSection : SyntheticSection {
+ IBTPltSection(Ctx &ctx)
+ : SyntheticSection(ctx, ".plt", SHT_PROGBITS, SHF_ALLOC | SHF_EXECINSTR,
+ 16) {}
+ bool isNeeded() const override { return ctx.in.plt->getNumEntries() > 0; }
+ size_t getSize() const override {
+ return 16 + ctx.in.plt->getNumEntries() * ctx.target->pltEntrySize;
+ }
+ void writeTo(uint8_t *buf) override {
+ ctx.target->writeIBTPlt(buf, ctx.in.plt->getNumEntries());
+ }
+};
} // namespace
// This is vector of NOP instructions of sizes from 1 to 8 bytes. The
@@ -355,6 +369,13 @@ bool X86_64::relaxOnce(int pass) const {
return changed;
}
+void X86_64::initTargetSections() {
+ if (ctx.arg.andFeatures & GNU_PROPERTY_X86_FEATURE_1_IBT) {
+ ctx.in.ibtPlt = std::make_unique<IBTPltSection>(ctx);
+ ctx.inputSections.push_back(ctx.in.ibtPlt.get());
+ }
+}
+
// Only needed to support relocations used by relocateNonAlloc and relocateEh.
RelExpr X86_64::getRelExpr(RelType type, const Symbol &s,
const uint8_t *loc) const {
diff --git a/lld/ELF/Config.h b/lld/ELF/Config.h
index 237df52194210..a9f74460f6f99 100644
--- a/lld/ELF/Config.h
+++ b/lld/ELF/Config.h
@@ -59,13 +59,10 @@ class BssSection;
class GdbIndexSection;
class GotPltSection;
class GotSection;
-class IBTPltSection;
class IgotPltSection;
class InputSection;
class IpltSection;
class MipsGotSection;
-class MipsRldMapSection;
-class PPC32Got2Section;
class PPC64LongBranchTargetSection;
class PltSection;
class RelocationBaseSection;
@@ -581,13 +578,13 @@ struct InStruct {
std::unique_ptr<MipsGotSection> mipsGot;
std::unique_ptr<SyntheticSection> mipsOptions;
std::unique_ptr<SyntheticSection> mipsReginfo;
- std::unique_ptr<MipsRldMapSection> mipsRldMap;
+ std::unique_ptr<SyntheticSection> mipsRldMap;
std::unique_ptr<SyntheticSection> partEnd;
std::unique_ptr<SyntheticSection> partIndex;
std::unique_ptr<PltSection> plt;
std::unique_ptr<IpltSection> iplt;
- std::unique_ptr<PPC32Got2Section> ppc32Got2;
- std::unique_ptr<IBTPltSection> ibtPlt;
+ std::unique_ptr<SyntheticSection> ppc32Got2;
+ std::unique_ptr<SyntheticSection> ibtPlt;
std::unique_ptr<RelocationBaseSection> relaPlt;
// Non-SHF_ALLOC sections
std::unique_ptr<SyntheticSection> debugNames;
diff --git a/lld/ELF/SyntheticSections.cpp b/lld/ELF/SyntheticSections.cpp
index 2cfc88d8389b0..78eb77fdc614d 100644
--- a/lld/ELF/SyntheticSections.cpp
+++ b/lld/ELF/SyntheticSections.cpp
@@ -90,176 +90,6 @@ MergeInputSection *elf::createCommentSection(Ctx &ctx) {
return sec;
}
-// .MIPS.abiflags section.
-template <class ELFT>
-MipsAbiFlagsSection<ELFT>::MipsAbiFlagsSection(Ctx &ctx,
- Elf_Mips_ABIFlags flags)
- : SyntheticSection(ctx, ".MIPS.abiflags", SHT_MIPS_ABIFLAGS, SHF_ALLOC, 8),
- flags(flags) {
- this->entsize = sizeof(Elf_Mips_ABIFlags);
-}
-
-template <class ELFT> void MipsAbiFlagsSection<ELFT>::writeTo(uint8_t *buf) {
- memcpy(buf, &flags, sizeof(flags));
-}
-
-template <class ELFT>
-std::unique_ptr<MipsAbiFlagsSection<ELFT>>
-MipsAbiFlagsSection<ELFT>::create(Ctx &ctx) {
- Elf_Mips_ABIFlags flags = {};
- bool create = false;
-
- for (InputSectionBase *sec : ctx.inputSections) {
- if (sec->type != SHT_MIPS_ABIFLAGS)
- continue;
- sec->markDead();
- create = true;
-
- const size_t size = sec->content().size();
- // Older version of BFD (such as the default FreeBSD linker) concatenate
- // .MIPS.abiflags instead of merging. To allow for this case (or potential
- // zero padding) we ignore everything after the first Elf_Mips_ABIFlags
- if (size < sizeof(Elf_Mips_ABIFlags)) {
- Err(ctx) << sec->file << ": invalid size of .MIPS.abiflags section: got "
- << size << " instead of " << sizeof(Elf_Mips_ABIFlags);
- return nullptr;
- }
- auto *s =
- reinterpret_cast<const Elf_Mips_ABIFlags *>(sec->content().data());
- if (s->version != 0) {
- Err(ctx) << sec->file << ": unexpected .MIPS.abiflags version "
- << s->version;
- return nullptr;
- }
-
- // LLD checks ISA compatibility in calcMipsEFlags(). Here we just
- // select the highest number of ISA/Rev/Ext.
- flags.isa_level = std::max(flags.isa_level, s->isa_level);
- flags.isa_rev = std::max(flags.isa_rev, s->isa_rev);
- flags.isa_ext = std::max(flags.isa_ext, s->isa_ext);
- flags.gpr_size = std::max(flags.gpr_size, s->gpr_size);
- flags.cpr1_size = std::max(flags.cpr1_size, s->cpr1_size);
- flags.cpr2_size = std::max(flags.cpr2_size, s->cpr2_size);
- flags.ases |= s->ases;
- flags.flags1 |= s->flags1;
- flags.flags2 |= s->flags2;
- flags.fp_abi =
- elf::getMipsFpAbiFlag(ctx, sec->file, flags.fp_abi, s->fp_abi);
- };
-
- if (create)
- return std::make_unique<MipsAbiFlagsSection<ELFT>>(ctx, flags);
- return nullptr;
-}
-
-// .MIPS.options section.
-template <class ELFT>
-MipsOptionsSection<ELFT>::MipsOptionsSection(Ctx &ctx, Elf_Mips_RegInfo reginfo)
- : SyntheticSection(ctx, ".MIPS.options", SHT_MIPS_OPTIONS, SHF_ALLOC, 8),
- reginfo(reginfo) {
- this->entsize = sizeof(Elf_Mips_Options) + sizeof(Elf_Mips_RegInfo);
-}
-
-template <class ELFT> void MipsOptionsSection<ELFT>::writeTo(uint8_t *buf) {
- auto *options = reinterpret_cast<Elf_Mips_Options *>(buf);
- options->kind = ODK_REGINFO;
- options->size = getSize();
-
- if (!ctx.arg.relocatable)
- reginfo.ri_gp_value = ctx.in.mipsGot->getGp();
- memcpy(buf + sizeof(Elf_Mips_Options), ®info, sizeof(reginfo));
-}
-
-template <class ELFT>
-std::unique_ptr<MipsOptionsSection<ELFT>>
-MipsOptionsSection<ELFT>::create(Ctx &ctx) {
- // N64 ABI only.
- if (!ELFT::Is64Bits)
- return nullptr;
-
- SmallVector<InputSectionBase *, 0> sections;
- for (InputSectionBase *sec : ctx.inputSections)
- if (sec->type == SHT_MIPS_OPTIONS)
- sections.push_back(sec);
-
- if (sections.empty())
- return nullptr;
-
- Elf_Mips_RegInfo reginfo = {};
- for (InputSectionBase *sec : sections) {
- sec->markDead();
-
- ArrayRef<uint8_t> d = sec->content();
- while (!d.empty()) {
- if (d.size() < sizeof(Elf_Mips_Options)) {
- Err(ctx) << sec->file << ": invalid size of .MIPS.options section";
- break;
- }
-
- auto *opt = reinterpret_cast<const Elf_Mips_Options *>(d.data());
- if (opt->kind == ODK_REGINFO) {
- reginfo.ri_gprmask |= opt->getRegInfo().ri_gprmask;
- sec->getFile<ELFT>()->mipsGp0 = opt->getRegInfo().ri_gp_value;
- break;
- }
-
- if (!opt->size) {
- Err(ctx) << sec->file << ": zero option descriptor size";
- break;
- }
- d = d.slice(opt->size);
- }
- };
-
- return std::make_unique<MipsOptionsSection<ELFT>>(ctx, reginfo);
-}
-
-// MIPS .reginfo section.
-template <class ELFT>
-MipsReginfoSection<ELFT>::MipsReginfoSection(Ctx &ctx, Elf_Mips_RegInfo reginfo)
- : SyntheticSection(ctx, ".reginfo", SHT_MIPS_REGINFO, SHF_ALLOC, 4),
- reginfo(reginfo) {
- this->entsize = sizeof(Elf_Mips_RegInfo);
-}
-
-template <class ELFT> void MipsReginfoSection<ELFT>::writeTo(uint8_t *buf) {
- if (!ctx.arg.relocatable)
- reginfo.ri_gp_value = ctx.in.mipsGot->getGp();
- memcpy(buf, ®info, sizeof(reginfo));
-}
-
-template <class ELFT>
-std::unique_ptr<MipsReginfoSection<ELFT>>
-MipsReginfoSection<ELFT>::create(Ctx &ctx) {
- // Section should be alive for O32 and N32 ABIs only.
- if (ELFT::Is64Bits)
- return nullptr;
-
- SmallVector<InputSectionBase *, 0> sections;
- for (InputSectionBase *sec : ctx.inputSections)
- if (sec->type == SHT_MIPS_REGINFO)
- sections.push_back(sec);
-
- if (sections.empty())
- return nullptr;
-
- Elf_Mips_RegInfo reginfo = {};
- for (InputSectionBase *sec : sections) {
- sec->markDead();
-
- if (sec->content().size() != sizeof(Elf_Mips_RegInfo)) {
- Err(ctx) << sec->file << ": invalid size of .reginfo section";
- return nullptr;
- }
-
- auto *r = reinterpret_cast<const Elf_Mips_RegInfo *>(sec->content().data());
- reginfo.ri_gprmask |= r->ri_gprmask;
- sec->getFile<ELFT>()->mipsGp0 = r->ri_gp_value;
- };
-
- return std::make_unique<MipsReginfoSection<ELFT>>(ctx, reginfo);
-}
-
InputSection *elf::createInterpSection(Ctx &ctx) {
// StringSaver guarantees that the returned string ends with '\0'.
StringRef s = ctx.saver.save(ctx.arg.dynamicLinker);
@@ -2746,20 +2576,6 @@ size_t PPC32GlinkSection::getSize() const {
//
// That said, the 2-PLT scheme is a part of the ABI, debuggers and other tools
// depend on it, so we implement the ABI.
-IBTPltSection::IBTPltSection(Ctx &ctx)
- : SyntheticSection(ctx, ".plt", SHT_PROGBITS, SHF_ALLOC | SHF_EXECINSTR,
- 16) {}
-
-void IBTPltSection::writeTo(uint8_t *buf) {
- ctx.target->writeIBTPlt(buf, ctx.in.plt->getNumEntries());
-}
-
-size_t IBTPltSection::getSize() const {
- // 16 is the header size of .plt.
- return 16 + ctx.in.plt->getNumEntries() * ctx.target->pltEntrySize;
-}
-
-bool IBTPltSection::isNeeded() const { return ctx.in.plt->getNumEntries() > 0; }
RelroPaddingSection::RelroPaddingSection(Ctx &ctx)
: SyntheticSection(ctx, ".relro_padding", SHT_NOBITS, SHF_ALLOC | SHF_WRITE,
@@ -4002,10 +3818,6 @@ void elf::combineEhSections(Ctx &ctx) {
});
}
-MipsRldMapSection::MipsRldMapSection(Ctx &ctx)
- : SyntheticSection(ctx, ".rld_map", SHT_PROGBITS, SHF_ALLOC | SHF_WRITE,
- ctx.arg.wordsize) {}
-
ARMExidxSyntheticSection::ARMExidxSyntheticSection(Ctx &ctx)
: SyntheticSection(ctx, ".ARM.exidx", SHT_ARM_EXIDX,
SHF_ALLOC | SHF_LINK_ORDER, ctx.arg.wordsize) {}
@@ -4283,35 +4095,6 @@ bool ThunkSection::assignOffsets() {
return changed;
}
-PPC32Got2Section::PPC32Got2Section(Ctx &ctx)
- : SyntheticSection(ctx, ".got2", SHT_PROGBITS, SHF_ALLOC | SHF_WRITE, 4) {}
-
-bool PPC32Got2Section::isNeeded() const {
- // See the comment below. This is not needed if there is no other
- // InputSection.
- for (SectionCommand *cmd : getParent()->commands)
- if (auto *isd = dyn_cast<InputSectionDescription>(cmd))
- for (InputSection *isec : isd->sections)
- if (isec != this)
- return true;
- return false;
-}
-
-void PPC32Got2Section::finalizeContents() {
- // PPC32 may create multiple GOT sections for -fPIC/-fPIE, one per file in
- // .got2 . This function computes outSecOff of each .got2 to be used in
- // PPC32PltCallStub::writeTo(). The purpose of this empty synthetic section is
- // to collect input sections named ".got2".
- for (SectionCommand *cmd : getParent()->commands)
- if (auto *isd = dyn_cast<InputSectionDescription>(cmd)) {
- for (InputSection *isec : isd->sections) {
- // isec->file may be nullptr for MergeSyntheticSection.
- if (isec != this && isec->file)
- isec->file->ppc32Got2 = isec;
- }
- }
-}
-
// If linking position-dependent code then the table will store the addresses
// directly in the binary so the section has type SHT_PROGBITS. If linking
// position-independent code the section has type SHT_NOBITS since it will be
@@ -4691,19 +4474,7 @@ template <class ELFT> void elf::createSyntheticSections(Ctx &ctx) {
ctx, hasDataRelRo ? ".data.rel.ro.bss" : ".bss.rel.ro", 0, 1);
add(*ctx.in.bssRelRo);
- // Add MIPS-specific sections.
- if (ctx.arg.emachine == EM_MIPS) {
- if (!ctx.arg.shared && ctx.hasDynsym) {
- ctx.in.mipsRldMap = std::make_unique<MipsRldMapSection>(ctx);
- add(*ctx.in.mipsRldMap);
- }
- if ((ctx.in.mipsAbiFlags = MipsAbiFlagsSection<ELFT>::create(ctx)))
- add(*ctx.in.mipsAbiFlags);
- if ((ctx.in.mipsOptions = MipsOptionsSection<ELFT>::create(ctx)))
- add(*ctx.in.mipsOptions);
- if ((ctx.in.mipsReginfo = MipsReginfoSection<ELFT>::create(ctx)))
- add(*ctx.in.mipsReginfo);
- }
+ ctx.target->initTargetSections();
StringRef relaDynName = ctx.arg.isRela ? ".rela.dyn" : ".rel.dyn";
@@ -4838,17 +4609,6 @@ template <class ELFT> void elf::createSyntheticSections(Ctx &ctx) {
add(*ctx.in.got);
}
- if (ctx.arg.emachine == EM_PPC) {
- ctx.in.ppc32Got2 = std::make_unique<PPC32Got2Section>(ctx);
- add(*ctx.in.ppc32Got2);
- }
-
- if (ctx.arg.emachine == EM_PPC64) {
- ctx.in.ppc64LongBranchTarget =
- std::make_unique<PPC64LongBranchTargetSection>(ctx);
- add(*ctx.in.ppc64LongBranchTarget);
- }
-
ctx.in.gotPlt = std::make_unique<GotPltSection>(ctx);
add(*ctx.in.gotPlt);
ctx.in.igotPlt = std::make_unique<IgotPltSection>(ctx);
@@ -4862,11 +4622,6 @@ template <class ELFT> void elf::createSyntheticSections(Ctx &ctx) {
add(*ctx.in.relroPadding);
}
- if (ctx.arg.emachine == EM_ARM) {
- ctx.in.armCmseSGSection = std::make_unique<ArmCmseSGSection>(ctx);
- add(*ctx.in.armCmseSGSection);
- }
-
// _GLOBAL_OFFSET_TABLE_ is defined relative to either .got.plt or .got. Treat
// it as a relocation and ensure the referenced section is created.
if (ctx.sym.globalOffsetTable && ctx.arg.emachine != EM_MIPS) {
@@ -4883,12 +4638,6 @@ template <class ELFT> void elf::createSyntheticSections(Ctx &ctx) {
/*threadCount=*/1);
add(*ctx.in.relaPlt);
- if ((ctx.arg.emachine == EM_386 || ctx.arg.emachine == EM_X86_64) &&
- (ctx.arg.andFeatures & GNU_PROPERTY_X86_FEATURE_1_IBT)) {
- ctx.in.ibtPlt = std::make_unique<IBTPltSection>(ctx);
- add(*ctx.in.ibtPlt);
- }
-
if (ctx.arg.emachine == EM_PPC)
ctx.in.plt = std::make_unique<PPC32GlinkSection>(ctx);
else
diff --git a/lld/ELF/SyntheticSections.h b/lld/ELF/SyntheticSections.h
index 1ae03dc24a2f2..3269b17b84d39 100644
--- a/lld/ELF/SyntheticSections.h
+++ b/lld/ELF/SyntheticSections.h
@@ -797,15 +797,6 @@ class PPC32GlinkSection : public PltSection {
static constexpr size_t footerSize = 64;
};
-// This is x86-only.
-class IBTPltSection : public SyntheticSection {
-public:
- IBTPltSection(Ctx &);
- void writeTo(uint8_t *Buf) override;
- bool isNeeded() const override;
- size_t getSize() const override;
-};
-
// Used to align the end of the PT_GNU_RELRO segment and the associated PT_LOAD
// segment to a common-page-size boundary. This padding section ensures that all
// pages in the PT_LOAD segment is covered by at least one section.
@@ -1141,67 +1132,6 @@ class MergeNoTailSection final : public MergeSyntheticSection {
size_t shardOffsets[numShards];
};
-// .MIPS.abiflags section.
-template <class ELFT>
-class MipsAbiFlagsSection final : public SyntheticSection {
- using Elf_Mips_ABIFlags = llvm::object::Elf_Mips_ABIFlags<ELFT>;
-
-public:
- static std::unique_ptr<MipsAbiFlagsSection> create(Ctx &);
-
- MipsAbiFlagsSection(Ctx &, Elf_Mips_ABIFlags flags);
- size_t getSize() const override { return sizeof(Elf_Mips_ABIFlags); }
- void writeTo(uint8_t *buf) override;
-
-private:
- Elf_Mips_ABIFlags flags;
-};
-
-// .MIPS.options section.
-template <class ELFT> class MipsOptionsSection final : public SyntheticSection {
- using Elf_Mips_Options = llvm::object::Elf_Mips_Options<ELFT>;
- using Elf_Mips_RegInfo = llvm::object::Elf_Mips_RegInfo<ELFT>;
-
-public:
- static std::unique_ptr<MipsOptionsSection<ELFT>> create(Ctx &);
-
- MipsOptionsSection(Ctx &, Elf_Mips_RegInfo reginfo);
- void writeTo(uint8_t *buf) override;
-
- size_t getSize() const override {
- return sizeof(Elf_Mips_Options) + sizeof(Elf_Mips_RegInfo);
- }
-
-private:
- Elf_Mips_RegInfo reginfo;
-};
-
-// MIPS .reginfo section.
-template <class ELFT> class MipsReginfoSection final : public SyntheticSection {
- using Elf_Mips_RegInfo = llvm::object::Elf_Mips_RegInfo<ELFT>;
-
-public:
- static std::unique_ptr<MipsReginfoSection> create(Ctx &);
-
- MipsReginfoSection(Ctx &, Elf_Mips_RegInfo reginfo);
- size_t getSize() const override { return sizeof(Elf_Mips_RegInfo); }
- void writeTo(uint8_t *buf) override;
-
-private:
- Elf_Mips_RegInfo reginfo;
-};
-
-// This is a MIPS specific section to hold a space within the data segment
-// of executable file which is pointed to by the DT_MIPS_RLD_MAP entry.
-// See "Dynamic section" in Chapter 5 in the following document:
-// ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf
-class MipsRldMapSection final : public SyntheticSection {
-public:
- MipsRldMapSection(Ctx &);
- size_t getSize() const override { return ctx.arg.wordsize; }
- void writeTo(uint8_t *buf) override {}
-};
-
// Representation of the combined .ARM.Exidx input sections. We process these
// as a SyntheticSection like .eh_frame as we need to merge duplicate entries
// and add terminating sentinel entries.
@@ -1306,56 +1236,6 @@ class ThunkSection final : public SyntheticSection {
size_t size = 0;
};
-// Cortex-M Security Extensions. Prefix for functions that should be exported
-// for the non-secure world.
-const char ACLESESYM_PREFIX[] = "__acle_se_";
-const int ACLESESYM_SIZE = 8;
-
-class ArmCmseSGVeneer {
-public:
- ArmCmseSGVeneer(Symbol *sym, Symbol *acleSeSym,
- std::optional<uint64_t> addr = std::nullopt)
- : sym(sym), acleSeSym(acleSeSym), entAddr{addr} {}
- static const size_t size{ACLESESYM_SIZE};
- std::optional<uint64_t> getAddr() const { return entAddr; };
-
- Symbol *sym;
- Symbol *acleSeSym;
- uint64_t offset = 0;
-
-private:
- const std::optional<uint64_t> entAddr;
-};
-
-class ArmCmseSGSection final : public SyntheticSection {
-public:
- ArmCmseSGSection(Ctx &ctx);
- bool isNeeded() const override { return !entries.empty(); }
- size_t getSize() const override;
- void writeTo(uint8_t *buf) override;
- void addSGVeneer(Symbol *sym, Symbol *ext_sym);
- void addMappingSymbol();
- void finalizeContents() override;
- void exportEntries(SymbolTableBaseSection *symTab);
- uint64_t impLibMaxAddr = 0;
-
-private:
- SmallVector<std::pair<Symbol *, Symbol *>, 0> entries;
- SmallVector<std::unique_ptr<ArmCmseSGVeneer>, 0> sgVeneers;
- uint64_t newEntries = 0;
-};
-
-// Used to compute outSecOff of .got2 in each object file. This is needed to
-// synthesize PLT entries for PPC32 Secure PLT ABI.
-class PPC32Got2Section final : public SyntheticSection {
-public:
- PPC32Got2Section(Ctx &);
- size_t getSize() const override { return 0; }
- bool isNeeded() const override;
- void finalizeContents() override;
- void writeTo(uint8_t *buf) override {}
-};
-
// This section is used to store the addresses of functions that are called
// in range-extending thunks on PowerPC64. When producing position dependent
// code the addresses are link-time constants and the table is written out to
diff --git a/lld/ELF/Target.h b/lld/ELF/Target.h
index 85fa683b84a6d..9ce33fdfc7c73 100644
--- a/lld/ELF/Target.h
+++ b/lld/ELF/Target.h
@@ -32,6 +32,7 @@ class TargetInfo {
public:
TargetInfo(Ctx &ctx) : ctx(ctx) {}
virtual uint32_t calcEFlags() const { return 0; }
+ virtual void initTargetSections() {}
virtual RelExpr getRelExpr(RelType type, const Symbol &s,
const uint8_t *loc) const = 0;
virtual RelType getDynRel(RelType type) const { return 0; }
>From 5d450a2e3e9b7fae3599e46c92e9642073f985b4 Mon Sep 17 00:00:00 2001
From: Fangrui Song <i at maskray.me>
Date: Sun, 1 Mar 2026 19:10:50 -0800
Subject: [PATCH 2/2] restore comments
---
lld/ELF/Arch/Mips.cpp | 4 ++++
lld/ELF/Arch/PPC.cpp | 2 ++
2 files changed, 6 insertions(+)
diff --git a/lld/ELF/Arch/Mips.cpp b/lld/ELF/Arch/Mips.cpp
index 1c319b1d760e4..efaa4e83056ff 100644
--- a/lld/ELF/Arch/Mips.cpp
+++ b/lld/ELF/Arch/Mips.cpp
@@ -44,6 +44,10 @@ template <class ELFT> class MIPS final : public TargetInfo {
bool usesOnlyLowPageBits(RelType type) const override;
};
+// This is a MIPS specific section to hold a space within the data segment
+// of executable file which is pointed to by the DT_MIPS_RLD_MAP entry.
+// See "Dynamic section" in Chapter 5 in the following document:
+// ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf
struct RldMapSection : SyntheticSection {
RldMapSection(Ctx &ctx)
: SyntheticSection(ctx, ".rld_map", SHT_PROGBITS, SHF_ALLOC | SHF_WRITE,
diff --git a/lld/ELF/Arch/PPC.cpp b/lld/ELF/Arch/PPC.cpp
index e6290874bec2f..0b6821c4a6eb5 100644
--- a/lld/ELF/Arch/PPC.cpp
+++ b/lld/ELF/Arch/PPC.cpp
@@ -61,6 +61,8 @@ class PPC final : public TargetInfo {
void relaxTlsIeToLe(uint8_t *loc, const Relocation &rel, uint64_t val) const;
};
+// Used to compute outSecOff of .got2 in each object file. This is needed to
+// synthesize PLT entries for PPC32 Secure PLT ABI.
struct Got2Section : SyntheticSection {
Got2Section(Ctx &ctx)
: SyntheticSection(ctx, ".got2", SHT_PROGBITS, SHF_ALLOC | SHF_WRITE, 4) {
More information about the llvm-commits
mailing list