// Copyright 2017 The Crashpad Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #ifndef CRASHPAD_SNAPSHOT_ELF_ELF_IMAGE_READER_H_ #define CRASHPAD_SNAPSHOT_ELF_ELF_IMAGE_READER_H_ #include #include #include #include #include #include "snapshot/elf/elf_dynamic_array_reader.h" #include "snapshot/elf/elf_symbol_table_reader.h" #include "util/misc/address_types.h" #include "util/misc/initialization_state.h" #include "util/misc/initialization_state_dcheck.h" #include "util/process/process_memory_range.h" namespace crashpad { //! \brief A reader for ELF images mapped into another process. //! //! This class is capable of reading both 32-bit and 64-bit images. class ElfImageReader { private: class ProgramHeaderTable; public: //! \brief This class enables reading note segments from an ELF image. //! //! Objects of this class should be created by calling //! ElfImageReader::Notes() or ElfImageReader::NotesWithNameAndType(). class NoteReader { public: NoteReader(const NoteReader&) = delete; NoteReader& operator=(const NoteReader&) = delete; ~NoteReader(); //! \brief The return value for NextNote(). enum class Result { //! \brief An error occurred. The NoteReader is invalidated and message is //! logged. kError, //! \brief A note was found. kSuccess, //! \brief No more notes were found. kNoMoreNotes, }; //! \brief A type large enough to hold a note type, potentially across //! bitness. using NoteType = decltype(Elf64_Nhdr::n_type); //! \brief Searches for the next note in the image. //! //! \param[out] name The name of the note owner, if not `nullptr`. //! \param[out] type A type for the note, if not `nullptr`. //! \param[out] desc The note descriptor. //! \param[out] desc_addr The address in the remote process' address space //! \a desc was read from. //! \return a #Result value. \a name, \a type, \a desc, and \a desc_addr are //! only valid if this method returns Result::kSuccess. Result NextNote(std::string* name, NoteType* type, std::string* desc, VMAddress* desc_addr); // private NoteReader(const ElfImageReader* elf_reader_, const ProcessMemoryRange* range, const ProgramHeaderTable* phdr_table, size_t max_note_size, const std::string& name_filter = std::string(), NoteType type_filter = 0, bool use_filter = false); private: // Reads the next note at the current segment address. Sets retry_ to true // and returns kError if use_filter_ is true and the note's name and type do // not match name_filter_ and type_filter_. template Result ReadNote(std::string* name, NoteType* type, std::string* desc, VMAddress* desc_addr); VMAddress current_address_; VMAddress segment_end_address_; const ElfImageReader* elf_reader_; // weak const ProcessMemoryRange* range_; // weak const ProgramHeaderTable* phdr_table_; // weak std::unique_ptr segment_range_; size_t phdr_index_; size_t max_note_size_; std::string name_filter_; NoteType type_filter_; bool use_filter_; bool is_valid_; bool retry_; }; ElfImageReader(); ElfImageReader(const ElfImageReader&) = delete; ElfImageReader& operator=(const ElfImageReader&) = delete; ~ElfImageReader(); //! \brief Initializes the reader. //! //! This method must be called once on an object and must be successfully //! called before any other method in this class may be called. //! //! \param[in] memory A memory reader for the remote process. //! \param[in] address The address in the remote process' address space where //! the ELF image is loaded. //! \param[in] verbose `true` if this method should log error messages during //! initialization. Setting this value to `false` will reduce the error //! messages relating to verifying the ELF image, but may not suppress //! logging entirely. bool Initialize(const ProcessMemoryRange& memory, VMAddress address, bool verbose = true); //! \brief Returns the base address of the image's memory range. //! //! This may differ from the address passed to Initialize() if the ELF header //! is not loaded at the start of the first `PT_LOAD` segment. VMAddress Address() const { return memory_.Base(); } //! \brief Returns the size of the range containing all loaded segments for //! this image. //! //! The size may include memory that is unmapped or mapped to other objects if //! this image's `PT_LOAD` segments are not contiguous. VMSize Size() const { return memory_.Size(); } //! \brief Returns the file type for the image. //! //! Possible values include `ET_EXEC` or `ET_DYN` from ``. uint16_t FileType() const; //! \brief Returns the load bias for the image. //! //! The load bias is the actual load address minus the preferred load address. VMOffset GetLoadBias() const { return load_bias_; } //! \brief Determines the name of this object using `DT_SONAME`, if present. //! //! \param[out] name The name of this object, only valid if this method //! returns `true`. //! \return `true` if a name was found for this object. bool SoName(std::string* name); //! \brief Reads information from the dynamic symbol table about the symbol //! identified by \a name. //! //! \param[in] name The name of the symbol to search for. //! \param[out] address The address of the symbol in the target process' //! address space, if found. //! \param[out] size The size of the symbol, if found. //! \return `true` if the symbol was found. bool GetDynamicSymbol(const std::string& name, VMAddress* address, VMSize* size); //! \brief Reads a `NUL`-terminated C string from this image's dynamic string //! table. //! //! \param[in] offset the byte offset in the string table to start reading. //! \param[out] string the string read. //! \return `true` on success. Otherwise `false` with a message logged. bool ReadDynamicStringTableAtOffset(VMSize offset, std::string* string); //! \brief Determine the debug address. //! //! The debug address is a pointer to an `r_debug` struct defined in //! ``. //! //! \param[out] debug the debug address, if found. //! \return `true` if the debug address was found. bool GetDebugAddress(VMAddress* debug); //! \brief Determine the address of `PT_DYNAMIC` segment. //! //! \param[out] address The address of the array, valid if this method returns //! `true`. //! \return `true` on success. Otherwise `false` with a message logged. bool GetDynamicArrayAddress(VMAddress* address); //! \brief Return the address of the program header table. VMAddress GetProgramHeaderTableAddress(); //! \brief Return a NoteReader for this image, which scans all PT_NOTE //! segments in the image. //! //! The returned NoteReader is only valid for the lifetime of the //! ElfImageReader that created it. //! //! \param[in] max_note_size The maximum note size to read. Notes whose //! combined name, descriptor, and padding size are greater than //! \a max_note_size will be silently skipped. //! \return A NoteReader object capable of reading notes in this image. std::unique_ptr Notes(size_t max_note_size); //! \brief Return a NoteReader for this image, which scans all PT_NOTE //! segments in the image, filtering by name and type. //! //! The returned NoteReader is only valid for the lifetime of the //! ElfImageReader that created it. //! //! \param[in] name The note name to match. //! \param[in] type The note type to match. //! \param[in] max_note_size The maximum note size to read. Notes whose //! combined name, descriptor, and padding size are greater than //! \a max_note_size will be silently skipped. //! \return A NoteReader object capable of reading notes in this image. std::unique_ptr NotesWithNameAndType(const std::string& name, NoteReader::NoteType type, size_t max_note_size); //! \brief Return a ProcessMemoryRange restricted to the range of this image. //! //! The caller does not take ownership of the returned object. const ProcessMemoryRange* Memory() const; //! \brief Retrieves the number of symbol table entries in `DT_SYMTAB` //! according to the data in the `DT_HASH` section. //! //! \note Exposed for testing, not normally otherwise useful. //! //! \param[out] number_of_symbol_table_entries The number of entries expected //! in `DT_SYMTAB`. //! \return `true` if a `DT_HASH` section was found, and was read //! successfully, otherwise `false` with an error logged. bool GetNumberOfSymbolEntriesFromDtHash( VMSize* number_of_symbol_table_entries); //! \brief Retrieves the number of symbol table entries in `DT_SYMTAB` //! according to the data in the `DT_GNU_HASH` section. //! //! \note Exposed for testing, not normally otherwise useful. //! //! \note Depending on the linker that generated the `DT_GNU_HASH` section, //! this value may not be as expected if there are zero exported symbols. //! //! \param[out] number_of_symbol_table_entries The number of entries expected //! in `DT_SYMTAB`. //! \return `true` if a `DT_GNU_HASH` section was found, and was read //! successfully, otherwise `false` with an error logged. bool GetNumberOfSymbolEntriesFromDtGnuHash( VMSize* number_of_symbol_table_entries); private: template class ProgramHeaderTableSpecific; bool InitializeProgramHeaders(bool verbose); bool InitializeDynamicArray(); bool InitializeDynamicSymbolTable(); bool GetAddressFromDynamicArray(uint64_t tag, bool log, VMAddress* address); union { Elf32_Ehdr header_32_; Elf64_Ehdr header_64_; }; VMAddress ehdr_address_; VMOffset load_bias_; ProcessMemoryRange memory_; std::unique_ptr program_headers_; std::unique_ptr dynamic_array_; std::unique_ptr symbol_table_; InitializationStateDcheck initialized_; InitializationState dynamic_array_initialized_; InitializationState symbol_table_initialized_; }; } // namespace crashpad #endif // CRASHPAD_SNAPSHOT_ELF_ELF_IMAGE_READER_H_