298 lines
8.6 KiB
C++
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
|