| //===- 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 |