summaryrefslogtreecommitdiff
path: root/lib/ReaderWriter/ELF/Atoms.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/ReaderWriter/ELF/Atoms.cpp')
-rw-r--r--lib/ReaderWriter/ELF/Atoms.cpp297
1 files changed, 297 insertions, 0 deletions
diff --git a/lib/ReaderWriter/ELF/Atoms.cpp b/lib/ReaderWriter/ELF/Atoms.cpp
new file mode 100644
index 000000000000..639633393161
--- /dev/null
+++ b/lib/ReaderWriter/ELF/Atoms.cpp
@@ -0,0 +1,297 @@
+//===- 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