gctf2023/pwn/flipper/dist/utils/add-debug/elf/elf.cc
2023-11-24 13:11:34 -05:00

335 lines
8.4 KiB
C++

#include "elf++.hh"
#include <cstring>
using namespace std;
ELFPP_BEGIN_NAMESPACE
template<template<typename E, byte_order Order> class Hdr>
void canon_hdr(Hdr<Elf64, byte_order::native> *out, const void *data,
elfclass ei_class, elfdata ei_data)
{
switch (ei_class) {
case elfclass::_32:
switch (ei_data) {
case elfdata::lsb:
out->from(*(Hdr<Elf32, byte_order::lsb>*)data);
break;
case elfdata::msb:
out->from(*(Hdr<Elf32, byte_order::msb>*)data);
break;
} // fallthrough
case elfclass::_64:
switch (ei_data) {
case elfdata::lsb:
out->from(*(Hdr<Elf64, byte_order::lsb>*)data);
break;
case elfdata::msb:
out->from(*(Hdr<Elf64, byte_order::msb>*)data);
return;
} // fallthrough
}
}
//////////////////////////////////////////////////////////////////
// class elf
//
struct elf::impl
{
impl(const shared_ptr<loader> &l)
: l(l) { }
const shared_ptr<loader> l;
Ehdr<> hdr;
vector<section> sections;
//vector<segment> segments;
section invalid_section;
};
elf::elf(const std::shared_ptr<loader> &l)
: m(make_shared<impl>(l))
{
// Read the first six bytes to check the magic number, ELF
// class, and byte order.
struct core_hdr
{
char ei_magic[4];
elfclass ei_class;
elfdata ei_data;
unsigned char ei_version;
} *core_hdr = (struct core_hdr*)l->load(0, sizeof *core_hdr);
// Check basic header
if (strncmp(core_hdr->ei_magic, "\x7f" "ELF", 4) != 0)
throw format_error("bad ELF magic number");
if (core_hdr->ei_version != 1)
throw format_error("unknown ELF version");
if (core_hdr->ei_class != elfclass::_32 &&
core_hdr->ei_class != elfclass::_64)
throw format_error("bad ELF class");
if (core_hdr->ei_data != elfdata::lsb &&
core_hdr->ei_data != elfdata::msb)
throw format_error("bad ELF data order");
// Read in the real header and canonicalize it
size_t hdr_size = (core_hdr->ei_class == elfclass::_32 ?
sizeof(Ehdr<Elf32>) : sizeof(Ehdr<Elf64>));
const void *hdr = l->load(0, hdr_size);
canon_hdr(&m->hdr, hdr, core_hdr->ei_class, core_hdr->ei_data);
// More checks
if (m->hdr.version != 1)
throw format_error("bad section ELF version");
if (m->hdr.shnum && m->hdr.shstrndx >= m->hdr.shnum)
throw format_error("bad section name string table index");
// Load sections
const void *sec_data = l->load(m->hdr.shoff,
m->hdr.shentsize * m->hdr.shnum);
for (unsigned i = 0; i < m->hdr.shnum; i++) {
const void *sec = ((const char*)sec_data) + i * m->hdr.shentsize;
// XXX Circular reference. Maybe this should be
// constructed on the fly? Canonicalizing the header
// isn't super-cheap.
m->sections.push_back(section(*this, sec));
}
}
const Ehdr<> &
elf::get_hdr() const
{
return m->hdr;
}
shared_ptr<loader>
elf::get_loader() const
{
return m->l;
}
const std::vector<section> &
elf::sections() const
{
return m->sections;
}
const section &
elf::get_section(const std::string &name) const
{
for (auto &sec : sections())
if (name == sec.get_name(nullptr))
return sec;
return m->invalid_section;
}
const section &
elf::get_section(unsigned index) const
{
if (index >= sections().size())
return m->invalid_section;
return sections().at(index);
}
//////////////////////////////////////////////////////////////////
// class section
//
std::string
enums::to_string(shn v)
{
if (v == shn::undef)
return "undef";
if (v == shn::abs)
return "abs";
if (v == shn::common)
return "common";
return std::to_string(v);
}
struct section::impl
{
impl(const elf &f)
: f(f), name(nullptr), data(nullptr) { }
const elf f;
Shdr<> hdr;
const char *name;
size_t name_len;
const void *data;
};
section::section(const elf &f, const void *hdr)
: m(make_shared<impl>(f))
{
canon_hdr(&m->hdr, hdr, f.get_hdr().ei_class, f.get_hdr().ei_data);
}
const Shdr<> &
section::get_hdr() const
{
return m->hdr;
}
const char *
section::get_name(size_t *len_out) const
{
// XXX Should the section name strtab be cached?
if (!m->name)
m->name = m->f.get_section(m->f.get_hdr().shstrndx)
.as_strtab().get(m->hdr.name, &m->name_len);
if (len_out)
*len_out = m->name_len;
return m->name;
}
string
section::get_name() const
{
return get_name(nullptr);
}
const void *
section::data() const
{
if (m->hdr.type == sht::nobits)
return nullptr;
if (!m->data)
m->data = m->f.get_loader()->load(m->hdr.offset, m->hdr.size);
return m->data;
}
size_t
section::size() const
{
return m->hdr.size;
}
strtab
section::as_strtab() const
{
if (m->hdr.type != sht::strtab)
throw section_type_mismatch("cannot use section as strtab");
return strtab(m->f, data(), size());
}
symtab
section::as_symtab() const
{
if (m->hdr.type != sht::symtab && m->hdr.type != sht::dynsym)
throw section_type_mismatch("cannot use section as symtab");
return symtab(m->f, data(), size(),
m->f.get_section(get_hdr().link).as_strtab());
}
//////////////////////////////////////////////////////////////////
// class strtab
//
struct strtab::impl
{
impl(const elf &f, const char *data, const char *end)
: f(f), data(data), end(end) { }
const elf f;
const char *data, *end;
};
strtab::strtab(elf f, const void *data, size_t size)
: m(make_shared<impl>(f, (const char*)data, (const char *)data + size))
{
}
const char *
strtab::get(Elf64::Off offset, size_t *len_out) const
{
const char *start = m->data + offset;
if (start >= m->end)
throw range_error("string offset " + std::to_string(offset) + " exceeds section size");
// Find the null terminator
const char *p = start;
while (p < m->end && *p)
p++;
if (p == m->end)
throw format_error("unterminated string");
if (len_out)
*len_out = p - start;
return start;
}
std::string
strtab::get(Elf64::Off offset) const
{
return get(offset, nullptr);
}
//////////////////////////////////////////////////////////////////
// class sym
//
sym::sym(elf f, const void *data, strtab strs)
: strs(strs)
{
canon_hdr(&this->data, data, f.get_hdr().ei_class, f.get_hdr().ei_data);
}
const char *
sym::get_name(size_t *len_out) const
{
return strs.get(get_data().name, len_out);
}
std::string
sym::get_name() const
{
return strs.get(get_data().name);
}
//////////////////////////////////////////////////////////////////
// class symtab
//
struct symtab::impl
{
impl(const elf &f, const char *data, const char *end, strtab strs)
: f(f), data(data), end(end), strs(strs) { }
const elf f;
const char *data, *end;
const strtab strs;
};
symtab::symtab(elf f, const void *data, size_t size, strtab strs)
: m(make_shared<impl>(f, (const char*)data, (const char *)data + size,
strs))
{
}
symtab::iterator::iterator(const symtab &tab, const char *pos)
: f(tab.m->f), strs(tab.m->strs), pos(pos)
{
if (f.get_hdr().ei_class == elfclass::_32)
stride = sizeof(Sym<Elf32>);
else
stride = sizeof(Sym<Elf64>);
}
symtab::iterator
symtab::begin() const
{
return iterator(*this, m->data);
}
symtab::iterator
symtab::end() const
{
return iterator(*this, m->end);
}
ELFPP_END_NAMESPACE