diff options
Diffstat (limited to 'lib/ReaderWriter/ELF/SegmentChunks.h')
-rw-r--r-- | lib/ReaderWriter/ELF/SegmentChunks.h | 686 |
1 files changed, 686 insertions, 0 deletions
diff --git a/lib/ReaderWriter/ELF/SegmentChunks.h b/lib/ReaderWriter/ELF/SegmentChunks.h new file mode 100644 index 0000000000000..f2a975aaeed0b --- /dev/null +++ b/lib/ReaderWriter/ELF/SegmentChunks.h @@ -0,0 +1,686 @@ +//===- lib/ReaderWriter/ELF/SegmentChunks.h -------------------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLD_READER_WRITER_ELF_SEGMENT_CHUNKS_H +#define LLD_READER_WRITER_ELF_SEGMENT_CHUNKS_H + +#include "Chunk.h" +#include "Layout.h" +#include "SectionChunks.h" +#include "Writer.h" +#include "lld/Core/range.h" +#include "lld/Core/Writer.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Object/ELF.h" +#include "llvm/Support/Allocator.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/ELF.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/FileOutputBuffer.h" +#include <memory> + +namespace lld { +namespace elf { + +template <typename ELFT> class DefaultLayout; + +/// \brief A segment can be divided into segment slices +/// depending on how the segments can be split +template<class ELFT> +class SegmentSlice { +public: + typedef typename std::vector<Chunk<ELFT> *>::iterator SectionIter; + + SegmentSlice() { } + + /// Set the start of the slice. + void setStart(int32_t s) { _startSection = s; } + + // Set the segment slice start and end iterators. This is used to walk through + // the sections that are part of the Segment slice + void setSections(range<SectionIter> sections) { _sections = sections; } + + // Return the fileOffset of the slice + uint64_t fileOffset() const { return _offset; } + + void setFileOffset(uint64_t offset) { _offset = offset; } + + // Return the size of the slice + uint64_t fileSize() const { return _fsize; } + + void setFileSize(uint64_t filesz) { _fsize = filesz; } + + // Return the start of the slice + int32_t startSection() const { return _startSection; } + + // Return the start address of the slice + uint64_t virtualAddr() const { return _addr; } + + // Return the memory size of the slice + uint64_t memSize() const { return _memSize; } + + // Return the alignment of the slice + uint64_t alignment() const { return _alignment; } + + void setMemSize(uint64_t memsz) { _memSize = memsz; } + + void setVirtualAddr(uint64_t addr) { _addr = addr; } + + void setAlign(uint64_t align) { _alignment = align; } + + static bool compare_slices(SegmentSlice<ELFT> *a, SegmentSlice<ELFT> *b) { + return a->startSection() < b->startSection(); + } + + range<SectionIter> sections() { return _sections; } + +private: + range<SectionIter> _sections; + int32_t _startSection; + uint64_t _addr; + uint64_t _offset; + uint64_t _alignment; + uint64_t _fsize; + uint64_t _memSize; +}; + +/// \brief A segment contains a set of sections, that have similar properties +// the sections are already separated based on different flags and properties +// the segment is just a way to concatenate sections to segments +template<class ELFT> +class Segment : public Chunk<ELFT> { +public: + typedef typename std::vector<SegmentSlice<ELFT> *>::iterator SliceIter; + typedef typename std::vector<Chunk<ELFT> *>::iterator SectionIter; + + Segment(const ELFLinkingContext &context, StringRef name, + const Layout::SegmentType type); + + /// \brief the Order of segments that appear in the output file + enum SegmentOrder { + permUnknown, + permRWX, + permRX, + permR, + permRWL, + permRW, + permNonAccess + }; + + /// append a section to a segment + virtual void append(Chunk<ELFT> *chunk); + + /// Sort segments depending on the property + /// If we have a Program Header segment, it should appear first + /// If we have a INTERP segment, that should appear after the Program Header + /// All Loadable segments appear next in this order + /// All Read Write Execute segments follow + /// All Read Execute segments appear next + /// All Read only segments appear first + /// All Write execute segments follow + static bool compareSegments(Segment<ELFT> *sega, Segment<ELFT> *segb); + + /// \brief Start assigning file offset to the segment chunks The fileoffset + /// needs to be page at the start of the segment and in addition the + /// fileoffset needs to be aligned to the max section alignment within the + /// segment. This is required so that the ELF property p_poffset % p_align = + /// p_vaddr mod p_align holds true. + /// The algorithm starts off by assigning the startOffset thats passed in as + /// parameter to the first section in the segment, if the difference between + /// the newly computed offset is greater than a page, then we create a segment + /// slice, as it would be a waste of virtual memory just to be filled with + /// zeroes + void assignFileOffsets(uint64_t startOffset); + + /// \brief Assign virtual addresses to the slices + void assignVirtualAddress(uint64_t addr); + + // Write the Segment + void write(ELFWriter *writer, TargetLayout<ELFT> &layout, + llvm::FileOutputBuffer &buffer); + + int64_t flags() const; + + /// Prepend a generic chunk to the segment. + void prepend(Chunk<ELFT> *c) { + _sections.insert(_sections.begin(), c); + } + + /// Finalize the segment before assigning File Offsets / Virtual addresses + void doPreFlight() {} + + /// Finalize the segment, before we want to write the segment header + /// information + void finalize() { + // We want to finalize the segment values for now only for non loadable + // segments, since those values are not set in the Layout + if (_segmentType == llvm::ELF::PT_LOAD) + return; + // The size is the difference of the + // last section to the first section, especially for TLS because + // the TLS segment contains both .tdata/.tbss + this->setFileOffset(_sections.front()->fileOffset()); + this->setVirtualAddr(_sections.front()->virtualAddr()); + size_t startFileOffset = _sections.front()->fileOffset(); + size_t startAddr = _sections.front()->virtualAddr(); + for (auto ai : _sections) { + this->_fsize = ai->fileOffset() + ai->fileSize() - startFileOffset; + this->_msize = ai->virtualAddr() + ai->memSize() - startAddr; + } + } + + // For LLVM RTTI + static bool classof(const Chunk<ELFT> *c) { + return c->kind() == Chunk<ELFT>::Kind::ELFSegment; + } + + // Getters + int32_t sectionCount() const { return _sections.size(); } + + /// \brief, this function returns the type of segment (PT_*) + Layout::SegmentType segmentType() { return _segmentType; } + + /// \brief return the segment type depending on the content, + /// If the content corresponds to Code, this will return Segment::Code + /// If the content corresponds to Data, this will return Segment::Data + /// If the content corresponds to TLS, this will return Segment::TLS + virtual int getContentType() const { + int64_t fl = flags(); + switch (_segmentType) { + case llvm::ELF::PT_LOAD: { + if (fl && llvm::ELF::PF_X) + return Chunk<ELFT>::ContentType::Code; + if (fl && llvm::ELF::PF_W) + return Chunk<ELFT>::ContentType::Data; + } + case llvm::ELF::PT_TLS: + return Chunk<ELFT>::ContentType::TLS; + case llvm::ELF::PT_NOTE: + return Chunk<ELFT>::ContentType::Note; + default: + return Chunk<ELFT>::ContentType::Unknown; + } + } + + int pageSize() const { return this->_context.getPageSize(); } + + int rawflags() const { return _atomflags; } + + int64_t atomflags() const { + switch (_atomflags) { + + case DefinedAtom::permUnknown: + return permUnknown; + + case DefinedAtom::permRWX: + return permRWX; + + case DefinedAtom::permR_X: + return permRX; + + case DefinedAtom::permR__: + return permR; + + case DefinedAtom::permRW_L: + return permRWL; + + case DefinedAtom::permRW_: + return permRW; + + case DefinedAtom::perm___: + default: + return permNonAccess; + } + } + + int64_t numSlices() const { return _segmentSlices.size(); } + + range<SliceIter> slices() { return _segmentSlices; } + + Chunk<ELFT> *firstSection() { return _sections[0]; } + +private: + + /// \brief Check if the chunk needs to be aligned + bool needAlign(Chunk<ELFT> *chunk) const { + if (chunk->getContentType() == Chunk<ELFT>::ContentType::Data && + _outputMagic == ELFLinkingContext::OutputMagic::NMAGIC) + return true; + return false; + } + + // Cached value of outputMagic + ELFLinkingContext::OutputMagic _outputMagic; + +protected: + /// \brief Section or some other chunk type. + std::vector<Chunk<ELFT> *> _sections; + std::vector<SegmentSlice<ELFT> *> _segmentSlices; + Layout::SegmentType _segmentType; + uint64_t _flags; + int64_t _atomflags; + llvm::BumpPtrAllocator _segmentAllocate; +}; + +/// This chunk represents a linker script expression that needs to be calculated +/// at the time the virtual addresses for the parent segment are being assigned. +template <class ELFT> class ExpressionChunk : public Chunk<ELFT> { +public: + ExpressionChunk(ELFLinkingContext &ctx, const script::SymbolAssignment *expr) + : Chunk<ELFT>(StringRef(), Chunk<ELFT>::Kind::Expression, ctx), + _expr(expr), _linkerScriptSema(ctx.linkerScriptSema()) { + this->_alignment = 1; + } + + static bool classof(const Chunk<ELFT> *c) { + return c->kind() == Chunk<ELFT>::Kind::Expression; + } + + int getContentType() const override { + return Chunk<ELFT>::ContentType::Unknown; + } + void write(ELFWriter *, TargetLayout<ELFT> &, + llvm::FileOutputBuffer &) override {} + void doPreFlight() override {} + void finalize() override {} + + std::error_code evalExpr(uint64_t &curPos) { + return _linkerScriptSema.evalExpr(_expr, curPos); + } + +private: + const script::SymbolAssignment *_expr; + script::Sema &_linkerScriptSema; +}; + +/// \brief A Program Header segment contains a set of chunks instead of sections +/// The segment doesn't contain any slice +template <class ELFT> class ProgramHeaderSegment : public Segment<ELFT> { +public: + ProgramHeaderSegment(const ELFLinkingContext &context) + : Segment<ELFT>(context, "PHDR", llvm::ELF::PT_PHDR) { + this->_alignment = 8; + this->_flags = (llvm::ELF::SHF_ALLOC | llvm::ELF::SHF_EXECINSTR); + } + + /// Finalize the segment, before we want to write the segment header + /// information + void finalize() { + // If the segment is of type Program Header, then the values fileOffset + // and the fileSize need to be picked up from the last section, the first + // section points to the ELF header and the second chunk points to the + // actual program headers + this->setFileOffset(this->_sections.back()->fileOffset()); + this->setVirtualAddr(this->_sections.back()->virtualAddr()); + this->_fsize = this->_sections.back()->fileSize(); + this->_msize = this->_sections.back()->memSize(); + } + +}; + +template <class ELFT> +Segment<ELFT>::Segment(const ELFLinkingContext &context, StringRef name, + const Layout::SegmentType type) + : Chunk<ELFT>(name, Chunk<ELFT>::Kind::ELFSegment, context), + _segmentType(type), _flags(0), _atomflags(0) { + this->_alignment = 0; + this->_fsize = 0; + _outputMagic = context.getOutputMagic(); +} + +// This function actually is used, but not in all instantiations of Segment. +LLVM_ATTRIBUTE_UNUSED +static DefinedAtom::ContentPermissions toAtomPerms(uint64_t flags) { + switch (flags & (SHF_ALLOC | SHF_WRITE | SHF_EXECINSTR)) { + case SHF_ALLOC | SHF_WRITE | SHF_EXECINSTR: + return DefinedAtom::permRWX; + case SHF_ALLOC | SHF_EXECINSTR: + return DefinedAtom::permR_X; + case SHF_ALLOC: + return DefinedAtom::permR__; + case SHF_ALLOC | SHF_WRITE: + return DefinedAtom::permRW_; + default: + return DefinedAtom::permUnknown; + } +} + +template <class ELFT> void Segment<ELFT>::append(Chunk<ELFT> *chunk) { + _sections.push_back(chunk); + Section<ELFT> *section = dyn_cast<Section<ELFT>>(chunk); + if (!section) + return; + if (_flags < section->getFlags()) + _flags |= section->getFlags(); + if (_atomflags < toAtomPerms(_flags)) + _atomflags = toAtomPerms(_flags); + if (this->_alignment < section->alignment()) + this->_alignment = section->alignment(); +} + +template <class ELFT> +bool Segment<ELFT>::compareSegments(Segment<ELFT> *sega, Segment<ELFT> *segb) { + int64_t type1 = sega->segmentType(); + int64_t type2 = segb->segmentType(); + + if (type1 == type2) + return sega->atomflags() < segb->atomflags(); + + // The single PT_PHDR segment is required to precede any loadable + // segment. We simply make it always first. + if (type1 == llvm::ELF::PT_PHDR) + return true; + if (type2 == llvm::ELF::PT_PHDR) + return false; + + // The single PT_INTERP segment is required to precede any loadable + // segment. We simply make it always second. + if (type1 == llvm::ELF::PT_INTERP) + return true; + if (type2 == llvm::ELF::PT_INTERP) + return false; + + // We then put PT_LOAD segments before any other segments. + if (type1 == llvm::ELF::PT_LOAD) + return true; + if (type2 == llvm::ELF::PT_LOAD) + return false; + + // We put the PT_GNU_RELRO segment last, because that is where the + // dynamic linker expects to find it + if (type1 == llvm::ELF::PT_GNU_RELRO) + return false; + if (type2 == llvm::ELF::PT_GNU_RELRO) + return true; + + // We put the PT_TLS segment last except for the PT_GNU_RELRO + // segment, because that is where the dynamic linker expects to find + if (type1 == llvm::ELF::PT_TLS) + return false; + if (type2 == llvm::ELF::PT_TLS) + return true; + + // Otherwise compare the types to establish an arbitrary ordering. + // FIXME: Should figure out if we should just make all other types compare + // equal, but if so, we should probably do the same for atom flags and change + // users of this to use stable_sort. + return type1 < type2; +} + +template <class ELFT> +void Segment<ELFT>::assignFileOffsets(uint64_t startOffset) { + uint64_t fileOffset = startOffset; + uint64_t curSliceFileOffset = fileOffset; + bool isDataPageAlignedForNMagic = false; + bool alignSegments = this->_context.alignSegments(); + uint64_t p_align = this->_context.getPageSize(); + uint64_t lastVirtualAddress = 0; + + this->setFileOffset(startOffset); + for (auto &slice : slices()) { + bool isFirstSection = true; + for (auto section : slice->sections()) { + // Handle linker script expressions, which may change the offset + if (!isFirstSection) + if (auto expr = dyn_cast<ExpressionChunk<ELFT>>(section)) + fileOffset += expr->virtualAddr() - lastVirtualAddress; + // Align fileoffset to the alignment of the section. + fileOffset = llvm::RoundUpToAlignment(fileOffset, section->alignment()); + // If the linker outputmagic is set to OutputMagic::NMAGIC, align the Data + // to a page boundary + if (isFirstSection && + _outputMagic != ELFLinkingContext::OutputMagic::NMAGIC && + _outputMagic != ELFLinkingContext::OutputMagic::OMAGIC) { + // Align to a page only if the output is not + // OutputMagic::NMAGIC/OutputMagic::OMAGIC + if (alignSegments) + fileOffset = llvm::RoundUpToAlignment(fileOffset, p_align); + else { + // Align according to ELF spec. + // in p75, http://www.sco.com/developers/devspecs/gabi41.pdf + uint64_t virtualAddress = slice->virtualAddr(); + Section<ELFT> *sect = dyn_cast<Section<ELFT>>(section); + if (sect && sect->isLoadableSection() && + ((virtualAddress & (p_align - 1)) != + (fileOffset & (p_align - 1)))) + fileOffset = llvm::RoundUpToAlignment(fileOffset, p_align) + + (virtualAddress % p_align); + } + } else if (!isDataPageAlignedForNMagic && needAlign(section)) { + fileOffset = + llvm::RoundUpToAlignment(fileOffset, this->_context.getPageSize()); + isDataPageAlignedForNMagic = true; + } + if (isFirstSection) { + slice->setFileOffset(fileOffset); + isFirstSection = false; + curSliceFileOffset = fileOffset; + } + section->setFileOffset(fileOffset); + fileOffset += section->fileSize(); + lastVirtualAddress = section->virtualAddr() + section->memSize(); + } + slice->setFileSize(fileOffset - curSliceFileOffset); + } + this->setFileSize(fileOffset - startOffset); +} + +/// \brief Assign virtual addresses to the slices +template <class ELFT> void Segment<ELFT>::assignVirtualAddress(uint64_t addr) { + int startSection = 0; + int currSection = 0; + SectionIter startSectionIter; + + // slice align is set to the max alignment of the chunks that are + // contained in the slice + uint64_t sliceAlign = 0; + // Current slice size + uint64_t curSliceSize = 0; + // Current Slice File Offset + uint64_t curSliceAddress = 0; + + startSectionIter = _sections.begin(); + startSection = 0; + bool isFirstSection = true; + bool isDataPageAlignedForNMagic = false; + uint64_t startAddr = addr; + SegmentSlice<ELFT> *slice = nullptr; + uint64_t tlsStartAddr = 0; + bool alignSegments = this->_context.alignSegments(); + StringRef prevOutputSectionName = StringRef(); + + for (auto si = _sections.begin(); si != _sections.end(); ++si) { + // If this is first section in the segment, page align the section start + // address. The linker needs to align the data section to a page boundary + // only if NMAGIC is set. + if (isFirstSection) { + isFirstSection = false; + if (alignSegments && + _outputMagic != ELFLinkingContext::OutputMagic::NMAGIC && + _outputMagic != ELFLinkingContext::OutputMagic::OMAGIC) + // Align to a page only if the output is not + // OutputMagic::NMAGIC/OutputMagic::OMAGIC + startAddr = + llvm::RoundUpToAlignment(startAddr, this->_context.getPageSize()); + else if (!isDataPageAlignedForNMagic && needAlign(*si)) { + // If the linker outputmagic is set to OutputMagic::NMAGIC, align the + // Data to a page boundary. + startAddr = + llvm::RoundUpToAlignment(startAddr, this->_context.getPageSize()); + isDataPageAlignedForNMagic = true; + } + // align the startOffset to the section alignment + uint64_t newAddr = llvm::RoundUpToAlignment(startAddr, (*si)->alignment()); + // Handle linker script expressions, which *may update newAddr* if the + // expression assigns to "." + if (auto expr = dyn_cast<ExpressionChunk<ELFT>>(*si)) + expr->evalExpr(newAddr); + curSliceAddress = newAddr; + sliceAlign = (*si)->alignment(); + (*si)->setVirtualAddr(curSliceAddress); + + // Handle TLS. + if (auto section = dyn_cast<Section<ELFT>>(*si)) { + if (section->getSegmentType() == llvm::ELF::PT_TLS) { + tlsStartAddr = + llvm::RoundUpToAlignment(tlsStartAddr, (*si)->alignment()); + section->assignVirtualAddress(tlsStartAddr); + tlsStartAddr += (*si)->memSize(); + } else { + section->assignVirtualAddress(newAddr); + } + } + // TBSS section is special in that it doesn't contribute to memory of any + // segment. If we see a tbss section, don't add memory size to addr The + // fileOffset is automatically taken care of since TBSS section does not + // end up using file size + if ((*si)->order() != DefaultLayout<ELFT>::ORDER_TBSS) + curSliceSize = (*si)->memSize(); + } else { + uint64_t curAddr = curSliceAddress + curSliceSize; + if (!isDataPageAlignedForNMagic && needAlign(*si)) { + // If the linker outputmagic is set to OutputMagic::NMAGIC, align the + // Data + // to a page boundary + curAddr = + llvm::RoundUpToAlignment(curAddr, this->_context.getPageSize()); + isDataPageAlignedForNMagic = true; + } + uint64_t newAddr = llvm::RoundUpToAlignment(curAddr, (*si)->alignment()); + // Handle linker script expressions, which *may update newAddr* if the + // expression assigns to "." + if (auto expr = dyn_cast<ExpressionChunk<ELFT>>(*si)) + expr->evalExpr(newAddr); + Section<ELFT> *sec = dyn_cast<Section<ELFT>>(*si); + StringRef curOutputSectionName; + if (sec) + curOutputSectionName = sec->outputSectionName(); + else { + // If this is a linker script expression, propagate the name of the + // previous section instead + if (isa<ExpressionChunk<ELFT>>(*si)) + curOutputSectionName = prevOutputSectionName; + else + curOutputSectionName = (*si)->name(); + } + bool autoCreateSlice = true; + if (curOutputSectionName == prevOutputSectionName) + autoCreateSlice = false; + // If the newAddress computed is more than a page away, let's create + // a separate segment, so that memory is not used up while running. + // Dont create a slice, if the new section falls in the same output + // section as the previous section. + if (autoCreateSlice && + ((newAddr - curAddr) > this->_context.getPageSize()) && + (_outputMagic != ELFLinkingContext::OutputMagic::NMAGIC && + _outputMagic != ELFLinkingContext::OutputMagic::OMAGIC)) { + auto sliceIter = + std::find_if(_segmentSlices.begin(), _segmentSlices.end(), + [startSection](SegmentSlice<ELFT> *s) -> bool { + return s->startSection() == startSection; + }); + if (sliceIter == _segmentSlices.end()) { + slice = new (_segmentAllocate.Allocate<SegmentSlice<ELFT>>()) + SegmentSlice<ELFT>(); + _segmentSlices.push_back(slice); + } else { + slice = (*sliceIter); + } + slice->setStart(startSection); + slice->setSections(make_range(startSectionIter, si)); + slice->setMemSize(curSliceSize); + slice->setAlign(sliceAlign); + slice->setVirtualAddr(curSliceAddress); + // Start new slice + curSliceAddress = newAddr; + (*si)->setVirtualAddr(curSliceAddress); + startSectionIter = si; + startSection = currSection; + if (auto section = dyn_cast<Section<ELFT>>(*si)) + section->assignVirtualAddress(newAddr); + curSliceSize = newAddr - curSliceAddress + (*si)->memSize(); + sliceAlign = (*si)->alignment(); + } else { + if (sliceAlign < (*si)->alignment()) + sliceAlign = (*si)->alignment(); + (*si)->setVirtualAddr(newAddr); + // Handle TLS. + if (auto section = dyn_cast<Section<ELFT>>(*si)) { + if (section->getSegmentType() == llvm::ELF::PT_TLS) { + tlsStartAddr = + llvm::RoundUpToAlignment(tlsStartAddr, (*si)->alignment()); + section->assignVirtualAddress(tlsStartAddr); + tlsStartAddr += (*si)->memSize(); + } else { + section->assignVirtualAddress(newAddr); + } + } + // TBSS section is special in that it doesn't contribute to memory of + // any segment. If we see a tbss section, don't add memory size to addr + // The fileOffset is automatically taken care of since TBSS section does + // not end up using file size. + if ((*si)->order() != DefaultLayout<ELFT>::ORDER_TBSS) + curSliceSize = newAddr - curSliceAddress + (*si)->memSize(); + else + curSliceSize = newAddr - curSliceAddress; + } + prevOutputSectionName = curOutputSectionName; + } + currSection++; + } + auto sliceIter = std::find_if(_segmentSlices.begin(), _segmentSlices.end(), + [startSection](SegmentSlice<ELFT> *s) -> bool { + return s->startSection() == startSection; + }); + if (sliceIter == _segmentSlices.end()) { + slice = new (_segmentAllocate.Allocate<SegmentSlice<ELFT>>()) + SegmentSlice<ELFT>(); + _segmentSlices.push_back(slice); + } else { + slice = (*sliceIter); + } + slice->setStart(startSection); + slice->setVirtualAddr(curSliceAddress); + slice->setMemSize(curSliceSize); + slice->setSections(make_range(startSectionIter, _sections.end())); + slice->setAlign(sliceAlign); + + // Set the segment memory size and the virtual address. + this->setMemSize(curSliceAddress - startAddr + curSliceSize); + this->setVirtualAddr(curSliceAddress); + std::stable_sort(_segmentSlices.begin(), _segmentSlices.end(), + SegmentSlice<ELFT>::compare_slices); +} + +// Write the Segment +template <class ELFT> +void Segment<ELFT>::write(ELFWriter *writer, TargetLayout<ELFT> &layout, + llvm::FileOutputBuffer &buffer) { + for (auto slice : slices()) + for (auto section : slice->sections()) + section->write(writer, layout, buffer); +} + +template<class ELFT> +int64_t +Segment<ELFT>::flags() const { + int64_t fl = 0; + if (_flags & llvm::ELF::SHF_ALLOC) + fl |= llvm::ELF::PF_R; + if (_flags & llvm::ELF::SHF_WRITE) + fl |= llvm::ELF::PF_W; + if (_flags & llvm::ELF::SHF_EXECINSTR) + fl |= llvm::ELF::PF_X; + return fl; +} +} // end namespace elf +} // end namespace lld + +#endif |