mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-16 01:06:32 +00:00

This permits an AArch64AbsLongThunk to be used in an environment where unaligned accesses are disabled. The AArch64AbsLongThunk does a load of an 8-byte address. When unaligned accesses are disabled this address must be 8-byte aligned. The vast majority of AArch64 systems will have unaligned accesses enabled in userspace. However, after a reset, before the MMU has been enabled, all memory accesses are to "device" memory, which requires aligned accesses. In systems with multi-stage boot loaders a thunk may be required to a later stage before the MMU has been enabled. As we only want to increase the alignment when the ldr is used we delay the increase in thunk alignment until we know we are going to write an ldr. We also need to account for the ThunkSection alignment increase when this happens. In some of the test updates, particularly those with shared CHECK lines with position independent thunks it was easier to ensure that the thunks started at an 8-byte aligned address in all cases.
5021 lines
183 KiB
C++
5021 lines
183 KiB
C++
//===- SyntheticSections.cpp ----------------------------------------------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This file contains linker-synthesized sections. Currently,
|
|
// synthetic sections are created either output sections or input sections,
|
|
// but we are rewriting code so that all synthetic sections are created as
|
|
// input sections.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "SyntheticSections.h"
|
|
#include "Config.h"
|
|
#include "DWARF.h"
|
|
#include "EhFrame.h"
|
|
#include "InputFiles.h"
|
|
#include "LinkerScript.h"
|
|
#include "OutputSections.h"
|
|
#include "SymbolTable.h"
|
|
#include "Symbols.h"
|
|
#include "Target.h"
|
|
#include "Thunks.h"
|
|
#include "Writer.h"
|
|
#include "lld/Common/CommonLinkerContext.h"
|
|
#include "lld/Common/DWARF.h"
|
|
#include "lld/Common/Strings.h"
|
|
#include "lld/Common/Version.h"
|
|
#include "llvm/ADT/STLExtras.h"
|
|
#include "llvm/ADT/Sequence.h"
|
|
#include "llvm/ADT/SetOperations.h"
|
|
#include "llvm/ADT/StringExtras.h"
|
|
#include "llvm/BinaryFormat/Dwarf.h"
|
|
#include "llvm/BinaryFormat/ELF.h"
|
|
#include "llvm/DebugInfo/DWARF/DWARFAcceleratorTable.h"
|
|
#include "llvm/DebugInfo/DWARF/DWARFDebugPubTable.h"
|
|
#include "llvm/Support/DJB.h"
|
|
#include "llvm/Support/Endian.h"
|
|
#include "llvm/Support/LEB128.h"
|
|
#include "llvm/Support/Parallel.h"
|
|
#include "llvm/Support/TimeProfiler.h"
|
|
#include <cinttypes>
|
|
#include <cstdlib>
|
|
|
|
using namespace llvm;
|
|
using namespace llvm::dwarf;
|
|
using namespace llvm::ELF;
|
|
using namespace llvm::object;
|
|
using namespace llvm::support;
|
|
using namespace lld;
|
|
using namespace lld::elf;
|
|
|
|
using llvm::support::endian::read32le;
|
|
using llvm::support::endian::write32le;
|
|
using llvm::support::endian::write64le;
|
|
|
|
constexpr size_t MergeNoTailSection::numShards;
|
|
|
|
static uint64_t readUint(Ctx &ctx, uint8_t *buf) {
|
|
return ctx.arg.is64 ? read64(ctx, buf) : read32(ctx, buf);
|
|
}
|
|
|
|
static void writeUint(Ctx &ctx, uint8_t *buf, uint64_t val) {
|
|
if (ctx.arg.is64)
|
|
write64(ctx, buf, val);
|
|
else
|
|
write32(ctx, buf, val);
|
|
}
|
|
|
|
// Returns an LLD version string.
|
|
static ArrayRef<uint8_t> getVersion(Ctx &ctx) {
|
|
// Check LLD_VERSION first for ease of testing.
|
|
// You can get consistent output by using the environment variable.
|
|
// This is only for testing.
|
|
StringRef s = getenv("LLD_VERSION");
|
|
if (s.empty())
|
|
s = ctx.saver.save(Twine("Linker: ") + getLLDVersion());
|
|
|
|
// +1 to include the terminating '\0'.
|
|
return {(const uint8_t *)s.data(), s.size() + 1};
|
|
}
|
|
|
|
// Creates a .comment section containing LLD version info.
|
|
// With this feature, you can identify LLD-generated binaries easily
|
|
// by "readelf --string-dump .comment <file>".
|
|
// The returned object is a mergeable string section.
|
|
MergeInputSection *elf::createCommentSection(Ctx &ctx) {
|
|
auto *sec =
|
|
make<MergeInputSection>(ctx, ".comment", SHT_PROGBITS,
|
|
SHF_MERGE | SHF_STRINGS, 1, getVersion(ctx));
|
|
sec->splitIntoPieces();
|
|
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);
|
|
ArrayRef<uint8_t> contents = {(const uint8_t *)s.data(), s.size() + 1};
|
|
|
|
return make<InputSection>(ctx.internalFile, ".interp", SHT_PROGBITS,
|
|
SHF_ALLOC,
|
|
/*addralign=*/1, /*entsize=*/0, contents);
|
|
}
|
|
|
|
Defined *elf::addSyntheticLocal(Ctx &ctx, StringRef name, uint8_t type,
|
|
uint64_t value, uint64_t size,
|
|
InputSectionBase §ion) {
|
|
Defined *s = makeDefined(ctx, section.file, name, STB_LOCAL, STV_DEFAULT,
|
|
type, value, size, §ion);
|
|
if (ctx.in.symTab)
|
|
ctx.in.symTab->addSymbol(s);
|
|
|
|
if (ctx.arg.emachine == EM_ARM && !ctx.arg.isLE && ctx.arg.armBe8 &&
|
|
(section.flags & SHF_EXECINSTR))
|
|
// Adding Linker generated mapping symbols to the arm specific mapping
|
|
// symbols list.
|
|
addArmSyntheticSectionMappingSymbol(s);
|
|
|
|
return s;
|
|
}
|
|
|
|
static size_t getHashSize(Ctx &ctx) {
|
|
switch (ctx.arg.buildId) {
|
|
case BuildIdKind::Fast:
|
|
return 8;
|
|
case BuildIdKind::Md5:
|
|
case BuildIdKind::Uuid:
|
|
return 16;
|
|
case BuildIdKind::Sha1:
|
|
return 20;
|
|
case BuildIdKind::Hexstring:
|
|
return ctx.arg.buildIdVector.size();
|
|
default:
|
|
llvm_unreachable("unknown BuildIdKind");
|
|
}
|
|
}
|
|
|
|
// This class represents a linker-synthesized .note.gnu.property section.
|
|
//
|
|
// In x86 and AArch64, object files may contain feature flags indicating the
|
|
// features that they have used. The flags are stored in a .note.gnu.property
|
|
// section.
|
|
//
|
|
// lld reads the sections from input files and merges them by computing AND of
|
|
// the flags. The result is written as a new .note.gnu.property section.
|
|
//
|
|
// If the flag is zero (which indicates that the intersection of the feature
|
|
// sets is empty, or some input files didn't have .note.gnu.property sections),
|
|
// we don't create this section.
|
|
GnuPropertySection::GnuPropertySection(Ctx &ctx)
|
|
: SyntheticSection(ctx, ".note.gnu.property", SHT_NOTE, SHF_ALLOC,
|
|
ctx.arg.wordsize) {}
|
|
|
|
void GnuPropertySection::writeTo(uint8_t *buf) {
|
|
write32(ctx, buf, 4); // Name size
|
|
write32(ctx, buf + 4, getSize() - 16); // Content size
|
|
write32(ctx, buf + 8, NT_GNU_PROPERTY_TYPE_0); // Type
|
|
memcpy(buf + 12, "GNU", 4); // Name string
|
|
|
|
uint32_t featureAndType = ctx.arg.emachine == EM_AARCH64
|
|
? GNU_PROPERTY_AARCH64_FEATURE_1_AND
|
|
: GNU_PROPERTY_X86_FEATURE_1_AND;
|
|
|
|
unsigned offset = 16;
|
|
if (ctx.arg.andFeatures != 0) {
|
|
write32(ctx, buf + offset + 0, featureAndType); // Feature type
|
|
write32(ctx, buf + offset + 4, 4); // Feature size
|
|
write32(ctx, buf + offset + 8, ctx.arg.andFeatures); // Feature flags
|
|
if (ctx.arg.is64)
|
|
write32(ctx, buf + offset + 12, 0); // Padding
|
|
offset += 16;
|
|
}
|
|
|
|
if (!ctx.aarch64PauthAbiCoreInfo.empty()) {
|
|
write32(ctx, buf + offset + 0, GNU_PROPERTY_AARCH64_FEATURE_PAUTH);
|
|
write32(ctx, buf + offset + 4, ctx.aarch64PauthAbiCoreInfo.size());
|
|
memcpy(buf + offset + 8, ctx.aarch64PauthAbiCoreInfo.data(),
|
|
ctx.aarch64PauthAbiCoreInfo.size());
|
|
}
|
|
}
|
|
|
|
size_t GnuPropertySection::getSize() const {
|
|
uint32_t contentSize = 0;
|
|
if (ctx.arg.andFeatures != 0)
|
|
contentSize += ctx.arg.is64 ? 16 : 12;
|
|
if (!ctx.aarch64PauthAbiCoreInfo.empty())
|
|
contentSize += 4 + 4 + ctx.aarch64PauthAbiCoreInfo.size();
|
|
assert(contentSize != 0);
|
|
return contentSize + 16;
|
|
}
|
|
|
|
BuildIdSection::BuildIdSection(Ctx &ctx)
|
|
: SyntheticSection(ctx, ".note.gnu.build-id", SHT_NOTE, SHF_ALLOC, 4),
|
|
hashSize(getHashSize(ctx)) {}
|
|
|
|
void BuildIdSection::writeTo(uint8_t *buf) {
|
|
write32(ctx, buf, 4); // Name size
|
|
write32(ctx, buf + 4, hashSize); // Content size
|
|
write32(ctx, buf + 8, NT_GNU_BUILD_ID); // Type
|
|
memcpy(buf + 12, "GNU", 4); // Name string
|
|
hashBuf = buf + 16;
|
|
}
|
|
|
|
void BuildIdSection::writeBuildId(ArrayRef<uint8_t> buf) {
|
|
assert(buf.size() == hashSize);
|
|
memcpy(hashBuf, buf.data(), hashSize);
|
|
}
|
|
|
|
BssSection::BssSection(Ctx &ctx, StringRef name, uint64_t size,
|
|
uint32_t alignment)
|
|
: SyntheticSection(ctx, name, SHT_NOBITS, SHF_ALLOC | SHF_WRITE,
|
|
alignment) {
|
|
this->bss = true;
|
|
this->size = size;
|
|
}
|
|
|
|
EhFrameSection::EhFrameSection(Ctx &ctx)
|
|
: SyntheticSection(ctx, ".eh_frame", SHT_PROGBITS, SHF_ALLOC, 1) {}
|
|
|
|
// Search for an existing CIE record or create a new one.
|
|
// CIE records from input object files are uniquified by their contents
|
|
// and where their relocations point to.
|
|
template <class ELFT, class RelTy>
|
|
CieRecord *EhFrameSection::addCie(EhSectionPiece &cie, ArrayRef<RelTy> rels) {
|
|
Symbol *personality = nullptr;
|
|
unsigned firstRelI = cie.firstRelocation;
|
|
if (firstRelI != (unsigned)-1)
|
|
personality = &cie.sec->file->getRelocTargetSym(rels[firstRelI]);
|
|
|
|
// Search for an existing CIE by CIE contents/relocation target pair.
|
|
CieRecord *&rec = cieMap[{cie.data(), personality}];
|
|
|
|
// If not found, create a new one.
|
|
if (!rec) {
|
|
rec = make<CieRecord>();
|
|
rec->cie = &cie;
|
|
cieRecords.push_back(rec);
|
|
}
|
|
return rec;
|
|
}
|
|
|
|
// There is one FDE per function. Returns a non-null pointer to the function
|
|
// symbol if the given FDE points to a live function.
|
|
template <class ELFT, class RelTy>
|
|
Defined *EhFrameSection::isFdeLive(EhSectionPiece &fde, ArrayRef<RelTy> rels) {
|
|
auto *sec = cast<EhInputSection>(fde.sec);
|
|
unsigned firstRelI = fde.firstRelocation;
|
|
|
|
// An FDE should point to some function because FDEs are to describe
|
|
// functions. That's however not always the case due to an issue of
|
|
// ld.gold with -r. ld.gold may discard only functions and leave their
|
|
// corresponding FDEs, which results in creating bad .eh_frame sections.
|
|
// To deal with that, we ignore such FDEs.
|
|
if (firstRelI == (unsigned)-1)
|
|
return nullptr;
|
|
|
|
const RelTy &rel = rels[firstRelI];
|
|
Symbol &b = sec->file->getRelocTargetSym(rel);
|
|
|
|
// FDEs for garbage-collected or merged-by-ICF sections, or sections in
|
|
// another partition, are dead.
|
|
if (auto *d = dyn_cast<Defined>(&b))
|
|
if (!d->folded && d->section && d->section->partition == partition)
|
|
return d;
|
|
return nullptr;
|
|
}
|
|
|
|
// .eh_frame is a sequence of CIE or FDE records. In general, there
|
|
// is one CIE record per input object file which is followed by
|
|
// a list of FDEs. This function searches an existing CIE or create a new
|
|
// one and associates FDEs to the CIE.
|
|
template <class ELFT, class RelTy>
|
|
void EhFrameSection::addRecords(EhInputSection *sec, ArrayRef<RelTy> rels) {
|
|
offsetToCie.clear();
|
|
for (EhSectionPiece &cie : sec->cies)
|
|
offsetToCie[cie.inputOff] = addCie<ELFT>(cie, rels);
|
|
for (EhSectionPiece &fde : sec->fdes) {
|
|
uint32_t id = endian::read32<ELFT::Endianness>(fde.data().data() + 4);
|
|
CieRecord *rec = offsetToCie[fde.inputOff + 4 - id];
|
|
if (!rec)
|
|
Fatal(ctx) << sec << ": invalid CIE reference";
|
|
|
|
if (!isFdeLive<ELFT>(fde, rels))
|
|
continue;
|
|
rec->fdes.push_back(&fde);
|
|
numFdes++;
|
|
}
|
|
}
|
|
|
|
template <class ELFT>
|
|
void EhFrameSection::addSectionAux(EhInputSection *sec) {
|
|
if (!sec->isLive())
|
|
return;
|
|
const RelsOrRelas<ELFT> rels =
|
|
sec->template relsOrRelas<ELFT>(/*supportsCrel=*/false);
|
|
if (rels.areRelocsRel())
|
|
addRecords<ELFT>(sec, rels.rels);
|
|
else
|
|
addRecords<ELFT>(sec, rels.relas);
|
|
}
|
|
|
|
// Used by ICF<ELFT>::handleLSDA(). This function is very similar to
|
|
// EhFrameSection::addRecords().
|
|
template <class ELFT, class RelTy>
|
|
void EhFrameSection::iterateFDEWithLSDAAux(
|
|
EhInputSection &sec, ArrayRef<RelTy> rels, DenseSet<size_t> &ciesWithLSDA,
|
|
llvm::function_ref<void(InputSection &)> fn) {
|
|
for (EhSectionPiece &cie : sec.cies)
|
|
if (hasLSDA(cie))
|
|
ciesWithLSDA.insert(cie.inputOff);
|
|
for (EhSectionPiece &fde : sec.fdes) {
|
|
uint32_t id = endian::read32<ELFT::Endianness>(fde.data().data() + 4);
|
|
if (!ciesWithLSDA.contains(fde.inputOff + 4 - id))
|
|
continue;
|
|
|
|
// The CIE has a LSDA argument. Call fn with d's section.
|
|
if (Defined *d = isFdeLive<ELFT>(fde, rels))
|
|
if (auto *s = dyn_cast_or_null<InputSection>(d->section))
|
|
fn(*s);
|
|
}
|
|
}
|
|
|
|
template <class ELFT>
|
|
void EhFrameSection::iterateFDEWithLSDA(
|
|
llvm::function_ref<void(InputSection &)> fn) {
|
|
DenseSet<size_t> ciesWithLSDA;
|
|
for (EhInputSection *sec : sections) {
|
|
ciesWithLSDA.clear();
|
|
const RelsOrRelas<ELFT> rels =
|
|
sec->template relsOrRelas<ELFT>(/*supportsCrel=*/false);
|
|
if (rels.areRelocsRel())
|
|
iterateFDEWithLSDAAux<ELFT>(*sec, rels.rels, ciesWithLSDA, fn);
|
|
else
|
|
iterateFDEWithLSDAAux<ELFT>(*sec, rels.relas, ciesWithLSDA, fn);
|
|
}
|
|
}
|
|
|
|
static void writeCieFde(Ctx &ctx, uint8_t *buf, ArrayRef<uint8_t> d) {
|
|
memcpy(buf, d.data(), d.size());
|
|
// Fix the size field. -4 since size does not include the size field itself.
|
|
write32(ctx, buf, d.size() - 4);
|
|
}
|
|
|
|
void EhFrameSection::finalizeContents() {
|
|
assert(!this->size); // Not finalized.
|
|
|
|
switch (ctx.arg.ekind) {
|
|
case ELFNoneKind:
|
|
llvm_unreachable("invalid ekind");
|
|
case ELF32LEKind:
|
|
for (EhInputSection *sec : sections)
|
|
addSectionAux<ELF32LE>(sec);
|
|
break;
|
|
case ELF32BEKind:
|
|
for (EhInputSection *sec : sections)
|
|
addSectionAux<ELF32BE>(sec);
|
|
break;
|
|
case ELF64LEKind:
|
|
for (EhInputSection *sec : sections)
|
|
addSectionAux<ELF64LE>(sec);
|
|
break;
|
|
case ELF64BEKind:
|
|
for (EhInputSection *sec : sections)
|
|
addSectionAux<ELF64BE>(sec);
|
|
break;
|
|
}
|
|
|
|
size_t off = 0;
|
|
for (CieRecord *rec : cieRecords) {
|
|
rec->cie->outputOff = off;
|
|
off += rec->cie->size;
|
|
|
|
for (EhSectionPiece *fde : rec->fdes) {
|
|
fde->outputOff = off;
|
|
off += fde->size;
|
|
}
|
|
}
|
|
|
|
// The LSB standard does not allow a .eh_frame section with zero
|
|
// Call Frame Information records. glibc unwind-dw2-fde.c
|
|
// classify_object_over_fdes expects there is a CIE record length 0 as a
|
|
// terminator. Thus we add one unconditionally.
|
|
off += 4;
|
|
|
|
this->size = off;
|
|
}
|
|
|
|
// Returns data for .eh_frame_hdr. .eh_frame_hdr is a binary search table
|
|
// to get an FDE from an address to which FDE is applied. This function
|
|
// returns a list of such pairs.
|
|
SmallVector<EhFrameSection::FdeData, 0> EhFrameSection::getFdeData() const {
|
|
uint8_t *buf = ctx.bufferStart + getParent()->offset + outSecOff;
|
|
SmallVector<FdeData, 0> ret;
|
|
|
|
uint64_t va = getPartition(ctx).ehFrameHdr->getVA();
|
|
for (CieRecord *rec : cieRecords) {
|
|
uint8_t enc = getFdeEncoding(rec->cie);
|
|
for (EhSectionPiece *fde : rec->fdes) {
|
|
uint64_t pc = getFdePc(buf, fde->outputOff, enc);
|
|
uint64_t fdeVA = getParent()->addr + fde->outputOff;
|
|
if (!isInt<32>(pc - va)) {
|
|
Err(ctx) << fde->sec << ": PC offset is too large: 0x"
|
|
<< Twine::utohexstr(pc - va);
|
|
continue;
|
|
}
|
|
ret.push_back({uint32_t(pc - va), uint32_t(fdeVA - va)});
|
|
}
|
|
}
|
|
|
|
// Sort the FDE list by their PC and uniqueify. Usually there is only
|
|
// one FDE for a PC (i.e. function), but if ICF merges two functions
|
|
// into one, there can be more than one FDEs pointing to the address.
|
|
auto less = [](const FdeData &a, const FdeData &b) {
|
|
return a.pcRel < b.pcRel;
|
|
};
|
|
llvm::stable_sort(ret, less);
|
|
auto eq = [](const FdeData &a, const FdeData &b) {
|
|
return a.pcRel == b.pcRel;
|
|
};
|
|
ret.erase(std::unique(ret.begin(), ret.end(), eq), ret.end());
|
|
|
|
return ret;
|
|
}
|
|
|
|
static uint64_t readFdeAddr(Ctx &ctx, uint8_t *buf, int size) {
|
|
switch (size) {
|
|
case DW_EH_PE_udata2:
|
|
return read16(ctx, buf);
|
|
case DW_EH_PE_sdata2:
|
|
return (int16_t)read16(ctx, buf);
|
|
case DW_EH_PE_udata4:
|
|
return read32(ctx, buf);
|
|
case DW_EH_PE_sdata4:
|
|
return (int32_t)read32(ctx, buf);
|
|
case DW_EH_PE_udata8:
|
|
case DW_EH_PE_sdata8:
|
|
return read64(ctx, buf);
|
|
case DW_EH_PE_absptr:
|
|
return readUint(ctx, buf);
|
|
}
|
|
Err(ctx) << "unknown FDE size encoding";
|
|
return 0;
|
|
}
|
|
|
|
// Returns the VA to which a given FDE (on a mmap'ed buffer) is applied to.
|
|
// We need it to create .eh_frame_hdr section.
|
|
uint64_t EhFrameSection::getFdePc(uint8_t *buf, size_t fdeOff,
|
|
uint8_t enc) const {
|
|
// The starting address to which this FDE applies is
|
|
// stored at FDE + 8 byte. And this offset is within
|
|
// the .eh_frame section.
|
|
size_t off = fdeOff + 8;
|
|
uint64_t addr = readFdeAddr(ctx, buf + off, enc & 0xf);
|
|
if ((enc & 0x70) == DW_EH_PE_absptr)
|
|
return ctx.arg.is64 ? addr : uint32_t(addr);
|
|
if ((enc & 0x70) == DW_EH_PE_pcrel)
|
|
return addr + getParent()->addr + off + outSecOff;
|
|
Err(ctx) << "unknown FDE size relative encoding";
|
|
return 0;
|
|
}
|
|
|
|
void EhFrameSection::writeTo(uint8_t *buf) {
|
|
// Write CIE and FDE records.
|
|
for (CieRecord *rec : cieRecords) {
|
|
size_t cieOffset = rec->cie->outputOff;
|
|
writeCieFde(ctx, buf + cieOffset, rec->cie->data());
|
|
|
|
for (EhSectionPiece *fde : rec->fdes) {
|
|
size_t off = fde->outputOff;
|
|
writeCieFde(ctx, buf + off, fde->data());
|
|
|
|
// FDE's second word should have the offset to an associated CIE.
|
|
// Write it.
|
|
write32(ctx, buf + off + 4, off + 4 - cieOffset);
|
|
}
|
|
}
|
|
|
|
// Apply relocations. .eh_frame section contents are not contiguous
|
|
// in the output buffer, but relocateAlloc() still works because
|
|
// getOffset() takes care of discontiguous section pieces.
|
|
for (EhInputSection *s : sections)
|
|
ctx.target->relocateAlloc(*s, buf);
|
|
|
|
if (getPartition(ctx).ehFrameHdr && getPartition(ctx).ehFrameHdr->getParent())
|
|
getPartition(ctx).ehFrameHdr->write();
|
|
}
|
|
|
|
GotSection::GotSection(Ctx &ctx)
|
|
: SyntheticSection(ctx, ".got", SHT_PROGBITS, SHF_ALLOC | SHF_WRITE,
|
|
ctx.target->gotEntrySize) {
|
|
numEntries = ctx.target->gotHeaderEntriesNum;
|
|
}
|
|
|
|
void GotSection::addConstant(const Relocation &r) { relocations.push_back(r); }
|
|
void GotSection::addEntry(const Symbol &sym) {
|
|
assert(sym.auxIdx == ctx.symAux.size() - 1);
|
|
ctx.symAux.back().gotIdx = numEntries++;
|
|
}
|
|
|
|
void GotSection::addAuthEntry(const Symbol &sym) {
|
|
authEntries.push_back({(numEntries - 1) * ctx.arg.wordsize, sym.isFunc()});
|
|
}
|
|
|
|
bool GotSection::addTlsDescEntry(const Symbol &sym) {
|
|
assert(sym.auxIdx == ctx.symAux.size() - 1);
|
|
ctx.symAux.back().tlsDescIdx = numEntries;
|
|
numEntries += 2;
|
|
return true;
|
|
}
|
|
|
|
void GotSection::addTlsDescAuthEntry() {
|
|
authEntries.push_back({(numEntries - 2) * ctx.arg.wordsize, true});
|
|
authEntries.push_back({(numEntries - 1) * ctx.arg.wordsize, false});
|
|
}
|
|
|
|
bool GotSection::addDynTlsEntry(const Symbol &sym) {
|
|
assert(sym.auxIdx == ctx.symAux.size() - 1);
|
|
ctx.symAux.back().tlsGdIdx = numEntries;
|
|
// Global Dynamic TLS entries take two GOT slots.
|
|
numEntries += 2;
|
|
return true;
|
|
}
|
|
|
|
// Reserves TLS entries for a TLS module ID and a TLS block offset.
|
|
// In total it takes two GOT slots.
|
|
bool GotSection::addTlsIndex() {
|
|
if (tlsIndexOff != uint32_t(-1))
|
|
return false;
|
|
tlsIndexOff = numEntries * ctx.arg.wordsize;
|
|
numEntries += 2;
|
|
return true;
|
|
}
|
|
|
|
uint32_t GotSection::getTlsDescOffset(const Symbol &sym) const {
|
|
return sym.getTlsDescIdx(ctx) * ctx.arg.wordsize;
|
|
}
|
|
|
|
uint64_t GotSection::getTlsDescAddr(const Symbol &sym) const {
|
|
return getVA() + getTlsDescOffset(sym);
|
|
}
|
|
|
|
uint64_t GotSection::getGlobalDynAddr(const Symbol &b) const {
|
|
return this->getVA() + b.getTlsGdIdx(ctx) * ctx.arg.wordsize;
|
|
}
|
|
|
|
uint64_t GotSection::getGlobalDynOffset(const Symbol &b) const {
|
|
return b.getTlsGdIdx(ctx) * ctx.arg.wordsize;
|
|
}
|
|
|
|
void GotSection::finalizeContents() {
|
|
if (ctx.arg.emachine == EM_PPC64 &&
|
|
numEntries <= ctx.target->gotHeaderEntriesNum &&
|
|
!ctx.sym.globalOffsetTable)
|
|
size = 0;
|
|
else
|
|
size = numEntries * ctx.arg.wordsize;
|
|
}
|
|
|
|
bool GotSection::isNeeded() const {
|
|
// Needed if the GOT symbol is used or the number of entries is more than just
|
|
// the header. A GOT with just the header may not be needed.
|
|
return hasGotOffRel || numEntries > ctx.target->gotHeaderEntriesNum;
|
|
}
|
|
|
|
void GotSection::writeTo(uint8_t *buf) {
|
|
// On PPC64 .got may be needed but empty. Skip the write.
|
|
if (size == 0)
|
|
return;
|
|
ctx.target->writeGotHeader(buf);
|
|
ctx.target->relocateAlloc(*this, buf);
|
|
for (const AuthEntryInfo &authEntry : authEntries) {
|
|
// https://github.com/ARM-software/abi-aa/blob/2024Q3/pauthabielf64/pauthabielf64.rst#default-signing-schema
|
|
// Signed GOT entries use the IA key for symbols of type STT_FUNC and the
|
|
// DA key for all other symbol types, with the address of the GOT entry as
|
|
// the modifier. The static linker must encode the signing schema into the
|
|
// GOT slot.
|
|
//
|
|
// https://github.com/ARM-software/abi-aa/blob/2024Q3/pauthabielf64/pauthabielf64.rst#encoding-the-signing-schema
|
|
// If address diversity is set and the discriminator
|
|
// is 0 then modifier = Place
|
|
uint8_t *dest = buf + authEntry.offset;
|
|
uint64_t key = authEntry.isSymbolFunc ? /*IA=*/0b00 : /*DA=*/0b10;
|
|
uint64_t addrDiversity = 1;
|
|
write64(ctx, dest, (addrDiversity << 63) | (key << 60));
|
|
}
|
|
}
|
|
|
|
static uint64_t getMipsPageAddr(uint64_t addr) {
|
|
return (addr + 0x8000) & ~0xffff;
|
|
}
|
|
|
|
static uint64_t getMipsPageCount(uint64_t size) {
|
|
return (size + 0xfffe) / 0xffff + 1;
|
|
}
|
|
|
|
MipsGotSection::MipsGotSection(Ctx &ctx)
|
|
: SyntheticSection(ctx, ".got", SHT_PROGBITS,
|
|
SHF_ALLOC | SHF_WRITE | SHF_MIPS_GPREL, 16) {}
|
|
|
|
void MipsGotSection::addEntry(InputFile &file, Symbol &sym, int64_t addend,
|
|
RelExpr expr) {
|
|
FileGot &g = getGot(file);
|
|
if (expr == RE_MIPS_GOT_LOCAL_PAGE) {
|
|
if (const OutputSection *os = sym.getOutputSection())
|
|
g.pagesMap.insert({os, {}});
|
|
else
|
|
g.local16.insert({{nullptr, getMipsPageAddr(sym.getVA(ctx, addend))}, 0});
|
|
} else if (sym.isTls())
|
|
g.tls.insert({&sym, 0});
|
|
else if (sym.isPreemptible && expr == R_ABS)
|
|
g.relocs.insert({&sym, 0});
|
|
else if (sym.isPreemptible)
|
|
g.global.insert({&sym, 0});
|
|
else if (expr == RE_MIPS_GOT_OFF32)
|
|
g.local32.insert({{&sym, addend}, 0});
|
|
else
|
|
g.local16.insert({{&sym, addend}, 0});
|
|
}
|
|
|
|
void MipsGotSection::addDynTlsEntry(InputFile &file, Symbol &sym) {
|
|
getGot(file).dynTlsSymbols.insert({&sym, 0});
|
|
}
|
|
|
|
void MipsGotSection::addTlsIndex(InputFile &file) {
|
|
getGot(file).dynTlsSymbols.insert({nullptr, 0});
|
|
}
|
|
|
|
size_t MipsGotSection::FileGot::getEntriesNum() const {
|
|
return getPageEntriesNum() + local16.size() + global.size() + relocs.size() +
|
|
tls.size() + dynTlsSymbols.size() * 2;
|
|
}
|
|
|
|
size_t MipsGotSection::FileGot::getPageEntriesNum() const {
|
|
size_t num = 0;
|
|
for (const std::pair<const OutputSection *, FileGot::PageBlock> &p : pagesMap)
|
|
num += p.second.count;
|
|
return num;
|
|
}
|
|
|
|
size_t MipsGotSection::FileGot::getIndexedEntriesNum() const {
|
|
size_t count = getPageEntriesNum() + local16.size() + global.size();
|
|
// If there are relocation-only entries in the GOT, TLS entries
|
|
// are allocated after them. TLS entries should be addressable
|
|
// by 16-bit index so count both reloc-only and TLS entries.
|
|
if (!tls.empty() || !dynTlsSymbols.empty())
|
|
count += relocs.size() + tls.size() + dynTlsSymbols.size() * 2;
|
|
return count;
|
|
}
|
|
|
|
MipsGotSection::FileGot &MipsGotSection::getGot(InputFile &f) {
|
|
if (f.mipsGotIndex == uint32_t(-1)) {
|
|
gots.emplace_back();
|
|
gots.back().file = &f;
|
|
f.mipsGotIndex = gots.size() - 1;
|
|
}
|
|
return gots[f.mipsGotIndex];
|
|
}
|
|
|
|
uint64_t MipsGotSection::getPageEntryOffset(const InputFile *f,
|
|
const Symbol &sym,
|
|
int64_t addend) const {
|
|
const FileGot &g = gots[f->mipsGotIndex];
|
|
uint64_t index = 0;
|
|
if (const OutputSection *outSec = sym.getOutputSection()) {
|
|
uint64_t secAddr = getMipsPageAddr(outSec->addr);
|
|
uint64_t symAddr = getMipsPageAddr(sym.getVA(ctx, addend));
|
|
index = g.pagesMap.lookup(outSec).firstIndex + (symAddr - secAddr) / 0xffff;
|
|
} else {
|
|
index =
|
|
g.local16.lookup({nullptr, getMipsPageAddr(sym.getVA(ctx, addend))});
|
|
}
|
|
return index * ctx.arg.wordsize;
|
|
}
|
|
|
|
uint64_t MipsGotSection::getSymEntryOffset(const InputFile *f, const Symbol &s,
|
|
int64_t addend) const {
|
|
const FileGot &g = gots[f->mipsGotIndex];
|
|
Symbol *sym = const_cast<Symbol *>(&s);
|
|
if (sym->isTls())
|
|
return g.tls.lookup(sym) * ctx.arg.wordsize;
|
|
if (sym->isPreemptible)
|
|
return g.global.lookup(sym) * ctx.arg.wordsize;
|
|
return g.local16.lookup({sym, addend}) * ctx.arg.wordsize;
|
|
}
|
|
|
|
uint64_t MipsGotSection::getTlsIndexOffset(const InputFile *f) const {
|
|
const FileGot &g = gots[f->mipsGotIndex];
|
|
return g.dynTlsSymbols.lookup(nullptr) * ctx.arg.wordsize;
|
|
}
|
|
|
|
uint64_t MipsGotSection::getGlobalDynOffset(const InputFile *f,
|
|
const Symbol &s) const {
|
|
const FileGot &g = gots[f->mipsGotIndex];
|
|
Symbol *sym = const_cast<Symbol *>(&s);
|
|
return g.dynTlsSymbols.lookup(sym) * ctx.arg.wordsize;
|
|
}
|
|
|
|
const Symbol *MipsGotSection::getFirstGlobalEntry() const {
|
|
if (gots.empty())
|
|
return nullptr;
|
|
const FileGot &primGot = gots.front();
|
|
if (!primGot.global.empty())
|
|
return primGot.global.front().first;
|
|
if (!primGot.relocs.empty())
|
|
return primGot.relocs.front().first;
|
|
return nullptr;
|
|
}
|
|
|
|
unsigned MipsGotSection::getLocalEntriesNum() const {
|
|
if (gots.empty())
|
|
return headerEntriesNum;
|
|
return headerEntriesNum + gots.front().getPageEntriesNum() +
|
|
gots.front().local16.size();
|
|
}
|
|
|
|
bool MipsGotSection::tryMergeGots(FileGot &dst, FileGot &src, bool isPrimary) {
|
|
FileGot tmp = dst;
|
|
set_union(tmp.pagesMap, src.pagesMap);
|
|
set_union(tmp.local16, src.local16);
|
|
set_union(tmp.global, src.global);
|
|
set_union(tmp.relocs, src.relocs);
|
|
set_union(tmp.tls, src.tls);
|
|
set_union(tmp.dynTlsSymbols, src.dynTlsSymbols);
|
|
|
|
size_t count = isPrimary ? headerEntriesNum : 0;
|
|
count += tmp.getIndexedEntriesNum();
|
|
|
|
if (count * ctx.arg.wordsize > ctx.arg.mipsGotSize)
|
|
return false;
|
|
|
|
std::swap(tmp, dst);
|
|
return true;
|
|
}
|
|
|
|
void MipsGotSection::finalizeContents() { updateAllocSize(ctx); }
|
|
|
|
bool MipsGotSection::updateAllocSize(Ctx &ctx) {
|
|
size = headerEntriesNum * ctx.arg.wordsize;
|
|
for (const FileGot &g : gots)
|
|
size += g.getEntriesNum() * ctx.arg.wordsize;
|
|
return false;
|
|
}
|
|
|
|
void MipsGotSection::build() {
|
|
if (gots.empty())
|
|
return;
|
|
|
|
std::vector<FileGot> mergedGots(1);
|
|
|
|
// For each GOT move non-preemptible symbols from the `Global`
|
|
// to `Local16` list. Preemptible symbol might become non-preemptible
|
|
// one if, for example, it gets a related copy relocation.
|
|
for (FileGot &got : gots) {
|
|
for (auto &p: got.global)
|
|
if (!p.first->isPreemptible)
|
|
got.local16.insert({{p.first, 0}, 0});
|
|
got.global.remove_if([&](const std::pair<Symbol *, size_t> &p) {
|
|
return !p.first->isPreemptible;
|
|
});
|
|
}
|
|
|
|
// For each GOT remove "reloc-only" entry if there is "global"
|
|
// entry for the same symbol. And add local entries which indexed
|
|
// using 32-bit value at the end of 16-bit entries.
|
|
for (FileGot &got : gots) {
|
|
got.relocs.remove_if([&](const std::pair<Symbol *, size_t> &p) {
|
|
return got.global.count(p.first);
|
|
});
|
|
set_union(got.local16, got.local32);
|
|
got.local32.clear();
|
|
}
|
|
|
|
// Evaluate number of "reloc-only" entries in the resulting GOT.
|
|
// To do that put all unique "reloc-only" and "global" entries
|
|
// from all GOTs to the future primary GOT.
|
|
FileGot *primGot = &mergedGots.front();
|
|
for (FileGot &got : gots) {
|
|
set_union(primGot->relocs, got.global);
|
|
set_union(primGot->relocs, got.relocs);
|
|
got.relocs.clear();
|
|
}
|
|
|
|
// Evaluate number of "page" entries in each GOT.
|
|
for (FileGot &got : gots) {
|
|
for (std::pair<const OutputSection *, FileGot::PageBlock> &p :
|
|
got.pagesMap) {
|
|
const OutputSection *os = p.first;
|
|
uint64_t secSize = 0;
|
|
for (SectionCommand *cmd : os->commands) {
|
|
if (auto *isd = dyn_cast<InputSectionDescription>(cmd))
|
|
for (InputSection *isec : isd->sections) {
|
|
uint64_t off = alignToPowerOf2(secSize, isec->addralign);
|
|
secSize = off + isec->getSize();
|
|
}
|
|
}
|
|
p.second.count = getMipsPageCount(secSize);
|
|
}
|
|
}
|
|
|
|
// Merge GOTs. Try to join as much as possible GOTs but do not exceed
|
|
// maximum GOT size. At first, try to fill the primary GOT because
|
|
// the primary GOT can be accessed in the most effective way. If it
|
|
// is not possible, try to fill the last GOT in the list, and finally
|
|
// create a new GOT if both attempts failed.
|
|
for (FileGot &srcGot : gots) {
|
|
InputFile *file = srcGot.file;
|
|
if (tryMergeGots(mergedGots.front(), srcGot, true)) {
|
|
file->mipsGotIndex = 0;
|
|
} else {
|
|
// If this is the first time we failed to merge with the primary GOT,
|
|
// MergedGots.back() will also be the primary GOT. We must make sure not
|
|
// to try to merge again with isPrimary=false, as otherwise, if the
|
|
// inputs are just right, we could allow the primary GOT to become 1 or 2
|
|
// words bigger due to ignoring the header size.
|
|
if (mergedGots.size() == 1 ||
|
|
!tryMergeGots(mergedGots.back(), srcGot, false)) {
|
|
mergedGots.emplace_back();
|
|
std::swap(mergedGots.back(), srcGot);
|
|
}
|
|
file->mipsGotIndex = mergedGots.size() - 1;
|
|
}
|
|
}
|
|
std::swap(gots, mergedGots);
|
|
|
|
// Reduce number of "reloc-only" entries in the primary GOT
|
|
// by subtracting "global" entries in the primary GOT.
|
|
primGot = &gots.front();
|
|
primGot->relocs.remove_if([&](const std::pair<Symbol *, size_t> &p) {
|
|
return primGot->global.count(p.first);
|
|
});
|
|
|
|
// Calculate indexes for each GOT entry.
|
|
size_t index = headerEntriesNum;
|
|
for (FileGot &got : gots) {
|
|
got.startIndex = &got == primGot ? 0 : index;
|
|
for (std::pair<const OutputSection *, FileGot::PageBlock> &p :
|
|
got.pagesMap) {
|
|
// For each output section referenced by GOT page relocations calculate
|
|
// and save into pagesMap an upper bound of MIPS GOT entries required
|
|
// to store page addresses of local symbols. We assume the worst case -
|
|
// each 64kb page of the output section has at least one GOT relocation
|
|
// against it. And take in account the case when the section intersects
|
|
// page boundaries.
|
|
p.second.firstIndex = index;
|
|
index += p.second.count;
|
|
}
|
|
for (auto &p: got.local16)
|
|
p.second = index++;
|
|
for (auto &p: got.global)
|
|
p.second = index++;
|
|
for (auto &p: got.relocs)
|
|
p.second = index++;
|
|
for (auto &p: got.tls)
|
|
p.second = index++;
|
|
for (auto &p: got.dynTlsSymbols) {
|
|
p.second = index;
|
|
index += 2;
|
|
}
|
|
}
|
|
|
|
// Update SymbolAux::gotIdx field to use this
|
|
// value later in the `sortMipsSymbols` function.
|
|
for (auto &p : primGot->global) {
|
|
if (p.first->auxIdx == 0)
|
|
p.first->allocateAux(ctx);
|
|
ctx.symAux.back().gotIdx = p.second;
|
|
}
|
|
for (auto &p : primGot->relocs) {
|
|
if (p.first->auxIdx == 0)
|
|
p.first->allocateAux(ctx);
|
|
ctx.symAux.back().gotIdx = p.second;
|
|
}
|
|
|
|
// Create dynamic relocations.
|
|
for (FileGot &got : gots) {
|
|
// Create dynamic relocations for TLS entries.
|
|
for (std::pair<Symbol *, size_t> &p : got.tls) {
|
|
Symbol *s = p.first;
|
|
uint64_t offset = p.second * ctx.arg.wordsize;
|
|
// When building a shared library we still need a dynamic relocation
|
|
// for the TP-relative offset as we don't know how much other data will
|
|
// 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});
|
|
}
|
|
for (std::pair<Symbol *, size_t> &p : got.dynTlsSymbols) {
|
|
Symbol *s = p.first;
|
|
uint64_t offset = p.second * ctx.arg.wordsize;
|
|
if (s == nullptr) {
|
|
if (!ctx.arg.shared)
|
|
continue;
|
|
ctx.mainPart->relaDyn->addReloc(
|
|
{ctx.target->tlsModuleIndexRel, this, offset});
|
|
} else {
|
|
// When building a shared library we still need a dynamic relocation
|
|
// for the module index. Therefore only checking for
|
|
// S->isPreemptible is not sufficient (this happens e.g. for
|
|
// thread-locals that have been marked as local through a linker script)
|
|
if (!s->isPreemptible && !ctx.arg.shared)
|
|
continue;
|
|
ctx.mainPart->relaDyn->addSymbolReloc(ctx.target->tlsModuleIndexRel,
|
|
*this, offset, *s);
|
|
// However, we can skip writing the TLS offset reloc for non-preemptible
|
|
// symbols since it is known even in shared libraries
|
|
if (!s->isPreemptible)
|
|
continue;
|
|
offset += ctx.arg.wordsize;
|
|
ctx.mainPart->relaDyn->addSymbolReloc(ctx.target->tlsOffsetRel, *this,
|
|
offset, *s);
|
|
}
|
|
}
|
|
|
|
// Do not create dynamic relocations for non-TLS
|
|
// entries in the primary GOT.
|
|
if (&got == primGot)
|
|
continue;
|
|
|
|
// Dynamic relocations for "global" entries.
|
|
for (const std::pair<Symbol *, size_t> &p : got.global) {
|
|
uint64_t offset = p.second * ctx.arg.wordsize;
|
|
ctx.mainPart->relaDyn->addSymbolReloc(ctx.target->relativeRel, *this,
|
|
offset, *p.first);
|
|
}
|
|
if (!ctx.arg.isPic)
|
|
continue;
|
|
// Dynamic relocations for "local" entries in case of PIC.
|
|
for (const std::pair<const OutputSection *, FileGot::PageBlock> &l :
|
|
got.pagesMap) {
|
|
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)});
|
|
}
|
|
}
|
|
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});
|
|
}
|
|
}
|
|
}
|
|
|
|
bool MipsGotSection::isNeeded() const {
|
|
// We add the .got section to the result for dynamic MIPS target because
|
|
// its address and properties are mentioned in the .dynamic section.
|
|
return !ctx.arg.relocatable;
|
|
}
|
|
|
|
uint64_t MipsGotSection::getGp(const InputFile *f) const {
|
|
// For files without related GOT or files refer a primary GOT
|
|
// returns "common" _gp value. For secondary GOTs calculate
|
|
// individual _gp values.
|
|
if (!f || f->mipsGotIndex == uint32_t(-1) || f->mipsGotIndex == 0)
|
|
return ctx.sym.mipsGp->getVA(ctx, 0);
|
|
return getVA() + gots[f->mipsGotIndex].startIndex * ctx.arg.wordsize + 0x7ff0;
|
|
}
|
|
|
|
void MipsGotSection::writeTo(uint8_t *buf) {
|
|
// Set the MSB of the second GOT slot. This is not required by any
|
|
// MIPS ABI documentation, though.
|
|
//
|
|
// There is a comment in glibc saying that "The MSB of got[1] of a
|
|
// gnu object is set to identify gnu objects," and in GNU gold it
|
|
// says "the second entry will be used by some runtime loaders".
|
|
// But how this field is being used is unclear.
|
|
//
|
|
// We are not really willing to mimic other linkers behaviors
|
|
// without understanding why they do that, but because all files
|
|
// generated by GNU tools have this special GOT value, and because
|
|
// we've been doing this for years, it is probably a safe bet to
|
|
// keep doing this for now. We really need to revisit this to see
|
|
// if we had to do this.
|
|
writeUint(ctx, buf + ctx.arg.wordsize,
|
|
(uint64_t)1 << (ctx.arg.wordsize * 8 - 1));
|
|
for (const FileGot &g : gots) {
|
|
auto write = [&](size_t i, const Symbol *s, int64_t a) {
|
|
uint64_t va = a;
|
|
if (s)
|
|
va = s->getVA(ctx, a);
|
|
writeUint(ctx, buf + i * ctx.arg.wordsize, va);
|
|
};
|
|
// Write 'page address' entries to the local part of the GOT.
|
|
for (const std::pair<const OutputSection *, FileGot::PageBlock> &l :
|
|
g.pagesMap) {
|
|
size_t pageCount = l.second.count;
|
|
uint64_t firstPageAddr = getMipsPageAddr(l.first->addr);
|
|
for (size_t pi = 0; pi < pageCount; ++pi)
|
|
write(l.second.firstIndex + pi, nullptr, firstPageAddr + pi * 0x10000);
|
|
}
|
|
// Local, global, TLS, reloc-only entries.
|
|
// If TLS entry has a corresponding dynamic relocations, leave it
|
|
// initialized by zero. Write down adjusted TLS symbol's values otherwise.
|
|
// To calculate the adjustments use offsets for thread-local storage.
|
|
// http://web.archive.org/web/20190324223224/https://www.linux-mips.org/wiki/NPTL
|
|
for (const std::pair<GotEntry, size_t> &p : g.local16)
|
|
write(p.second, p.first.first, p.first.second);
|
|
// Write VA to the primary GOT only. For secondary GOTs that
|
|
// will be done by REL32 dynamic relocations.
|
|
if (&g == &gots.front())
|
|
for (const std::pair<Symbol *, size_t> &p : g.global)
|
|
write(p.second, p.first, 0);
|
|
for (const std::pair<Symbol *, size_t> &p : g.relocs)
|
|
write(p.second, p.first, 0);
|
|
for (const std::pair<Symbol *, size_t> &p : g.tls)
|
|
write(p.second, p.first,
|
|
p.first->isPreemptible || ctx.arg.shared ? 0 : -0x7000);
|
|
for (const std::pair<Symbol *, size_t> &p : g.dynTlsSymbols) {
|
|
if (p.first == nullptr && !ctx.arg.shared)
|
|
write(p.second, nullptr, 1);
|
|
else if (p.first && !p.first->isPreemptible) {
|
|
// If we are emitting a shared library with relocations we mustn't write
|
|
// anything to the GOT here. When using Elf_Rel relocations the value
|
|
// one will be treated as an addend and will cause crashes at runtime
|
|
if (!ctx.arg.shared)
|
|
write(p.second, nullptr, 1);
|
|
write(p.second + 1, p.first, -0x8000);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// On PowerPC the .plt section is used to hold the table of function addresses
|
|
// instead of the .got.plt, and the type is SHT_NOBITS similar to a .bss
|
|
// section. I don't know why we have a BSS style type for the section but it is
|
|
// consistent across both 64-bit PowerPC ABIs as well as the 32-bit PowerPC ABI.
|
|
GotPltSection::GotPltSection(Ctx &ctx)
|
|
: SyntheticSection(ctx, ".got.plt", SHT_PROGBITS, SHF_ALLOC | SHF_WRITE,
|
|
ctx.arg.wordsize) {
|
|
if (ctx.arg.emachine == EM_PPC) {
|
|
name = ".plt";
|
|
} else if (ctx.arg.emachine == EM_PPC64) {
|
|
type = SHT_NOBITS;
|
|
name = ".plt";
|
|
}
|
|
}
|
|
|
|
void GotPltSection::addEntry(Symbol &sym) {
|
|
assert(sym.auxIdx == ctx.symAux.size() - 1 &&
|
|
ctx.symAux.back().pltIdx == entries.size());
|
|
entries.push_back(&sym);
|
|
}
|
|
|
|
size_t GotPltSection::getSize() const {
|
|
return (ctx.target->gotPltHeaderEntriesNum + entries.size()) *
|
|
ctx.target->gotEntrySize;
|
|
}
|
|
|
|
void GotPltSection::writeTo(uint8_t *buf) {
|
|
ctx.target->writeGotPltHeader(buf);
|
|
buf += ctx.target->gotPltHeaderEntriesNum * ctx.target->gotEntrySize;
|
|
for (const Symbol *b : entries) {
|
|
ctx.target->writeGotPlt(buf, *b);
|
|
buf += ctx.target->gotEntrySize;
|
|
}
|
|
}
|
|
|
|
bool GotPltSection::isNeeded() const {
|
|
// We need to emit GOTPLT even if it's empty if there's a relocation relative
|
|
// to it.
|
|
return !entries.empty() || hasGotPltOffRel;
|
|
}
|
|
|
|
static StringRef getIgotPltName(Ctx &ctx) {
|
|
// On ARM the IgotPltSection is part of the GotSection.
|
|
if (ctx.arg.emachine == EM_ARM)
|
|
return ".got";
|
|
|
|
// On PowerPC64 the GotPltSection is renamed to '.plt' so the IgotPltSection
|
|
// needs to be named the same.
|
|
if (ctx.arg.emachine == EM_PPC64)
|
|
return ".plt";
|
|
|
|
return ".got.plt";
|
|
}
|
|
|
|
// On PowerPC64 the GotPltSection type is SHT_NOBITS so we have to follow suit
|
|
// with the IgotPltSection.
|
|
IgotPltSection::IgotPltSection(Ctx &ctx)
|
|
: SyntheticSection(ctx, getIgotPltName(ctx),
|
|
ctx.arg.emachine == EM_PPC64 ? SHT_NOBITS : SHT_PROGBITS,
|
|
SHF_ALLOC | SHF_WRITE, ctx.target->gotEntrySize) {}
|
|
|
|
void IgotPltSection::addEntry(Symbol &sym) {
|
|
assert(ctx.symAux.back().pltIdx == entries.size());
|
|
entries.push_back(&sym);
|
|
}
|
|
|
|
size_t IgotPltSection::getSize() const {
|
|
return entries.size() * ctx.target->gotEntrySize;
|
|
}
|
|
|
|
void IgotPltSection::writeTo(uint8_t *buf) {
|
|
for (const Symbol *b : entries) {
|
|
ctx.target->writeIgotPlt(buf, *b);
|
|
buf += ctx.target->gotEntrySize;
|
|
}
|
|
}
|
|
|
|
StringTableSection::StringTableSection(Ctx &ctx, StringRef name, bool dynamic)
|
|
: SyntheticSection(ctx, name, SHT_STRTAB, dynamic ? (uint64_t)SHF_ALLOC : 0,
|
|
1),
|
|
dynamic(dynamic) {
|
|
// ELF string tables start with a NUL byte.
|
|
strings.push_back("");
|
|
stringMap.try_emplace(CachedHashStringRef(""), 0);
|
|
size = 1;
|
|
}
|
|
|
|
// Adds a string to the string table. If `hashIt` is true we hash and check for
|
|
// duplicates. It is optional because the name of global symbols are already
|
|
// uniqued and hashing them again has a big cost for a small value: uniquing
|
|
// them with some other string that happens to be the same.
|
|
unsigned StringTableSection::addString(StringRef s, bool hashIt) {
|
|
if (hashIt) {
|
|
auto r = stringMap.try_emplace(CachedHashStringRef(s), size);
|
|
if (!r.second)
|
|
return r.first->second;
|
|
}
|
|
if (s.empty())
|
|
return 0;
|
|
unsigned ret = this->size;
|
|
this->size = this->size + s.size() + 1;
|
|
strings.push_back(s);
|
|
return ret;
|
|
}
|
|
|
|
void StringTableSection::writeTo(uint8_t *buf) {
|
|
for (StringRef s : strings) {
|
|
memcpy(buf, s.data(), s.size());
|
|
buf[s.size()] = '\0';
|
|
buf += s.size() + 1;
|
|
}
|
|
}
|
|
|
|
// Returns the number of entries in .gnu.version_d: the number of
|
|
// non-VER_NDX_LOCAL-non-VER_NDX_GLOBAL definitions, plus 1.
|
|
// Note that we don't support vd_cnt > 1 yet.
|
|
static unsigned getVerDefNum(Ctx &ctx) {
|
|
return namedVersionDefs(ctx).size() + 1;
|
|
}
|
|
|
|
template <class ELFT>
|
|
DynamicSection<ELFT>::DynamicSection(Ctx &ctx)
|
|
: SyntheticSection(ctx, ".dynamic", SHT_DYNAMIC, SHF_ALLOC | SHF_WRITE,
|
|
ctx.arg.wordsize) {
|
|
this->entsize = ELFT::Is64Bits ? 16 : 8;
|
|
|
|
// .dynamic section is not writable on MIPS and on Fuchsia OS
|
|
// which passes -z rodynamic.
|
|
// See "Special Section" in Chapter 4 in the following document:
|
|
// ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf
|
|
if (ctx.arg.emachine == EM_MIPS || ctx.arg.zRodynamic)
|
|
this->flags = SHF_ALLOC;
|
|
}
|
|
|
|
// The output section .rela.dyn may include these synthetic sections:
|
|
//
|
|
// - part.relaDyn
|
|
// - ctx.in.relaPlt: this is included if a linker script places .rela.plt inside
|
|
// .rela.dyn
|
|
//
|
|
// DT_RELASZ is the total size of the included sections.
|
|
static uint64_t addRelaSz(Ctx &ctx, const RelocationBaseSection &relaDyn) {
|
|
size_t size = relaDyn.getSize();
|
|
if (ctx.in.relaPlt->getParent() == relaDyn.getParent())
|
|
size += ctx.in.relaPlt->getSize();
|
|
return size;
|
|
}
|
|
|
|
// A Linker script may assign the RELA relocation sections to the same
|
|
// output section. When this occurs we cannot just use the OutputSection
|
|
// Size. Moreover the [DT_JMPREL, DT_JMPREL + DT_PLTRELSZ) is permitted to
|
|
// overlap with the [DT_RELA, DT_RELA + DT_RELASZ).
|
|
static uint64_t addPltRelSz(Ctx &ctx) { return ctx.in.relaPlt->getSize(); }
|
|
|
|
// Add remaining entries to complete .dynamic contents.
|
|
template <class ELFT>
|
|
std::vector<std::pair<int32_t, uint64_t>>
|
|
DynamicSection<ELFT>::computeContents() {
|
|
elf::Partition &part = getPartition(ctx);
|
|
bool isMain = part.name.empty();
|
|
std::vector<std::pair<int32_t, uint64_t>> entries;
|
|
|
|
auto addInt = [&](int32_t tag, uint64_t val) {
|
|
entries.emplace_back(tag, val);
|
|
};
|
|
auto addInSec = [&](int32_t tag, const InputSection &sec) {
|
|
entries.emplace_back(tag, sec.getVA());
|
|
};
|
|
|
|
for (StringRef s : ctx.arg.filterList)
|
|
addInt(DT_FILTER, part.dynStrTab->addString(s));
|
|
for (StringRef s : ctx.arg.auxiliaryList)
|
|
addInt(DT_AUXILIARY, part.dynStrTab->addString(s));
|
|
|
|
if (!ctx.arg.rpath.empty())
|
|
addInt(ctx.arg.enableNewDtags ? DT_RUNPATH : DT_RPATH,
|
|
part.dynStrTab->addString(ctx.arg.rpath));
|
|
|
|
for (SharedFile *file : ctx.sharedFiles)
|
|
if (file->isNeeded)
|
|
addInt(DT_NEEDED, part.dynStrTab->addString(file->soName));
|
|
|
|
if (isMain) {
|
|
if (!ctx.arg.soName.empty())
|
|
addInt(DT_SONAME, part.dynStrTab->addString(ctx.arg.soName));
|
|
} else {
|
|
if (!ctx.arg.soName.empty())
|
|
addInt(DT_NEEDED, part.dynStrTab->addString(ctx.arg.soName));
|
|
addInt(DT_SONAME, part.dynStrTab->addString(part.name));
|
|
}
|
|
|
|
// Set DT_FLAGS and DT_FLAGS_1.
|
|
uint32_t dtFlags = 0;
|
|
uint32_t dtFlags1 = 0;
|
|
if (ctx.arg.bsymbolic == BsymbolicKind::All)
|
|
dtFlags |= DF_SYMBOLIC;
|
|
if (ctx.arg.zGlobal)
|
|
dtFlags1 |= DF_1_GLOBAL;
|
|
if (ctx.arg.zInitfirst)
|
|
dtFlags1 |= DF_1_INITFIRST;
|
|
if (ctx.arg.zInterpose)
|
|
dtFlags1 |= DF_1_INTERPOSE;
|
|
if (ctx.arg.zNodefaultlib)
|
|
dtFlags1 |= DF_1_NODEFLIB;
|
|
if (ctx.arg.zNodelete)
|
|
dtFlags1 |= DF_1_NODELETE;
|
|
if (ctx.arg.zNodlopen)
|
|
dtFlags1 |= DF_1_NOOPEN;
|
|
if (ctx.arg.pie)
|
|
dtFlags1 |= DF_1_PIE;
|
|
if (ctx.arg.zNow) {
|
|
dtFlags |= DF_BIND_NOW;
|
|
dtFlags1 |= DF_1_NOW;
|
|
}
|
|
if (ctx.arg.zOrigin) {
|
|
dtFlags |= DF_ORIGIN;
|
|
dtFlags1 |= DF_1_ORIGIN;
|
|
}
|
|
if (!ctx.arg.zText)
|
|
dtFlags |= DF_TEXTREL;
|
|
if (ctx.hasTlsIe && ctx.arg.shared)
|
|
dtFlags |= DF_STATIC_TLS;
|
|
|
|
if (dtFlags)
|
|
addInt(DT_FLAGS, dtFlags);
|
|
if (dtFlags1)
|
|
addInt(DT_FLAGS_1, dtFlags1);
|
|
|
|
// DT_DEBUG is a pointer to debug information used by debuggers at runtime. We
|
|
// need it for each process, so we don't write it for DSOs. The loader writes
|
|
// the pointer into this entry.
|
|
//
|
|
// DT_DEBUG is the only .dynamic entry that needs to be written to. Some
|
|
// systems (currently only Fuchsia OS) provide other means to give the
|
|
// debugger this information. Such systems may choose make .dynamic read-only.
|
|
// If the target is such a system (used -z rodynamic) don't write DT_DEBUG.
|
|
if (!ctx.arg.shared && !ctx.arg.relocatable && !ctx.arg.zRodynamic)
|
|
addInt(DT_DEBUG, 0);
|
|
|
|
if (part.relaDyn->isNeeded()) {
|
|
addInSec(part.relaDyn->dynamicTag, *part.relaDyn);
|
|
entries.emplace_back(part.relaDyn->sizeDynamicTag,
|
|
addRelaSz(ctx, *part.relaDyn));
|
|
|
|
bool isRela = ctx.arg.isRela;
|
|
addInt(isRela ? DT_RELAENT : DT_RELENT,
|
|
isRela ? sizeof(Elf_Rela) : sizeof(Elf_Rel));
|
|
|
|
// MIPS dynamic loader does not support RELCOUNT tag.
|
|
// The problem is in the tight relation between dynamic
|
|
// relocations and GOT. So do not emit this tag on MIPS.
|
|
if (ctx.arg.emachine != EM_MIPS) {
|
|
size_t numRelativeRels = part.relaDyn->getRelativeRelocCount();
|
|
if (ctx.arg.zCombreloc && numRelativeRels)
|
|
addInt(isRela ? DT_RELACOUNT : DT_RELCOUNT, numRelativeRels);
|
|
}
|
|
}
|
|
if (part.relrDyn && part.relrDyn->getParent() &&
|
|
!part.relrDyn->relocs.empty()) {
|
|
addInSec(ctx.arg.useAndroidRelrTags ? DT_ANDROID_RELR : DT_RELR,
|
|
*part.relrDyn);
|
|
addInt(ctx.arg.useAndroidRelrTags ? DT_ANDROID_RELRSZ : DT_RELRSZ,
|
|
part.relrDyn->getParent()->size);
|
|
addInt(ctx.arg.useAndroidRelrTags ? DT_ANDROID_RELRENT : DT_RELRENT,
|
|
sizeof(Elf_Relr));
|
|
}
|
|
if (part.relrAuthDyn && part.relrAuthDyn->getParent() &&
|
|
!part.relrAuthDyn->relocs.empty()) {
|
|
addInSec(DT_AARCH64_AUTH_RELR, *part.relrAuthDyn);
|
|
addInt(DT_AARCH64_AUTH_RELRSZ, part.relrAuthDyn->getParent()->size);
|
|
addInt(DT_AARCH64_AUTH_RELRENT, sizeof(Elf_Relr));
|
|
}
|
|
if (isMain && ctx.in.relaPlt->isNeeded()) {
|
|
addInSec(DT_JMPREL, *ctx.in.relaPlt);
|
|
entries.emplace_back(DT_PLTRELSZ, addPltRelSz(ctx));
|
|
switch (ctx.arg.emachine) {
|
|
case EM_MIPS:
|
|
addInSec(DT_MIPS_PLTGOT, *ctx.in.gotPlt);
|
|
break;
|
|
case EM_S390:
|
|
addInSec(DT_PLTGOT, *ctx.in.got);
|
|
break;
|
|
case EM_SPARCV9:
|
|
addInSec(DT_PLTGOT, *ctx.in.plt);
|
|
break;
|
|
case EM_AARCH64:
|
|
if (llvm::find_if(ctx.in.relaPlt->relocs, [&ctx = ctx](
|
|
const DynamicReloc &r) {
|
|
return r.type == ctx.target->pltRel &&
|
|
r.sym->stOther & STO_AARCH64_VARIANT_PCS;
|
|
}) != ctx.in.relaPlt->relocs.end())
|
|
addInt(DT_AARCH64_VARIANT_PCS, 0);
|
|
addInSec(DT_PLTGOT, *ctx.in.gotPlt);
|
|
break;
|
|
case EM_RISCV:
|
|
if (llvm::any_of(ctx.in.relaPlt->relocs, [&ctx = ctx](
|
|
const DynamicReloc &r) {
|
|
return r.type == ctx.target->pltRel &&
|
|
(r.sym->stOther & STO_RISCV_VARIANT_CC);
|
|
}))
|
|
addInt(DT_RISCV_VARIANT_CC, 0);
|
|
[[fallthrough]];
|
|
default:
|
|
addInSec(DT_PLTGOT, *ctx.in.gotPlt);
|
|
break;
|
|
}
|
|
addInt(DT_PLTREL, ctx.arg.isRela ? DT_RELA : DT_REL);
|
|
}
|
|
|
|
if (ctx.arg.emachine == EM_AARCH64) {
|
|
if (ctx.arg.andFeatures & GNU_PROPERTY_AARCH64_FEATURE_1_BTI)
|
|
addInt(DT_AARCH64_BTI_PLT, 0);
|
|
if (ctx.arg.zPacPlt)
|
|
addInt(DT_AARCH64_PAC_PLT, 0);
|
|
|
|
if (hasMemtag(ctx)) {
|
|
addInt(DT_AARCH64_MEMTAG_MODE, ctx.arg.androidMemtagMode == NT_MEMTAG_LEVEL_ASYNC);
|
|
addInt(DT_AARCH64_MEMTAG_HEAP, ctx.arg.androidMemtagHeap);
|
|
addInt(DT_AARCH64_MEMTAG_STACK, ctx.arg.androidMemtagStack);
|
|
if (ctx.mainPart->memtagGlobalDescriptors->isNeeded()) {
|
|
addInSec(DT_AARCH64_MEMTAG_GLOBALS,
|
|
*ctx.mainPart->memtagGlobalDescriptors);
|
|
addInt(DT_AARCH64_MEMTAG_GLOBALSSZ,
|
|
ctx.mainPart->memtagGlobalDescriptors->getSize());
|
|
}
|
|
}
|
|
}
|
|
|
|
addInSec(DT_SYMTAB, *part.dynSymTab);
|
|
addInt(DT_SYMENT, sizeof(Elf_Sym));
|
|
addInSec(DT_STRTAB, *part.dynStrTab);
|
|
addInt(DT_STRSZ, part.dynStrTab->getSize());
|
|
if (!ctx.arg.zText)
|
|
addInt(DT_TEXTREL, 0);
|
|
if (part.gnuHashTab && part.gnuHashTab->getParent())
|
|
addInSec(DT_GNU_HASH, *part.gnuHashTab);
|
|
if (part.hashTab && part.hashTab->getParent())
|
|
addInSec(DT_HASH, *part.hashTab);
|
|
|
|
if (isMain) {
|
|
if (ctx.out.preinitArray) {
|
|
addInt(DT_PREINIT_ARRAY, ctx.out.preinitArray->addr);
|
|
addInt(DT_PREINIT_ARRAYSZ, ctx.out.preinitArray->size);
|
|
}
|
|
if (ctx.out.initArray) {
|
|
addInt(DT_INIT_ARRAY, ctx.out.initArray->addr);
|
|
addInt(DT_INIT_ARRAYSZ, ctx.out.initArray->size);
|
|
}
|
|
if (ctx.out.finiArray) {
|
|
addInt(DT_FINI_ARRAY, ctx.out.finiArray->addr);
|
|
addInt(DT_FINI_ARRAYSZ, ctx.out.finiArray->size);
|
|
}
|
|
|
|
if (Symbol *b = ctx.symtab->find(ctx.arg.init))
|
|
if (b->isDefined())
|
|
addInt(DT_INIT, b->getVA(ctx));
|
|
if (Symbol *b = ctx.symtab->find(ctx.arg.fini))
|
|
if (b->isDefined())
|
|
addInt(DT_FINI, b->getVA(ctx));
|
|
}
|
|
|
|
if (part.verSym && part.verSym->isNeeded())
|
|
addInSec(DT_VERSYM, *part.verSym);
|
|
if (part.verDef && part.verDef->isLive()) {
|
|
addInSec(DT_VERDEF, *part.verDef);
|
|
addInt(DT_VERDEFNUM, getVerDefNum(ctx));
|
|
}
|
|
if (part.verNeed && part.verNeed->isNeeded()) {
|
|
addInSec(DT_VERNEED, *part.verNeed);
|
|
unsigned needNum = 0;
|
|
for (SharedFile *f : ctx.sharedFiles)
|
|
if (!f->vernauxs.empty())
|
|
++needNum;
|
|
addInt(DT_VERNEEDNUM, needNum);
|
|
}
|
|
|
|
if (ctx.arg.emachine == EM_MIPS) {
|
|
addInt(DT_MIPS_RLD_VERSION, 1);
|
|
addInt(DT_MIPS_FLAGS, RHF_NOTPOT);
|
|
addInt(DT_MIPS_BASE_ADDRESS, ctx.target->getImageBase());
|
|
addInt(DT_MIPS_SYMTABNO, part.dynSymTab->getNumSymbols());
|
|
addInt(DT_MIPS_LOCAL_GOTNO, ctx.in.mipsGot->getLocalEntriesNum());
|
|
|
|
if (const Symbol *b = ctx.in.mipsGot->getFirstGlobalEntry())
|
|
addInt(DT_MIPS_GOTSYM, b->dynsymIndex);
|
|
else
|
|
addInt(DT_MIPS_GOTSYM, part.dynSymTab->getNumSymbols());
|
|
addInSec(DT_PLTGOT, *ctx.in.mipsGot);
|
|
if (ctx.in.mipsRldMap) {
|
|
if (!ctx.arg.pie)
|
|
addInSec(DT_MIPS_RLD_MAP, *ctx.in.mipsRldMap);
|
|
// Store the offset to the .rld_map section
|
|
// relative to the address of the tag.
|
|
addInt(DT_MIPS_RLD_MAP_REL,
|
|
ctx.in.mipsRldMap->getVA() - (getVA() + entries.size() * entsize));
|
|
}
|
|
}
|
|
|
|
// DT_PPC_GOT indicates to glibc Secure PLT is used. If DT_PPC_GOT is absent,
|
|
// glibc assumes the old-style BSS PLT layout which we don't support.
|
|
if (ctx.arg.emachine == EM_PPC)
|
|
addInSec(DT_PPC_GOT, *ctx.in.got);
|
|
|
|
// Glink dynamic tag is required by the V2 abi if the plt section isn't empty.
|
|
if (ctx.arg.emachine == EM_PPC64 && ctx.in.plt->isNeeded()) {
|
|
// The Glink tag points to 32 bytes before the first lazy symbol resolution
|
|
// stub, which starts directly after the header.
|
|
addInt(DT_PPC64_GLINK,
|
|
ctx.in.plt->getVA() + ctx.target->pltHeaderSize - 32);
|
|
}
|
|
|
|
if (ctx.arg.emachine == EM_PPC64)
|
|
addInt(DT_PPC64_OPT, ctx.target->ppc64DynamicSectionOpt);
|
|
|
|
addInt(DT_NULL, 0);
|
|
return entries;
|
|
}
|
|
|
|
template <class ELFT> void DynamicSection<ELFT>::finalizeContents() {
|
|
if (OutputSection *sec = getPartition(ctx).dynStrTab->getParent())
|
|
getParent()->link = sec->sectionIndex;
|
|
this->size = computeContents().size() * this->entsize;
|
|
}
|
|
|
|
template <class ELFT> void DynamicSection<ELFT>::writeTo(uint8_t *buf) {
|
|
auto *p = reinterpret_cast<Elf_Dyn *>(buf);
|
|
|
|
for (std::pair<int32_t, uint64_t> kv : computeContents()) {
|
|
p->d_tag = kv.first;
|
|
p->d_un.d_val = kv.second;
|
|
++p;
|
|
}
|
|
}
|
|
|
|
uint64_t DynamicReloc::getOffset() const {
|
|
return inputSec->getVA(offsetInSec);
|
|
}
|
|
|
|
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");
|
|
}
|
|
|
|
uint32_t DynamicReloc::getSymIndex(SymbolTableBaseSection *symTab) const {
|
|
if (!needsDynSymIndex())
|
|
return 0;
|
|
|
|
size_t index = symTab->getSymbolIndex(*sym);
|
|
assert((index != 0 ||
|
|
(type != symTab->ctx.target->gotRel &&
|
|
type != symTab->ctx.target->pltRel) ||
|
|
!symTab->ctx.mainPart->dynSymTab->getParent()) &&
|
|
"GOT or PLT relocation must refer to symbol in dynamic symbol table");
|
|
return index;
|
|
}
|
|
|
|
RelocationBaseSection::RelocationBaseSection(Ctx &ctx, StringRef name,
|
|
uint32_t type, int32_t dynamicTag,
|
|
int32_t sizeDynamicTag,
|
|
bool combreloc,
|
|
unsigned concurrency)
|
|
: SyntheticSection(ctx, name, type, SHF_ALLOC, ctx.arg.wordsize),
|
|
dynamicTag(dynamicTag), sizeDynamicTag(sizeDynamicTag),
|
|
relocsVec(concurrency), combreloc(combreloc) {}
|
|
|
|
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);
|
|
}
|
|
|
|
void RelocationBaseSection::addAddendOnlyRelocIfNonPreemptible(
|
|
RelType dynType, InputSectionBase &isec, uint64_t offsetInSec, Symbol &sym,
|
|
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});
|
|
else
|
|
addReloc(DynamicReloc::AddendOnlyWithTargetVA, dynType, isec, offsetInSec,
|
|
sym, 0, R_ABS, addendRelType);
|
|
}
|
|
|
|
void RelocationBaseSection::mergeRels() {
|
|
size_t newSize = relocs.size();
|
|
for (const auto &v : relocsVec)
|
|
newSize += v.size();
|
|
relocs.reserve(newSize);
|
|
for (const auto &v : relocsVec)
|
|
llvm::append_range(relocs, v);
|
|
relocsVec.clear();
|
|
}
|
|
|
|
void RelocationBaseSection::partitionRels() {
|
|
if (!combreloc)
|
|
return;
|
|
const RelType relativeRel = ctx.target->relativeRel;
|
|
numRelativeRelocs =
|
|
std::stable_partition(relocs.begin(), relocs.end(),
|
|
[=](auto &r) { return r.type == relativeRel; }) -
|
|
relocs.begin();
|
|
}
|
|
|
|
void RelocationBaseSection::finalizeContents() {
|
|
SymbolTableBaseSection *symTab = getPartition(ctx).dynSymTab.get();
|
|
|
|
// When linking glibc statically, .rel{,a}.plt contains R_*_IRELATIVE
|
|
// relocations due to IFUNC (e.g. strcpy). sh_link will be set to 0 in that
|
|
// case.
|
|
if (symTab && symTab->getParent())
|
|
getParent()->link = symTab->getParent()->sectionIndex;
|
|
else
|
|
getParent()->link = 0;
|
|
|
|
if (ctx.in.relaPlt.get() == this && ctx.in.gotPlt->getParent()) {
|
|
getParent()->flags |= ELF::SHF_INFO_LINK;
|
|
getParent()->info = ctx.in.gotPlt->getParent()->sectionIndex;
|
|
}
|
|
}
|
|
|
|
void DynamicReloc::computeRaw(Ctx &ctx, SymbolTableBaseSection *symt) {
|
|
r_offset = getOffset();
|
|
r_sym = getSymIndex(symt);
|
|
addend = computeAddend(ctx);
|
|
kind = AddendOnly; // Catch errors
|
|
}
|
|
|
|
void RelocationBaseSection::computeRels() {
|
|
SymbolTableBaseSection *symTab = getPartition(ctx).dynSymTab.get();
|
|
parallelForEach(relocs, [&ctx = ctx, symTab](DynamicReloc &rel) {
|
|
rel.computeRaw(ctx, symTab);
|
|
});
|
|
|
|
auto irelative = std::stable_partition(
|
|
relocs.begin() + numRelativeRelocs, relocs.end(),
|
|
[t = ctx.target->iRelativeRel](auto &r) { return r.type != t; });
|
|
|
|
// Sort by (!IsRelative,SymIndex,r_offset). DT_REL[A]COUNT requires us to
|
|
// place R_*_RELATIVE first. SymIndex is to improve locality, while r_offset
|
|
// is to make results easier to read.
|
|
if (combreloc) {
|
|
auto nonRelative = relocs.begin() + numRelativeRelocs;
|
|
parallelSort(relocs.begin(), nonRelative,
|
|
[&](auto &a, auto &b) { return a.r_offset < b.r_offset; });
|
|
// Non-relative relocations are few, so don't bother with parallelSort.
|
|
llvm::sort(nonRelative, irelative, [&](auto &a, auto &b) {
|
|
return std::tie(a.r_sym, a.r_offset) < std::tie(b.r_sym, b.r_offset);
|
|
});
|
|
}
|
|
}
|
|
|
|
template <class ELFT>
|
|
RelocationSection<ELFT>::RelocationSection(Ctx &ctx, StringRef name,
|
|
bool combreloc, unsigned concurrency)
|
|
: RelocationBaseSection(ctx, name, ctx.arg.isRela ? SHT_RELA : SHT_REL,
|
|
ctx.arg.isRela ? DT_RELA : DT_REL,
|
|
ctx.arg.isRela ? DT_RELASZ : DT_RELSZ, combreloc,
|
|
concurrency) {
|
|
this->entsize = ctx.arg.isRela ? sizeof(Elf_Rela) : sizeof(Elf_Rel);
|
|
}
|
|
|
|
template <class ELFT> void RelocationSection<ELFT>::writeTo(uint8_t *buf) {
|
|
computeRels();
|
|
for (const DynamicReloc &rel : relocs) {
|
|
auto *p = reinterpret_cast<Elf_Rela *>(buf);
|
|
p->r_offset = rel.r_offset;
|
|
p->setSymbolAndType(rel.r_sym, rel.type, ctx.arg.isMips64EL);
|
|
if (ctx.arg.isRela)
|
|
p->r_addend = rel.addend;
|
|
buf += ctx.arg.isRela ? sizeof(Elf_Rela) : sizeof(Elf_Rel);
|
|
}
|
|
}
|
|
|
|
RelrBaseSection::RelrBaseSection(Ctx &ctx, unsigned concurrency,
|
|
bool isAArch64Auth)
|
|
: SyntheticSection(
|
|
ctx, isAArch64Auth ? ".relr.auth.dyn" : ".relr.dyn",
|
|
isAArch64Auth
|
|
? SHT_AARCH64_AUTH_RELR
|
|
: (ctx.arg.useAndroidRelrTags ? SHT_ANDROID_RELR : SHT_RELR),
|
|
SHF_ALLOC, ctx.arg.wordsize),
|
|
relocsVec(concurrency) {}
|
|
|
|
void RelrBaseSection::mergeRels() {
|
|
size_t newSize = relocs.size();
|
|
for (const auto &v : relocsVec)
|
|
newSize += v.size();
|
|
relocs.reserve(newSize);
|
|
for (const auto &v : relocsVec)
|
|
llvm::append_range(relocs, v);
|
|
relocsVec.clear();
|
|
}
|
|
|
|
template <class ELFT>
|
|
AndroidPackedRelocationSection<ELFT>::AndroidPackedRelocationSection(
|
|
Ctx &ctx, StringRef name, unsigned concurrency)
|
|
: RelocationBaseSection(
|
|
ctx, name, ctx.arg.isRela ? SHT_ANDROID_RELA : SHT_ANDROID_REL,
|
|
ctx.arg.isRela ? DT_ANDROID_RELA : DT_ANDROID_REL,
|
|
ctx.arg.isRela ? DT_ANDROID_RELASZ : DT_ANDROID_RELSZ,
|
|
/*combreloc=*/false, concurrency) {
|
|
this->entsize = 1;
|
|
}
|
|
|
|
template <class ELFT>
|
|
bool AndroidPackedRelocationSection<ELFT>::updateAllocSize(Ctx &ctx) {
|
|
// This function computes the contents of an Android-format packed relocation
|
|
// section.
|
|
//
|
|
// This format compresses relocations by using relocation groups to factor out
|
|
// fields that are common between relocations and storing deltas from previous
|
|
// relocations in SLEB128 format (which has a short representation for small
|
|
// numbers). A good example of a relocation type with common fields is
|
|
// R_*_RELATIVE, which is normally used to represent function pointers in
|
|
// vtables. In the REL format, each relative relocation has the same r_info
|
|
// field, and is only different from other relative relocations in terms of
|
|
// the r_offset field. By sorting relocations by offset, grouping them by
|
|
// r_info and representing each relocation with only the delta from the
|
|
// previous offset, each 8-byte relocation can be compressed to as little as 1
|
|
// byte (or less with run-length encoding). This relocation packer was able to
|
|
// reduce the size of the relocation section in an Android Chromium DSO from
|
|
// 2,911,184 bytes to 174,693 bytes, or 6% of the original size.
|
|
//
|
|
// A relocation section consists of a header containing the literal bytes
|
|
// 'APS2' followed by a sequence of SLEB128-encoded integers. The first two
|
|
// elements are the total number of relocations in the section and an initial
|
|
// r_offset value. The remaining elements define a sequence of relocation
|
|
// groups. Each relocation group starts with a header consisting of the
|
|
// following elements:
|
|
//
|
|
// - the number of relocations in the relocation group
|
|
// - flags for the relocation group
|
|
// - (if RELOCATION_GROUPED_BY_OFFSET_DELTA_FLAG is set) the r_offset delta
|
|
// for each relocation in the group.
|
|
// - (if RELOCATION_GROUPED_BY_INFO_FLAG is set) the value of the r_info
|
|
// field for each relocation in the group.
|
|
// - (if RELOCATION_GROUP_HAS_ADDEND_FLAG and
|
|
// RELOCATION_GROUPED_BY_ADDEND_FLAG are set) the r_addend delta for
|
|
// each relocation in the group.
|
|
//
|
|
// Following the relocation group header are descriptions of each of the
|
|
// relocations in the group. They consist of the following elements:
|
|
//
|
|
// - (if RELOCATION_GROUPED_BY_OFFSET_DELTA_FLAG is not set) the r_offset
|
|
// delta for this relocation.
|
|
// - (if RELOCATION_GROUPED_BY_INFO_FLAG is not set) the value of the r_info
|
|
// field for this relocation.
|
|
// - (if RELOCATION_GROUP_HAS_ADDEND_FLAG is set and
|
|
// RELOCATION_GROUPED_BY_ADDEND_FLAG is not set) the r_addend delta for
|
|
// this relocation.
|
|
|
|
size_t oldSize = relocData.size();
|
|
|
|
relocData = {'A', 'P', 'S', '2'};
|
|
raw_svector_ostream os(relocData);
|
|
auto add = [&](int64_t v) { encodeSLEB128(v, os); };
|
|
|
|
// The format header includes the number of relocations and the initial
|
|
// offset (we set this to zero because the first relocation group will
|
|
// perform the initial adjustment).
|
|
add(relocs.size());
|
|
add(0);
|
|
|
|
std::vector<Elf_Rela> relatives, nonRelatives;
|
|
|
|
for (const DynamicReloc &rel : relocs) {
|
|
Elf_Rela r;
|
|
r.r_offset = rel.getOffset();
|
|
r.setSymbolAndType(rel.getSymIndex(getPartition(ctx).dynSymTab.get()),
|
|
rel.type, false);
|
|
r.r_addend = ctx.arg.isRela ? rel.computeAddend(ctx) : 0;
|
|
|
|
if (r.getType(ctx.arg.isMips64EL) == ctx.target->relativeRel)
|
|
relatives.push_back(r);
|
|
else
|
|
nonRelatives.push_back(r);
|
|
}
|
|
|
|
llvm::sort(relatives, [](const Elf_Rel &a, const Elf_Rel &b) {
|
|
return a.r_offset < b.r_offset;
|
|
});
|
|
|
|
// Try to find groups of relative relocations which are spaced one word
|
|
// apart from one another. These generally correspond to vtable entries. The
|
|
// format allows these groups to be encoded using a sort of run-length
|
|
// encoding, but each group will cost 7 bytes in addition to the offset from
|
|
// the previous group, so it is only profitable to do this for groups of
|
|
// size 8 or larger.
|
|
std::vector<Elf_Rela> ungroupedRelatives;
|
|
std::vector<std::vector<Elf_Rela>> relativeGroups;
|
|
for (auto i = relatives.begin(), e = relatives.end(); i != e;) {
|
|
std::vector<Elf_Rela> group;
|
|
do {
|
|
group.push_back(*i++);
|
|
} while (i != e && (i - 1)->r_offset + ctx.arg.wordsize == i->r_offset);
|
|
|
|
if (group.size() < 8)
|
|
ungroupedRelatives.insert(ungroupedRelatives.end(), group.begin(),
|
|
group.end());
|
|
else
|
|
relativeGroups.emplace_back(std::move(group));
|
|
}
|
|
|
|
// For non-relative relocations, we would like to:
|
|
// 1. Have relocations with the same symbol offset to be consecutive, so
|
|
// that the runtime linker can speed-up symbol lookup by implementing an
|
|
// 1-entry cache.
|
|
// 2. Group relocations by r_info to reduce the size of the relocation
|
|
// section.
|
|
// Since the symbol offset is the high bits in r_info, sorting by r_info
|
|
// allows us to do both.
|
|
//
|
|
// For Rela, we also want to sort by r_addend when r_info is the same. This
|
|
// enables us to group by r_addend as well.
|
|
llvm::sort(nonRelatives, [](const Elf_Rela &a, const Elf_Rela &b) {
|
|
if (a.r_info != b.r_info)
|
|
return a.r_info < b.r_info;
|
|
if (a.r_addend != b.r_addend)
|
|
return a.r_addend < b.r_addend;
|
|
return a.r_offset < b.r_offset;
|
|
});
|
|
|
|
// Group relocations with the same r_info. Note that each group emits a group
|
|
// header and that may make the relocation section larger. It is hard to
|
|
// estimate the size of a group header as the encoded size of that varies
|
|
// based on r_info. However, we can approximate this trade-off by the number
|
|
// of values encoded. Each group header contains 3 values, and each relocation
|
|
// in a group encodes one less value, as compared to when it is not grouped.
|
|
// Therefore, we only group relocations if there are 3 or more of them with
|
|
// the same r_info.
|
|
//
|
|
// For Rela, the addend for most non-relative relocations is zero, and thus we
|
|
// can usually get a smaller relocation section if we group relocations with 0
|
|
// addend as well.
|
|
std::vector<Elf_Rela> ungroupedNonRelatives;
|
|
std::vector<std::vector<Elf_Rela>> nonRelativeGroups;
|
|
for (auto i = nonRelatives.begin(), e = nonRelatives.end(); i != e;) {
|
|
auto j = i + 1;
|
|
while (j != e && i->r_info == j->r_info &&
|
|
(!ctx.arg.isRela || i->r_addend == j->r_addend))
|
|
++j;
|
|
if (j - i < 3 || (ctx.arg.isRela && i->r_addend != 0))
|
|
ungroupedNonRelatives.insert(ungroupedNonRelatives.end(), i, j);
|
|
else
|
|
nonRelativeGroups.emplace_back(i, j);
|
|
i = j;
|
|
}
|
|
|
|
// Sort ungrouped relocations by offset to minimize the encoded length.
|
|
llvm::sort(ungroupedNonRelatives, [](const Elf_Rela &a, const Elf_Rela &b) {
|
|
return a.r_offset < b.r_offset;
|
|
});
|
|
|
|
unsigned hasAddendIfRela =
|
|
ctx.arg.isRela ? RELOCATION_GROUP_HAS_ADDEND_FLAG : 0;
|
|
|
|
uint64_t offset = 0;
|
|
uint64_t addend = 0;
|
|
|
|
// Emit the run-length encoding for the groups of adjacent relative
|
|
// relocations. Each group is represented using two groups in the packed
|
|
// format. The first is used to set the current offset to the start of the
|
|
// group (and also encodes the first relocation), and the second encodes the
|
|
// remaining relocations.
|
|
for (std::vector<Elf_Rela> &g : relativeGroups) {
|
|
// The first relocation in the group.
|
|
add(1);
|
|
add(RELOCATION_GROUPED_BY_OFFSET_DELTA_FLAG |
|
|
RELOCATION_GROUPED_BY_INFO_FLAG | hasAddendIfRela);
|
|
add(g[0].r_offset - offset);
|
|
add(ctx.target->relativeRel);
|
|
if (ctx.arg.isRela) {
|
|
add(g[0].r_addend - addend);
|
|
addend = g[0].r_addend;
|
|
}
|
|
|
|
// The remaining relocations.
|
|
add(g.size() - 1);
|
|
add(RELOCATION_GROUPED_BY_OFFSET_DELTA_FLAG |
|
|
RELOCATION_GROUPED_BY_INFO_FLAG | hasAddendIfRela);
|
|
add(ctx.arg.wordsize);
|
|
add(ctx.target->relativeRel);
|
|
if (ctx.arg.isRela) {
|
|
for (const auto &i : llvm::drop_begin(g)) {
|
|
add(i.r_addend - addend);
|
|
addend = i.r_addend;
|
|
}
|
|
}
|
|
|
|
offset = g.back().r_offset;
|
|
}
|
|
|
|
// Now the ungrouped relatives.
|
|
if (!ungroupedRelatives.empty()) {
|
|
add(ungroupedRelatives.size());
|
|
add(RELOCATION_GROUPED_BY_INFO_FLAG | hasAddendIfRela);
|
|
add(ctx.target->relativeRel);
|
|
for (Elf_Rela &r : ungroupedRelatives) {
|
|
add(r.r_offset - offset);
|
|
offset = r.r_offset;
|
|
if (ctx.arg.isRela) {
|
|
add(r.r_addend - addend);
|
|
addend = r.r_addend;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Grouped non-relatives.
|
|
for (ArrayRef<Elf_Rela> g : nonRelativeGroups) {
|
|
add(g.size());
|
|
add(RELOCATION_GROUPED_BY_INFO_FLAG);
|
|
add(g[0].r_info);
|
|
for (const Elf_Rela &r : g) {
|
|
add(r.r_offset - offset);
|
|
offset = r.r_offset;
|
|
}
|
|
addend = 0;
|
|
}
|
|
|
|
// Finally the ungrouped non-relative relocations.
|
|
if (!ungroupedNonRelatives.empty()) {
|
|
add(ungroupedNonRelatives.size());
|
|
add(hasAddendIfRela);
|
|
for (Elf_Rela &r : ungroupedNonRelatives) {
|
|
add(r.r_offset - offset);
|
|
offset = r.r_offset;
|
|
add(r.r_info);
|
|
if (ctx.arg.isRela) {
|
|
add(r.r_addend - addend);
|
|
addend = r.r_addend;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Don't allow the section to shrink; otherwise the size of the section can
|
|
// oscillate infinitely.
|
|
if (relocData.size() < oldSize)
|
|
relocData.append(oldSize - relocData.size(), 0);
|
|
|
|
// Returns whether the section size changed. We need to keep recomputing both
|
|
// section layout and the contents of this section until the size converges
|
|
// because changing this section's size can affect section layout, which in
|
|
// turn can affect the sizes of the LEB-encoded integers stored in this
|
|
// section.
|
|
return relocData.size() != oldSize;
|
|
}
|
|
|
|
template <class ELFT>
|
|
RelrSection<ELFT>::RelrSection(Ctx &ctx, unsigned concurrency,
|
|
bool isAArch64Auth)
|
|
: RelrBaseSection(ctx, concurrency, isAArch64Auth) {
|
|
this->entsize = ctx.arg.wordsize;
|
|
}
|
|
|
|
template <class ELFT> bool RelrSection<ELFT>::updateAllocSize(Ctx &ctx) {
|
|
// This function computes the contents of an SHT_RELR packed relocation
|
|
// section.
|
|
//
|
|
// Proposal for adding SHT_RELR sections to generic-abi is here:
|
|
// https://groups.google.com/forum/#!topic/generic-abi/bX460iggiKg
|
|
//
|
|
// The encoded sequence of Elf64_Relr entries in a SHT_RELR section looks
|
|
// like [ AAAAAAAA BBBBBBB1 BBBBBBB1 ... AAAAAAAA BBBBBB1 ... ]
|
|
//
|
|
// i.e. start with an address, followed by any number of bitmaps. The address
|
|
// entry encodes 1 relocation. The subsequent bitmap entries encode up to 63
|
|
// relocations each, at subsequent offsets following the last address entry.
|
|
//
|
|
// The bitmap entries must have 1 in the least significant bit. The assumption
|
|
// here is that an address cannot have 1 in lsb. Odd addresses are not
|
|
// supported.
|
|
//
|
|
// Excluding the least significant bit in the bitmap, each non-zero bit in
|
|
// the bitmap represents a relocation to be applied to a corresponding machine
|
|
// word that follows the base address word. The second least significant bit
|
|
// represents the machine word immediately following the initial address, and
|
|
// each bit that follows represents the next word, in linear order. As such,
|
|
// a single bitmap can encode up to 31 relocations in a 32-bit object, and
|
|
// 63 relocations in a 64-bit object.
|
|
//
|
|
// This encoding has a couple of interesting properties:
|
|
// 1. Looking at any entry, it is clear whether it's an address or a bitmap:
|
|
// even means address, odd means bitmap.
|
|
// 2. Just a simple list of addresses is a valid encoding.
|
|
|
|
size_t oldSize = relrRelocs.size();
|
|
relrRelocs.clear();
|
|
|
|
const size_t wordsize = sizeof(typename ELFT::uint);
|
|
|
|
// Number of bits to use for the relocation offsets bitmap.
|
|
// Must be either 63 or 31.
|
|
const size_t nBits = wordsize * 8 - 1;
|
|
|
|
// Get offsets for all relative relocations and sort them.
|
|
std::unique_ptr<uint64_t[]> offsets(new uint64_t[relocs.size()]);
|
|
for (auto [i, r] : llvm::enumerate(relocs))
|
|
offsets[i] = r.getOffset();
|
|
llvm::sort(offsets.get(), offsets.get() + relocs.size());
|
|
|
|
// For each leading relocation, find following ones that can be folded
|
|
// as a bitmap and fold them.
|
|
for (size_t i = 0, e = relocs.size(); i != e;) {
|
|
// Add a leading relocation.
|
|
relrRelocs.push_back(Elf_Relr(offsets[i]));
|
|
uint64_t base = offsets[i] + wordsize;
|
|
++i;
|
|
|
|
// Find foldable relocations to construct bitmaps.
|
|
for (;;) {
|
|
uint64_t bitmap = 0;
|
|
for (; i != e; ++i) {
|
|
uint64_t d = offsets[i] - base;
|
|
if (d >= nBits * wordsize || d % wordsize)
|
|
break;
|
|
bitmap |= uint64_t(1) << (d / wordsize);
|
|
}
|
|
if (!bitmap)
|
|
break;
|
|
relrRelocs.push_back(Elf_Relr((bitmap << 1) | 1));
|
|
base += nBits * wordsize;
|
|
}
|
|
}
|
|
|
|
// Don't allow the section to shrink; otherwise the size of the section can
|
|
// oscillate infinitely. Trailing 1s do not decode to more relocations.
|
|
if (relrRelocs.size() < oldSize) {
|
|
Log(ctx) << ".relr.dyn needs " << (oldSize - relrRelocs.size())
|
|
<< " padding word(s)";
|
|
relrRelocs.resize(oldSize, Elf_Relr(1));
|
|
}
|
|
|
|
return relrRelocs.size() != oldSize;
|
|
}
|
|
|
|
SymbolTableBaseSection::SymbolTableBaseSection(Ctx &ctx,
|
|
StringTableSection &strTabSec)
|
|
: SyntheticSection(ctx, strTabSec.isDynamic() ? ".dynsym" : ".symtab",
|
|
strTabSec.isDynamic() ? SHT_DYNSYM : SHT_SYMTAB,
|
|
strTabSec.isDynamic() ? (uint64_t)SHF_ALLOC : 0,
|
|
ctx.arg.wordsize),
|
|
strTabSec(strTabSec) {}
|
|
|
|
// Orders symbols according to their positions in the GOT,
|
|
// in compliance with MIPS ABI rules.
|
|
// See "Global Offset Table" in Chapter 5 in the following document
|
|
// for detailed description:
|
|
// ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf
|
|
static void sortMipsSymbols(Ctx &ctx, SmallVector<SymbolTableEntry, 0> &syms) {
|
|
llvm::stable_sort(syms,
|
|
[&](const SymbolTableEntry &l, const SymbolTableEntry &r) {
|
|
// Sort entries related to non-local preemptible symbols
|
|
// by GOT indexes. All other entries go to the beginning
|
|
// of a dynsym in arbitrary order.
|
|
if (l.sym->isInGot(ctx) && r.sym->isInGot(ctx))
|
|
return l.sym->getGotIdx(ctx) < r.sym->getGotIdx(ctx);
|
|
if (!l.sym->isInGot(ctx) && !r.sym->isInGot(ctx))
|
|
return false;
|
|
return !l.sym->isInGot(ctx);
|
|
});
|
|
}
|
|
|
|
void SymbolTableBaseSection::finalizeContents() {
|
|
if (OutputSection *sec = strTabSec.getParent())
|
|
getParent()->link = sec->sectionIndex;
|
|
|
|
if (this->type != SHT_DYNSYM) {
|
|
sortSymTabSymbols();
|
|
return;
|
|
}
|
|
|
|
// If it is a .dynsym, there should be no local symbols, but we need
|
|
// to do a few things for the dynamic linker.
|
|
|
|
// Section's Info field has the index of the first non-local symbol.
|
|
// Because the first symbol entry is a null entry, 1 is the first.
|
|
getParent()->info = 1;
|
|
|
|
if (getPartition(ctx).gnuHashTab) {
|
|
// NB: It also sorts Symbols to meet the GNU hash table requirements.
|
|
getPartition(ctx).gnuHashTab->addSymbols(symbols);
|
|
} else if (ctx.arg.emachine == EM_MIPS) {
|
|
sortMipsSymbols(ctx, symbols);
|
|
}
|
|
|
|
// Only the main partition's dynsym indexes are stored in the symbols
|
|
// themselves. All other partitions use a lookup table.
|
|
if (this == ctx.mainPart->dynSymTab.get()) {
|
|
size_t i = 0;
|
|
for (const SymbolTableEntry &s : symbols)
|
|
s.sym->dynsymIndex = ++i;
|
|
}
|
|
}
|
|
|
|
// The ELF spec requires that all local symbols precede global symbols, so we
|
|
// sort symbol entries in this function. (For .dynsym, we don't do that because
|
|
// symbols for dynamic linking are inherently all globals.)
|
|
//
|
|
// Aside from above, we put local symbols in groups starting with the STT_FILE
|
|
// symbol. That is convenient for purpose of identifying where are local symbols
|
|
// coming from.
|
|
void SymbolTableBaseSection::sortSymTabSymbols() {
|
|
// Move all local symbols before global symbols.
|
|
auto e = std::stable_partition(
|
|
symbols.begin(), symbols.end(),
|
|
[](const SymbolTableEntry &s) { return s.sym->isLocal(); });
|
|
size_t numLocals = e - symbols.begin();
|
|
getParent()->info = numLocals + 1;
|
|
|
|
// We want to group the local symbols by file. For that we rebuild the local
|
|
// part of the symbols vector. We do not need to care about the STT_FILE
|
|
// symbols, they are already naturally placed first in each group. That
|
|
// happens because STT_FILE is always the first symbol in the object and hence
|
|
// precede all other local symbols we add for a file.
|
|
MapVector<InputFile *, SmallVector<SymbolTableEntry, 0>> arr;
|
|
for (const SymbolTableEntry &s : llvm::make_range(symbols.begin(), e))
|
|
arr[s.sym->file].push_back(s);
|
|
|
|
auto i = symbols.begin();
|
|
for (auto &p : arr)
|
|
for (SymbolTableEntry &entry : p.second)
|
|
*i++ = entry;
|
|
}
|
|
|
|
void SymbolTableBaseSection::addSymbol(Symbol *b) {
|
|
// Adding a local symbol to a .dynsym is a bug.
|
|
assert(this->type != SHT_DYNSYM || !b->isLocal());
|
|
symbols.push_back({b, strTabSec.addString(b->getName(), false)});
|
|
}
|
|
|
|
size_t SymbolTableBaseSection::getSymbolIndex(const Symbol &sym) {
|
|
if (this == ctx.mainPart->dynSymTab.get())
|
|
return sym.dynsymIndex;
|
|
|
|
// Initializes symbol lookup tables lazily. This is used only for -r,
|
|
// --emit-relocs and dynsyms in partitions other than the main one.
|
|
llvm::call_once(onceFlag, [&] {
|
|
symbolIndexMap.reserve(symbols.size());
|
|
size_t i = 0;
|
|
for (const SymbolTableEntry &e : symbols) {
|
|
if (e.sym->type == STT_SECTION)
|
|
sectionIndexMap[e.sym->getOutputSection()] = ++i;
|
|
else
|
|
symbolIndexMap[e.sym] = ++i;
|
|
}
|
|
});
|
|
|
|
// Section symbols are mapped based on their output sections
|
|
// to maintain their semantics.
|
|
if (sym.type == STT_SECTION)
|
|
return sectionIndexMap.lookup(sym.getOutputSection());
|
|
return symbolIndexMap.lookup(&sym);
|
|
}
|
|
|
|
template <class ELFT>
|
|
SymbolTableSection<ELFT>::SymbolTableSection(Ctx &ctx,
|
|
StringTableSection &strTabSec)
|
|
: SymbolTableBaseSection(ctx, strTabSec) {
|
|
this->entsize = sizeof(Elf_Sym);
|
|
}
|
|
|
|
static BssSection *getCommonSec(bool relocatable, Symbol *sym) {
|
|
if (relocatable)
|
|
if (auto *d = dyn_cast<Defined>(sym))
|
|
return dyn_cast_or_null<BssSection>(d->section);
|
|
return nullptr;
|
|
}
|
|
|
|
static uint32_t getSymSectionIndex(Symbol *sym) {
|
|
assert(!(sym->hasFlag(NEEDS_COPY) && sym->isObject()));
|
|
if (!isa<Defined>(sym) || sym->hasFlag(NEEDS_COPY))
|
|
return SHN_UNDEF;
|
|
if (const OutputSection *os = sym->getOutputSection())
|
|
return os->sectionIndex >= SHN_LORESERVE ? (uint32_t)SHN_XINDEX
|
|
: os->sectionIndex;
|
|
return SHN_ABS;
|
|
}
|
|
|
|
// Write the internal symbol table contents to the output symbol table.
|
|
template <class ELFT> void SymbolTableSection<ELFT>::writeTo(uint8_t *buf) {
|
|
// The first entry is a null entry as per the ELF spec.
|
|
buf += sizeof(Elf_Sym);
|
|
|
|
auto *eSym = reinterpret_cast<Elf_Sym *>(buf);
|
|
bool relocatable = ctx.arg.relocatable;
|
|
for (SymbolTableEntry &ent : symbols) {
|
|
Symbol *sym = ent.sym;
|
|
bool isDefinedHere = type == SHT_SYMTAB || sym->partition == partition;
|
|
|
|
// Set st_name, st_info and st_other.
|
|
eSym->st_name = ent.strTabOffset;
|
|
eSym->setBindingAndType(sym->binding, sym->type);
|
|
eSym->st_other = sym->stOther;
|
|
|
|
if (BssSection *commonSec = getCommonSec(relocatable, sym)) {
|
|
// When -r is specified, a COMMON symbol is not allocated. Its st_shndx
|
|
// holds SHN_COMMON and st_value holds the alignment.
|
|
eSym->st_shndx = SHN_COMMON;
|
|
eSym->st_value = commonSec->addralign;
|
|
eSym->st_size = cast<Defined>(sym)->size;
|
|
} else {
|
|
const uint32_t shndx = getSymSectionIndex(sym);
|
|
if (isDefinedHere) {
|
|
eSym->st_shndx = shndx;
|
|
eSym->st_value = sym->getVA(ctx);
|
|
// Copy symbol size if it is a defined symbol. st_size is not
|
|
// significant for undefined symbols, so whether copying it or not is up
|
|
// to us if that's the case. We'll leave it as zero because by not
|
|
// setting a value, we can get the exact same outputs for two sets of
|
|
// input files that differ only in undefined symbol size in DSOs.
|
|
eSym->st_size = shndx != SHN_UNDEF ? cast<Defined>(sym)->size : 0;
|
|
} else {
|
|
eSym->st_shndx = 0;
|
|
eSym->st_value = 0;
|
|
eSym->st_size = 0;
|
|
}
|
|
}
|
|
|
|
++eSym;
|
|
}
|
|
|
|
// On MIPS we need to mark symbol which has a PLT entry and requires
|
|
// pointer equality by STO_MIPS_PLT flag. That is necessary to help
|
|
// dynamic linker distinguish such symbols and MIPS lazy-binding stubs.
|
|
// https://sourceware.org/ml/binutils/2008-07/txt00000.txt
|
|
if (ctx.arg.emachine == EM_MIPS) {
|
|
auto *eSym = reinterpret_cast<Elf_Sym *>(buf);
|
|
|
|
for (SymbolTableEntry &ent : symbols) {
|
|
Symbol *sym = ent.sym;
|
|
if (sym->isInPlt(ctx) && sym->hasFlag(NEEDS_COPY))
|
|
eSym->st_other |= STO_MIPS_PLT;
|
|
if (isMicroMips(ctx)) {
|
|
// We already set the less-significant bit for symbols
|
|
// marked by the `STO_MIPS_MICROMIPS` flag and for microMIPS PLT
|
|
// records. That allows us to distinguish such symbols in
|
|
// the `MIPS<ELFT>::relocate()` routine. Now we should
|
|
// clear that bit for non-dynamic symbol table, so tools
|
|
// like `objdump` will be able to deal with a correct
|
|
// symbol position.
|
|
if (sym->isDefined() &&
|
|
((sym->stOther & STO_MIPS_MICROMIPS) || sym->hasFlag(NEEDS_COPY))) {
|
|
if (!strTabSec.isDynamic())
|
|
eSym->st_value &= ~1;
|
|
eSym->st_other |= STO_MIPS_MICROMIPS;
|
|
}
|
|
}
|
|
if (ctx.arg.relocatable)
|
|
if (auto *d = dyn_cast<Defined>(sym))
|
|
if (isMipsPIC<ELFT>(d))
|
|
eSym->st_other |= STO_MIPS_PIC;
|
|
++eSym;
|
|
}
|
|
}
|
|
}
|
|
|
|
SymtabShndxSection::SymtabShndxSection(Ctx &ctx)
|
|
: SyntheticSection(ctx, ".symtab_shndx", SHT_SYMTAB_SHNDX, 0, 4) {
|
|
this->entsize = 4;
|
|
}
|
|
|
|
void SymtabShndxSection::writeTo(uint8_t *buf) {
|
|
// We write an array of 32 bit values, where each value has 1:1 association
|
|
// with an entry in ctx.in.symTab if the corresponding entry contains
|
|
// SHN_XINDEX, we need to write actual index, otherwise, we must write
|
|
// SHN_UNDEF(0).
|
|
buf += 4; // Ignore .symtab[0] entry.
|
|
bool relocatable = ctx.arg.relocatable;
|
|
for (const SymbolTableEntry &entry : ctx.in.symTab->getSymbols()) {
|
|
if (!getCommonSec(relocatable, entry.sym) &&
|
|
getSymSectionIndex(entry.sym) == SHN_XINDEX)
|
|
write32(ctx, buf, entry.sym->getOutputSection()->sectionIndex);
|
|
buf += 4;
|
|
}
|
|
}
|
|
|
|
bool SymtabShndxSection::isNeeded() const {
|
|
// SHT_SYMTAB can hold symbols with section indices values up to
|
|
// SHN_LORESERVE. If we need more, we want to use extension SHT_SYMTAB_SHNDX
|
|
// section. Problem is that we reveal the final section indices a bit too
|
|
// late, and we do not know them here. For simplicity, we just always create
|
|
// a .symtab_shndx section when the amount of output sections is huge.
|
|
size_t size = 0;
|
|
for (SectionCommand *cmd : ctx.script->sectionCommands)
|
|
if (isa<OutputDesc>(cmd))
|
|
++size;
|
|
return size >= SHN_LORESERVE;
|
|
}
|
|
|
|
void SymtabShndxSection::finalizeContents() {
|
|
getParent()->link = ctx.in.symTab->getParent()->sectionIndex;
|
|
}
|
|
|
|
size_t SymtabShndxSection::getSize() const {
|
|
return ctx.in.symTab->getNumSymbols() * 4;
|
|
}
|
|
|
|
// .hash and .gnu.hash sections contain on-disk hash tables that map
|
|
// symbol names to their dynamic symbol table indices. Their purpose
|
|
// is to help the dynamic linker resolve symbols quickly. If ELF files
|
|
// don't have them, the dynamic linker has to do linear search on all
|
|
// dynamic symbols, which makes programs slower. Therefore, a .hash
|
|
// section is added to a DSO by default.
|
|
//
|
|
// The Unix semantics of resolving dynamic symbols is somewhat expensive.
|
|
// Each ELF file has a list of DSOs that the ELF file depends on and a
|
|
// list of dynamic symbols that need to be resolved from any of the
|
|
// DSOs. That means resolving all dynamic symbols takes O(m)*O(n)
|
|
// where m is the number of DSOs and n is the number of dynamic
|
|
// symbols. For modern large programs, both m and n are large. So
|
|
// making each step faster by using hash tables substantially
|
|
// improves time to load programs.
|
|
//
|
|
// (Note that this is not the only way to design the shared library.
|
|
// For instance, the Windows DLL takes a different approach. On
|
|
// Windows, each dynamic symbol has a name of DLL from which the symbol
|
|
// has to be resolved. That makes the cost of symbol resolution O(n).
|
|
// This disables some hacky techniques you can use on Unix such as
|
|
// LD_PRELOAD, but this is arguably better semantics than the Unix ones.)
|
|
//
|
|
// Due to historical reasons, we have two different hash tables, .hash
|
|
// and .gnu.hash. They are for the same purpose, and .gnu.hash is a new
|
|
// and better version of .hash. .hash is just an on-disk hash table, but
|
|
// .gnu.hash has a bloom filter in addition to a hash table to skip
|
|
// DSOs very quickly. If you are sure that your dynamic linker knows
|
|
// about .gnu.hash, you want to specify --hash-style=gnu. Otherwise, a
|
|
// safe bet is to specify --hash-style=both for backward compatibility.
|
|
GnuHashTableSection::GnuHashTableSection(Ctx &ctx)
|
|
: SyntheticSection(ctx, ".gnu.hash", SHT_GNU_HASH, SHF_ALLOC,
|
|
ctx.arg.wordsize) {}
|
|
|
|
void GnuHashTableSection::finalizeContents() {
|
|
if (OutputSection *sec = getPartition(ctx).dynSymTab->getParent())
|
|
getParent()->link = sec->sectionIndex;
|
|
|
|
// Computes bloom filter size in word size. We want to allocate 12
|
|
// bits for each symbol. It must be a power of two.
|
|
if (symbols.empty()) {
|
|
maskWords = 1;
|
|
} else {
|
|
uint64_t numBits = symbols.size() * 12;
|
|
maskWords = NextPowerOf2(numBits / (ctx.arg.wordsize * 8));
|
|
}
|
|
|
|
size = 16; // Header
|
|
size += ctx.arg.wordsize * maskWords; // Bloom filter
|
|
size += nBuckets * 4; // Hash buckets
|
|
size += symbols.size() * 4; // Hash values
|
|
}
|
|
|
|
void GnuHashTableSection::writeTo(uint8_t *buf) {
|
|
// Write a header.
|
|
write32(ctx, buf, nBuckets);
|
|
write32(ctx, buf + 4,
|
|
getPartition(ctx).dynSymTab->getNumSymbols() - symbols.size());
|
|
write32(ctx, buf + 8, maskWords);
|
|
write32(ctx, buf + 12, Shift2);
|
|
buf += 16;
|
|
|
|
// Write the 2-bit bloom filter.
|
|
const unsigned c = ctx.arg.is64 ? 64 : 32;
|
|
for (const Entry &sym : symbols) {
|
|
// When C = 64, we choose a word with bits [6:...] and set 1 to two bits in
|
|
// the word using bits [0:5] and [26:31].
|
|
size_t i = (sym.hash / c) & (maskWords - 1);
|
|
uint64_t val = readUint(ctx, buf + i * ctx.arg.wordsize);
|
|
val |= uint64_t(1) << (sym.hash % c);
|
|
val |= uint64_t(1) << ((sym.hash >> Shift2) % c);
|
|
writeUint(ctx, buf + i * ctx.arg.wordsize, val);
|
|
}
|
|
buf += ctx.arg.wordsize * maskWords;
|
|
|
|
// Write the hash table.
|
|
uint32_t *buckets = reinterpret_cast<uint32_t *>(buf);
|
|
uint32_t oldBucket = -1;
|
|
uint32_t *values = buckets + nBuckets;
|
|
for (auto i = symbols.begin(), e = symbols.end(); i != e; ++i) {
|
|
// Write a hash value. It represents a sequence of chains that share the
|
|
// same hash modulo value. The last element of each chain is terminated by
|
|
// LSB 1.
|
|
uint32_t hash = i->hash;
|
|
bool isLastInChain = (i + 1) == e || i->bucketIdx != (i + 1)->bucketIdx;
|
|
hash = isLastInChain ? hash | 1 : hash & ~1;
|
|
write32(ctx, values++, hash);
|
|
|
|
if (i->bucketIdx == oldBucket)
|
|
continue;
|
|
// Write a hash bucket. Hash buckets contain indices in the following hash
|
|
// value table.
|
|
write32(ctx, buckets + i->bucketIdx,
|
|
getPartition(ctx).dynSymTab->getSymbolIndex(*i->sym));
|
|
oldBucket = i->bucketIdx;
|
|
}
|
|
}
|
|
|
|
// Add symbols to this symbol hash table. Note that this function
|
|
// destructively sort a given vector -- which is needed because
|
|
// GNU-style hash table places some sorting requirements.
|
|
void GnuHashTableSection::addSymbols(SmallVectorImpl<SymbolTableEntry> &v) {
|
|
// We cannot use 'auto' for Mid because GCC 6.1 cannot deduce
|
|
// its type correctly.
|
|
auto mid =
|
|
std::stable_partition(v.begin(), v.end(), [&](const SymbolTableEntry &s) {
|
|
return !s.sym->isDefined() || s.sym->partition != partition;
|
|
});
|
|
|
|
// We chose load factor 4 for the on-disk hash table. For each hash
|
|
// collision, the dynamic linker will compare a uint32_t hash value.
|
|
// Since the integer comparison is quite fast, we believe we can
|
|
// make the load factor even larger. 4 is just a conservative choice.
|
|
//
|
|
// Note that we don't want to create a zero-sized hash table because
|
|
// Android loader as of 2018 doesn't like a .gnu.hash containing such
|
|
// table. If that's the case, we create a hash table with one unused
|
|
// dummy slot.
|
|
nBuckets = std::max<size_t>((v.end() - mid) / 4, 1);
|
|
|
|
if (mid == v.end())
|
|
return;
|
|
|
|
for (SymbolTableEntry &ent : llvm::make_range(mid, v.end())) {
|
|
Symbol *b = ent.sym;
|
|
uint32_t hash = hashGnu(b->getName());
|
|
uint32_t bucketIdx = hash % nBuckets;
|
|
symbols.push_back({b, ent.strTabOffset, hash, bucketIdx});
|
|
}
|
|
|
|
llvm::sort(symbols, [](const Entry &l, const Entry &r) {
|
|
return std::tie(l.bucketIdx, l.strTabOffset) <
|
|
std::tie(r.bucketIdx, r.strTabOffset);
|
|
});
|
|
|
|
v.erase(mid, v.end());
|
|
for (const Entry &ent : symbols)
|
|
v.push_back({ent.sym, ent.strTabOffset});
|
|
}
|
|
|
|
HashTableSection::HashTableSection(Ctx &ctx)
|
|
: SyntheticSection(ctx, ".hash", SHT_HASH, SHF_ALLOC, 4) {
|
|
this->entsize = 4;
|
|
}
|
|
|
|
void HashTableSection::finalizeContents() {
|
|
SymbolTableBaseSection *symTab = getPartition(ctx).dynSymTab.get();
|
|
|
|
if (OutputSection *sec = symTab->getParent())
|
|
getParent()->link = sec->sectionIndex;
|
|
|
|
unsigned numEntries = 2; // nbucket and nchain.
|
|
numEntries += symTab->getNumSymbols(); // The chain entries.
|
|
|
|
// Create as many buckets as there are symbols.
|
|
numEntries += symTab->getNumSymbols();
|
|
this->size = numEntries * 4;
|
|
}
|
|
|
|
void HashTableSection::writeTo(uint8_t *buf) {
|
|
SymbolTableBaseSection *symTab = getPartition(ctx).dynSymTab.get();
|
|
unsigned numSymbols = symTab->getNumSymbols();
|
|
|
|
uint32_t *p = reinterpret_cast<uint32_t *>(buf);
|
|
write32(ctx, p++, numSymbols); // nbucket
|
|
write32(ctx, p++, numSymbols); // nchain
|
|
|
|
uint32_t *buckets = p;
|
|
uint32_t *chains = p + numSymbols;
|
|
|
|
for (const SymbolTableEntry &s : symTab->getSymbols()) {
|
|
Symbol *sym = s.sym;
|
|
StringRef name = sym->getName();
|
|
unsigned i = sym->dynsymIndex;
|
|
uint32_t hash = hashSysV(name) % numSymbols;
|
|
chains[i] = buckets[hash];
|
|
write32(ctx, buckets + hash, i);
|
|
}
|
|
}
|
|
|
|
PltSection::PltSection(Ctx &ctx)
|
|
: SyntheticSection(ctx, ".plt", SHT_PROGBITS, SHF_ALLOC | SHF_EXECINSTR,
|
|
16),
|
|
headerSize(ctx.target->pltHeaderSize) {
|
|
// On PowerPC, this section contains lazy symbol resolvers.
|
|
if (ctx.arg.emachine == EM_PPC64) {
|
|
name = ".glink";
|
|
addralign = 4;
|
|
}
|
|
|
|
// On x86 when IBT is enabled, this section contains the second PLT (lazy
|
|
// symbol resolvers).
|
|
if ((ctx.arg.emachine == EM_386 || ctx.arg.emachine == EM_X86_64) &&
|
|
(ctx.arg.andFeatures & GNU_PROPERTY_X86_FEATURE_1_IBT))
|
|
name = ".plt.sec";
|
|
|
|
// The PLT needs to be writable on SPARC as the dynamic linker will
|
|
// modify the instructions in the PLT entries.
|
|
if (ctx.arg.emachine == EM_SPARCV9)
|
|
this->flags |= SHF_WRITE;
|
|
}
|
|
|
|
void PltSection::writeTo(uint8_t *buf) {
|
|
// At beginning of PLT, we have code to call the dynamic
|
|
// linker to resolve dynsyms at runtime. Write such code.
|
|
ctx.target->writePltHeader(buf);
|
|
size_t off = headerSize;
|
|
|
|
for (const Symbol *sym : entries) {
|
|
ctx.target->writePlt(buf + off, *sym, getVA() + off);
|
|
off += ctx.target->pltEntrySize;
|
|
}
|
|
}
|
|
|
|
void PltSection::addEntry(Symbol &sym) {
|
|
assert(sym.auxIdx == ctx.symAux.size() - 1);
|
|
ctx.symAux.back().pltIdx = entries.size();
|
|
entries.push_back(&sym);
|
|
}
|
|
|
|
size_t PltSection::getSize() const {
|
|
return headerSize + entries.size() * ctx.target->pltEntrySize;
|
|
}
|
|
|
|
bool PltSection::isNeeded() const {
|
|
// For -z retpolineplt, .iplt needs the .plt header.
|
|
return !entries.empty() || (ctx.arg.zRetpolineplt && ctx.in.iplt->isNeeded());
|
|
}
|
|
|
|
// Used by ARM to add mapping symbols in the PLT section, which aid
|
|
// disassembly.
|
|
void PltSection::addSymbols() {
|
|
ctx.target->addPltHeaderSymbols(*this);
|
|
|
|
size_t off = headerSize;
|
|
for (size_t i = 0; i < entries.size(); ++i) {
|
|
ctx.target->addPltSymbols(*this, off);
|
|
off += ctx.target->pltEntrySize;
|
|
}
|
|
}
|
|
|
|
IpltSection::IpltSection(Ctx &ctx)
|
|
: SyntheticSection(ctx, ".iplt", SHT_PROGBITS, SHF_ALLOC | SHF_EXECINSTR,
|
|
16) {
|
|
if (ctx.arg.emachine == EM_PPC || ctx.arg.emachine == EM_PPC64) {
|
|
name = ".glink";
|
|
addralign = 4;
|
|
}
|
|
}
|
|
|
|
void IpltSection::writeTo(uint8_t *buf) {
|
|
uint32_t off = 0;
|
|
for (const Symbol *sym : entries) {
|
|
ctx.target->writeIplt(buf + off, *sym, getVA() + off);
|
|
off += ctx.target->ipltEntrySize;
|
|
}
|
|
}
|
|
|
|
size_t IpltSection::getSize() const {
|
|
return entries.size() * ctx.target->ipltEntrySize;
|
|
}
|
|
|
|
void IpltSection::addEntry(Symbol &sym) {
|
|
assert(sym.auxIdx == ctx.symAux.size() - 1);
|
|
ctx.symAux.back().pltIdx = entries.size();
|
|
entries.push_back(&sym);
|
|
}
|
|
|
|
// ARM uses mapping symbols to aid disassembly.
|
|
void IpltSection::addSymbols() {
|
|
size_t off = 0;
|
|
for (size_t i = 0, e = entries.size(); i != e; ++i) {
|
|
ctx.target->addPltSymbols(*this, off);
|
|
off += ctx.target->pltEntrySize;
|
|
}
|
|
}
|
|
|
|
PPC32GlinkSection::PPC32GlinkSection(Ctx &ctx) : PltSection(ctx) {
|
|
name = ".glink";
|
|
addralign = 4;
|
|
}
|
|
|
|
void PPC32GlinkSection::writeTo(uint8_t *buf) {
|
|
writePPC32GlinkSection(ctx, buf, entries.size());
|
|
}
|
|
|
|
size_t PPC32GlinkSection::getSize() const {
|
|
return headerSize + entries.size() * ctx.target->pltEntrySize + footerSize;
|
|
}
|
|
|
|
// This is an x86-only extra PLT section and used only when a security
|
|
// enhancement feature called CET is enabled. In this comment, I'll explain what
|
|
// the feature is and why we have two PLT sections if CET is enabled.
|
|
//
|
|
// So, what does CET do? CET introduces a new restriction to indirect jump
|
|
// instructions. CET works this way. Assume that CET is enabled. Then, if you
|
|
// execute an indirect jump instruction, the processor verifies that a special
|
|
// "landing pad" instruction (which is actually a repurposed NOP instruction and
|
|
// now called "endbr32" or "endbr64") is at the jump target. If the jump target
|
|
// does not start with that instruction, the processor raises an exception
|
|
// instead of continuing executing code.
|
|
//
|
|
// If CET is enabled, the compiler emits endbr to all locations where indirect
|
|
// jumps may jump to.
|
|
//
|
|
// This mechanism makes it extremely hard to transfer the control to a middle of
|
|
// a function that is not supporsed to be a indirect jump target, preventing
|
|
// certain types of attacks such as ROP or JOP.
|
|
//
|
|
// Note that the processors in the market as of 2019 don't actually support the
|
|
// feature. Only the spec is available at the moment.
|
|
//
|
|
// Now, I'll explain why we have this extra PLT section for CET.
|
|
//
|
|
// Since you can indirectly jump to a PLT entry, we have to make PLT entries
|
|
// start with endbr. The problem is there's no extra space for endbr (which is 4
|
|
// bytes long), as the PLT entry is only 16 bytes long and all bytes are already
|
|
// used.
|
|
//
|
|
// In order to deal with the issue, we split a PLT entry into two PLT entries.
|
|
// Remember that each PLT entry contains code to jump to an address read from
|
|
// .got.plt AND code to resolve a dynamic symbol lazily. With the 2-PLT scheme,
|
|
// the former code is written to .plt.sec, and the latter code is written to
|
|
// .plt.
|
|
//
|
|
// Lazy symbol resolution in the 2-PLT scheme works in the usual way, except
|
|
// that the regular .plt is now called .plt.sec and .plt is repurposed to
|
|
// contain only code for lazy symbol resolution.
|
|
//
|
|
// In other words, this is how the 2-PLT scheme works. Application code is
|
|
// supposed to jump to .plt.sec to call an external function. Each .plt.sec
|
|
// entry contains code to read an address from a corresponding .got.plt entry
|
|
// and jump to that address. Addresses in .got.plt initially point to .plt, so
|
|
// when an application calls an external function for the first time, the
|
|
// control is transferred to a function that resolves a symbol name from
|
|
// external shared object files. That function then rewrites a .got.plt entry
|
|
// with a resolved address, so that the subsequent function calls directly jump
|
|
// to a desired location from .plt.sec.
|
|
//
|
|
// There is an open question as to whether the 2-PLT scheme was desirable or
|
|
// not. We could have simply extended the PLT entry size to 32-bytes to
|
|
// accommodate endbr, and that scheme would have been much simpler than the
|
|
// 2-PLT scheme. One reason to split PLT was, by doing that, we could keep hot
|
|
// code (.plt.sec) from cold code (.plt). But as far as I know no one proved
|
|
// that the optimization actually makes a difference.
|
|
//
|
|
// 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,
|
|
1) {}
|
|
|
|
RandomizePaddingSection::RandomizePaddingSection(Ctx &ctx, uint64_t size,
|
|
OutputSection *parent)
|
|
: SyntheticSection(ctx, ".randomize_padding", SHT_PROGBITS, SHF_ALLOC, 1),
|
|
size(size) {
|
|
this->parent = parent;
|
|
}
|
|
|
|
void RandomizePaddingSection::writeTo(uint8_t *buf) {
|
|
std::array<uint8_t, 4> filler = getParent()->getFiller(ctx);
|
|
uint8_t *end = buf + size;
|
|
for (; buf + 4 <= end; buf += 4)
|
|
memcpy(buf, &filler[0], 4);
|
|
memcpy(buf, &filler[0], end - buf);
|
|
}
|
|
|
|
// The string hash function for .gdb_index.
|
|
static uint32_t computeGdbHash(StringRef s) {
|
|
uint32_t h = 0;
|
|
for (uint8_t c : s)
|
|
h = h * 67 + toLower(c) - 113;
|
|
return h;
|
|
}
|
|
|
|
// 4-byte alignment ensures that values in the hash lookup table and the name
|
|
// table are aligned.
|
|
DebugNamesBaseSection::DebugNamesBaseSection(Ctx &ctx)
|
|
: SyntheticSection(ctx, ".debug_names", SHT_PROGBITS, 0, 4) {}
|
|
|
|
// Get the size of the .debug_names section header in bytes for DWARF32:
|
|
static uint32_t getDebugNamesHeaderSize(uint32_t augmentationStringSize) {
|
|
return /* unit length */ 4 +
|
|
/* version */ 2 +
|
|
/* padding */ 2 +
|
|
/* CU count */ 4 +
|
|
/* TU count */ 4 +
|
|
/* Foreign TU count */ 4 +
|
|
/* Bucket Count */ 4 +
|
|
/* Name Count */ 4 +
|
|
/* Abbrev table size */ 4 +
|
|
/* Augmentation string size */ 4 +
|
|
/* Augmentation string */ augmentationStringSize;
|
|
}
|
|
|
|
static Expected<DebugNamesBaseSection::IndexEntry *>
|
|
readEntry(uint64_t &offset, const DWARFDebugNames::NameIndex &ni,
|
|
uint64_t entriesBase, DWARFDataExtractor &namesExtractor,
|
|
const LLDDWARFSection &namesSec) {
|
|
auto ie = makeThreadLocal<DebugNamesBaseSection::IndexEntry>();
|
|
ie->poolOffset = offset;
|
|
Error err = Error::success();
|
|
uint64_t ulebVal = namesExtractor.getULEB128(&offset, &err);
|
|
if (err)
|
|
return createStringError(inconvertibleErrorCode(),
|
|
"invalid abbrev code: %s",
|
|
llvm::toString(std::move(err)).c_str());
|
|
if (!isUInt<32>(ulebVal))
|
|
return createStringError(inconvertibleErrorCode(),
|
|
"abbrev code too large for DWARF32: %" PRIu64,
|
|
ulebVal);
|
|
ie->abbrevCode = static_cast<uint32_t>(ulebVal);
|
|
auto it = ni.getAbbrevs().find_as(ie->abbrevCode);
|
|
if (it == ni.getAbbrevs().end())
|
|
return createStringError(inconvertibleErrorCode(),
|
|
"abbrev code not found in abbrev table: %" PRIu32,
|
|
ie->abbrevCode);
|
|
|
|
DebugNamesBaseSection::AttrValue attr, cuAttr = {0, 0};
|
|
for (DWARFDebugNames::AttributeEncoding a : it->Attributes) {
|
|
if (a.Index == dwarf::DW_IDX_parent) {
|
|
if (a.Form == dwarf::DW_FORM_ref4) {
|
|
attr.attrValue = namesExtractor.getU32(&offset, &err);
|
|
attr.attrSize = 4;
|
|
ie->parentOffset = entriesBase + attr.attrValue;
|
|
} else if (a.Form != DW_FORM_flag_present)
|
|
return createStringError(inconvertibleErrorCode(),
|
|
"invalid form for DW_IDX_parent");
|
|
} else {
|
|
switch (a.Form) {
|
|
case DW_FORM_data1:
|
|
case DW_FORM_ref1: {
|
|
attr.attrValue = namesExtractor.getU8(&offset, &err);
|
|
attr.attrSize = 1;
|
|
break;
|
|
}
|
|
case DW_FORM_data2:
|
|
case DW_FORM_ref2: {
|
|
attr.attrValue = namesExtractor.getU16(&offset, &err);
|
|
attr.attrSize = 2;
|
|
break;
|
|
}
|
|
case DW_FORM_data4:
|
|
case DW_FORM_ref4: {
|
|
attr.attrValue = namesExtractor.getU32(&offset, &err);
|
|
attr.attrSize = 4;
|
|
break;
|
|
}
|
|
default:
|
|
return createStringError(
|
|
inconvertibleErrorCode(),
|
|
"unrecognized form encoding %d in abbrev table", a.Form);
|
|
}
|
|
}
|
|
if (err)
|
|
return createStringError(inconvertibleErrorCode(),
|
|
"error while reading attributes: %s",
|
|
llvm::toString(std::move(err)).c_str());
|
|
if (a.Index == DW_IDX_compile_unit)
|
|
cuAttr = attr;
|
|
else if (a.Form != DW_FORM_flag_present)
|
|
ie->attrValues.push_back(attr);
|
|
}
|
|
// Canonicalize abbrev by placing the CU/TU index at the end.
|
|
ie->attrValues.push_back(cuAttr);
|
|
return ie;
|
|
}
|
|
|
|
void DebugNamesBaseSection::parseDebugNames(
|
|
Ctx &ctx, InputChunk &inputChunk, OutputChunk &chunk,
|
|
DWARFDataExtractor &namesExtractor, DataExtractor &strExtractor,
|
|
function_ref<SmallVector<uint32_t, 0>(
|
|
uint32_t numCus, const DWARFDebugNames::Header &,
|
|
const DWARFDebugNames::DWARFDebugNamesOffsets &)>
|
|
readOffsets) {
|
|
const LLDDWARFSection &namesSec = inputChunk.section;
|
|
DenseMap<uint32_t, IndexEntry *> offsetMap;
|
|
// Number of CUs seen in previous NameIndex sections within current chunk.
|
|
uint32_t numCus = 0;
|
|
for (const DWARFDebugNames::NameIndex &ni : *inputChunk.llvmDebugNames) {
|
|
NameData &nd = inputChunk.nameData.emplace_back();
|
|
nd.hdr = ni.getHeader();
|
|
if (nd.hdr.Format != DwarfFormat::DWARF32) {
|
|
Err(ctx) << namesSec.sec
|
|
<< ": found DWARF64, which is currently unsupported";
|
|
return;
|
|
}
|
|
if (nd.hdr.Version != 5) {
|
|
Err(ctx) << namesSec.sec << ": unsupported version: " << nd.hdr.Version;
|
|
return;
|
|
}
|
|
uint32_t dwarfSize = dwarf::getDwarfOffsetByteSize(DwarfFormat::DWARF32);
|
|
DWARFDebugNames::DWARFDebugNamesOffsets locs = ni.getOffsets();
|
|
if (locs.EntriesBase > namesExtractor.getData().size()) {
|
|
Err(ctx) << namesSec.sec << ": entry pool start is beyond end of section";
|
|
return;
|
|
}
|
|
|
|
SmallVector<uint32_t, 0> entryOffsets = readOffsets(numCus, nd.hdr, locs);
|
|
|
|
// Read the entry pool.
|
|
offsetMap.clear();
|
|
nd.nameEntries.resize(nd.hdr.NameCount);
|
|
for (auto i : seq(nd.hdr.NameCount)) {
|
|
NameEntry &ne = nd.nameEntries[i];
|
|
uint64_t strOffset = locs.StringOffsetsBase + i * dwarfSize;
|
|
ne.stringOffset = strOffset;
|
|
uint64_t strp = namesExtractor.getRelocatedValue(dwarfSize, &strOffset);
|
|
StringRef name = strExtractor.getCStrRef(&strp);
|
|
ne.name = name.data();
|
|
ne.hashValue = caseFoldingDjbHash(name);
|
|
|
|
// Read a series of index entries that end with abbreviation code 0.
|
|
uint64_t offset = locs.EntriesBase + entryOffsets[i];
|
|
while (offset < namesSec.Data.size() && namesSec.Data[offset] != 0) {
|
|
// Read & store all entries (for the same string).
|
|
Expected<IndexEntry *> ieOrErr =
|
|
readEntry(offset, ni, locs.EntriesBase, namesExtractor, namesSec);
|
|
if (!ieOrErr) {
|
|
Err(ctx) << namesSec.sec << ": " << ieOrErr.takeError();
|
|
return;
|
|
}
|
|
ne.indexEntries.push_back(std::move(*ieOrErr));
|
|
}
|
|
if (offset >= namesSec.Data.size())
|
|
Err(ctx) << namesSec.sec << ": index entry is out of bounds";
|
|
|
|
for (IndexEntry &ie : ne.entries())
|
|
offsetMap[ie.poolOffset] = &ie;
|
|
}
|
|
|
|
// Assign parent pointers, which will be used to update DW_IDX_parent index
|
|
// attributes. Note: offsetMap[0] does not exist, so parentOffset == 0 will
|
|
// get parentEntry == null as well.
|
|
for (NameEntry &ne : nd.nameEntries)
|
|
for (IndexEntry &ie : ne.entries())
|
|
ie.parentEntry = offsetMap.lookup(ie.parentOffset);
|
|
numCus += nd.hdr.CompUnitCount;
|
|
}
|
|
}
|
|
|
|
// Compute the form for output DW_IDX_compile_unit attributes, similar to
|
|
// DIEInteger::BestForm. The input form (often DW_FORM_data1) may not hold all
|
|
// the merged CU indices.
|
|
std::pair<uint8_t, dwarf::Form> static getMergedCuCountForm(
|
|
uint32_t compUnitCount) {
|
|
if (compUnitCount > UINT16_MAX)
|
|
return {4, DW_FORM_data4};
|
|
if (compUnitCount > UINT8_MAX)
|
|
return {2, DW_FORM_data2};
|
|
return {1, DW_FORM_data1};
|
|
}
|
|
|
|
void DebugNamesBaseSection::computeHdrAndAbbrevTable(
|
|
MutableArrayRef<InputChunk> inputChunks) {
|
|
TimeTraceScope timeScope("Merge .debug_names", "hdr and abbrev table");
|
|
size_t numCu = 0;
|
|
hdr.Format = DwarfFormat::DWARF32;
|
|
hdr.Version = 5;
|
|
hdr.CompUnitCount = 0;
|
|
hdr.LocalTypeUnitCount = 0;
|
|
hdr.ForeignTypeUnitCount = 0;
|
|
hdr.AugmentationStringSize = 0;
|
|
|
|
// Compute CU and TU counts.
|
|
for (auto i : seq(numChunks)) {
|
|
InputChunk &inputChunk = inputChunks[i];
|
|
inputChunk.baseCuIdx = numCu;
|
|
numCu += chunks[i].compUnits.size();
|
|
for (const NameData &nd : inputChunk.nameData) {
|
|
hdr.CompUnitCount += nd.hdr.CompUnitCount;
|
|
// TODO: We don't handle type units yet, so LocalTypeUnitCount &
|
|
// ForeignTypeUnitCount are left as 0.
|
|
if (nd.hdr.LocalTypeUnitCount || nd.hdr.ForeignTypeUnitCount)
|
|
Warn(ctx) << inputChunk.section.sec
|
|
<< ": type units are not implemented";
|
|
// If augmentation strings are not identical, use an empty string.
|
|
if (i == 0) {
|
|
hdr.AugmentationStringSize = nd.hdr.AugmentationStringSize;
|
|
hdr.AugmentationString = nd.hdr.AugmentationString;
|
|
} else if (hdr.AugmentationString != nd.hdr.AugmentationString) {
|
|
// There are conflicting augmentation strings, so it's best for the
|
|
// merged index to not use an augmentation string.
|
|
hdr.AugmentationStringSize = 0;
|
|
hdr.AugmentationString.clear();
|
|
}
|
|
}
|
|
}
|
|
|
|
// Create the merged abbrev table, uniquifyinng the input abbrev tables and
|
|
// computing mapping from old (per-cu) abbrev codes to new (merged) abbrev
|
|
// codes.
|
|
FoldingSet<Abbrev> abbrevSet;
|
|
// Determine the form for the DW_IDX_compile_unit attributes in the merged
|
|
// index. The input form may not be big enough for all CU indices.
|
|
dwarf::Form cuAttrForm = getMergedCuCountForm(hdr.CompUnitCount).second;
|
|
for (InputChunk &inputChunk : inputChunks) {
|
|
for (auto [i, ni] : enumerate(*inputChunk.llvmDebugNames)) {
|
|
for (const DWARFDebugNames::Abbrev &oldAbbrev : ni.getAbbrevs()) {
|
|
// Canonicalize abbrev by placing the CU/TU index at the end,
|
|
// similar to 'parseDebugNames'.
|
|
Abbrev abbrev;
|
|
DWARFDebugNames::AttributeEncoding cuAttr(DW_IDX_compile_unit,
|
|
cuAttrForm);
|
|
abbrev.code = oldAbbrev.Code;
|
|
abbrev.tag = oldAbbrev.Tag;
|
|
for (DWARFDebugNames::AttributeEncoding a : oldAbbrev.Attributes) {
|
|
if (a.Index == DW_IDX_compile_unit)
|
|
cuAttr.Index = a.Index;
|
|
else
|
|
abbrev.attributes.push_back({a.Index, a.Form});
|
|
}
|
|
// Put the CU/TU index at the end of the attributes list.
|
|
abbrev.attributes.push_back(cuAttr);
|
|
|
|
// Profile the abbrev, get or assign a new code, then record the abbrev
|
|
// code mapping.
|
|
FoldingSetNodeID id;
|
|
abbrev.Profile(id);
|
|
uint32_t newCode;
|
|
void *insertPos;
|
|
if (Abbrev *existing = abbrevSet.FindNodeOrInsertPos(id, insertPos)) {
|
|
// Found it; we've already seen an identical abbreviation.
|
|
newCode = existing->code;
|
|
} else {
|
|
Abbrev *abbrev2 =
|
|
new (abbrevAlloc.Allocate()) Abbrev(std::move(abbrev));
|
|
abbrevSet.InsertNode(abbrev2, insertPos);
|
|
abbrevTable.push_back(abbrev2);
|
|
newCode = abbrevTable.size();
|
|
abbrev2->code = newCode;
|
|
}
|
|
inputChunk.nameData[i].abbrevCodeMap[oldAbbrev.Code] = newCode;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Compute the merged abbrev table.
|
|
raw_svector_ostream os(abbrevTableBuf);
|
|
for (Abbrev *abbrev : abbrevTable) {
|
|
encodeULEB128(abbrev->code, os);
|
|
encodeULEB128(abbrev->tag, os);
|
|
for (DWARFDebugNames::AttributeEncoding a : abbrev->attributes) {
|
|
encodeULEB128(a.Index, os);
|
|
encodeULEB128(a.Form, os);
|
|
}
|
|
os.write("\0", 2); // attribute specification end
|
|
}
|
|
os.write(0); // abbrev table end
|
|
hdr.AbbrevTableSize = abbrevTableBuf.size();
|
|
}
|
|
|
|
void DebugNamesBaseSection::Abbrev::Profile(FoldingSetNodeID &id) const {
|
|
id.AddInteger(tag);
|
|
for (const DWARFDebugNames::AttributeEncoding &attr : attributes) {
|
|
id.AddInteger(attr.Index);
|
|
id.AddInteger(attr.Form);
|
|
}
|
|
}
|
|
|
|
std::pair<uint32_t, uint32_t> DebugNamesBaseSection::computeEntryPool(
|
|
MutableArrayRef<InputChunk> inputChunks) {
|
|
TimeTraceScope timeScope("Merge .debug_names", "entry pool");
|
|
// Collect and de-duplicate all the names (preserving all the entries).
|
|
// Speed it up using multithreading, as the number of symbols can be in the
|
|
// order of millions.
|
|
const size_t concurrency =
|
|
bit_floor(std::min<size_t>(ctx.arg.threadCount, numShards));
|
|
const size_t shift = 32 - countr_zero(numShards);
|
|
const uint8_t cuAttrSize = getMergedCuCountForm(hdr.CompUnitCount).first;
|
|
DenseMap<CachedHashStringRef, size_t> maps[numShards];
|
|
|
|
parallelFor(0, concurrency, [&](size_t threadId) {
|
|
for (auto i : seq(numChunks)) {
|
|
InputChunk &inputChunk = inputChunks[i];
|
|
for (auto j : seq(inputChunk.nameData.size())) {
|
|
NameData &nd = inputChunk.nameData[j];
|
|
// Deduplicate the NameEntry records (based on the string/name),
|
|
// appending all IndexEntries from duplicate NameEntry records to
|
|
// the single preserved copy.
|
|
for (NameEntry &ne : nd.nameEntries) {
|
|
auto shardId = ne.hashValue >> shift;
|
|
if ((shardId & (concurrency - 1)) != threadId)
|
|
continue;
|
|
|
|
ne.chunkIdx = i;
|
|
for (IndexEntry &ie : ne.entries()) {
|
|
// Update the IndexEntry's abbrev code to match the merged
|
|
// abbreviations.
|
|
ie.abbrevCode = nd.abbrevCodeMap[ie.abbrevCode];
|
|
// Update the DW_IDX_compile_unit attribute (the last one after
|
|
// canonicalization) to have correct merged offset value and size.
|
|
auto &back = ie.attrValues.back();
|
|
back.attrValue += inputChunk.baseCuIdx + j;
|
|
back.attrSize = cuAttrSize;
|
|
}
|
|
|
|
auto &nameVec = nameVecs[shardId];
|
|
auto [it, inserted] = maps[shardId].try_emplace(
|
|
CachedHashStringRef(ne.name, ne.hashValue), nameVec.size());
|
|
if (inserted)
|
|
nameVec.push_back(std::move(ne));
|
|
else
|
|
nameVec[it->second].indexEntries.append(std::move(ne.indexEntries));
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
// Compute entry offsets in parallel. First, compute offsets relative to the
|
|
// current shard.
|
|
uint32_t offsets[numShards];
|
|
parallelFor(0, numShards, [&](size_t shard) {
|
|
uint32_t offset = 0;
|
|
for (NameEntry &ne : nameVecs[shard]) {
|
|
ne.entryOffset = offset;
|
|
for (IndexEntry &ie : ne.entries()) {
|
|
ie.poolOffset = offset;
|
|
offset += getULEB128Size(ie.abbrevCode);
|
|
for (AttrValue value : ie.attrValues)
|
|
offset += value.attrSize;
|
|
}
|
|
++offset; // index entry sentinel
|
|
}
|
|
offsets[shard] = offset;
|
|
});
|
|
// Then add shard offsets.
|
|
std::partial_sum(offsets, std::end(offsets), offsets);
|
|
parallelFor(1, numShards, [&](size_t shard) {
|
|
uint32_t offset = offsets[shard - 1];
|
|
for (NameEntry &ne : nameVecs[shard]) {
|
|
ne.entryOffset += offset;
|
|
for (IndexEntry &ie : ne.entries())
|
|
ie.poolOffset += offset;
|
|
}
|
|
});
|
|
|
|
// Update the DW_IDX_parent entries that refer to real parents (have
|
|
// DW_FORM_ref4).
|
|
parallelFor(0, numShards, [&](size_t shard) {
|
|
for (NameEntry &ne : nameVecs[shard]) {
|
|
for (IndexEntry &ie : ne.entries()) {
|
|
if (!ie.parentEntry)
|
|
continue;
|
|
// Abbrevs are indexed starting at 1; vector starts at 0. (abbrevCode
|
|
// corresponds to position in the merged table vector).
|
|
const Abbrev *abbrev = abbrevTable[ie.abbrevCode - 1];
|
|
for (const auto &[a, v] : zip_equal(abbrev->attributes, ie.attrValues))
|
|
if (a.Index == DW_IDX_parent && a.Form == DW_FORM_ref4)
|
|
v.attrValue = ie.parentEntry->poolOffset;
|
|
}
|
|
}
|
|
});
|
|
|
|
// Return (entry pool size, number of entries).
|
|
uint32_t num = 0;
|
|
for (auto &map : maps)
|
|
num += map.size();
|
|
return {offsets[numShards - 1], num};
|
|
}
|
|
|
|
void DebugNamesBaseSection::init(
|
|
function_ref<void(InputFile *, InputChunk &, OutputChunk &)> parseFile) {
|
|
TimeTraceScope timeScope("Merge .debug_names");
|
|
// Collect and remove input .debug_names sections. Save InputSection pointers
|
|
// to relocate string offsets in `writeTo`.
|
|
SetVector<InputFile *> files;
|
|
for (InputSectionBase *s : ctx.inputSections) {
|
|
InputSection *isec = dyn_cast<InputSection>(s);
|
|
if (!isec)
|
|
continue;
|
|
if (!(s->flags & SHF_ALLOC) && s->name == ".debug_names") {
|
|
s->markDead();
|
|
inputSections.push_back(isec);
|
|
files.insert(isec->file);
|
|
}
|
|
}
|
|
|
|
// Parse input .debug_names sections and extract InputChunk and OutputChunk
|
|
// data. OutputChunk contains CU information, which will be needed by
|
|
// `writeTo`.
|
|
auto inputChunksPtr = std::make_unique<InputChunk[]>(files.size());
|
|
MutableArrayRef<InputChunk> inputChunks(inputChunksPtr.get(), files.size());
|
|
numChunks = files.size();
|
|
chunks = std::make_unique<OutputChunk[]>(files.size());
|
|
{
|
|
TimeTraceScope timeScope("Merge .debug_names", "parse");
|
|
parallelFor(0, files.size(), [&](size_t i) {
|
|
parseFile(files[i], inputChunks[i], chunks[i]);
|
|
});
|
|
}
|
|
|
|
// Compute section header (except unit_length), abbrev table, and entry pool.
|
|
computeHdrAndAbbrevTable(inputChunks);
|
|
uint32_t entryPoolSize;
|
|
std::tie(entryPoolSize, hdr.NameCount) = computeEntryPool(inputChunks);
|
|
hdr.BucketCount = dwarf::getDebugNamesBucketCount(hdr.NameCount);
|
|
|
|
// Compute the section size. Subtract 4 to get the unit_length for DWARF32.
|
|
uint32_t hdrSize = getDebugNamesHeaderSize(hdr.AugmentationStringSize);
|
|
size = findDebugNamesOffsets(hdrSize, hdr).EntriesBase + entryPoolSize;
|
|
hdr.UnitLength = size - 4;
|
|
}
|
|
|
|
template <class ELFT>
|
|
DebugNamesSection<ELFT>::DebugNamesSection(Ctx &ctx)
|
|
: DebugNamesBaseSection(ctx) {
|
|
init([&](InputFile *f, InputChunk &inputChunk, OutputChunk &chunk) {
|
|
auto *file = cast<ObjFile<ELFT>>(f);
|
|
DWARFContext dwarf(std::make_unique<LLDDwarfObj<ELFT>>(file));
|
|
auto &dobj = static_cast<const LLDDwarfObj<ELFT> &>(dwarf.getDWARFObj());
|
|
chunk.infoSec = dobj.getInfoSection();
|
|
DWARFDataExtractor namesExtractor(dobj, dobj.getNamesSection(),
|
|
ELFT::Endianness == endianness::little,
|
|
ELFT::Is64Bits ? 8 : 4);
|
|
// .debug_str is needed to get symbol names from string offsets.
|
|
DataExtractor strExtractor(dobj.getStrSection(),
|
|
ELFT::Endianness == endianness::little,
|
|
ELFT::Is64Bits ? 8 : 4);
|
|
inputChunk.section = dobj.getNamesSection();
|
|
|
|
inputChunk.llvmDebugNames.emplace(namesExtractor, strExtractor);
|
|
if (Error e = inputChunk.llvmDebugNames->extract()) {
|
|
Err(ctx) << dobj.getNamesSection().sec << ": " << std::move(e);
|
|
}
|
|
parseDebugNames(
|
|
ctx, inputChunk, chunk, namesExtractor, strExtractor,
|
|
[&chunk, namesData = dobj.getNamesSection().Data.data()](
|
|
uint32_t numCus, const DWARFDebugNames::Header &hdr,
|
|
const DWARFDebugNames::DWARFDebugNamesOffsets &locs) {
|
|
// Read CU offsets, which are relocated by .debug_info + X
|
|
// relocations. Record the section offset to be relocated by
|
|
// `finalizeContents`.
|
|
chunk.compUnits.resize_for_overwrite(numCus + hdr.CompUnitCount);
|
|
for (auto i : seq(hdr.CompUnitCount))
|
|
chunk.compUnits[numCus + i] = locs.CUsBase + i * 4;
|
|
|
|
// Read entry offsets.
|
|
const char *p = namesData + locs.EntryOffsetsBase;
|
|
SmallVector<uint32_t, 0> entryOffsets;
|
|
entryOffsets.resize_for_overwrite(hdr.NameCount);
|
|
for (uint32_t &offset : entryOffsets)
|
|
offset = endian::readNext<uint32_t, ELFT::Endianness, unaligned>(p);
|
|
return entryOffsets;
|
|
});
|
|
});
|
|
}
|
|
|
|
template <class ELFT>
|
|
template <class RelTy>
|
|
void DebugNamesSection<ELFT>::getNameRelocs(
|
|
const InputFile &file, DenseMap<uint32_t, uint32_t> &relocs,
|
|
Relocs<RelTy> rels) {
|
|
for (const RelTy &rel : rels) {
|
|
Symbol &sym = file.getRelocTargetSym(rel);
|
|
relocs[rel.r_offset] = sym.getVA(ctx, getAddend<ELFT>(rel));
|
|
}
|
|
}
|
|
|
|
template <class ELFT> void DebugNamesSection<ELFT>::finalizeContents() {
|
|
// Get relocations of .debug_names sections.
|
|
auto relocs = std::make_unique<DenseMap<uint32_t, uint32_t>[]>(numChunks);
|
|
parallelFor(0, numChunks, [&](size_t i) {
|
|
InputSection *sec = inputSections[i];
|
|
invokeOnRelocs(*sec, getNameRelocs, *sec->file, relocs.get()[i]);
|
|
|
|
// Relocate CU offsets with .debug_info + X relocations.
|
|
OutputChunk &chunk = chunks.get()[i];
|
|
for (auto [j, cuOffset] : enumerate(chunk.compUnits))
|
|
cuOffset = relocs.get()[i].lookup(cuOffset);
|
|
});
|
|
|
|
// Relocate string offsets in the name table with .debug_str + X relocations.
|
|
parallelForEach(nameVecs, [&](auto &nameVec) {
|
|
for (NameEntry &ne : nameVec)
|
|
ne.stringOffset = relocs.get()[ne.chunkIdx].lookup(ne.stringOffset);
|
|
});
|
|
}
|
|
|
|
template <class ELFT> void DebugNamesSection<ELFT>::writeTo(uint8_t *buf) {
|
|
[[maybe_unused]] const uint8_t *const beginBuf = buf;
|
|
// Write the header.
|
|
endian::writeNext<uint32_t, ELFT::Endianness>(buf, hdr.UnitLength);
|
|
endian::writeNext<uint16_t, ELFT::Endianness>(buf, hdr.Version);
|
|
buf += 2; // padding
|
|
endian::writeNext<uint32_t, ELFT::Endianness>(buf, hdr.CompUnitCount);
|
|
endian::writeNext<uint32_t, ELFT::Endianness>(buf, hdr.LocalTypeUnitCount);
|
|
endian::writeNext<uint32_t, ELFT::Endianness>(buf, hdr.ForeignTypeUnitCount);
|
|
endian::writeNext<uint32_t, ELFT::Endianness>(buf, hdr.BucketCount);
|
|
endian::writeNext<uint32_t, ELFT::Endianness>(buf, hdr.NameCount);
|
|
endian::writeNext<uint32_t, ELFT::Endianness>(buf, hdr.AbbrevTableSize);
|
|
endian::writeNext<uint32_t, ELFT::Endianness>(buf,
|
|
hdr.AugmentationStringSize);
|
|
memcpy(buf, hdr.AugmentationString.c_str(), hdr.AugmentationString.size());
|
|
buf += hdr.AugmentationStringSize;
|
|
|
|
// Write the CU list.
|
|
for (auto &chunk : getChunks())
|
|
for (uint32_t cuOffset : chunk.compUnits)
|
|
endian::writeNext<uint32_t, ELFT::Endianness>(buf, cuOffset);
|
|
|
|
// TODO: Write the local TU list, then the foreign TU list..
|
|
|
|
// Write the hash lookup table.
|
|
SmallVector<SmallVector<NameEntry *, 0>, 0> buckets(hdr.BucketCount);
|
|
// Symbols enter into a bucket whose index is the hash modulo bucket_count.
|
|
for (auto &nameVec : nameVecs)
|
|
for (NameEntry &ne : nameVec)
|
|
buckets[ne.hashValue % hdr.BucketCount].push_back(&ne);
|
|
|
|
// Write buckets (accumulated bucket counts).
|
|
uint32_t bucketIdx = 1;
|
|
for (const SmallVector<NameEntry *, 0> &bucket : buckets) {
|
|
if (!bucket.empty())
|
|
endian::write32<ELFT::Endianness>(buf, bucketIdx);
|
|
buf += 4;
|
|
bucketIdx += bucket.size();
|
|
}
|
|
// Write the hashes.
|
|
for (const SmallVector<NameEntry *, 0> &bucket : buckets)
|
|
for (const NameEntry *e : bucket)
|
|
endian::writeNext<uint32_t, ELFT::Endianness>(buf, e->hashValue);
|
|
|
|
// Write the name table. The name entries are ordered by bucket_idx and
|
|
// correspond one-to-one with the hash lookup table.
|
|
//
|
|
// First, write the relocated string offsets.
|
|
for (const SmallVector<NameEntry *, 0> &bucket : buckets)
|
|
for (const NameEntry *ne : bucket)
|
|
endian::writeNext<uint32_t, ELFT::Endianness>(buf, ne->stringOffset);
|
|
|
|
// Then write the entry offsets.
|
|
for (const SmallVector<NameEntry *, 0> &bucket : buckets)
|
|
for (const NameEntry *ne : bucket)
|
|
endian::writeNext<uint32_t, ELFT::Endianness>(buf, ne->entryOffset);
|
|
|
|
// Write the abbrev table.
|
|
buf = llvm::copy(abbrevTableBuf, buf);
|
|
|
|
// Write the entry pool. Unlike the name table, the name entries follow the
|
|
// nameVecs order computed by `computeEntryPool`.
|
|
for (auto &nameVec : nameVecs) {
|
|
for (NameEntry &ne : nameVec) {
|
|
// Write all the entries for the string.
|
|
for (const IndexEntry &ie : ne.entries()) {
|
|
buf += encodeULEB128(ie.abbrevCode, buf);
|
|
for (AttrValue value : ie.attrValues) {
|
|
switch (value.attrSize) {
|
|
case 1:
|
|
*buf++ = value.attrValue;
|
|
break;
|
|
case 2:
|
|
endian::writeNext<uint16_t, ELFT::Endianness>(buf, value.attrValue);
|
|
break;
|
|
case 4:
|
|
endian::writeNext<uint32_t, ELFT::Endianness>(buf, value.attrValue);
|
|
break;
|
|
default:
|
|
llvm_unreachable("invalid attrSize");
|
|
}
|
|
}
|
|
}
|
|
++buf; // index entry sentinel
|
|
}
|
|
}
|
|
assert(uint64_t(buf - beginBuf) == size);
|
|
}
|
|
|
|
GdbIndexSection::GdbIndexSection(Ctx &ctx)
|
|
: SyntheticSection(ctx, ".gdb_index", SHT_PROGBITS, 0, 1) {}
|
|
|
|
// Returns the desired size of an on-disk hash table for a .gdb_index section.
|
|
// There's a tradeoff between size and collision rate. We aim 75% utilization.
|
|
size_t GdbIndexSection::computeSymtabSize() const {
|
|
return std::max<size_t>(NextPowerOf2(symbols.size() * 4 / 3), 1024);
|
|
}
|
|
|
|
static SmallVector<GdbIndexSection::CuEntry, 0>
|
|
readCuList(DWARFContext &dwarf) {
|
|
SmallVector<GdbIndexSection::CuEntry, 0> ret;
|
|
for (std::unique_ptr<DWARFUnit> &cu : dwarf.compile_units())
|
|
ret.push_back({cu->getOffset(), cu->getLength() + 4});
|
|
return ret;
|
|
}
|
|
|
|
static SmallVector<GdbIndexSection::AddressEntry, 0>
|
|
readAddressAreas(Ctx &ctx, DWARFContext &dwarf, InputSection *sec) {
|
|
SmallVector<GdbIndexSection::AddressEntry, 0> ret;
|
|
|
|
uint32_t cuIdx = 0;
|
|
for (std::unique_ptr<DWARFUnit> &cu : dwarf.compile_units()) {
|
|
if (Error e = cu->tryExtractDIEsIfNeeded(false)) {
|
|
Warn(ctx) << sec << ": " << std::move(e);
|
|
return {};
|
|
}
|
|
Expected<DWARFAddressRangesVector> ranges = cu->collectAddressRanges();
|
|
if (!ranges) {
|
|
Warn(ctx) << sec << ": " << ranges.takeError();
|
|
return {};
|
|
}
|
|
|
|
ArrayRef<InputSectionBase *> sections = sec->file->getSections();
|
|
for (DWARFAddressRange &r : *ranges) {
|
|
if (r.SectionIndex == -1ULL)
|
|
continue;
|
|
// Range list with zero size has no effect.
|
|
InputSectionBase *s = sections[r.SectionIndex];
|
|
if (s && s != &InputSection::discarded && s->isLive())
|
|
if (r.LowPC != r.HighPC)
|
|
ret.push_back({cast<InputSection>(s), r.LowPC, r.HighPC, cuIdx});
|
|
}
|
|
++cuIdx;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
template <class ELFT>
|
|
static SmallVector<GdbIndexSection::NameAttrEntry, 0>
|
|
readPubNamesAndTypes(Ctx &ctx, const LLDDwarfObj<ELFT> &obj,
|
|
const SmallVectorImpl<GdbIndexSection::CuEntry> &cus) {
|
|
const LLDDWARFSection &pubNames = obj.getGnuPubnamesSection();
|
|
const LLDDWARFSection &pubTypes = obj.getGnuPubtypesSection();
|
|
|
|
SmallVector<GdbIndexSection::NameAttrEntry, 0> ret;
|
|
for (const LLDDWARFSection *pub : {&pubNames, &pubTypes}) {
|
|
DWARFDataExtractor data(obj, *pub, ELFT::Endianness == endianness::little,
|
|
ELFT::Is64Bits ? 8 : 4);
|
|
DWARFDebugPubTable table;
|
|
table.extract(data, /*GnuStyle=*/true, [&](Error e) {
|
|
Warn(ctx) << pub->sec << ": " << std::move(e);
|
|
});
|
|
for (const DWARFDebugPubTable::Set &set : table.getData()) {
|
|
// The value written into the constant pool is kind << 24 | cuIndex. As we
|
|
// don't know how many compilation units precede this object to compute
|
|
// cuIndex, we compute (kind << 24 | cuIndexInThisObject) instead, and add
|
|
// the number of preceding compilation units later.
|
|
uint32_t i = llvm::partition_point(cus,
|
|
[&](GdbIndexSection::CuEntry cu) {
|
|
return cu.cuOffset < set.Offset;
|
|
}) -
|
|
cus.begin();
|
|
for (const DWARFDebugPubTable::Entry &ent : set.Entries)
|
|
ret.push_back({{ent.Name, computeGdbHash(ent.Name)},
|
|
(ent.Descriptor.toBits() << 24) | i});
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
// Create a list of symbols from a given list of symbol names and types
|
|
// by uniquifying them by name.
|
|
static std::pair<SmallVector<GdbIndexSection::GdbSymbol, 0>, size_t>
|
|
createSymbols(
|
|
Ctx &ctx,
|
|
ArrayRef<SmallVector<GdbIndexSection::NameAttrEntry, 0>> nameAttrs,
|
|
const SmallVector<GdbIndexSection::GdbChunk, 0> &chunks) {
|
|
using GdbSymbol = GdbIndexSection::GdbSymbol;
|
|
using NameAttrEntry = GdbIndexSection::NameAttrEntry;
|
|
|
|
// For each chunk, compute the number of compilation units preceding it.
|
|
uint32_t cuIdx = 0;
|
|
std::unique_ptr<uint32_t[]> cuIdxs(new uint32_t[chunks.size()]);
|
|
for (uint32_t i = 0, e = chunks.size(); i != e; ++i) {
|
|
cuIdxs[i] = cuIdx;
|
|
cuIdx += chunks[i].compilationUnits.size();
|
|
}
|
|
|
|
// Collect the compilation unitss for each unique name. Speed it up using
|
|
// multi-threading as the number of symbols can be in the order of millions.
|
|
// Shard GdbSymbols by hash's high bits.
|
|
constexpr size_t numShards = 32;
|
|
const size_t concurrency =
|
|
llvm::bit_floor(std::min<size_t>(ctx.arg.threadCount, numShards));
|
|
const size_t shift = 32 - llvm::countr_zero(numShards);
|
|
auto map =
|
|
std::make_unique<DenseMap<CachedHashStringRef, size_t>[]>(numShards);
|
|
auto symbols = std::make_unique<SmallVector<GdbSymbol, 0>[]>(numShards);
|
|
parallelFor(0, concurrency, [&](size_t threadId) {
|
|
uint32_t i = 0;
|
|
for (ArrayRef<NameAttrEntry> entries : nameAttrs) {
|
|
for (const NameAttrEntry &ent : entries) {
|
|
size_t shardId = ent.name.hash() >> shift;
|
|
if ((shardId & (concurrency - 1)) != threadId)
|
|
continue;
|
|
|
|
uint32_t v = ent.cuIndexAndAttrs + cuIdxs[i];
|
|
auto [it, inserted] =
|
|
map[shardId].try_emplace(ent.name, symbols[shardId].size());
|
|
if (inserted)
|
|
symbols[shardId].push_back({ent.name, {v}, 0, 0});
|
|
else
|
|
symbols[shardId][it->second].cuVector.push_back(v);
|
|
}
|
|
++i;
|
|
}
|
|
});
|
|
|
|
size_t numSymbols = 0;
|
|
for (ArrayRef<GdbSymbol> v : ArrayRef(symbols.get(), numShards))
|
|
numSymbols += v.size();
|
|
|
|
// The return type is a flattened vector, so we'll copy each vector
|
|
// contents to Ret.
|
|
SmallVector<GdbSymbol, 0> ret;
|
|
ret.reserve(numSymbols);
|
|
for (SmallVector<GdbSymbol, 0> &vec :
|
|
MutableArrayRef(symbols.get(), numShards))
|
|
for (GdbSymbol &sym : vec)
|
|
ret.push_back(std::move(sym));
|
|
|
|
// CU vectors and symbol names are adjacent in the output file.
|
|
// We can compute their offsets in the output file now.
|
|
size_t off = 0;
|
|
for (GdbSymbol &sym : ret) {
|
|
sym.cuVectorOff = off;
|
|
off += (sym.cuVector.size() + 1) * 4;
|
|
}
|
|
for (GdbSymbol &sym : ret) {
|
|
sym.nameOff = off;
|
|
off += sym.name.size() + 1;
|
|
}
|
|
// If off overflows, the last symbol's nameOff likely overflows.
|
|
if (!isUInt<32>(off))
|
|
Err(ctx) << "--gdb-index: constant pool size (" << off
|
|
<< ") exceeds UINT32_MAX";
|
|
|
|
return {ret, off};
|
|
}
|
|
|
|
// Returns a newly-created .gdb_index section.
|
|
template <class ELFT>
|
|
std::unique_ptr<GdbIndexSection> GdbIndexSection::create(Ctx &ctx) {
|
|
llvm::TimeTraceScope timeScope("Create gdb index");
|
|
|
|
// Collect InputFiles with .debug_info. See the comment in
|
|
// LLDDwarfObj<ELFT>::LLDDwarfObj. If we do lightweight parsing in the future,
|
|
// note that isec->data() may uncompress the full content, which should be
|
|
// parallelized.
|
|
SetVector<InputFile *> files;
|
|
for (InputSectionBase *s : ctx.inputSections) {
|
|
InputSection *isec = dyn_cast<InputSection>(s);
|
|
if (!isec)
|
|
continue;
|
|
// .debug_gnu_pub{names,types} are useless in executables.
|
|
// They are present in input object files solely for creating
|
|
// a .gdb_index. So we can remove them from the output.
|
|
if (s->name == ".debug_gnu_pubnames" || s->name == ".debug_gnu_pubtypes")
|
|
s->markDead();
|
|
else if (isec->name == ".debug_info")
|
|
files.insert(isec->file);
|
|
}
|
|
// Drop .rel[a].debug_gnu_pub{names,types} for --emit-relocs.
|
|
llvm::erase_if(ctx.inputSections, [](InputSectionBase *s) {
|
|
if (auto *isec = dyn_cast<InputSection>(s))
|
|
if (InputSectionBase *rel = isec->getRelocatedSection())
|
|
return !rel->isLive();
|
|
return !s->isLive();
|
|
});
|
|
|
|
SmallVector<GdbChunk, 0> chunks(files.size());
|
|
SmallVector<SmallVector<NameAttrEntry, 0>, 0> nameAttrs(files.size());
|
|
|
|
parallelFor(0, files.size(), [&](size_t i) {
|
|
// To keep memory usage low, we don't want to keep cached DWARFContext, so
|
|
// avoid getDwarf() here.
|
|
ObjFile<ELFT> *file = cast<ObjFile<ELFT>>(files[i]);
|
|
DWARFContext dwarf(std::make_unique<LLDDwarfObj<ELFT>>(file));
|
|
auto &dobj = static_cast<const LLDDwarfObj<ELFT> &>(dwarf.getDWARFObj());
|
|
|
|
// If the are multiple compile units .debug_info (very rare ld -r --unique),
|
|
// this only picks the last one. Other address ranges are lost.
|
|
chunks[i].sec = dobj.getInfoSection();
|
|
chunks[i].compilationUnits = readCuList(dwarf);
|
|
chunks[i].addressAreas = readAddressAreas(ctx, dwarf, chunks[i].sec);
|
|
nameAttrs[i] =
|
|
readPubNamesAndTypes<ELFT>(ctx, dobj, chunks[i].compilationUnits);
|
|
});
|
|
|
|
auto ret = std::make_unique<GdbIndexSection>(ctx);
|
|
ret->chunks = std::move(chunks);
|
|
std::tie(ret->symbols, ret->size) =
|
|
createSymbols(ctx, nameAttrs, ret->chunks);
|
|
|
|
// Count the areas other than the constant pool.
|
|
ret->size += sizeof(GdbIndexHeader) + ret->computeSymtabSize() * 8;
|
|
for (GdbChunk &chunk : ret->chunks)
|
|
ret->size +=
|
|
chunk.compilationUnits.size() * 16 + chunk.addressAreas.size() * 20;
|
|
|
|
return ret;
|
|
}
|
|
|
|
void GdbIndexSection::writeTo(uint8_t *buf) {
|
|
// Write the header.
|
|
auto *hdr = reinterpret_cast<GdbIndexHeader *>(buf);
|
|
uint8_t *start = buf;
|
|
hdr->version = 7;
|
|
buf += sizeof(*hdr);
|
|
|
|
// Write the CU list.
|
|
hdr->cuListOff = buf - start;
|
|
for (GdbChunk &chunk : chunks) {
|
|
for (CuEntry &cu : chunk.compilationUnits) {
|
|
write64le(buf, chunk.sec->outSecOff + cu.cuOffset);
|
|
write64le(buf + 8, cu.cuLength);
|
|
buf += 16;
|
|
}
|
|
}
|
|
|
|
// Write the address area.
|
|
hdr->cuTypesOff = buf - start;
|
|
hdr->addressAreaOff = buf - start;
|
|
uint32_t cuOff = 0;
|
|
for (GdbChunk &chunk : chunks) {
|
|
for (AddressEntry &e : chunk.addressAreas) {
|
|
// In the case of ICF there may be duplicate address range entries.
|
|
const uint64_t baseAddr = e.section->repl->getVA(0);
|
|
write64le(buf, baseAddr + e.lowAddress);
|
|
write64le(buf + 8, baseAddr + e.highAddress);
|
|
write32le(buf + 16, e.cuIndex + cuOff);
|
|
buf += 20;
|
|
}
|
|
cuOff += chunk.compilationUnits.size();
|
|
}
|
|
|
|
// Write the on-disk open-addressing hash table containing symbols.
|
|
hdr->symtabOff = buf - start;
|
|
size_t symtabSize = computeSymtabSize();
|
|
uint32_t mask = symtabSize - 1;
|
|
|
|
for (GdbSymbol &sym : symbols) {
|
|
uint32_t h = sym.name.hash();
|
|
uint32_t i = h & mask;
|
|
uint32_t step = ((h * 17) & mask) | 1;
|
|
|
|
while (read32le(buf + i * 8))
|
|
i = (i + step) & mask;
|
|
|
|
write32le(buf + i * 8, sym.nameOff);
|
|
write32le(buf + i * 8 + 4, sym.cuVectorOff);
|
|
}
|
|
|
|
buf += symtabSize * 8;
|
|
|
|
// Write the string pool.
|
|
hdr->constantPoolOff = buf - start;
|
|
parallelForEach(symbols, [&](GdbSymbol &sym) {
|
|
memcpy(buf + sym.nameOff, sym.name.data(), sym.name.size());
|
|
});
|
|
|
|
// Write the CU vectors.
|
|
for (GdbSymbol &sym : symbols) {
|
|
write32le(buf, sym.cuVector.size());
|
|
buf += 4;
|
|
for (uint32_t val : sym.cuVector) {
|
|
write32le(buf, val);
|
|
buf += 4;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool GdbIndexSection::isNeeded() const { return !chunks.empty(); }
|
|
|
|
EhFrameHeader::EhFrameHeader(Ctx &ctx)
|
|
: SyntheticSection(ctx, ".eh_frame_hdr", SHT_PROGBITS, SHF_ALLOC, 4) {}
|
|
|
|
void EhFrameHeader::writeTo(uint8_t *buf) {
|
|
// Unlike most sections, the EhFrameHeader section is written while writing
|
|
// another section, namely EhFrameSection, which calls the write() function
|
|
// below from its writeTo() function. This is necessary because the contents
|
|
// of EhFrameHeader depend on the relocated contents of EhFrameSection and we
|
|
// don't know which order the sections will be written in.
|
|
}
|
|
|
|
// .eh_frame_hdr contains a binary search table of pointers to FDEs.
|
|
// Each entry of the search table consists of two values,
|
|
// the starting PC from where FDEs covers, and the FDE's address.
|
|
// It is sorted by PC.
|
|
void EhFrameHeader::write() {
|
|
uint8_t *buf = ctx.bufferStart + getParent()->offset + outSecOff;
|
|
using FdeData = EhFrameSection::FdeData;
|
|
SmallVector<FdeData, 0> fdes = getPartition(ctx).ehFrame->getFdeData();
|
|
|
|
buf[0] = 1;
|
|
buf[1] = DW_EH_PE_pcrel | DW_EH_PE_sdata4;
|
|
buf[2] = DW_EH_PE_udata4;
|
|
buf[3] = DW_EH_PE_datarel | DW_EH_PE_sdata4;
|
|
write32(ctx, buf + 4,
|
|
getPartition(ctx).ehFrame->getParent()->addr - this->getVA() - 4);
|
|
write32(ctx, buf + 8, fdes.size());
|
|
buf += 12;
|
|
|
|
for (FdeData &fde : fdes) {
|
|
write32(ctx, buf, fde.pcRel);
|
|
write32(ctx, buf + 4, fde.fdeVARel);
|
|
buf += 8;
|
|
}
|
|
}
|
|
|
|
size_t EhFrameHeader::getSize() const {
|
|
// .eh_frame_hdr has a 12 bytes header followed by an array of FDEs.
|
|
return 12 + getPartition(ctx).ehFrame->numFdes * 8;
|
|
}
|
|
|
|
bool EhFrameHeader::isNeeded() const {
|
|
return isLive() && getPartition(ctx).ehFrame->isNeeded();
|
|
}
|
|
|
|
VersionDefinitionSection::VersionDefinitionSection(Ctx &ctx)
|
|
: SyntheticSection(ctx, ".gnu.version_d", SHT_GNU_verdef, SHF_ALLOC,
|
|
sizeof(uint32_t)) {}
|
|
|
|
StringRef VersionDefinitionSection::getFileDefName() {
|
|
if (!getPartition(ctx).name.empty())
|
|
return getPartition(ctx).name;
|
|
if (!ctx.arg.soName.empty())
|
|
return ctx.arg.soName;
|
|
return ctx.arg.outputFile;
|
|
}
|
|
|
|
void VersionDefinitionSection::finalizeContents() {
|
|
fileDefNameOff = getPartition(ctx).dynStrTab->addString(getFileDefName());
|
|
for (const VersionDefinition &v : namedVersionDefs(ctx))
|
|
verDefNameOffs.push_back(getPartition(ctx).dynStrTab->addString(v.name));
|
|
|
|
if (OutputSection *sec = getPartition(ctx).dynStrTab->getParent())
|
|
getParent()->link = sec->sectionIndex;
|
|
|
|
// sh_info should be set to the number of definitions. This fact is missed in
|
|
// documentation, but confirmed by binutils community:
|
|
// https://sourceware.org/ml/binutils/2014-11/msg00355.html
|
|
getParent()->info = getVerDefNum(ctx);
|
|
}
|
|
|
|
void VersionDefinitionSection::writeOne(uint8_t *buf, uint32_t index,
|
|
StringRef name, size_t nameOff) {
|
|
uint16_t flags = index == 1 ? VER_FLG_BASE : 0;
|
|
|
|
// Write a verdef.
|
|
write16(ctx, buf, 1); // vd_version
|
|
write16(ctx, buf + 2, flags); // vd_flags
|
|
write16(ctx, buf + 4, index); // vd_ndx
|
|
write16(ctx, buf + 6, 1); // vd_cnt
|
|
write32(ctx, buf + 8, hashSysV(name)); // vd_hash
|
|
write32(ctx, buf + 12, 20); // vd_aux
|
|
write32(ctx, buf + 16, 28); // vd_next
|
|
|
|
// Write a veraux.
|
|
write32(ctx, buf + 20, nameOff); // vda_name
|
|
write32(ctx, buf + 24, 0); // vda_next
|
|
}
|
|
|
|
void VersionDefinitionSection::writeTo(uint8_t *buf) {
|
|
writeOne(buf, 1, getFileDefName(), fileDefNameOff);
|
|
|
|
auto nameOffIt = verDefNameOffs.begin();
|
|
for (const VersionDefinition &v : namedVersionDefs(ctx)) {
|
|
buf += EntrySize;
|
|
writeOne(buf, v.id, v.name, *nameOffIt++);
|
|
}
|
|
|
|
// Need to terminate the last version definition.
|
|
write32(ctx, buf + 16, 0); // vd_next
|
|
}
|
|
|
|
size_t VersionDefinitionSection::getSize() const {
|
|
return EntrySize * getVerDefNum(ctx);
|
|
}
|
|
|
|
// .gnu.version is a table where each entry is 2 byte long.
|
|
VersionTableSection::VersionTableSection(Ctx &ctx)
|
|
: SyntheticSection(ctx, ".gnu.version", SHT_GNU_versym, SHF_ALLOC,
|
|
sizeof(uint16_t)) {
|
|
this->entsize = 2;
|
|
}
|
|
|
|
void VersionTableSection::finalizeContents() {
|
|
if (OutputSection *osec = getPartition(ctx).dynSymTab->getParent())
|
|
getParent()->link = osec->sectionIndex;
|
|
}
|
|
|
|
size_t VersionTableSection::getSize() const {
|
|
return (getPartition(ctx).dynSymTab->getSymbols().size() + 1) * 2;
|
|
}
|
|
|
|
void VersionTableSection::writeTo(uint8_t *buf) {
|
|
buf += 2;
|
|
for (const SymbolTableEntry &s : getPartition(ctx).dynSymTab->getSymbols()) {
|
|
// For an unextracted lazy symbol (undefined weak), it must have been
|
|
// converted to Undefined and have VER_NDX_GLOBAL version here.
|
|
assert(!s.sym->isLazy());
|
|
write16(ctx, buf, s.sym->versionId);
|
|
buf += 2;
|
|
}
|
|
}
|
|
|
|
bool VersionTableSection::isNeeded() const {
|
|
return isLive() &&
|
|
(getPartition(ctx).verDef || getPartition(ctx).verNeed->isNeeded());
|
|
}
|
|
|
|
void elf::addVerneed(Ctx &ctx, Symbol &ss) {
|
|
auto &file = cast<SharedFile>(*ss.file);
|
|
if (ss.versionId == VER_NDX_GLOBAL)
|
|
return;
|
|
|
|
if (file.vernauxs.empty())
|
|
file.vernauxs.resize(file.verdefs.size());
|
|
|
|
// Select a version identifier for the vernaux data structure, if we haven't
|
|
// already allocated one. The verdef identifiers cover the range
|
|
// [1..getVerDefNum(ctx)]; this causes the vernaux identifiers to start from
|
|
// getVerDefNum(ctx)+1.
|
|
if (file.vernauxs[ss.versionId] == 0)
|
|
file.vernauxs[ss.versionId] = ++ctx.vernauxNum + getVerDefNum(ctx);
|
|
|
|
ss.versionId = file.vernauxs[ss.versionId];
|
|
}
|
|
|
|
template <class ELFT>
|
|
VersionNeedSection<ELFT>::VersionNeedSection(Ctx &ctx)
|
|
: SyntheticSection(ctx, ".gnu.version_r", SHT_GNU_verneed, SHF_ALLOC,
|
|
sizeof(uint32_t)) {}
|
|
|
|
template <class ELFT> void VersionNeedSection<ELFT>::finalizeContents() {
|
|
for (SharedFile *f : ctx.sharedFiles) {
|
|
if (f->vernauxs.empty())
|
|
continue;
|
|
verneeds.emplace_back();
|
|
Verneed &vn = verneeds.back();
|
|
vn.nameStrTab = getPartition(ctx).dynStrTab->addString(f->soName);
|
|
bool isLibc = ctx.arg.relrGlibc && f->soName.starts_with("libc.so.");
|
|
bool isGlibc2 = false;
|
|
for (unsigned i = 0; i != f->vernauxs.size(); ++i) {
|
|
if (f->vernauxs[i] == 0)
|
|
continue;
|
|
auto *verdef =
|
|
reinterpret_cast<const typename ELFT::Verdef *>(f->verdefs[i]);
|
|
StringRef ver(f->getStringTable().data() + verdef->getAux()->vda_name);
|
|
if (isLibc && ver.starts_with("GLIBC_2."))
|
|
isGlibc2 = true;
|
|
vn.vernauxs.push_back({verdef->vd_hash, f->vernauxs[i],
|
|
getPartition(ctx).dynStrTab->addString(ver)});
|
|
}
|
|
if (isGlibc2) {
|
|
const char *ver = "GLIBC_ABI_DT_RELR";
|
|
vn.vernauxs.push_back({hashSysV(ver),
|
|
++ctx.vernauxNum + getVerDefNum(ctx),
|
|
getPartition(ctx).dynStrTab->addString(ver)});
|
|
}
|
|
}
|
|
|
|
if (OutputSection *sec = getPartition(ctx).dynStrTab->getParent())
|
|
getParent()->link = sec->sectionIndex;
|
|
getParent()->info = verneeds.size();
|
|
}
|
|
|
|
template <class ELFT> void VersionNeedSection<ELFT>::writeTo(uint8_t *buf) {
|
|
// The Elf_Verneeds need to appear first, followed by the Elf_Vernauxs.
|
|
auto *verneed = reinterpret_cast<Elf_Verneed *>(buf);
|
|
auto *vernaux = reinterpret_cast<Elf_Vernaux *>(verneed + verneeds.size());
|
|
|
|
for (auto &vn : verneeds) {
|
|
// Create an Elf_Verneed for this DSO.
|
|
verneed->vn_version = 1;
|
|
verneed->vn_cnt = vn.vernauxs.size();
|
|
verneed->vn_file = vn.nameStrTab;
|
|
verneed->vn_aux =
|
|
reinterpret_cast<char *>(vernaux) - reinterpret_cast<char *>(verneed);
|
|
verneed->vn_next = sizeof(Elf_Verneed);
|
|
++verneed;
|
|
|
|
// Create the Elf_Vernauxs for this Elf_Verneed.
|
|
for (auto &vna : vn.vernauxs) {
|
|
vernaux->vna_hash = vna.hash;
|
|
vernaux->vna_flags = 0;
|
|
vernaux->vna_other = vna.verneedIndex;
|
|
vernaux->vna_name = vna.nameStrTab;
|
|
vernaux->vna_next = sizeof(Elf_Vernaux);
|
|
++vernaux;
|
|
}
|
|
|
|
vernaux[-1].vna_next = 0;
|
|
}
|
|
verneed[-1].vn_next = 0;
|
|
}
|
|
|
|
template <class ELFT> size_t VersionNeedSection<ELFT>::getSize() const {
|
|
return verneeds.size() * sizeof(Elf_Verneed) +
|
|
ctx.vernauxNum * sizeof(Elf_Vernaux);
|
|
}
|
|
|
|
template <class ELFT> bool VersionNeedSection<ELFT>::isNeeded() const {
|
|
return isLive() && ctx.vernauxNum != 0;
|
|
}
|
|
|
|
void MergeSyntheticSection::addSection(MergeInputSection *ms) {
|
|
ms->parent = this;
|
|
sections.push_back(ms);
|
|
assert(addralign == ms->addralign || !(ms->flags & SHF_STRINGS));
|
|
addralign = std::max(addralign, ms->addralign);
|
|
}
|
|
|
|
MergeTailSection::MergeTailSection(Ctx &ctx, StringRef name, uint32_t type,
|
|
uint64_t flags, uint32_t alignment)
|
|
: MergeSyntheticSection(ctx, name, type, flags, alignment),
|
|
builder(StringTableBuilder::RAW, llvm::Align(alignment)) {}
|
|
|
|
size_t MergeTailSection::getSize() const { return builder.getSize(); }
|
|
|
|
void MergeTailSection::writeTo(uint8_t *buf) { builder.write(buf); }
|
|
|
|
void MergeTailSection::finalizeContents() {
|
|
// Add all string pieces to the string table builder to create section
|
|
// contents.
|
|
for (MergeInputSection *sec : sections)
|
|
for (size_t i = 0, e = sec->pieces.size(); i != e; ++i)
|
|
if (sec->pieces[i].live)
|
|
builder.add(sec->getData(i));
|
|
|
|
// Fix the string table content. After this, the contents will never change.
|
|
builder.finalize();
|
|
|
|
// finalize() fixed tail-optimized strings, so we can now get
|
|
// offsets of strings. Get an offset for each string and save it
|
|
// to a corresponding SectionPiece for easy access.
|
|
for (MergeInputSection *sec : sections)
|
|
for (size_t i = 0, e = sec->pieces.size(); i != e; ++i)
|
|
if (sec->pieces[i].live)
|
|
sec->pieces[i].outputOff = builder.getOffset(sec->getData(i));
|
|
}
|
|
|
|
void MergeNoTailSection::writeTo(uint8_t *buf) {
|
|
parallelFor(0, numShards,
|
|
[&](size_t i) { shards[i].write(buf + shardOffsets[i]); });
|
|
}
|
|
|
|
// This function is very hot (i.e. it can take several seconds to finish)
|
|
// because sometimes the number of inputs is in an order of magnitude of
|
|
// millions. So, we use multi-threading.
|
|
//
|
|
// For any strings S and T, we know S is not mergeable with T if S's hash
|
|
// value is different from T's. If that's the case, we can safely put S and
|
|
// T into different string builders without worrying about merge misses.
|
|
// We do it in parallel.
|
|
void MergeNoTailSection::finalizeContents() {
|
|
// Initializes string table builders.
|
|
for (size_t i = 0; i < numShards; ++i)
|
|
shards.emplace_back(StringTableBuilder::RAW, llvm::Align(addralign));
|
|
|
|
// Concurrency level. Must be a power of 2 to avoid expensive modulo
|
|
// operations in the following tight loop.
|
|
const size_t concurrency =
|
|
llvm::bit_floor(std::min<size_t>(ctx.arg.threadCount, numShards));
|
|
|
|
// Add section pieces to the builders.
|
|
parallelFor(0, concurrency, [&](size_t threadId) {
|
|
for (MergeInputSection *sec : sections) {
|
|
for (size_t i = 0, e = sec->pieces.size(); i != e; ++i) {
|
|
if (!sec->pieces[i].live)
|
|
continue;
|
|
size_t shardId = getShardId(sec->pieces[i].hash);
|
|
if ((shardId & (concurrency - 1)) == threadId)
|
|
sec->pieces[i].outputOff = shards[shardId].add(sec->getData(i));
|
|
}
|
|
}
|
|
});
|
|
|
|
// Compute an in-section offset for each shard.
|
|
size_t off = 0;
|
|
for (size_t i = 0; i < numShards; ++i) {
|
|
shards[i].finalizeInOrder();
|
|
if (shards[i].getSize() > 0)
|
|
off = alignToPowerOf2(off, addralign);
|
|
shardOffsets[i] = off;
|
|
off += shards[i].getSize();
|
|
}
|
|
size = off;
|
|
|
|
// So far, section pieces have offsets from beginning of shards, but
|
|
// we want offsets from beginning of the whole section. Fix them.
|
|
parallelForEach(sections, [&](MergeInputSection *sec) {
|
|
for (size_t i = 0, e = sec->pieces.size(); i != e; ++i)
|
|
if (sec->pieces[i].live)
|
|
sec->pieces[i].outputOff +=
|
|
shardOffsets[getShardId(sec->pieces[i].hash)];
|
|
});
|
|
}
|
|
|
|
template <class ELFT> void elf::splitSections(Ctx &ctx) {
|
|
llvm::TimeTraceScope timeScope("Split sections");
|
|
// splitIntoPieces needs to be called on each MergeInputSection
|
|
// before calling finalizeContents().
|
|
parallelForEach(ctx.objectFiles, [](ELFFileBase *file) {
|
|
for (InputSectionBase *sec : file->getSections()) {
|
|
if (!sec)
|
|
continue;
|
|
if (auto *s = dyn_cast<MergeInputSection>(sec))
|
|
s->splitIntoPieces();
|
|
else if (auto *eh = dyn_cast<EhInputSection>(sec))
|
|
eh->split<ELFT>();
|
|
}
|
|
});
|
|
}
|
|
|
|
void elf::combineEhSections(Ctx &ctx) {
|
|
llvm::TimeTraceScope timeScope("Combine EH sections");
|
|
for (EhInputSection *sec : ctx.ehInputSections) {
|
|
EhFrameSection &eh = *sec->getPartition(ctx).ehFrame;
|
|
sec->parent = &eh;
|
|
eh.addralign = std::max(eh.addralign, sec->addralign);
|
|
eh.sections.push_back(sec);
|
|
llvm::append_range(eh.dependentSections, sec->dependentSections);
|
|
}
|
|
|
|
if (!ctx.mainPart->armExidx)
|
|
return;
|
|
llvm::erase_if(ctx.inputSections, [&](InputSectionBase *s) {
|
|
// Ignore dead sections and the partition end marker (.part.end),
|
|
// whose partition number is out of bounds.
|
|
if (!s->isLive() || s->partition == 255)
|
|
return false;
|
|
Partition &part = s->getPartition(ctx);
|
|
return s->kind() == SectionBase::Regular && part.armExidx &&
|
|
part.armExidx->addSection(cast<InputSection>(s));
|
|
});
|
|
}
|
|
|
|
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) {}
|
|
|
|
static InputSection *findExidxSection(InputSection *isec) {
|
|
for (InputSection *d : isec->dependentSections)
|
|
if (d->type == SHT_ARM_EXIDX && d->isLive())
|
|
return d;
|
|
return nullptr;
|
|
}
|
|
|
|
static bool isValidExidxSectionDep(InputSection *isec) {
|
|
return (isec->flags & SHF_ALLOC) && (isec->flags & SHF_EXECINSTR) &&
|
|
isec->getSize() > 0;
|
|
}
|
|
|
|
bool ARMExidxSyntheticSection::addSection(InputSection *isec) {
|
|
if (isec->type == SHT_ARM_EXIDX) {
|
|
if (InputSection *dep = isec->getLinkOrderDep())
|
|
if (isValidExidxSectionDep(dep)) {
|
|
exidxSections.push_back(isec);
|
|
// Every exidxSection is 8 bytes, we need an estimate of
|
|
// size before assignAddresses can be called. Final size
|
|
// will only be known after finalize is called.
|
|
size += 8;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
if (isValidExidxSectionDep(isec)) {
|
|
executableSections.push_back(isec);
|
|
return false;
|
|
}
|
|
|
|
// FIXME: we do not output a relocation section when --emit-relocs is used
|
|
// as we do not have relocation sections for linker generated table entries
|
|
// and we would have to erase at a late stage relocations from merged entries.
|
|
// Given that exception tables are already position independent and a binary
|
|
// analyzer could derive the relocations we choose to erase the relocations.
|
|
if (ctx.arg.emitRelocs && isec->type == SHT_REL)
|
|
if (InputSectionBase *ex = isec->getRelocatedSection())
|
|
if (isa<InputSection>(ex) && ex->type == SHT_ARM_EXIDX)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
// References to .ARM.Extab Sections have bit 31 clear and are not the
|
|
// special EXIDX_CANTUNWIND bit-pattern.
|
|
static bool isExtabRef(uint32_t unwind) {
|
|
return (unwind & 0x80000000) == 0 && unwind != 0x1;
|
|
}
|
|
|
|
// Return true if the .ARM.exidx section Cur can be merged into the .ARM.exidx
|
|
// section Prev, where Cur follows Prev in the table. This can be done if the
|
|
// unwinding instructions in Cur are identical to Prev. Linker generated
|
|
// EXIDX_CANTUNWIND entries are represented by nullptr as they do not have an
|
|
// InputSection.
|
|
static bool isDuplicateArmExidxSec(Ctx &ctx, InputSection *prev,
|
|
InputSection *cur) {
|
|
// Get the last table Entry from the previous .ARM.exidx section. If Prev is
|
|
// nullptr then it will be a synthesized EXIDX_CANTUNWIND entry.
|
|
uint32_t prevUnwind = 1;
|
|
if (prev)
|
|
prevUnwind =
|
|
read32(ctx, prev->content().data() + prev->content().size() - 4);
|
|
if (isExtabRef(prevUnwind))
|
|
return false;
|
|
|
|
// We consider the unwind instructions of an .ARM.exidx table entry
|
|
// a duplicate if the previous unwind instructions if:
|
|
// - Both are the special EXIDX_CANTUNWIND.
|
|
// - Both are the same inline unwind instructions.
|
|
// We do not attempt to follow and check links into .ARM.extab tables as
|
|
// consecutive identical entries are rare and the effort to check that they
|
|
// are identical is high.
|
|
|
|
// If Cur is nullptr then this is synthesized EXIDX_CANTUNWIND entry.
|
|
if (cur == nullptr)
|
|
return prevUnwind == 1;
|
|
|
|
for (uint32_t offset = 4; offset < (uint32_t)cur->content().size(); offset +=8) {
|
|
uint32_t curUnwind = read32(ctx, cur->content().data() + offset);
|
|
if (isExtabRef(curUnwind) || curUnwind != prevUnwind)
|
|
return false;
|
|
}
|
|
// All table entries in this .ARM.exidx Section can be merged into the
|
|
// previous Section.
|
|
return true;
|
|
}
|
|
|
|
// The .ARM.exidx table must be sorted in ascending order of the address of the
|
|
// functions the table describes. std::optionally duplicate adjacent table
|
|
// entries can be removed. At the end of the function the executableSections
|
|
// must be sorted in ascending order of address, Sentinel is set to the
|
|
// InputSection with the highest address and any InputSections that have
|
|
// mergeable .ARM.exidx table entries are removed from it.
|
|
void ARMExidxSyntheticSection::finalizeContents() {
|
|
// Ensure that any fixed-point iterations after the first see the original set
|
|
// of sections.
|
|
if (!originalExecutableSections.empty())
|
|
executableSections = originalExecutableSections;
|
|
else if (ctx.arg.enableNonContiguousRegions)
|
|
originalExecutableSections = executableSections;
|
|
|
|
// The executableSections and exidxSections that we use to derive the final
|
|
// contents of this SyntheticSection are populated before
|
|
// processSectionCommands() and ICF. A /DISCARD/ entry in SECTIONS command or
|
|
// ICF may remove executable InputSections and their dependent .ARM.exidx
|
|
// section that we recorded earlier.
|
|
auto isDiscarded = [](const InputSection *isec) { return !isec->isLive(); };
|
|
llvm::erase_if(exidxSections, isDiscarded);
|
|
// We need to remove discarded InputSections and InputSections without
|
|
// .ARM.exidx sections that if we generated the .ARM.exidx it would be out
|
|
// of range.
|
|
auto isDiscardedOrOutOfRange = [this](InputSection *isec) {
|
|
if (!isec->isLive())
|
|
return true;
|
|
if (findExidxSection(isec))
|
|
return false;
|
|
int64_t off = static_cast<int64_t>(isec->getVA() - getVA());
|
|
return off != llvm::SignExtend64(off, 31);
|
|
};
|
|
llvm::erase_if(executableSections, isDiscardedOrOutOfRange);
|
|
|
|
// Sort the executable sections that may or may not have associated
|
|
// .ARM.exidx sections by order of ascending address. This requires the
|
|
// relative positions of InputSections and OutputSections to be known.
|
|
auto compareByFilePosition = [](const InputSection *a,
|
|
const InputSection *b) {
|
|
OutputSection *aOut = a->getParent();
|
|
OutputSection *bOut = b->getParent();
|
|
|
|
if (aOut != bOut)
|
|
return aOut->addr < bOut->addr;
|
|
return a->outSecOff < b->outSecOff;
|
|
};
|
|
llvm::stable_sort(executableSections, compareByFilePosition);
|
|
sentinel = executableSections.back();
|
|
// std::optionally merge adjacent duplicate entries.
|
|
if (ctx.arg.mergeArmExidx) {
|
|
SmallVector<InputSection *, 0> selectedSections;
|
|
selectedSections.reserve(executableSections.size());
|
|
selectedSections.push_back(executableSections[0]);
|
|
size_t prev = 0;
|
|
for (size_t i = 1; i < executableSections.size(); ++i) {
|
|
InputSection *ex1 = findExidxSection(executableSections[prev]);
|
|
InputSection *ex2 = findExidxSection(executableSections[i]);
|
|
if (!isDuplicateArmExidxSec(ctx, ex1, ex2)) {
|
|
selectedSections.push_back(executableSections[i]);
|
|
prev = i;
|
|
}
|
|
}
|
|
executableSections = std::move(selectedSections);
|
|
}
|
|
// offset is within the SyntheticSection.
|
|
size_t offset = 0;
|
|
size = 0;
|
|
for (InputSection *isec : executableSections) {
|
|
if (InputSection *d = findExidxSection(isec)) {
|
|
d->outSecOff = offset;
|
|
d->parent = getParent();
|
|
offset += d->getSize();
|
|
} else {
|
|
offset += 8;
|
|
}
|
|
}
|
|
// Size includes Sentinel.
|
|
size = offset + 8;
|
|
}
|
|
|
|
InputSection *ARMExidxSyntheticSection::getLinkOrderDep() const {
|
|
return executableSections.front();
|
|
}
|
|
|
|
// To write the .ARM.exidx table from the ExecutableSections we have three cases
|
|
// 1.) The InputSection has a .ARM.exidx InputSection in its dependent sections.
|
|
// We write the .ARM.exidx section contents and apply its relocations.
|
|
// 2.) The InputSection does not have a dependent .ARM.exidx InputSection. We
|
|
// must write the contents of an EXIDX_CANTUNWIND directly. We use the
|
|
// start of the InputSection as the purpose of the linker generated
|
|
// section is to terminate the address range of the previous entry.
|
|
// 3.) A trailing EXIDX_CANTUNWIND sentinel section is required at the end of
|
|
// the table to terminate the address range of the final entry.
|
|
void ARMExidxSyntheticSection::writeTo(uint8_t *buf) {
|
|
|
|
// A linker generated CANTUNWIND entry is made up of two words:
|
|
// 0x0 with R_ARM_PREL31 relocation to target.
|
|
// 0x1 with EXIDX_CANTUNWIND.
|
|
uint64_t offset = 0;
|
|
for (InputSection *isec : executableSections) {
|
|
assert(isec->getParent() != nullptr);
|
|
if (InputSection *d = findExidxSection(isec)) {
|
|
for (int dataOffset = 0; dataOffset != (int)d->content().size();
|
|
dataOffset += 4)
|
|
write32(ctx, buf + offset + dataOffset,
|
|
read32(ctx, d->content().data() + dataOffset));
|
|
// Recalculate outSecOff as finalizeAddressDependentContent()
|
|
// may have altered syntheticSection outSecOff.
|
|
d->outSecOff = offset + outSecOff;
|
|
ctx.target->relocateAlloc(*d, buf + offset);
|
|
offset += d->getSize();
|
|
} else {
|
|
// A Linker generated CANTUNWIND section.
|
|
write32(ctx, buf + offset + 0, 0x0);
|
|
write32(ctx, buf + offset + 4, 0x1);
|
|
uint64_t s = isec->getVA();
|
|
uint64_t p = getVA() + offset;
|
|
ctx.target->relocateNoSym(buf + offset, R_ARM_PREL31, s - p);
|
|
offset += 8;
|
|
}
|
|
}
|
|
// Write Sentinel CANTUNWIND entry.
|
|
write32(ctx, buf + offset + 0, 0x0);
|
|
write32(ctx, buf + offset + 4, 0x1);
|
|
uint64_t s = sentinel->getVA(sentinel->getSize());
|
|
uint64_t p = getVA() + offset;
|
|
ctx.target->relocateNoSym(buf + offset, R_ARM_PREL31, s - p);
|
|
assert(size == offset + 8);
|
|
}
|
|
|
|
bool ARMExidxSyntheticSection::isNeeded() const {
|
|
return llvm::any_of(exidxSections,
|
|
[](InputSection *isec) { return isec->isLive(); });
|
|
}
|
|
|
|
ThunkSection::ThunkSection(Ctx &ctx, OutputSection *os, uint64_t off)
|
|
: SyntheticSection(ctx, ".text.thunk", SHT_PROGBITS,
|
|
SHF_ALLOC | SHF_EXECINSTR,
|
|
ctx.arg.emachine == EM_PPC64 ? 16 : 4) {
|
|
this->parent = os;
|
|
this->outSecOff = off;
|
|
}
|
|
|
|
size_t ThunkSection::getSize() const {
|
|
if (roundUpSizeForErrata)
|
|
return alignTo(size, 4096);
|
|
return size;
|
|
}
|
|
|
|
void ThunkSection::addThunk(Thunk *t) {
|
|
thunks.push_back(t);
|
|
t->addSymbols(*this);
|
|
}
|
|
|
|
void ThunkSection::writeTo(uint8_t *buf) {
|
|
for (Thunk *t : thunks)
|
|
t->writeTo(buf + t->offset);
|
|
}
|
|
|
|
InputSection *ThunkSection::getTargetInputSection() const {
|
|
if (thunks.empty())
|
|
return nullptr;
|
|
const Thunk *t = thunks.front();
|
|
return t->getTargetInputSection();
|
|
}
|
|
|
|
bool ThunkSection::assignOffsets() {
|
|
uint64_t off = 0;
|
|
bool changed = false;
|
|
for (Thunk *t : thunks) {
|
|
if (t->alignment > addralign) {
|
|
addralign = t->alignment;
|
|
changed = true;
|
|
}
|
|
off = alignToPowerOf2(off, t->alignment);
|
|
t->setOffset(off);
|
|
uint32_t size = t->size();
|
|
t->getThunkTargetSym()->size = size;
|
|
off += size;
|
|
}
|
|
if (off != size)
|
|
changed = true;
|
|
size = off;
|
|
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
|
|
// allocated and filled in by the dynamic linker.
|
|
PPC64LongBranchTargetSection::PPC64LongBranchTargetSection(Ctx &ctx)
|
|
: SyntheticSection(ctx, ".branch_lt",
|
|
ctx.arg.isPic ? SHT_NOBITS : SHT_PROGBITS,
|
|
SHF_ALLOC | SHF_WRITE, 8) {}
|
|
|
|
uint64_t PPC64LongBranchTargetSection::getEntryVA(const Symbol *sym,
|
|
int64_t addend) {
|
|
return getVA() + entry_index.find({sym, addend})->second * 8;
|
|
}
|
|
|
|
std::optional<uint32_t>
|
|
PPC64LongBranchTargetSection::addEntry(const Symbol *sym, int64_t addend) {
|
|
auto res =
|
|
entry_index.try_emplace(std::make_pair(sym, addend), entries.size());
|
|
if (!res.second)
|
|
return std::nullopt;
|
|
entries.emplace_back(sym, addend);
|
|
return res.first->second;
|
|
}
|
|
|
|
size_t PPC64LongBranchTargetSection::getSize() const {
|
|
return entries.size() * 8;
|
|
}
|
|
|
|
void PPC64LongBranchTargetSection::writeTo(uint8_t *buf) {
|
|
// If linking non-pic we have the final addresses of the targets and they get
|
|
// written to the table directly. For pic the dynamic linker will allocate
|
|
// the section and fill it.
|
|
if (ctx.arg.isPic)
|
|
return;
|
|
|
|
for (auto entry : entries) {
|
|
const Symbol *sym = entry.first;
|
|
int64_t addend = entry.second;
|
|
assert(sym->getVA(ctx));
|
|
// Need calls to branch to the local entry-point since a long-branch
|
|
// must be a local-call.
|
|
write64(ctx, buf,
|
|
sym->getVA(ctx, addend) +
|
|
getPPC64GlobalEntryToLocalEntryOffset(ctx, sym->stOther));
|
|
buf += 8;
|
|
}
|
|
}
|
|
|
|
bool PPC64LongBranchTargetSection::isNeeded() const {
|
|
// `removeUnusedSyntheticSections()` is called before thunk allocation which
|
|
// is too early to determine if this section will be empty or not. We need
|
|
// Finalized to keep the section alive until after thunk creation. Finalized
|
|
// only gets set to true once `finalizeSections()` is called after thunk
|
|
// creation. Because of this, if we don't create any long-branch thunks we end
|
|
// up with an empty .branch_lt section in the binary.
|
|
return !finalized || !entries.empty();
|
|
}
|
|
|
|
static uint8_t getAbiVersion(Ctx &ctx) {
|
|
// MIPS non-PIC executable gets ABI version 1.
|
|
if (ctx.arg.emachine == EM_MIPS) {
|
|
if (!ctx.arg.isPic && !ctx.arg.relocatable &&
|
|
(ctx.arg.eflags & (EF_MIPS_PIC | EF_MIPS_CPIC)) == EF_MIPS_CPIC)
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
if (ctx.arg.emachine == EM_AMDGPU && !ctx.objectFiles.empty()) {
|
|
uint8_t ver = ctx.objectFiles[0]->abiVersion;
|
|
for (InputFile *file : ArrayRef(ctx.objectFiles).slice(1))
|
|
if (file->abiVersion != ver)
|
|
Err(ctx) << "incompatible ABI version: " << file;
|
|
return ver;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
template <typename ELFT>
|
|
void elf::writeEhdr(Ctx &ctx, uint8_t *buf, Partition &part) {
|
|
memcpy(buf, "\177ELF", 4);
|
|
|
|
auto *eHdr = reinterpret_cast<typename ELFT::Ehdr *>(buf);
|
|
eHdr->e_ident[EI_CLASS] = ELFT::Is64Bits ? ELFCLASS64 : ELFCLASS32;
|
|
eHdr->e_ident[EI_DATA] =
|
|
ELFT::Endianness == endianness::little ? ELFDATA2LSB : ELFDATA2MSB;
|
|
eHdr->e_ident[EI_VERSION] = EV_CURRENT;
|
|
eHdr->e_ident[EI_OSABI] = ctx.arg.osabi;
|
|
eHdr->e_ident[EI_ABIVERSION] = getAbiVersion(ctx);
|
|
eHdr->e_machine = ctx.arg.emachine;
|
|
eHdr->e_version = EV_CURRENT;
|
|
eHdr->e_flags = ctx.arg.eflags;
|
|
eHdr->e_ehsize = sizeof(typename ELFT::Ehdr);
|
|
eHdr->e_phnum = part.phdrs.size();
|
|
eHdr->e_shentsize = sizeof(typename ELFT::Shdr);
|
|
|
|
if (!ctx.arg.relocatable) {
|
|
eHdr->e_phoff = sizeof(typename ELFT::Ehdr);
|
|
eHdr->e_phentsize = sizeof(typename ELFT::Phdr);
|
|
}
|
|
}
|
|
|
|
template <typename ELFT> void elf::writePhdrs(uint8_t *buf, Partition &part) {
|
|
// Write the program header table.
|
|
auto *hBuf = reinterpret_cast<typename ELFT::Phdr *>(buf);
|
|
for (std::unique_ptr<PhdrEntry> &p : part.phdrs) {
|
|
hBuf->p_type = p->p_type;
|
|
hBuf->p_flags = p->p_flags;
|
|
hBuf->p_offset = p->p_offset;
|
|
hBuf->p_vaddr = p->p_vaddr;
|
|
hBuf->p_paddr = p->p_paddr;
|
|
hBuf->p_filesz = p->p_filesz;
|
|
hBuf->p_memsz = p->p_memsz;
|
|
hBuf->p_align = p->p_align;
|
|
++hBuf;
|
|
}
|
|
}
|
|
|
|
template <typename ELFT>
|
|
PartitionElfHeaderSection<ELFT>::PartitionElfHeaderSection(Ctx &ctx)
|
|
: SyntheticSection(ctx, "", SHT_LLVM_PART_EHDR, SHF_ALLOC, 1) {}
|
|
|
|
template <typename ELFT>
|
|
size_t PartitionElfHeaderSection<ELFT>::getSize() const {
|
|
return sizeof(typename ELFT::Ehdr);
|
|
}
|
|
|
|
template <typename ELFT>
|
|
void PartitionElfHeaderSection<ELFT>::writeTo(uint8_t *buf) {
|
|
writeEhdr<ELFT>(ctx, buf, getPartition(ctx));
|
|
|
|
// Loadable partitions are always ET_DYN.
|
|
auto *eHdr = reinterpret_cast<typename ELFT::Ehdr *>(buf);
|
|
eHdr->e_type = ET_DYN;
|
|
}
|
|
|
|
template <typename ELFT>
|
|
PartitionProgramHeadersSection<ELFT>::PartitionProgramHeadersSection(Ctx &ctx)
|
|
: SyntheticSection(ctx, ".phdrs", SHT_LLVM_PART_PHDR, SHF_ALLOC, 1) {}
|
|
|
|
template <typename ELFT>
|
|
size_t PartitionProgramHeadersSection<ELFT>::getSize() const {
|
|
return sizeof(typename ELFT::Phdr) * getPartition(ctx).phdrs.size();
|
|
}
|
|
|
|
template <typename ELFT>
|
|
void PartitionProgramHeadersSection<ELFT>::writeTo(uint8_t *buf) {
|
|
writePhdrs<ELFT>(buf, getPartition(ctx));
|
|
}
|
|
|
|
PartitionIndexSection::PartitionIndexSection(Ctx &ctx)
|
|
: SyntheticSection(ctx, ".rodata", SHT_PROGBITS, SHF_ALLOC, 4) {}
|
|
|
|
size_t PartitionIndexSection::getSize() const {
|
|
return 12 * (ctx.partitions.size() - 1);
|
|
}
|
|
|
|
void PartitionIndexSection::finalizeContents() {
|
|
for (size_t i = 1; i != ctx.partitions.size(); ++i)
|
|
ctx.partitions[i].nameStrTab =
|
|
ctx.mainPart->dynStrTab->addString(ctx.partitions[i].name);
|
|
}
|
|
|
|
void PartitionIndexSection::writeTo(uint8_t *buf) {
|
|
uint64_t va = getVA();
|
|
for (size_t i = 1; i != ctx.partitions.size(); ++i) {
|
|
write32(ctx, buf,
|
|
ctx.mainPart->dynStrTab->getVA() + ctx.partitions[i].nameStrTab -
|
|
va);
|
|
write32(ctx, buf + 4, ctx.partitions[i].elfHeader->getVA() - (va + 4));
|
|
|
|
SyntheticSection *next = i == ctx.partitions.size() - 1
|
|
? ctx.in.partEnd.get()
|
|
: ctx.partitions[i + 1].elfHeader.get();
|
|
write32(ctx, buf + 8, next->getVA() - ctx.partitions[i].elfHeader->getVA());
|
|
|
|
va += 12;
|
|
buf += 12;
|
|
}
|
|
}
|
|
|
|
static bool needsInterpSection(Ctx &ctx) {
|
|
return !ctx.arg.relocatable && !ctx.arg.shared &&
|
|
!ctx.arg.dynamicLinker.empty() && ctx.script->needsInterpSection();
|
|
}
|
|
|
|
bool elf::hasMemtag(Ctx &ctx) {
|
|
return ctx.arg.emachine == EM_AARCH64 &&
|
|
ctx.arg.androidMemtagMode != ELF::NT_MEMTAG_LEVEL_NONE;
|
|
}
|
|
|
|
// Fully static executables don't support MTE globals at this point in time, as
|
|
// we currently rely on:
|
|
// - A dynamic loader to process relocations, and
|
|
// - Dynamic entries.
|
|
// This restriction could be removed in future by re-using some of the ideas
|
|
// that ifuncs use in fully static executables.
|
|
bool elf::canHaveMemtagGlobals(Ctx &ctx) {
|
|
return hasMemtag(ctx) &&
|
|
(ctx.arg.relocatable || ctx.arg.shared || needsInterpSection(ctx));
|
|
}
|
|
|
|
constexpr char kMemtagAndroidNoteName[] = "Android";
|
|
void MemtagAndroidNote::writeTo(uint8_t *buf) {
|
|
static_assert(
|
|
sizeof(kMemtagAndroidNoteName) == 8,
|
|
"Android 11 & 12 have an ABI that the note name is 8 bytes long. Keep it "
|
|
"that way for backwards compatibility.");
|
|
|
|
write32(ctx, buf, sizeof(kMemtagAndroidNoteName));
|
|
write32(ctx, buf + 4, sizeof(uint32_t));
|
|
write32(ctx, buf + 8, ELF::NT_ANDROID_TYPE_MEMTAG);
|
|
memcpy(buf + 12, kMemtagAndroidNoteName, sizeof(kMemtagAndroidNoteName));
|
|
buf += 12 + alignTo(sizeof(kMemtagAndroidNoteName), 4);
|
|
|
|
uint32_t value = 0;
|
|
value |= ctx.arg.androidMemtagMode;
|
|
if (ctx.arg.androidMemtagHeap)
|
|
value |= ELF::NT_MEMTAG_HEAP;
|
|
// Note, MTE stack is an ABI break. Attempting to run an MTE stack-enabled
|
|
// binary on Android 11 or 12 will result in a checkfail in the loader.
|
|
if (ctx.arg.androidMemtagStack)
|
|
value |= ELF::NT_MEMTAG_STACK;
|
|
write32(ctx, buf, value); // note value
|
|
}
|
|
|
|
size_t MemtagAndroidNote::getSize() const {
|
|
return sizeof(llvm::ELF::Elf64_Nhdr) +
|
|
/*namesz=*/alignTo(sizeof(kMemtagAndroidNoteName), 4) +
|
|
/*descsz=*/sizeof(uint32_t);
|
|
}
|
|
|
|
void PackageMetadataNote::writeTo(uint8_t *buf) {
|
|
write32(ctx, buf, 4);
|
|
write32(ctx, buf + 4, ctx.arg.packageMetadata.size() + 1);
|
|
write32(ctx, buf + 8, FDO_PACKAGING_METADATA);
|
|
memcpy(buf + 12, "FDO", 4);
|
|
memcpy(buf + 16, ctx.arg.packageMetadata.data(),
|
|
ctx.arg.packageMetadata.size());
|
|
}
|
|
|
|
size_t PackageMetadataNote::getSize() const {
|
|
return sizeof(llvm::ELF::Elf64_Nhdr) + 4 +
|
|
alignTo(ctx.arg.packageMetadata.size() + 1, 4);
|
|
}
|
|
|
|
// Helper function, return the size of the ULEB128 for 'v', optionally writing
|
|
// it to `*(buf + offset)` if `buf` is non-null.
|
|
static size_t computeOrWriteULEB128(uint64_t v, uint8_t *buf, size_t offset) {
|
|
if (buf)
|
|
return encodeULEB128(v, buf + offset);
|
|
return getULEB128Size(v);
|
|
}
|
|
|
|
// https://github.com/ARM-software/abi-aa/blob/main/memtagabielf64/memtagabielf64.rst#83encoding-of-sht_aarch64_memtag_globals_dynamic
|
|
constexpr uint64_t kMemtagStepSizeBits = 3;
|
|
constexpr uint64_t kMemtagGranuleSize = 16;
|
|
static size_t
|
|
createMemtagGlobalDescriptors(Ctx &ctx,
|
|
const SmallVector<const Symbol *, 0> &symbols,
|
|
uint8_t *buf = nullptr) {
|
|
size_t sectionSize = 0;
|
|
uint64_t lastGlobalEnd = 0;
|
|
|
|
for (const Symbol *sym : symbols) {
|
|
if (!includeInSymtab(ctx, *sym))
|
|
continue;
|
|
const uint64_t addr = sym->getVA(ctx);
|
|
const uint64_t size = sym->getSize();
|
|
|
|
if (addr <= kMemtagGranuleSize && buf != nullptr)
|
|
Err(ctx) << "address of the tagged symbol \"" << sym->getName()
|
|
<< "\" falls in the ELF header. This is indicative of a "
|
|
"compiler/linker bug";
|
|
if (addr % kMemtagGranuleSize != 0)
|
|
Err(ctx) << "address of the tagged symbol \"" << sym->getName()
|
|
<< "\" at 0x" << Twine::utohexstr(addr)
|
|
<< "\" is not granule (16-byte) aligned";
|
|
if (size == 0)
|
|
Err(ctx) << "size of the tagged symbol \"" << sym->getName()
|
|
<< "\" is not allowed to be zero";
|
|
if (size % kMemtagGranuleSize != 0)
|
|
Err(ctx) << "size of the tagged symbol \"" << sym->getName()
|
|
<< "\" (size 0x" << Twine::utohexstr(size)
|
|
<< ") is not granule (16-byte) aligned";
|
|
|
|
const uint64_t sizeToEncode = size / kMemtagGranuleSize;
|
|
const uint64_t stepToEncode = ((addr - lastGlobalEnd) / kMemtagGranuleSize)
|
|
<< kMemtagStepSizeBits;
|
|
if (sizeToEncode < (1 << kMemtagStepSizeBits)) {
|
|
sectionSize += computeOrWriteULEB128(stepToEncode | sizeToEncode, buf, sectionSize);
|
|
} else {
|
|
sectionSize += computeOrWriteULEB128(stepToEncode, buf, sectionSize);
|
|
sectionSize += computeOrWriteULEB128(sizeToEncode - 1, buf, sectionSize);
|
|
}
|
|
lastGlobalEnd = addr + size;
|
|
}
|
|
|
|
return sectionSize;
|
|
}
|
|
|
|
bool MemtagGlobalDescriptors::updateAllocSize(Ctx &ctx) {
|
|
size_t oldSize = getSize();
|
|
std::stable_sort(symbols.begin(), symbols.end(),
|
|
[&ctx = ctx](const Symbol *s1, const Symbol *s2) {
|
|
return s1->getVA(ctx) < s2->getVA(ctx);
|
|
});
|
|
return oldSize != getSize();
|
|
}
|
|
|
|
void MemtagGlobalDescriptors::writeTo(uint8_t *buf) {
|
|
createMemtagGlobalDescriptors(ctx, symbols, buf);
|
|
}
|
|
|
|
size_t MemtagGlobalDescriptors::getSize() const {
|
|
return createMemtagGlobalDescriptors(ctx, symbols);
|
|
}
|
|
|
|
static OutputSection *findSection(Ctx &ctx, StringRef name) {
|
|
for (SectionCommand *cmd : ctx.script->sectionCommands)
|
|
if (auto *osd = dyn_cast<OutputDesc>(cmd))
|
|
if (osd->osec.name == name)
|
|
return &osd->osec;
|
|
return nullptr;
|
|
}
|
|
|
|
static Defined *addOptionalRegular(Ctx &ctx, StringRef name, SectionBase *sec,
|
|
uint64_t val, uint8_t stOther = STV_HIDDEN) {
|
|
Symbol *s = ctx.symtab->find(name);
|
|
if (!s || s->isDefined() || s->isCommon())
|
|
return nullptr;
|
|
|
|
s->resolve(ctx, Defined{ctx, ctx.internalFile, StringRef(), STB_GLOBAL,
|
|
stOther, STT_NOTYPE, val,
|
|
/*size=*/0, sec});
|
|
s->isUsedInRegularObj = true;
|
|
return cast<Defined>(s);
|
|
}
|
|
|
|
template <class ELFT> void elf::createSyntheticSections(Ctx &ctx) {
|
|
// Add the .interp section first because it is not a SyntheticSection.
|
|
// The removeUnusedSyntheticSections() function relies on the
|
|
// SyntheticSections coming last.
|
|
if (needsInterpSection(ctx)) {
|
|
for (size_t i = 1; i <= ctx.partitions.size(); ++i) {
|
|
InputSection *sec = createInterpSection(ctx);
|
|
sec->partition = i;
|
|
ctx.inputSections.push_back(sec);
|
|
}
|
|
}
|
|
|
|
auto add = [&](SyntheticSection &sec) { ctx.inputSections.push_back(&sec); };
|
|
|
|
if (ctx.arg.zSectionHeader)
|
|
ctx.in.shStrTab =
|
|
std::make_unique<StringTableSection>(ctx, ".shstrtab", false);
|
|
|
|
ctx.out.programHeaders =
|
|
std::make_unique<OutputSection>(ctx, "", 0, SHF_ALLOC);
|
|
ctx.out.programHeaders->addralign = ctx.arg.wordsize;
|
|
|
|
if (ctx.arg.strip != StripPolicy::All) {
|
|
ctx.in.strTab = std::make_unique<StringTableSection>(ctx, ".strtab", false);
|
|
ctx.in.symTab =
|
|
std::make_unique<SymbolTableSection<ELFT>>(ctx, *ctx.in.strTab);
|
|
ctx.in.symTabShndx = std::make_unique<SymtabShndxSection>(ctx);
|
|
}
|
|
|
|
ctx.in.bss = std::make_unique<BssSection>(ctx, ".bss", 0, 1);
|
|
add(*ctx.in.bss);
|
|
|
|
// If there is a SECTIONS command and a .data.rel.ro section name use name
|
|
// .data.rel.ro.bss so that we match in the .data.rel.ro output section.
|
|
// This makes sure our relro is contiguous.
|
|
bool hasDataRelRo =
|
|
ctx.script->hasSectionsCommand && findSection(ctx, ".data.rel.ro");
|
|
ctx.in.bssRelRo = std::make_unique<BssSection>(
|
|
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);
|
|
}
|
|
|
|
StringRef relaDynName = ctx.arg.isRela ? ".rela.dyn" : ".rel.dyn";
|
|
|
|
const unsigned threadCount = ctx.arg.threadCount;
|
|
for (Partition &part : ctx.partitions) {
|
|
auto add = [&](SyntheticSection &sec) {
|
|
sec.partition = part.getNumber(ctx);
|
|
ctx.inputSections.push_back(&sec);
|
|
};
|
|
|
|
if (!part.name.empty()) {
|
|
part.elfHeader = std::make_unique<PartitionElfHeaderSection<ELFT>>(ctx);
|
|
part.elfHeader->name = part.name;
|
|
add(*part.elfHeader);
|
|
|
|
part.programHeaders =
|
|
std::make_unique<PartitionProgramHeadersSection<ELFT>>(ctx);
|
|
add(*part.programHeaders);
|
|
}
|
|
|
|
if (ctx.arg.buildId != BuildIdKind::None) {
|
|
part.buildId = std::make_unique<BuildIdSection>(ctx);
|
|
add(*part.buildId);
|
|
}
|
|
|
|
// dynSymTab is always present to simplify several finalizeSections
|
|
// functions.
|
|
part.dynStrTab = std::make_unique<StringTableSection>(ctx, ".dynstr", true);
|
|
part.dynSymTab =
|
|
std::make_unique<SymbolTableSection<ELFT>>(ctx, *part.dynStrTab);
|
|
|
|
if (ctx.arg.relocatable)
|
|
continue;
|
|
part.dynamic = std::make_unique<DynamicSection<ELFT>>(ctx);
|
|
|
|
if (hasMemtag(ctx)) {
|
|
part.memtagAndroidNote = std::make_unique<MemtagAndroidNote>(ctx);
|
|
add(*part.memtagAndroidNote);
|
|
if (canHaveMemtagGlobals(ctx)) {
|
|
part.memtagGlobalDescriptors =
|
|
std::make_unique<MemtagGlobalDescriptors>(ctx);
|
|
add(*part.memtagGlobalDescriptors);
|
|
}
|
|
}
|
|
|
|
if (ctx.arg.androidPackDynRelocs)
|
|
part.relaDyn = std::make_unique<AndroidPackedRelocationSection<ELFT>>(
|
|
ctx, relaDynName, threadCount);
|
|
else
|
|
part.relaDyn = std::make_unique<RelocationSection<ELFT>>(
|
|
ctx, relaDynName, ctx.arg.zCombreloc, threadCount);
|
|
|
|
if (ctx.hasDynsym) {
|
|
add(*part.dynSymTab);
|
|
|
|
part.verSym = std::make_unique<VersionTableSection>(ctx);
|
|
add(*part.verSym);
|
|
|
|
if (!namedVersionDefs(ctx).empty()) {
|
|
part.verDef = std::make_unique<VersionDefinitionSection>(ctx);
|
|
add(*part.verDef);
|
|
}
|
|
|
|
part.verNeed = std::make_unique<VersionNeedSection<ELFT>>(ctx);
|
|
add(*part.verNeed);
|
|
|
|
if (ctx.arg.gnuHash) {
|
|
part.gnuHashTab = std::make_unique<GnuHashTableSection>(ctx);
|
|
add(*part.gnuHashTab);
|
|
}
|
|
|
|
if (ctx.arg.sysvHash) {
|
|
part.hashTab = std::make_unique<HashTableSection>(ctx);
|
|
add(*part.hashTab);
|
|
}
|
|
|
|
add(*part.dynamic);
|
|
add(*part.dynStrTab);
|
|
}
|
|
add(*part.relaDyn);
|
|
|
|
if (ctx.arg.relrPackDynRelocs) {
|
|
part.relrDyn = std::make_unique<RelrSection<ELFT>>(ctx, threadCount);
|
|
add(*part.relrDyn);
|
|
part.relrAuthDyn = std::make_unique<RelrSection<ELFT>>(
|
|
ctx, threadCount, /*isAArch64Auth=*/true);
|
|
add(*part.relrAuthDyn);
|
|
}
|
|
|
|
if (ctx.arg.ehFrameHdr) {
|
|
part.ehFrameHdr = std::make_unique<EhFrameHeader>(ctx);
|
|
add(*part.ehFrameHdr);
|
|
}
|
|
part.ehFrame = std::make_unique<EhFrameSection>(ctx);
|
|
add(*part.ehFrame);
|
|
|
|
if (ctx.arg.emachine == EM_ARM) {
|
|
// This section replaces all the individual .ARM.exidx InputSections.
|
|
part.armExidx = std::make_unique<ARMExidxSyntheticSection>(ctx);
|
|
add(*part.armExidx);
|
|
}
|
|
|
|
if (!ctx.arg.packageMetadata.empty()) {
|
|
part.packageMetadataNote = std::make_unique<PackageMetadataNote>(ctx);
|
|
add(*part.packageMetadataNote);
|
|
}
|
|
}
|
|
|
|
if (ctx.partitions.size() != 1) {
|
|
// Create the partition end marker. This needs to be in partition number 255
|
|
// so that it is sorted after all other partitions. It also has other
|
|
// special handling (see createPhdrs() and combineEhSections()).
|
|
ctx.in.partEnd =
|
|
std::make_unique<BssSection>(ctx, ".part.end", ctx.arg.maxPageSize, 1);
|
|
ctx.in.partEnd->partition = 255;
|
|
add(*ctx.in.partEnd);
|
|
|
|
ctx.in.partIndex = std::make_unique<PartitionIndexSection>(ctx);
|
|
addOptionalRegular(ctx, "__part_index_begin", ctx.in.partIndex.get(), 0);
|
|
addOptionalRegular(ctx, "__part_index_end", ctx.in.partIndex.get(),
|
|
ctx.in.partIndex->getSize());
|
|
add(*ctx.in.partIndex);
|
|
}
|
|
|
|
// Add .got. MIPS' .got is so different from the other archs,
|
|
// it has its own class.
|
|
if (ctx.arg.emachine == EM_MIPS) {
|
|
ctx.in.mipsGot = std::make_unique<MipsGotSection>(ctx);
|
|
add(*ctx.in.mipsGot);
|
|
} else {
|
|
ctx.in.got = std::make_unique<GotSection>(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);
|
|
add(*ctx.in.igotPlt);
|
|
// Add .relro_padding if DATA_SEGMENT_RELRO_END is used; otherwise, add the
|
|
// section in the absence of PHDRS/SECTIONS commands.
|
|
if (ctx.arg.zRelro &&
|
|
((ctx.script->phdrsCommands.empty() && !ctx.script->hasSectionsCommand) ||
|
|
ctx.script->seenRelroEnd)) {
|
|
ctx.in.relroPadding = std::make_unique<RelroPaddingSection>(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) {
|
|
if (ctx.target->gotBaseSymInGotPlt)
|
|
ctx.in.gotPlt->hasGotPltOffRel = true;
|
|
else
|
|
ctx.in.got->hasGotOffRel = true;
|
|
}
|
|
|
|
// We always need to add rel[a].plt to output if it has entries.
|
|
// Even for static linking it can contain R_[*]_IRELATIVE relocations.
|
|
ctx.in.relaPlt = std::make_unique<RelocationSection<ELFT>>(
|
|
ctx, ctx.arg.isRela ? ".rela.plt" : ".rel.plt", /*sort=*/false,
|
|
/*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
|
|
ctx.in.plt = std::make_unique<PltSection>(ctx);
|
|
add(*ctx.in.plt);
|
|
ctx.in.iplt = std::make_unique<IpltSection>(ctx);
|
|
add(*ctx.in.iplt);
|
|
|
|
if (ctx.arg.andFeatures || !ctx.aarch64PauthAbiCoreInfo.empty()) {
|
|
ctx.in.gnuProperty = std::make_unique<GnuPropertySection>(ctx);
|
|
add(*ctx.in.gnuProperty);
|
|
}
|
|
|
|
if (ctx.arg.debugNames) {
|
|
ctx.in.debugNames = std::make_unique<DebugNamesSection<ELFT>>(ctx);
|
|
add(*ctx.in.debugNames);
|
|
}
|
|
|
|
if (ctx.arg.gdbIndex) {
|
|
ctx.in.gdbIndex = GdbIndexSection::create<ELFT>(ctx);
|
|
add(*ctx.in.gdbIndex);
|
|
}
|
|
|
|
// .note.GNU-stack is always added when we are creating a re-linkable
|
|
// object file. Other linkers are using the presence of this marker
|
|
// section to control the executable-ness of the stack area, but that
|
|
// is irrelevant these days. Stack area should always be non-executable
|
|
// by default. So we emit this section unconditionally.
|
|
if (ctx.arg.relocatable) {
|
|
ctx.in.gnuStack = std::make_unique<GnuStackSection>(ctx);
|
|
add(*ctx.in.gnuStack);
|
|
}
|
|
|
|
if (ctx.in.symTab)
|
|
add(*ctx.in.symTab);
|
|
if (ctx.in.symTabShndx)
|
|
add(*ctx.in.symTabShndx);
|
|
if (ctx.in.shStrTab)
|
|
add(*ctx.in.shStrTab);
|
|
if (ctx.in.strTab)
|
|
add(*ctx.in.strTab);
|
|
}
|
|
|
|
template void elf::splitSections<ELF32LE>(Ctx &);
|
|
template void elf::splitSections<ELF32BE>(Ctx &);
|
|
template void elf::splitSections<ELF64LE>(Ctx &);
|
|
template void elf::splitSections<ELF64BE>(Ctx &);
|
|
|
|
template void EhFrameSection::iterateFDEWithLSDA<ELF32LE>(
|
|
function_ref<void(InputSection &)>);
|
|
template void EhFrameSection::iterateFDEWithLSDA<ELF32BE>(
|
|
function_ref<void(InputSection &)>);
|
|
template void EhFrameSection::iterateFDEWithLSDA<ELF64LE>(
|
|
function_ref<void(InputSection &)>);
|
|
template void EhFrameSection::iterateFDEWithLSDA<ELF64BE>(
|
|
function_ref<void(InputSection &)>);
|
|
|
|
template class elf::SymbolTableSection<ELF32LE>;
|
|
template class elf::SymbolTableSection<ELF32BE>;
|
|
template class elf::SymbolTableSection<ELF64LE>;
|
|
template class elf::SymbolTableSection<ELF64BE>;
|
|
|
|
template void elf::writeEhdr<ELF32LE>(Ctx &, uint8_t *Buf, Partition &Part);
|
|
template void elf::writeEhdr<ELF32BE>(Ctx &, uint8_t *Buf, Partition &Part);
|
|
template void elf::writeEhdr<ELF64LE>(Ctx &, uint8_t *Buf, Partition &Part);
|
|
template void elf::writeEhdr<ELF64BE>(Ctx &, uint8_t *Buf, Partition &Part);
|
|
|
|
template void elf::writePhdrs<ELF32LE>(uint8_t *Buf, Partition &Part);
|
|
template void elf::writePhdrs<ELF32BE>(uint8_t *Buf, Partition &Part);
|
|
template void elf::writePhdrs<ELF64LE>(uint8_t *Buf, Partition &Part);
|
|
template void elf::writePhdrs<ELF64BE>(uint8_t *Buf, Partition &Part);
|
|
|
|
template void elf::createSyntheticSections<ELF32LE>(Ctx &);
|
|
template void elf::createSyntheticSections<ELF32BE>(Ctx &);
|
|
template void elf::createSyntheticSections<ELF64LE>(Ctx &);
|
|
template void elf::createSyntheticSections<ELF64BE>(Ctx &);
|