aboutsummaryrefslogtreecommitdiff
path: root/elf_parser.h
diff options
context:
space:
mode:
Diffstat (limited to 'elf_parser.h')
-rw-r--r--elf_parser.h228
1 files changed, 228 insertions, 0 deletions
diff --git a/elf_parser.h b/elf_parser.h
new file mode 100644
index 0000000..0fbe726
--- /dev/null
+++ b/elf_parser.h
@@ -0,0 +1,228 @@
+#ifndef UTILS_ELF_PARSER_H
+#define UTILS_ELF_PARSER_H
+
+#include <memory>
+#include <type_traits>
+
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+
+#include <elf.h>
+
+#define ENSURE(cond) \
+ do { \
+ if (!(cond)) { \
+ fprintf(stderr, __FILE__ ":%d '" #cond "' failed\n", __LINE__); \
+ abort(); \
+ } \
+ } while (0)
+
+// -- CURSOR -------------------------------------------------------------------
+
+/// cursor
+///
+/// A simple cursor over a slice of bytes, allowing to extract trivial types
+/// with added bound checks.
+class cursor {
+ public:
+ cursor(const unsigned char* bytes, size_t len) : m_bytes(bytes), m_len(len) {
+ ENSURE(bytes != nullptr);
+ }
+
+ /// Move cursor to the offset OFF.
+ void set_off(size_t off) {
+ ENSURE(off < m_len);
+ m_off = off;
+ }
+
+ /// Create a sub-slice from the cursor at offset OFF for LEN bytes.
+ [[nodiscard]] cursor slice(size_t off, size_t len) const {
+ ENSURE(off < m_len);
+ ENSURE(len <= m_len - off);
+ return {m_bytes + off, len};
+ }
+
+ /// Extract an instance of type T at the current cursor position, advance the
+ /// cursor by sizeof(T) or INC, if INC!=0.
+ template <typename T>
+ [[nodiscard]] T extract(size_t inc = 0) {
+ static_assert(std::is_trivially_constructible<T>::value,
+ "T trivial construct");
+ static_assert(std::is_trivially_copyable<T>::value, "T trivial copy");
+ ENSURE(sizeof(T) <= m_len - m_off);
+
+ T val;
+ std::memcpy(&val, m_bytes + m_off, sizeof(val));
+ m_off += inc == 0 ? sizeof(val) : inc;
+ return val;
+ }
+
+ /// Get a raw pointer at offset OFF.
+ [[nodiscard]] const unsigned char* ptr(size_t off) const {
+ ENSURE(off < m_len);
+ return m_bytes + off;
+ }
+
+ private:
+ const unsigned char* m_bytes;
+ const size_t m_len;
+ size_t m_off{0};
+};
+
+// -- ELF INTERFACE ------------------------------------------------------------
+
+/// Interface to interact with parsed elf files.
+struct elf {
+ [[nodiscard]] static std::unique_ptr<elf> parse(const unsigned char* bytes,
+ size_t len);
+ virtual ~elf() = default;
+
+ virtual void dynsyms(bool (*)(const char* name, char type, char bind)) = 0;
+};
+
+// -- ELF PARSER ---------------------------------------------------------------
+
+namespace detail {
+/// Trait describing 32-bit elf types.
+struct elf32 {
+ using ehdr = Elf32_Ehdr;
+ using shdr = Elf32_Shdr;
+ using dyn = Elf32_Dyn;
+ using sym = Elf32_Sym;
+
+ static constexpr unsigned char st_type(unsigned char st_info) {
+ return ELF32_ST_TYPE(st_info);
+ }
+ static constexpr unsigned char st_bind(unsigned char st_info) {
+ return ELF32_ST_BIND(st_info);
+ }
+};
+
+/// Trait describing 64-bit elf types.
+struct elf64 {
+ using ehdr = Elf64_Ehdr;
+ using shdr = Elf64_Shdr;
+ using dyn = Elf64_Dyn;
+ using sym = Elf64_Sym;
+
+ static constexpr unsigned char st_type(unsigned char st_info) {
+ return ELF64_ST_TYPE(st_info);
+ }
+ static constexpr unsigned char st_bind(unsigned char st_info) {
+ return ELF64_ST_BIND(st_info);
+ }
+};
+
+/// Elf file parser.
+template <typename Elf>
+class elf_parser : public elf {
+ using elf_ehdr = typename Elf::ehdr;
+ using elf_shdr = typename Elf::shdr;
+ using elf_dyn = typename Elf::dyn;
+ using elf_sym = typename Elf::sym;
+
+ public:
+ elf_parser(const unsigned char* bytes, size_t len) : m_bytes(bytes, len) {
+ // Extract elf header.
+ auto ehdr = m_bytes.extract<elf_ehdr>();
+
+ // Get section header associated with the string table for section names.
+ ENSURE(ehdr.e_shstrndx < ehdr.e_shnum);
+ m_bytes.set_off(ehdr.e_shoff + ehdr.e_shstrndx * ehdr.e_shentsize);
+ auto shdr_names = m_bytes.extract<elf_shdr>();
+
+ // Get slice for the section headers.
+ cursor shdr_bytes =
+ m_bytes.slice(ehdr.e_shoff, ehdr.e_shnum * ehdr.e_shentsize);
+
+ // Iterate section headers.
+ for (size_t i = 0; i < ehdr.e_shnum; ++i) {
+ // Extract section header.
+ auto shdr = shdr_bytes.extract<elf_shdr>(ehdr.e_shentsize);
+
+ switch (shdr.sh_type) {
+ case SHT_STRTAB:
+ if (std::memcmp(bytes + shdr_names.sh_offset + shdr.sh_name,
+ ".dynstr\0", 8) == 0) {
+ ENSURE(m_dynstrtab == 0);
+ m_dynstrtab = shdr.sh_offset;
+ m_dynstrlen = shdr.sh_size;
+ }
+ break;
+ case SHT_DYNSYM:
+ ENSURE(m_dynsymtab == 0);
+ m_dynsymtab = shdr.sh_offset;
+ m_dynsyment = shdr.sh_entsize;
+ m_dynsymnum = shdr.sh_size / m_dynsyment;
+ }
+ }
+
+ ENSURE(m_dynstrtab > 0);
+ ENSURE(m_dynstrlen > 0);
+ ENSURE(m_dynsymtab > 0);
+ ENSURE(m_dynsyment > 0);
+ ENSURE(m_dynsymnum > 0);
+ }
+
+ void dynsyms(bool (*handle)(const char* name,
+ char type,
+ char bind)) override {
+ cursor sym_bytes = m_bytes.slice(m_dynsymtab, m_dynsymnum * m_dynsyment);
+ cursor str_bytes = m_bytes.slice(m_dynstrtab, m_dynstrlen);
+ for (size_t i = 0; i < m_dynsymnum; ++i) {
+ auto sym = sym_bytes.extract<elf_sym>(m_dynsyment);
+ const char* name =
+ reinterpret_cast<const char*>(str_bytes.ptr(sym.st_name));
+ if (!handle(name, Elf::st_type(sym.st_info), Elf::st_bind(sym.st_info))) {
+ return;
+ }
+ }
+ }
+
+ private:
+ cursor m_bytes;
+
+ size_t m_dynstrtab{0};
+ size_t m_dynstrlen{0};
+
+ size_t m_dynsymtab{0};
+ size_t m_dynsyment{0};
+ size_t m_dynsymnum{0};
+};
+} // namespace detail
+
+// -- IMPL: ELF::PARSE ---------------------------------------------------------
+
+inline std::unique_ptr<elf> elf::parse(const unsigned char* bytes, size_t len) {
+ ENSURE(EI_NIDENT <= len);
+
+ // Check elf file magic.
+ ENSURE(std::memcmp(bytes, ELFMAG, SELFMAG) == 0);
+
+ // Support only native endianess.
+ switch (bytes[EI_DATA]) {
+ case ELFDATA2LSB:
+ ENSURE(__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__);
+ break;
+ case ELFDATA2MSB:
+ ENSURE(__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__);
+ break;
+ default:
+ ENSURE(false);
+ }
+
+ // Parse with correct bitness.
+ switch (bytes[EI_CLASS]) {
+ case ELFCLASS32:
+ puts("parse elf32");
+ return std::make_unique<detail::elf_parser<detail::elf32>>(bytes, len);
+ case ELFCLASS64:
+ puts("parse elf64");
+ return std::make_unique<detail::elf_parser<detail::elf64>>(bytes, len);
+ default:
+ ENSURE(false);
+ }
+}
+
+#endif