Files
clang-p2996/lld/lib/ReaderWriter/ELF/TargetLayout.cpp
Simon Atanasyan 5606dac67e [ELF] Allow TargetLayout descendants to control assignment sections to segments
The TargetLayout class puts two sections into the same segment if they
have equal segment types and the same section flags (SHF_xxx). To be
able to merge some sort of sections into the same segment we drop some
flags before comparison. For example to merge string sections into Data
segment we drop SHF_STRINGS and SHF_MERGE flags.

The patch allows TargetLayout descendants to drop some target specific
section flags. MIPS target needs that to merge .MIPS.options section
which has SHF_MIPS_NOSTRIP flag into the LOAD segment.

http://reviews.llvm.org/D9160

llvm-svn: 235487
2015-04-22 07:57:35 +00:00

703 lines
23 KiB
C++

//===- lib/ReaderWriter/ELF/TargetLayout.cpp ------------------------------===//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "TargetLayout.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/Support/Errc.h"
namespace lld {
namespace elf {
template <class ELFT>
typename TargetLayout<ELFT>::SectionOrder
TargetLayout<ELFT>::getSectionOrder(StringRef name, int32_t contentType,
int32_t contentPermissions) {
switch (contentType) {
case DefinedAtom::typeResolver:
case DefinedAtom::typeCode:
return llvm::StringSwitch<typename TargetLayout<ELFT>::SectionOrder>(name)
.StartsWith(".eh_frame_hdr", ORDER_EH_FRAMEHDR)
.StartsWith(".eh_frame", ORDER_EH_FRAME)
.StartsWith(".init", ORDER_INIT)
.StartsWith(".fini", ORDER_FINI)
.StartsWith(".hash", ORDER_HASH)
.Default(ORDER_TEXT);
case DefinedAtom::typeConstant:
return ORDER_RODATA;
case DefinedAtom::typeData:
case DefinedAtom::typeDataFast:
return llvm::StringSwitch<typename TargetLayout<ELFT>::SectionOrder>(name)
.StartsWith(".init_array", ORDER_INIT_ARRAY)
.StartsWith(".fini_array", ORDER_FINI_ARRAY)
.StartsWith(".dynamic", ORDER_DYNAMIC)
.StartsWith(".ctors", ORDER_CTORS)
.StartsWith(".dtors", ORDER_DTORS)
.Default(ORDER_DATA);
case DefinedAtom::typeZeroFill:
case DefinedAtom::typeZeroFillFast:
return ORDER_BSS;
case DefinedAtom::typeGOT:
return llvm::StringSwitch<typename TargetLayout<ELFT>::SectionOrder>(name)
.StartsWith(".got.plt", ORDER_GOT_PLT)
.Default(ORDER_GOT);
case DefinedAtom::typeStub:
return ORDER_PLT;
case DefinedAtom::typeRONote:
return ORDER_RO_NOTE;
case DefinedAtom::typeRWNote:
return ORDER_RW_NOTE;
case DefinedAtom::typeNoAlloc:
return ORDER_NOALLOC;
case DefinedAtom::typeThreadData:
return ORDER_TDATA;
case DefinedAtom::typeThreadZeroFill:
return ORDER_TBSS;
default:
// If we get passed in a section push it to OTHER
if (contentPermissions == DefinedAtom::perm___)
return ORDER_OTHER;
return ORDER_NOT_DEFINED;
}
}
/// \brief This maps the input sections to the output section names
template <class ELFT>
StringRef TargetLayout<ELFT>::getInputSectionName(const DefinedAtom *da) const {
if (da->sectionChoice() == DefinedAtom::sectionBasedOnContent) {
switch (da->contentType()) {
case DefinedAtom::typeCode:
return ".text";
case DefinedAtom::typeData:
return ".data";
case DefinedAtom::typeConstant:
return ".rodata";
case DefinedAtom::typeZeroFill:
return ".bss";
case DefinedAtom::typeThreadData:
return ".tdata";
case DefinedAtom::typeThreadZeroFill:
return ".tbss";
default:
break;
}
}
return da->customSectionName();
}
/// \brief This maps the input sections to the output section names.
template <class ELFT>
StringRef
TargetLayout<ELFT>::getOutputSectionName(StringRef archivePath,
StringRef memberPath,
StringRef inputSectionName) const {
StringRef outputSectionName;
if (_linkerScriptSema.hasLayoutCommands()) {
script::Sema::SectionKey key = {archivePath, memberPath, inputSectionName};
outputSectionName = _linkerScriptSema.getOutputSection(key);
if (!outputSectionName.empty())
return outputSectionName;
}
return llvm::StringSwitch<StringRef>(inputSectionName)
.StartsWith(".text", ".text")
.StartsWith(".ctors", ".ctors")
.StartsWith(".dtors", ".dtors")
.StartsWith(".rodata", ".rodata")
.StartsWith(".gcc_except_table", ".gcc_except_table")
.StartsWith(".data.rel.ro", ".data.rel.ro")
.StartsWith(".data.rel.local", ".data.rel.local")
.StartsWith(".data", ".data")
.StartsWith(".tdata", ".tdata")
.StartsWith(".tbss", ".tbss")
.StartsWith(".init_array", ".init_array")
.StartsWith(".fini_array", ".fini_array")
.Default(inputSectionName);
}
/// \brief Gets the segment for a output section
template <class ELFT>
typename TargetLayout<ELFT>::SegmentType
TargetLayout<ELFT>::getSegmentType(Section<ELFT> *section) const {
switch (section->order()) {
case ORDER_INTERP:
return llvm::ELF::PT_INTERP;
case ORDER_TEXT:
case ORDER_HASH:
case ORDER_DYNAMIC_SYMBOLS:
case ORDER_DYNAMIC_STRINGS:
case ORDER_DYNAMIC_RELOCS:
case ORDER_DYNAMIC_PLT_RELOCS:
case ORDER_REL:
case ORDER_INIT:
case ORDER_PLT:
case ORDER_FINI:
case ORDER_RODATA:
case ORDER_EH_FRAME:
case ORDER_CTORS:
case ORDER_DTORS:
return llvm::ELF::PT_LOAD;
case ORDER_RO_NOTE:
case ORDER_RW_NOTE:
return llvm::ELF::PT_NOTE;
case ORDER_DYNAMIC:
return llvm::ELF::PT_DYNAMIC;
case ORDER_EH_FRAMEHDR:
return llvm::ELF::PT_GNU_EH_FRAME;
case ORDER_GOT:
case ORDER_GOT_PLT:
case ORDER_DATA:
case ORDER_BSS:
case ORDER_INIT_ARRAY:
case ORDER_FINI_ARRAY:
return llvm::ELF::PT_LOAD;
case ORDER_TDATA:
case ORDER_TBSS:
return llvm::ELF::PT_TLS;
default:
return llvm::ELF::PT_NULL;
}
}
template <class ELFT>
bool TargetLayout<ELFT>::hasOutputSegment(Section<ELFT> *section) {
switch (section->order()) {
case ORDER_INTERP:
case ORDER_HASH:
case ORDER_DYNAMIC_SYMBOLS:
case ORDER_DYNAMIC_STRINGS:
case ORDER_DYNAMIC_RELOCS:
case ORDER_DYNAMIC_PLT_RELOCS:
case ORDER_REL:
case ORDER_INIT:
case ORDER_PLT:
case ORDER_TEXT:
case ORDER_FINI:
case ORDER_RODATA:
case ORDER_EH_FRAME:
case ORDER_EH_FRAMEHDR:
case ORDER_TDATA:
case ORDER_TBSS:
case ORDER_RO_NOTE:
case ORDER_RW_NOTE:
case ORDER_DYNAMIC:
case ORDER_CTORS:
case ORDER_DTORS:
case ORDER_GOT:
case ORDER_GOT_PLT:
case ORDER_DATA:
case ORDER_INIT_ARRAY:
case ORDER_FINI_ARRAY:
case ORDER_BSS:
case ORDER_NOALLOC:
return true;
default:
return section->hasOutputSegment();
}
}
template <class ELFT>
AtomSection<ELFT> *
TargetLayout<ELFT>::createSection(StringRef sectionName, int32_t contentType,
DefinedAtom::ContentPermissions permissions,
SectionOrder sectionOrder) {
return new (_allocator) AtomSection<ELFT>(_ctx, sectionName, contentType,
permissions, sectionOrder);
}
template <class ELFT>
AtomSection<ELFT> *
TargetLayout<ELFT>::getSection(StringRef sectionName, int32_t contentType,
DefinedAtom::ContentPermissions permissions,
const DefinedAtom *da) {
const SectionKey sectionKey(sectionName, permissions, da->file().path());
SectionOrder sectionOrder =
getSectionOrder(sectionName, contentType, permissions);
auto sec = _sectionMap.find(sectionKey);
if (sec != _sectionMap.end())
return sec->second;
AtomSection<ELFT> *newSec =
createSection(sectionName, contentType, permissions, sectionOrder);
newSec->setOutputSectionName(getOutputSectionName(
da->file().archivePath(), da->file().memberPath(), sectionName));
newSec->setOrder(sectionOrder);
newSec->setArchiveNameOrPath(da->file().archivePath());
newSec->setMemberNameOrPath(da->file().memberPath());
_sections.push_back(newSec);
_sectionMap.insert(std::make_pair(sectionKey, newSec));
return newSec;
}
template <class ELFT>
ErrorOr<const AtomLayout *> TargetLayout<ELFT>::addAtom(const Atom *atom) {
if (const DefinedAtom *definedAtom = dyn_cast<DefinedAtom>(atom)) {
// HACK: Ignore undefined atoms. We need to adjust the interface so that
// undefined atoms can still be included in the output symbol table for
// -noinhibit-exec.
if (definedAtom->contentType() == DefinedAtom::typeUnknown)
return make_error_code(llvm::errc::invalid_argument);
const DefinedAtom::ContentPermissions permissions =
definedAtom->permissions();
const DefinedAtom::ContentType contentType = definedAtom->contentType();
StringRef sectionName = getInputSectionName(definedAtom);
AtomSection<ELFT> *section =
getSection(sectionName, contentType, permissions, definedAtom);
// Add runtime relocations to the .rela section.
for (const auto &reloc : *definedAtom) {
bool isLocalReloc = true;
if (_ctx.isDynamicRelocation(*reloc)) {
getDynamicRelocationTable()->addRelocation(*definedAtom, *reloc);
isLocalReloc = false;
} else if (_ctx.isPLTRelocation(*reloc)) {
getPLTRelocationTable()->addRelocation(*definedAtom, *reloc);
isLocalReloc = false;
}
if (!reloc->target())
continue;
// Ignore undefined atoms that are not target of dynamic relocations
if (isa<UndefinedAtom>(reloc->target()) && isLocalReloc)
continue;
if (_ctx.isCopyRelocation(*reloc)) {
_copiedDynSymNames.insert(definedAtom->name());
continue;
}
_referencedDynAtoms.insert(reloc->target());
}
return section->appendAtom(atom);
}
const AbsoluteAtom *absoluteAtom = cast<AbsoluteAtom>(atom);
// Absolute atoms are not part of any section, they are global for the whole
// link
_absoluteAtoms.push_back(
new (_allocator) AtomLayout(absoluteAtom, 0, absoluteAtom->value()));
return _absoluteAtoms.back();
}
/// Output sections with the same name into a OutputSection
template <class ELFT> void TargetLayout<ELFT>::createOutputSections() {
OutputSection<ELFT> *outputSection;
for (auto &si : _sections) {
Section<ELFT> *section = dyn_cast<Section<ELFT>>(si);
if (!section)
continue;
const std::pair<StringRef, OutputSection<ELFT> *> currentOutputSection(
section->outputSectionName(), nullptr);
std::pair<typename OutputSectionMapT::iterator, bool> outputSectionInsert(
_outputSectionMap.insert(currentOutputSection));
if (!outputSectionInsert.second) {
outputSection = outputSectionInsert.first->second;
} else {
outputSection = new (_allocator.Allocate<OutputSection<ELFT>>())
OutputSection<ELFT>(section->outputSectionName());
_outputSections.push_back(outputSection);
outputSectionInsert.first->second = outputSection;
}
outputSection->appendSection(section);
}
}
template <class ELFT>
uint64_t
TargetLayout<ELFT>::getLookupSectionFlags(const OutputSection<ELFT> *os) const {
uint64_t flags = os->flags();
if (!(flags & llvm::ELF::SHF_WRITE) && _ctx.mergeRODataToTextSegment())
flags &= ~llvm::ELF::SHF_EXECINSTR;
// Merge string sections into Data segment itself
flags &= ~(llvm::ELF::SHF_STRINGS | llvm::ELF::SHF_MERGE);
// Merge the TLS section into the DATA segment itself
flags &= ~(llvm::ELF::SHF_TLS);
return flags;
}
template <class ELFT> void TargetLayout<ELFT>::assignSectionsToSegments() {
ScopedTask task(getDefaultDomain(), "assignSectionsToSegments");
ELFLinkingContext::OutputMagic outputMagic = _ctx.getOutputMagic();
// sort the sections by their order as defined by the layout
sortInputSections();
// Create output sections.
createOutputSections();
// Finalize output section layout.
finalizeOutputSectionLayout();
// Set the ordinal after sorting the sections
int ordinal = 1;
for (auto osi : _outputSections) {
osi->setOrdinal(ordinal);
for (auto ai : osi->sections()) {
ai->setOrdinal(ordinal);
}
++ordinal;
}
for (auto osi : _outputSections) {
for (auto section : osi->sections()) {
if (!hasOutputSegment(section))
continue;
osi->setLoadableSection(section->isLoadableSection());
// Get the segment type for the section
int64_t segmentType = getSegmentType(section);
osi->setHasSegment();
section->setSegmentType(segmentType);
StringRef segmentName = section->segmentKindToStr();
uint64_t lookupSectionFlag = getLookupSectionFlags(osi);
Segment<ELFT> *segment;
// We need a separate segment for sections that don't have
// the segment type to be PT_LOAD
if (segmentType != llvm::ELF::PT_LOAD) {
const AdditionalSegmentKey key(segmentType, lookupSectionFlag);
const std::pair<AdditionalSegmentKey, Segment<ELFT> *>
additionalSegment(key, nullptr);
std::pair<typename AdditionalSegmentMapT::iterator, bool>
additionalSegmentInsert(
_additionalSegmentMap.insert(additionalSegment));
if (!additionalSegmentInsert.second) {
segment = additionalSegmentInsert.first->second;
} else {
segment =
new (_allocator) Segment<ELFT>(_ctx, segmentName, segmentType);
additionalSegmentInsert.first->second = segment;
_segments.push_back(segment);
}
segment->append(section);
}
if (segmentType == llvm::ELF::PT_NULL)
continue;
// If the output magic is set to OutputMagic::NMAGIC or
// OutputMagic::OMAGIC, Place the data alongside text in one single
// segment
if (outputMagic == ELFLinkingContext::OutputMagic::NMAGIC ||
outputMagic == ELFLinkingContext::OutputMagic::OMAGIC)
lookupSectionFlag = llvm::ELF::SHF_EXECINSTR | llvm::ELF::SHF_ALLOC |
llvm::ELF::SHF_WRITE;
// Use the flags of the merged Section for the segment
const SegmentKey key("PT_LOAD", lookupSectionFlag);
const std::pair<SegmentKey, Segment<ELFT> *> currentSegment(key, nullptr);
std::pair<typename SegmentMapT::iterator, bool> segmentInsert(
_segmentMap.insert(currentSegment));
if (!segmentInsert.second) {
segment = segmentInsert.first->second;
} else {
segment =
new (_allocator) Segment<ELFT>(_ctx, "PT_LOAD", llvm::ELF::PT_LOAD);
segmentInsert.first->second = segment;
_segments.push_back(segment);
}
// Insert chunks with linker script expressions that occur at this
// point, just before appending a new input section
addExtraChunksToSegment(segment, section->archivePath(),
section->memberPath(),
section->inputSectionName());
segment->append(section);
}
}
if (_ctx.isDynamic() && !_ctx.isDynamicLibrary()) {
Segment<ELFT> *segment = new (_allocator) ProgramHeaderSegment<ELFT>(_ctx);
_segments.push_back(segment);
segment->append(_elfHeader);
segment->append(_programHeader);
}
}
template <class ELFT> void TargetLayout<ELFT>::assignVirtualAddress() {
if (_segments.empty())
return;
std::sort(_segments.begin(), _segments.end(), Segment<ELFT>::compareSegments);
uint64_t baseAddress = _ctx.getBaseAddress();
// HACK: This is a super dirty hack. The elf header and program header are
// not part of a section, but we need them to be loaded at the base address
// so that AT_PHDR is set correctly by the loader and so they are accessible
// at runtime. To do this we simply prepend them to the first loadable Segment
// and let the layout logic take care of it.
Segment<ELFT> *firstLoadSegment = nullptr;
for (auto si : _segments) {
if (si->segmentType() == llvm::ELF::PT_LOAD) {
firstLoadSegment = si;
si->firstSection()->setAlign(si->alignment());
break;
}
}
assert(firstLoadSegment != nullptr && "No loadable segment!");
firstLoadSegment->prepend(_programHeader);
firstLoadSegment->prepend(_elfHeader);
bool newSegmentHeaderAdded = true;
bool virtualAddressAssigned = false;
bool fileOffsetAssigned = false;
while (true) {
for (auto si : _segments) {
si->finalize();
// Don't add PT_NULL segments into the program header
if (si->segmentType() != llvm::ELF::PT_NULL)
newSegmentHeaderAdded = _programHeader->addSegment(si);
}
if (!newSegmentHeaderAdded && virtualAddressAssigned)
break;
uint64_t address = baseAddress;
// start assigning virtual addresses
for (auto &si : _segments) {
if ((si->segmentType() != llvm::ELF::PT_LOAD) &&
(si->segmentType() != llvm::ELF::PT_NULL))
continue;
if (si->segmentType() == llvm::ELF::PT_NULL) {
si->assignVirtualAddress(0 /*non loadable*/);
} else {
if (virtualAddressAssigned && (address != baseAddress) &&
(address == si->virtualAddr()))
break;
si->assignVirtualAddress(address);
}
address = si->virtualAddr() + si->memSize();
}
uint64_t baseFileOffset = 0;
uint64_t fileoffset = baseFileOffset;
for (auto &si : _segments) {
if ((si->segmentType() != llvm::ELF::PT_LOAD) &&
(si->segmentType() != llvm::ELF::PT_NULL))
continue;
if (fileOffsetAssigned && (fileoffset != baseFileOffset) &&
(fileoffset == si->fileOffset()))
break;
si->assignFileOffsets(fileoffset);
fileoffset = si->fileOffset() + si->fileSize();
}
virtualAddressAssigned = true;
fileOffsetAssigned = true;
_programHeader->resetProgramHeaders();
}
Section<ELFT> *section;
// Fix the offsets of all the atoms within a section
for (auto &si : _sections) {
section = dyn_cast<Section<ELFT>>(si);
if (section && TargetLayout<ELFT>::hasOutputSegment(section))
section->assignFileOffsets(section->fileOffset());
}
// Set the size of the merged Sections
for (auto osi : _outputSections) {
uint64_t sectionfileoffset = 0;
uint64_t startFileOffset = 0;
uint64_t sectionsize = 0;
bool isFirstSection = true;
for (auto si : osi->sections()) {
if (isFirstSection) {
startFileOffset = si->fileOffset();
isFirstSection = false;
}
sectionfileoffset = si->fileOffset();
sectionsize = si->fileSize();
}
sectionsize = (sectionfileoffset - startFileOffset) + sectionsize;
osi->setFileOffset(startFileOffset);
osi->setSize(sectionsize);
}
// Set the virtual addr of the merged Sections
for (auto osi : _outputSections) {
uint64_t sectionstartaddr = 0;
uint64_t startaddr = 0;
uint64_t sectionsize = 0;
bool isFirstSection = true;
for (auto si : osi->sections()) {
if (isFirstSection) {
startaddr = si->virtualAddr();
isFirstSection = false;
}
sectionstartaddr = si->virtualAddr();
sectionsize = si->memSize();
}
sectionsize = (sectionstartaddr - startaddr) + sectionsize;
osi->setMemSize(sectionsize);
osi->setAddr(startaddr);
}
}
template <class ELFT>
void TargetLayout<ELFT>::assignFileOffsetsForMiscSections() {
uint64_t fileoffset = 0;
uint64_t size = 0;
for (auto si : _segments) {
// Don't calculate offsets from non loadable segments
if ((si->segmentType() != llvm::ELF::PT_LOAD) &&
(si->segmentType() != llvm::ELF::PT_NULL))
continue;
fileoffset = si->fileOffset();
size = si->fileSize();
}
fileoffset = fileoffset + size;
Section<ELFT> *section;
for (auto si : _sections) {
section = dyn_cast<Section<ELFT>>(si);
if (section && TargetLayout<ELFT>::hasOutputSegment(section))
continue;
fileoffset = llvm::RoundUpToAlignment(fileoffset, si->alignment());
si->setFileOffset(fileoffset);
si->setVirtualAddr(0);
fileoffset += si->fileSize();
}
}
template <class ELFT> void TargetLayout<ELFT>::sortInputSections() {
// First, sort according to default layout's order
std::stable_sort(
_sections.begin(), _sections.end(),
[](Chunk<ELFT> *A, Chunk<ELFT> *B) { return A->order() < B->order(); });
if (!_linkerScriptSema.hasLayoutCommands())
return;
// Sort the sections by their order as defined by the linker script
std::stable_sort(
this->_sections.begin(), this->_sections.end(),
[this](Chunk<ELFT> *A, Chunk<ELFT> *B) {
auto *a = dyn_cast<Section<ELFT>>(A);
auto *b = dyn_cast<Section<ELFT>>(B);
if (a == nullptr)
return false;
if (b == nullptr)
return true;
return _linkerScriptSema.less(
{a->archivePath(), a->memberPath(), a->inputSectionName()},
{b->archivePath(), b->memberPath(), b->inputSectionName()});
});
// Now try to arrange sections with no mapping rules to sections with
// similar content
auto p = this->_sections.begin();
// Find first section that has no assigned rule id
while (p != this->_sections.end()) {
auto *sect = dyn_cast<AtomSection<ELFT>>(*p);
if (!sect)
break;
if (!_linkerScriptSema.hasMapping({sect->archivePath(), sect->memberPath(),
sect->inputSectionName()}))
break;
++p;
}
// For all sections that have no assigned rule id, try to move them near a
// section with similar contents
if (p != this->_sections.begin()) {
for (; p != this->_sections.end(); ++p) {
auto q = p;
--q;
while (q != this->_sections.begin() &&
(*q)->getContentType() != (*p)->getContentType())
--q;
if ((*q)->getContentType() != (*p)->getContentType())
continue;
++q;
for (auto i = p; i != q;) {
auto next = i--;
std::iter_swap(i, next);
}
}
}
}
template <class ELFT>
const AtomLayout *
TargetLayout<ELFT>::findAtomLayoutByName(StringRef name) const {
for (auto sec : _sections)
if (auto section = dyn_cast<Section<ELFT>>(sec))
if (auto *al = section->findAtomLayoutByName(name))
return al;
return nullptr;
}
template <class ELFT>
void TargetLayout<ELFT>::addExtraChunksToSegment(Segment<ELFT> *segment,
StringRef archivePath,
StringRef memberPath,
StringRef sectionName) {
if (!_linkerScriptSema.hasLayoutCommands())
return;
std::vector<const script::SymbolAssignment *> exprs =
_linkerScriptSema.getExprs({archivePath, memberPath, sectionName});
for (auto expr : exprs) {
auto expChunk =
new (this->_allocator) ExpressionChunk<ELFT>(this->_ctx, expr);
segment->append(expChunk);
}
}
template <class ELFT>
RelocationTable<ELFT> *TargetLayout<ELFT>::getDynamicRelocationTable() {
if (!_dynamicRelocationTable) {
_dynamicRelocationTable = std::move(createRelocationTable(
_ctx.isRelaOutputFormat() ? ".rela.dyn" : ".rel.dyn",
ORDER_DYNAMIC_RELOCS));
addSection(_dynamicRelocationTable.get());
}
return _dynamicRelocationTable.get();
}
template <class ELFT>
RelocationTable<ELFT> *TargetLayout<ELFT>::getPLTRelocationTable() {
if (!_pltRelocationTable) {
_pltRelocationTable = std::move(createRelocationTable(
_ctx.isRelaOutputFormat() ? ".rela.plt" : ".rel.plt",
ORDER_DYNAMIC_PLT_RELOCS));
addSection(_pltRelocationTable.get());
}
return _pltRelocationTable.get();
}
template <class ELFT> uint64_t TargetLayout<ELFT>::getTLSSize() const {
for (const auto &phdr : *_programHeader)
if (phdr->p_type == llvm::ELF::PT_TLS)
return phdr->p_memsz;
return 0;
}
template class TargetLayout<ELF32LE>;
template class TargetLayout<ELF32BE>;
template class TargetLayout<ELF64LE>;
template class TargetLayout<ELF64BE>;
} // end namespace elf
} // end namespace lld