unioil-loyalty-rn-app/ios/Pods/Flipper-Folly/folly/experimental/symbolizer/Elf.h

326 lines
10 KiB
C++

/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* 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.
*/
// ELF file parser
#pragma once
#define FOLLY_EXPERIMENTAL_SYMBOLIZER_ELF_H_
#include <cstdio>
#include <initializer_list>
#include <stdexcept>
#include <system_error>
#include <folly/Conv.h>
#include <folly/Likely.h>
#include <folly/Range.h>
#include <folly/lang/SafeAssert.h>
#include <folly/portability/Config.h>
#if FOLLY_HAVE_ELF
#include <elf.h>
#include <link.h> // For ElfW()
namespace folly {
namespace symbolizer {
#if defined(ElfW)
#define FOLLY_ELF_ELFW(name) ElfW(name)
#elif defined(__FreeBSD__)
#define FOLLY_ELF_ELFW(name) Elf_##name
#endif
using ElfAddr = FOLLY_ELF_ELFW(Addr);
using ElfEhdr = FOLLY_ELF_ELFW(Ehdr);
using ElfOff = FOLLY_ELF_ELFW(Off);
using ElfPhdr = FOLLY_ELF_ELFW(Phdr);
using ElfShdr = FOLLY_ELF_ELFW(Shdr);
using ElfSym = FOLLY_ELF_ELFW(Sym);
/**
* ELF file parser.
*
* We handle native files only (32-bit files on a 32-bit platform, 64-bit files
* on a 64-bit platform), and only executables (ET_EXEC) shared objects
* (ET_DYN) and core files (ET_CORE).
*/
class ElfFile {
public:
class Options {
public:
constexpr Options() noexcept {}
constexpr bool writable() const noexcept { return writable_; }
constexpr Options& writable(bool const value) noexcept {
writable_ = value;
return *this;
}
private:
bool writable_ = false;
};
ElfFile() noexcept;
// Note: may throw, call openNoThrow() explicitly if you don't want to throw
explicit ElfFile(const char* name, Options const& options = Options());
// Open the ELF file.
// Returns 0 on success, kSystemError (guaranteed to be -1) (and sets errno)
// on IO error, kInvalidElfFile (and sets errno to EINVAL) for an invalid
// Elf file. On error, if msg is not nullptr, sets *msg to a static string
// indicating what failed.
enum OpenResultCode : int {
kSuccess = 0,
kSystemError = -1,
kInvalidElfFile = -2,
};
struct OpenResult {
OpenResultCode code{};
char const* msg{};
/* implicit */ constexpr operator OpenResultCode() const noexcept {
return code;
}
};
// Open the ELF file. Does not throw on error.
OpenResult openNoThrow(
const char* name, Options const& options = Options()) noexcept;
// Like openNoThrow, but follow .gnu_debuglink if present
OpenResult openAndFollow(
const char* name, Options const& options = Options()) noexcept;
// Open the ELF file. Throws on error.
void open(const char* name, Options const& options = Options());
~ElfFile();
ElfFile(ElfFile&& other) noexcept;
ElfFile& operator=(ElfFile&& other) noexcept;
/** Retrieve the ELF header */
const ElfEhdr& elfHeader() const noexcept { return at<ElfEhdr>(0); }
/**
* Get the base address, the address where the file should be loaded if
* no relocations happened.
*/
uintptr_t getBaseAddress() const noexcept { return baseAddress_; }
/** Find a section given its name */
const ElfShdr* getSectionByName(const char* name) const noexcept;
/** Find a section given its index in the section header table */
const ElfShdr* getSectionByIndex(size_t idx) const noexcept;
/** Retrieve the name of a section */
const char* getSectionName(const ElfShdr& section) const noexcept;
/** Get the actual section body */
folly::StringPiece getSectionBody(const ElfShdr& section) const noexcept;
/** Retrieve a string from a string table section */
const char* getString(
const ElfShdr& stringTable, size_t offset) const noexcept;
/**
* Iterate over all strings in a string table section for as long as
* fn(str) returns false.
* Returns the current ("found") string when fn returned true, or nullptr
* if fn returned false for all strings in the table.
*/
template <class Fn>
const char* iterateStrings(const ElfShdr& stringTable, Fn fn) const
noexcept(is_nothrow_invocable_v<Fn, const char*>);
/**
* Iterate over program headers as long as fn(section) returns false.
* Returns a pointer to the current ("found") section when fn returned
* true, or nullptr if fn returned false for all sections.
*/
template <class Fn>
const ElfPhdr* iterateProgramHeaders(Fn fn) const
noexcept(is_nothrow_invocable_v<Fn, ElfPhdr const&>);
/**
* Iterate over all sections for as long as fn(section) returns false.
* Returns a pointer to the current ("found") section when fn returned
* true, or nullptr if fn returned false for all sections.
*/
template <class Fn>
const ElfShdr* iterateSections(Fn fn) const
noexcept(is_nothrow_invocable_v<Fn, ElfShdr const&>);
/**
* Iterate over all sections with a given type. Similar to
* iterateSections(), but filtered only for sections with the given type.
*/
template <class Fn>
const ElfShdr* iterateSectionsWithType(uint32_t type, Fn fn) const
noexcept(is_nothrow_invocable_v<Fn, ElfShdr const&>);
/**
* Iterate over all sections with a given types. Similar to
* iterateSectionWithTypes(), but filtered on multiple types.
*/
template <class Fn>
const ElfShdr* iterateSectionsWithTypes(
std::initializer_list<uint32_t> types, Fn fn) const
noexcept(is_nothrow_invocable_v<Fn, ElfShdr const&>);
/**
* Iterate over all symbols witin a given section.
*
* Returns a pointer to the current ("found") symbol when fn returned true,
* or nullptr if fn returned false for all symbols.
*/
template <class Fn>
const ElfSym* iterateSymbols(const ElfShdr& section, Fn fn) const
noexcept(is_nothrow_invocable_v<Fn, ElfSym const&>);
template <class Fn>
const ElfSym* iterateSymbolsWithType(
const ElfShdr& section, uint32_t type, Fn fn) const
noexcept(is_nothrow_invocable_v<Fn, ElfSym const&>);
template <class Fn>
const ElfSym* iterateSymbolsWithTypes(
const ElfShdr& section,
std::initializer_list<uint32_t> types,
Fn fn) const noexcept(is_nothrow_invocable_v<Fn, ElfSym const&>);
/**
* Find symbol definition by address.
* Note that this is the file virtual address, so you need to undo
* any relocation that might have happened.
*
* Returns {nullptr, nullptr} if not found.
*/
typedef std::pair<const ElfShdr*, const ElfSym*> Symbol;
Symbol getDefinitionByAddress(uintptr_t address) const noexcept;
/**
* Find symbol definition by name.
*
* If a symbol with this name cannot be found, a <nullptr, nullptr> Symbol
* will be returned. This is O(N) in the number of symbols in the file.
*
* Returns {nullptr, nullptr} if not found.
*/
Symbol getSymbolByName(const char* name) const noexcept;
/**
* Get the value of a symbol.
*/
template <class T>
const T& getSymbolValue(const ElfSym* symbol) const noexcept {
const ElfShdr* section = getSectionByIndex(symbol->st_shndx);
FOLLY_SAFE_CHECK(section, "Symbol's section index is invalid");
return valueAt<T>(*section, symbol->st_value);
}
/**
* Get the value of the object stored at the given address.
*
* This is the function that you want to use in conjunction with
* getSymbolValue() to follow pointers. For example, to get the value of
* a char* symbol, you'd do something like this:
*
* auto sym = getSymbolByName("someGlobalValue");
* auto addr = getSymbolValue<ElfAddr>(sym.second);
* const char* str = &getSymbolValue<const char>(addr);
*/
template <class T>
const T& getAddressValue(const ElfAddr addr) const noexcept {
const ElfShdr* section = getSectionContainingAddress(addr);
FOLLY_SAFE_CHECK(section, "Address does not refer to existing section");
return valueAt<T>(*section, addr);
}
/**
* Retrieve symbol name.
*/
const char* getSymbolName(Symbol symbol) const noexcept;
/** Find the section containing the given address */
const ElfShdr* getSectionContainingAddress(ElfAddr addr) const noexcept;
private:
OpenResult init() noexcept;
void reset() noexcept;
ElfFile(const ElfFile&) = delete;
ElfFile& operator=(const ElfFile&) = delete;
void validateStringTable(const ElfShdr& stringTable) const noexcept;
template <class T>
const T& at(ElfOff offset) const noexcept {
static_assert(std::is_pod<T>::value, "non-pod");
FOLLY_SAFE_CHECK(
offset + sizeof(T) <= length_,
"Offset (",
static_cast<size_t>(offset),
" + ",
sizeof(T),
") is not contained within our mapped file (",
filepath_,
") of length ",
length_);
return *reinterpret_cast<T*>(file_ + offset);
}
template <class T>
const T& valueAt(const ElfShdr& section, const ElfAddr addr) const noexcept {
// For exectuables and shared objects, st_value holds a virtual address
// that refers to the memory owned by sections. Since we didn't map the
// sections into the addresses that they're expecting (sh_addr), but
// instead just mmapped the entire file directly, we need to translate
// between addresses and offsets into the file.
//
// TODO: For other file types, st_value holds a file offset directly. Since
// I don't have a use-case for that right now, just assert that
// nobody wants this. We can always add it later.
FOLLY_SAFE_CHECK(
elfHeader().e_type == ET_EXEC || elfHeader().e_type == ET_DYN ||
elfHeader().e_type == ET_CORE,
"Only exectuables, shared objects and cores are supported");
FOLLY_SAFE_CHECK(
addr >= section.sh_addr &&
(addr + sizeof(T)) <= (section.sh_addr + section.sh_size),
"Address is not contained within the provided segment");
return at<T>(section.sh_offset + (addr - section.sh_addr));
}
static constexpr size_t kFilepathMaxLen = 512;
char filepath_[kFilepathMaxLen] = {};
int fd_;
char* file_; // mmap() location
size_t length_; // mmap() length
uintptr_t baseAddress_;
};
} // namespace symbolizer
} // namespace folly
#include <folly/experimental/symbolizer/Elf-inl.h>
#endif // FOLLY_HAVE_ELF