| // Copyright 2016 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #pragma once |
| |
| #include <climits> |
| #include <cstddef> |
| #include <cstdint> |
| #ifdef __APPLE__ |
| // TODO(dje): Private copy until available on osx. |
| #include <garnet/lib/debugger_utils/third_party/musl/include/elf.h> |
| #else |
| #include <elf.h> |
| #endif |
| #include <memory> |
| #include <string> |
| |
| #include "byte_block.h" |
| |
| namespace debugger_utils { |
| |
| // 32+64 support, bi-endian, mmap support can come later when needed |
| |
| #if UINT_MAX == ULONG_MAX |
| |
| using ElfHeader = Elf32_Ehdr; |
| using ElfSegmentHeader = Elf32_Phdr; |
| using ElfSectionHeader = Elf32_Shdr; |
| using ElfRawSymbol = Elf32_Sym; |
| |
| #else |
| |
| using ElfHeader = Elf64_Ehdr; |
| using ElfSegmentHeader = Elf64_Phdr; |
| using ElfSectionHeader = Elf64_Shdr; |
| using ElfRawSymbol = Elf64_Sym; |
| |
| #endif |
| |
| enum class ElfError { |
| OK = 0, |
| IO, |
| BADELF, |
| NOMEM, |
| }; |
| |
| const char* ElfErrorName(ElfError er); |
| |
| class ElfReader; |
| |
| class ElfSectionContents { |
| public: |
| ~ElfSectionContents(); |
| |
| // Return the size in bytes of the section. |
| // TODO(dje): 32x64 |
| size_t GetSize() const { return header_.sh_size; } |
| |
| // Return the number of entries in the section, assuming the section is |
| // one that has "entries". E.g., symbol sections have entries, text sections |
| // do not. For sections that don't have "entries" zero is returned. |
| size_t GetNumEntries() const; |
| |
| // Fetch symbol |entry_number|. |
| // The section must have type SHT_SYMTAB or SHT_DYNSYM. |
| // WARNING: Space for the result may be reused for each call. |
| // [We don't byteswap today, but when we do that is how this will work. |
| // Symbols are generally used to create internal symbol tables and thus |
| // are generally discarded immediately after use.] |
| const ElfRawSymbol& GetSymbolEntry(size_t entry_number); |
| |
| const ElfSectionHeader& header() const { return header_; } |
| |
| const void* contents() const { return contents_; } |
| |
| private: |
| friend class ElfReader; |
| |
| // Takes ownership of |contents|. |
| // TODO(dje): separate method for mmap |
| ElfSectionContents(const ElfSectionHeader& header, void* contents); |
| |
| // A copy is made of the header to separate the lifetime of the section's |
| // contents from Reader. We could just save the pieces we use/need but |
| // this is simple enough and saves us from having to continually add more. |
| // Note that while we don't byteswap today, ElfSectionHeader contains the |
| // ready-to-use version. |
| const ElfSectionHeader header_; |
| void* contents_; |
| }; |
| |
| class ElfReader { |
| public: |
| static ElfError Create(const std::string& file_name, |
| std::shared_ptr<ByteBlock> byte_block, |
| uint32_t options, uint64_t base, |
| std::unique_ptr<ElfReader>* out); |
| ~ElfReader(); |
| |
| const std::string& file_name() const { return file_name_; } |
| |
| // Read the ELF header at offset |base| in |m|. |
| // The header is written in to |hdr|. |
| static bool ReadHeader(const ByteBlock& m, uint64_t base, ElfHeader* hdr); |
| |
| // Return true if |hdr| is a valid ELF header. |
| static bool VerifyHeader(const ElfHeader* hdr); |
| |
| const ElfHeader& header() { return header_; } |
| |
| // Return the number of program segments. |
| size_t GetNumSegments() const { return header_.e_phnum; } |
| |
| // Read the program segment headers in. |
| // This is a no-op if they are already read in. |
| // This must be called before any call to GetSegment(). |
| ElfError ReadSegmentHeaders(); |
| |
| // Free space allocated by ReadSegmentHeaders(); |
| void FreeSegmentHeaders(); |
| |
| // Return the program segment header of |segment_number|. |
| // |segment_number| must be valid, and ReadSegmentHeaders() must have |
| // already been called. |
| const ElfSegmentHeader& GetSegmentHeader(size_t segment_number); |
| |
| // Return the number of sections. |
| size_t GetNumSections() const { return header_.e_shnum; } |
| |
| // Read the section headers in. |
| // This is a no-op if they are already read in. |
| // This must be called before any call to GetSection(). |
| ElfError ReadSectionHeaders(); |
| |
| // Free space allocated by ReadSectionHeaders(); |
| void FreeSectionHeaders(); |
| |
| // Return the section header of |section_number|. |
| // |section_number| must be valid, and ReadSectionHeaders() must have |
| // already been called. |
| const ElfSectionHeader& GetSectionHeader(size_t section_number); |
| |
| // Return the section header with type |type|, an SHT_* value. |
| // Returns nullptr if not found. |
| const ElfSectionHeader* GetSectionHeaderByType(unsigned type); |
| |
| // Fetch the contents of |sh|. |
| // This version malloc's space for the section, reads the contents into |
| // the buffer, and assigns it to SectionContents. |
| ElfError GetSectionContents( |
| const ElfSectionHeader& sh, |
| std::unique_ptr<ElfSectionContents>* out_contents); |
| |
| // Maximum length in bytes of a build id. |
| static constexpr size_t kMaxBuildIdSize = 64; |
| |
| // Store the build id, if present, in |buf|. |
| // |buf_size| must be at least kMaxBuildIdSize * 2 + 1. |
| // If a build id is not found |buf| is "" and OK is returned. |
| // TODO(dje): As with other changes deferred for later, |
| // one might consider using std::string here. Later. |
| ElfError ReadBuildId(char* buf, size_t buf_size); |
| |
| // Read |length| bytes at |address| in the ELF object an store in |buffer|. |
| // |address| is the offset from the beginning of the ELF object. |
| bool Read(uint64_t address, void* buffer, size_t length) const; |
| |
| private: |
| ElfReader(const std::string& file_name, std::shared_ptr<ByteBlock> byte_block, |
| uint64_t base); |
| |
| // For debugging/informational purposes only. |
| const std::string file_name_; |
| |
| // This is the API to read/write from wherever the ELF object lives. |
| // It could be in process memory, or in a file, or wherever. |
| const std::shared_ptr<ByteBlock> byte_block_; |
| |
| // The offset in |byte_block_| of the start of the ELF object. |
| const uint64_t base_; |
| |
| ElfHeader header_; |
| |
| const ElfSegmentHeader* segment_headers_ = nullptr; |
| const ElfSectionHeader* section_headers_ = nullptr; |
| }; |
| |
| } // namespace debugger_utils |