llvm-project/lld/lib/ReaderWriter/ELF/Atoms.cpp

298 lines
8.6 KiB
C++

//===- lib/ReaderWriter/ELF/Atoms.cpp -------------------------------------===//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "Atoms.h"
#include "DynamicFile.h"
#include "ELFFile.h"
#include "TargetHandler.h"
namespace lld {
namespace elf {
template <class ELFT> AbsoluteAtom::Scope ELFAbsoluteAtom<ELFT>::scope() const {
if (_symbol->getVisibility() == llvm::ELF::STV_HIDDEN)
return scopeLinkageUnit;
if (_symbol->getBinding() == llvm::ELF::STB_LOCAL)
return scopeTranslationUnit;
return scopeGlobal;
}
template <class ELFT>
UndefinedAtom::CanBeNull ELFUndefinedAtom<ELFT>::canBeNull() const {
if (_symbol->getBinding() == llvm::ELF::STB_WEAK)
return CanBeNull::canBeNullAtBuildtime;
return CanBeNull::canBeNullNever;
}
template <class ELFT> uint64_t ELFDefinedAtom<ELFT>::size() const {
// Common symbols are not allocated in object files,
// so use st_size to tell how many bytes are required.
if (_symbol && (_symbol->getType() == llvm::ELF::STT_COMMON ||
_symbol->st_shndx == llvm::ELF::SHN_COMMON))
return (uint64_t)_symbol->st_size;
return _contentData.size();
}
template <class ELFT> AbsoluteAtom::Scope ELFDefinedAtom<ELFT>::scope() const {
if (!_symbol)
return scopeGlobal;
if (_symbol->getVisibility() == llvm::ELF::STV_HIDDEN)
return scopeLinkageUnit;
if (_symbol->getBinding() != llvm::ELF::STB_LOCAL)
return scopeGlobal;
return scopeTranslationUnit;
}
template <class ELFT> DefinedAtom::Merge ELFDefinedAtom<ELFT>::merge() const {
if (!_symbol)
return mergeNo;
if (_symbol->getBinding() == llvm::ELF::STB_WEAK)
return mergeAsWeak;
if (_symbol->getType() == llvm::ELF::STT_COMMON ||
_symbol->st_shndx == llvm::ELF::SHN_COMMON)
return mergeAsTentative;
return mergeNo;
}
template <class ELFT>
DefinedAtom::ContentType ELFDefinedAtom<ELFT>::doContentType() const {
using namespace llvm::ELF;
if (_section->sh_type == SHT_GROUP)
return typeGroupComdat;
if (!_symbol && _sectionName.startswith(".gnu.linkonce"))
return typeGnuLinkOnce;
uint64_t flags = _section->sh_flags;
if (!(flags & SHF_ALLOC)) {
if (_section->sh_type == SHT_NOTE)
return (flags == SHF_WRITE) ? typeRWNote : typeRONote;
return _contentType = typeNoAlloc;
}
if (_section->sh_flags == (SHF_ALLOC | SHF_WRITE | SHF_TLS))
return _section->sh_type == SHT_NOBITS ? typeThreadZeroFill
: typeThreadData;
if (_section->sh_flags == SHF_ALLOC && _section->sh_type == SHT_PROGBITS)
return _contentType = typeConstant;
if (_symbol->getType() == STT_GNU_IFUNC)
return _contentType = typeResolver;
if (_symbol->st_shndx == SHN_COMMON)
return _contentType = typeZeroFill;
if (_section->sh_type == SHT_PROGBITS) {
flags &= ~SHF_ALLOC;
flags &= ~SHF_GROUP;
if ((flags & SHF_STRINGS) || (flags & SHF_MERGE))
return typeConstant;
if (flags == SHF_WRITE)
return typeData;
return typeCode;
}
if (_section->sh_type == SHT_NOTE) {
flags &= ~SHF_ALLOC;
return (flags == SHF_WRITE) ? typeRWNote : typeRONote;
}
if (_section->sh_type == SHT_NOBITS)
return typeZeroFill;
if (_section->sh_type == SHT_NULL)
if (_symbol->getType() == STT_COMMON || _symbol->st_shndx == SHN_COMMON)
return typeZeroFill;
if (_section->sh_type == SHT_INIT_ARRAY ||
_section->sh_type == SHT_FINI_ARRAY)
return typeData;
return typeUnknown;
}
template <class ELFT>
DefinedAtom::ContentType ELFDefinedAtom<ELFT>::contentType() const {
if (_contentType != typeUnknown)
return _contentType;
_contentType = doContentType();
return _contentType;
}
template <class ELFT>
DefinedAtom::Alignment ELFDefinedAtom<ELFT>::alignment() const {
if (!_symbol)
return 1;
// Obtain proper value of st_value field.
const auto symValue = getSymbolValue();
// Unallocated common symbols specify their alignment constraints in
// st_value.
if ((_symbol->getType() == llvm::ELF::STT_COMMON) ||
_symbol->st_shndx == llvm::ELF::SHN_COMMON) {
return symValue;
}
if (_section->sh_addralign == 0) {
// sh_addralign of 0 means no alignment
return Alignment(1, symValue);
}
return Alignment(_section->sh_addralign, symValue % _section->sh_addralign);
}
// Do we have a choice for ELF? All symbols live in explicit sections.
template <class ELFT>
DefinedAtom::SectionChoice ELFDefinedAtom<ELFT>::sectionChoice() const {
switch (contentType()) {
case typeCode:
case typeData:
case typeZeroFill:
case typeThreadZeroFill:
case typeThreadData:
case typeConstant:
if ((_sectionName == ".text") || (_sectionName == ".data") ||
(_sectionName == ".bss") || (_sectionName == ".rodata") ||
(_sectionName == ".tdata") || (_sectionName == ".tbss"))
return sectionBasedOnContent;
default:
break;
}
return sectionCustomRequired;
}
template <class ELFT>
StringRef ELFDefinedAtom<ELFT>::customSectionName() const {
if ((contentType() == typeZeroFill) ||
(_symbol && _symbol->st_shndx == llvm::ELF::SHN_COMMON))
return ".bss";
return _sectionName;
}
template <class ELFT>
DefinedAtom::ContentPermissions ELFDefinedAtom<ELFT>::permissions() const {
if (_permissions != permUnknown)
return _permissions;
uint64_t flags = _section->sh_flags;
if (!(flags & llvm::ELF::SHF_ALLOC))
return _permissions = perm___;
switch (_section->sh_type) {
// permRW_L is for sections modified by the runtime
// loader.
case llvm::ELF::SHT_REL:
case llvm::ELF::SHT_RELA:
return _permissions = permRW_L;
case llvm::ELF::SHT_DYNAMIC:
case llvm::ELF::SHT_PROGBITS:
case llvm::ELF::SHT_NOTE:
flags &= ~llvm::ELF::SHF_ALLOC;
flags &= ~llvm::ELF::SHF_GROUP;
switch (flags) {
// Code
case llvm::ELF::SHF_EXECINSTR:
return _permissions = permR_X;
case (llvm::ELF::SHF_WRITE | llvm::ELF::SHF_EXECINSTR):
return _permissions = permRWX;
// Data
case llvm::ELF::SHF_WRITE:
return _permissions = permRW_;
// Strings
case llvm::ELF::SHF_MERGE:
case llvm::ELF::SHF_STRINGS:
return _permissions = permR__;
default:
if (flags & llvm::ELF::SHF_WRITE)
return _permissions = permRW_;
return _permissions = permR__;
}
case llvm::ELF::SHT_NOBITS:
return _permissions = permRW_;
case llvm::ELF::SHT_INIT_ARRAY:
case llvm::ELF::SHT_FINI_ARRAY:
return _permissions = permRW_;
default:
return _permissions = perm___;
}
}
template <class ELFT>
DefinedAtom::reference_iterator ELFDefinedAtom<ELFT>::begin() const {
uintptr_t index = _referenceStartIndex;
const void *it = reinterpret_cast<const void *>(index);
return reference_iterator(*this, it);
}
template <class ELFT>
DefinedAtom::reference_iterator ELFDefinedAtom<ELFT>::end() const {
uintptr_t index = _referenceEndIndex;
const void *it = reinterpret_cast<const void *>(index);
return reference_iterator(*this, it);
}
template <class ELFT>
const Reference *ELFDefinedAtom<ELFT>::derefIterator(const void *It) const {
uintptr_t index = reinterpret_cast<uintptr_t>(It);
assert(index >= _referenceStartIndex);
assert(index < _referenceEndIndex);
return ((_referenceList)[index]);
}
template <class ELFT>
void ELFDefinedAtom<ELFT>::incrementIterator(const void *&It) const {
uintptr_t index = reinterpret_cast<uintptr_t>(It);
++index;
It = reinterpret_cast<const void *>(index);
}
template <class ELFT>
void ELFDefinedAtom<ELFT>::addReference(ELFReference<ELFT> *reference) {
_referenceList.push_back(reference);
_referenceEndIndex = _referenceList.size();
}
template <class ELFT> AbsoluteAtom::Scope ELFDynamicAtom<ELFT>::scope() const {
if (_symbol->getVisibility() == llvm::ELF::STV_HIDDEN)
return scopeLinkageUnit;
if (_symbol->getBinding() != llvm::ELF::STB_LOCAL)
return scopeGlobal;
return scopeTranslationUnit;
}
template <class ELFT>
SharedLibraryAtom::Type ELFDynamicAtom<ELFT>::type() const {
switch (_symbol->getType()) {
case llvm::ELF::STT_FUNC:
case llvm::ELF::STT_GNU_IFUNC:
return Type::Code;
case llvm::ELF::STT_OBJECT:
return Type::Data;
default:
return Type::Unknown;
}
}
#define INSTANTIATE(klass) \
template class klass<ELF32LE>; \
template class klass<ELF32BE>; \
template class klass<ELF64LE>; \
template class klass<ELF64BE>
INSTANTIATE(ELFAbsoluteAtom);
INSTANTIATE(ELFDefinedAtom);
INSTANTIATE(ELFDynamicAtom);
INSTANTIATE(ELFUndefinedAtom);
} // end namespace elf
} // end namespace lld