summaryrefslogtreecommitdiffstats
path: root/build/unix/elfhack/elfxx.h
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--build/unix/elfhack/elfxx.h700
1 files changed, 700 insertions, 0 deletions
diff --git a/build/unix/elfhack/elfxx.h b/build/unix/elfhack/elfxx.h
new file mode 100644
index 0000000000..26dce9b9cd
--- /dev/null
+++ b/build/unix/elfhack/elfxx.h
@@ -0,0 +1,700 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <stdexcept>
+#include <list>
+#include <vector>
+#include <cstring>
+#include <fstream>
+#include <algorithm>
+#include <elf.h>
+#include <asm/byteorder.h>
+
+// Technically, __*_to_cpu and __cpu_to* function are equivalent,
+// so swap can use either of both.
+#define def_swap(endian, type, bits) \
+ static inline type##bits##_t swap(type##bits##_t i) { \
+ return __##endian##bits##_to_cpu(i); \
+ }
+
+class little_endian {
+ public:
+ def_swap(le, uint, 16);
+ def_swap(le, uint, 32);
+ def_swap(le, uint, 64);
+ def_swap(le, int, 16);
+ def_swap(le, int, 32);
+ def_swap(le, int, 64);
+};
+
+class big_endian {
+ public:
+ def_swap(be, uint, 16);
+ def_swap(be, uint, 32);
+ def_swap(be, uint, 64);
+ def_swap(be, int, 16);
+ def_swap(be, int, 32);
+ def_swap(be, int, 64);
+};
+
+// forward declaration
+class ElfSection;
+class ElfSegment;
+// TODO: Rename Elf_* types
+class Elf_Ehdr;
+class Elf_Phdr;
+class Elf;
+class ElfDynamic_Section;
+class ElfStrtab_Section;
+
+template <typename X>
+class FixedSizeData {
+ public:
+ struct Wrapper {
+ X value;
+ };
+ typedef Wrapper Type32;
+ typedef Wrapper Type64;
+
+ template <class endian, typename R, typename T>
+ static void swap(T& t, R& r) {
+ r.value = endian::swap(t.value);
+ }
+};
+
+class Elf_Ehdr_Traits {
+ public:
+ typedef Elf32_Ehdr Type32;
+ typedef Elf64_Ehdr Type64;
+
+ template <class endian, typename R, typename T>
+ static void swap(T& t, R& r);
+};
+
+class Elf_Phdr_Traits {
+ public:
+ typedef Elf32_Phdr Type32;
+ typedef Elf64_Phdr Type64;
+
+ template <class endian, typename R, typename T>
+ static void swap(T& t, R& r);
+};
+
+class Elf_Shdr_Traits {
+ public:
+ typedef Elf32_Shdr Type32;
+ typedef Elf64_Shdr Type64;
+
+ template <class endian, typename R, typename T>
+ static void swap(T& t, R& r);
+};
+
+class Elf_Dyn_Traits {
+ public:
+ typedef Elf32_Dyn Type32;
+ typedef Elf64_Dyn Type64;
+
+ template <class endian, typename R, typename T>
+ static void swap(T& t, R& r);
+};
+
+class Elf_Sym_Traits {
+ public:
+ typedef Elf32_Sym Type32;
+ typedef Elf64_Sym Type64;
+
+ template <class endian, typename R, typename T>
+ static void swap(T& t, R& r);
+};
+
+class Elf_Rel_Traits {
+ public:
+ typedef Elf32_Rel Type32;
+ typedef Elf64_Rel Type64;
+
+ template <class endian, typename R, typename T>
+ static void swap(T& t, R& r);
+};
+
+class Elf_Rela_Traits {
+ public:
+ typedef Elf32_Rela Type32;
+ typedef Elf64_Rela Type64;
+
+ template <class endian, typename R, typename T>
+ static void swap(T& t, R& r);
+};
+
+class ElfValue {
+ public:
+ virtual unsigned int getValue() { return 0; }
+ virtual ElfSection* getSection() { return nullptr; }
+};
+
+class ElfPlainValue : public ElfValue {
+ unsigned int value;
+
+ public:
+ ElfPlainValue(unsigned int val) : value(val){};
+ unsigned int getValue() { return value; }
+};
+
+class ElfLocation : public ElfValue {
+ ElfSection* section;
+ unsigned int offset;
+
+ public:
+ enum position { ABSOLUTE, RELATIVE };
+ ElfLocation() : section(nullptr), offset(0){};
+ ElfLocation(ElfSection* section, unsigned int off,
+ enum position pos = RELATIVE);
+ ElfLocation(unsigned int location, Elf* elf);
+ unsigned int getValue();
+ ElfSection* getSection() { return section; }
+ const char* getBuffer();
+};
+
+class ElfSize : public ElfValue {
+ ElfSection* section;
+
+ public:
+ ElfSize(ElfSection* s) : section(s){};
+ unsigned int getValue();
+ ElfSection* getSection() { return section; }
+};
+
+class ElfEntSize : public ElfValue {
+ ElfSection* section;
+
+ public:
+ ElfEntSize(ElfSection* s) : section(s){};
+ unsigned int getValue();
+ ElfSection* getSection() { return section; }
+};
+
+template <typename T>
+class serializable : public T::Type64 {
+ public:
+ serializable(){};
+ serializable(const typename T::Type64& p) : T::Type64(p){};
+
+ private:
+ template <typename R>
+ void init(const char* buf, size_t len, unsigned char ei_data) {
+ R e;
+ assert(len >= sizeof(e));
+ memcpy(&e, buf, sizeof(e));
+ if (ei_data == ELFDATA2LSB) {
+ T::template swap<little_endian>(e, *this);
+ return;
+ } else if (ei_data == ELFDATA2MSB) {
+ T::template swap<big_endian>(e, *this);
+ return;
+ }
+ throw std::runtime_error("Unsupported ELF data encoding");
+ }
+
+ template <typename R>
+ void serialize(const char* buf, size_t len, unsigned char ei_data) {
+ assert(len >= sizeof(R));
+ if (ei_data == ELFDATA2LSB) {
+ T::template swap<little_endian>(*this, *(R*)buf);
+ return;
+ } else if (ei_data == ELFDATA2MSB) {
+ T::template swap<big_endian>(*this, *(R*)buf);
+ return;
+ }
+ throw std::runtime_error("Unsupported ELF data encoding");
+ }
+
+ public:
+ serializable(const char* buf, size_t len, unsigned char ei_class,
+ unsigned char ei_data) {
+ if (ei_class == ELFCLASS32) {
+ init<typename T::Type32>(buf, len, ei_data);
+ return;
+ } else if (ei_class == ELFCLASS64) {
+ init<typename T::Type64>(buf, len, ei_data);
+ return;
+ }
+ throw std::runtime_error("Unsupported ELF class");
+ }
+
+ serializable(std::ifstream& file, unsigned char ei_class,
+ unsigned char ei_data) {
+ if (ei_class == ELFCLASS32) {
+ typename T::Type32 e;
+ file.read((char*)&e, sizeof(e));
+ init<typename T::Type32>((char*)&e, sizeof(e), ei_data);
+ return;
+ } else if (ei_class == ELFCLASS64) {
+ typename T::Type64 e;
+ file.read((char*)&e, sizeof(e));
+ init<typename T::Type64>((char*)&e, sizeof(e), ei_data);
+ return;
+ }
+ throw std::runtime_error("Unsupported ELF class or data encoding");
+ }
+
+ void serialize(std::ofstream& file, unsigned char ei_class,
+ unsigned char ei_data) {
+ if (ei_class == ELFCLASS32) {
+ typename T::Type32 e;
+ serialize<typename T::Type32>((char*)&e, sizeof(e), ei_data);
+ file.write((char*)&e, sizeof(e));
+ return;
+ } else if (ei_class == ELFCLASS64) {
+ typename T::Type64 e;
+ serialize<typename T::Type64>((char*)&e, sizeof(e), ei_data);
+ file.write((char*)&e, sizeof(e));
+ return;
+ }
+ throw std::runtime_error("Unsupported ELF class or data encoding");
+ }
+
+ void serialize(char* buf, size_t len, unsigned char ei_class,
+ unsigned char ei_data) {
+ if (ei_class == ELFCLASS32) {
+ serialize<typename T::Type32>(buf, len, ei_data);
+ return;
+ } else if (ei_class == ELFCLASS64) {
+ serialize<typename T::Type64>(buf, len, ei_data);
+ return;
+ }
+ throw std::runtime_error("Unsupported ELF class");
+ }
+
+ static inline unsigned int size(unsigned char ei_class) {
+ if (ei_class == ELFCLASS32)
+ return sizeof(typename T::Type32);
+ else if (ei_class == ELFCLASS64)
+ return sizeof(typename T::Type64);
+ return 0;
+ }
+};
+
+typedef serializable<Elf_Shdr_Traits> Elf_Shdr;
+
+class Elf {
+ public:
+ Elf(std::ifstream& file);
+ ~Elf();
+
+ /* index == -1 is treated as index == ehdr.e_shstrndx */
+ ElfSection* getSection(int index);
+
+ ElfSection* getSectionAt(Elf64_Off offset);
+
+ ElfSegment* getSegmentByType(unsigned int type, ElfSegment* last = nullptr);
+
+ ElfDynamic_Section* getDynSection();
+
+ void normalize();
+ void write(std::ofstream& file);
+
+ unsigned char getClass();
+ unsigned char getData();
+ unsigned char getType();
+ unsigned char getMachine();
+ unsigned int getSize();
+
+ void insertSegmentAfter(ElfSegment* previous, ElfSegment* segment) {
+ std::vector<ElfSegment*>::iterator prev =
+ std::find(segments.begin(), segments.end(), previous);
+ segments.insert(prev + 1, segment);
+ }
+
+ void removeSegment(ElfSegment* segment);
+
+ private:
+ Elf_Ehdr* ehdr;
+ ElfLocation eh_entry;
+ ElfStrtab_Section* eh_shstrndx;
+ ElfSection** sections;
+ std::vector<ElfSegment*> segments;
+ ElfSection *shdr_section, *phdr_section;
+ /* Values used only during initialization */
+ Elf_Shdr** tmp_shdr;
+ std::ifstream* tmp_file;
+};
+
+class ElfSection {
+ public:
+ typedef union {
+ ElfSection* section;
+ int index;
+ } SectionInfo;
+
+ ElfSection(Elf_Shdr& s, std::ifstream* file, Elf* parent);
+
+ virtual ~ElfSection() { free(data); }
+
+ const char* getName() { return name; }
+ unsigned int getType() { return shdr.sh_type; }
+ unsigned int getFlags() { return shdr.sh_flags; }
+ Elf64_Addr getAddr();
+ Elf64_Off getSize() { return shdr.sh_size; }
+ unsigned int getAddrAlign() { return shdr.sh_addralign; }
+ unsigned int getEntSize() { return shdr.sh_entsize; }
+ const char* getData() { return data; }
+ ElfSection* getLink() { return link; }
+ SectionInfo getInfo() { return info; }
+
+ void shrink(unsigned int newsize) {
+ if (newsize < shdr.sh_size) {
+ shdr.sh_size = newsize;
+ markDirty();
+ }
+ }
+
+ void grow(unsigned int newsize) {
+ if (newsize > shdr.sh_size) {
+ data = static_cast<char*>(realloc(data, newsize));
+ memset(data + shdr.sh_size, 0, newsize - shdr.sh_size);
+ shdr.sh_size = newsize;
+ markDirty();
+ }
+ }
+
+ Elf64_Off getOffset();
+ int getIndex();
+ Elf_Shdr& getShdr();
+
+ ElfSection* getNext() { return next; }
+ ElfSection* getPrevious() { return previous; }
+
+ virtual bool isRelocatable() {
+ return ((getType() == SHT_SYMTAB) || (getType() == SHT_STRTAB) ||
+ (getType() == SHT_RELA) || (getType() == SHT_HASH) ||
+ (getType() == SHT_NOTE) || (getType() == SHT_REL) ||
+ (getType() == SHT_DYNSYM) || (getType() == SHT_GNU_HASH) ||
+ (getType() == SHT_GNU_verdef) || (getType() == SHT_GNU_verneed) ||
+ (getType() == SHT_GNU_versym) || getSegmentByType(PT_INTERP)) &&
+ (getFlags() & SHF_ALLOC);
+ }
+
+ void insertAfter(ElfSection* section, bool dirty = true) {
+ if (previous != nullptr) previous->next = next;
+ if (next != nullptr) next->previous = previous;
+ previous = section;
+ if (section != nullptr) {
+ next = section->next;
+ section->next = this;
+ } else
+ next = nullptr;
+ if (next != nullptr) next->previous = this;
+ if (dirty) markDirty();
+ insertInSegments(section->segments);
+ }
+
+ virtual void insertBefore(ElfSection* section, bool dirty = true) {
+ if (previous != nullptr) previous->next = next;
+ if (next != nullptr) next->previous = previous;
+ next = section;
+ if (section != nullptr) {
+ previous = section->previous;
+ section->previous = this;
+ } else
+ previous = nullptr;
+ if (previous != nullptr) previous->next = this;
+ if (dirty) markDirty();
+ insertInSegments(section->segments);
+ }
+
+ void markDirty() {
+ if (link != nullptr) shdr.sh_link = -1;
+ if (info.index) shdr.sh_info = -1;
+ shdr.sh_offset = -1;
+ if (isRelocatable()) shdr.sh_addr = -1;
+ if (next) next->markDirty();
+ }
+
+ virtual void serialize(std::ofstream& file, unsigned char ei_class,
+ unsigned char ei_data) {
+ if (getType() == SHT_NOBITS) return;
+ file.seekp(getOffset());
+ file.write(data, getSize());
+ }
+
+ ElfSegment* getSegmentByType(unsigned int type);
+
+ private:
+ friend class ElfSegment;
+
+ void addToSegment(ElfSegment* segment) { segments.push_back(segment); }
+
+ void removeFromSegment(ElfSegment* segment) {
+ std::vector<ElfSegment*>::iterator i =
+ std::find(segments.begin(), segments.end(), segment);
+ segments.erase(i, i + 1);
+ }
+
+ void insertInSegments(std::vector<ElfSegment*>& segs);
+
+ protected:
+ Elf_Shdr shdr;
+ char* data;
+ const char* name;
+
+ private:
+ ElfSection* link;
+ SectionInfo info;
+ ElfSection *next, *previous;
+ int index;
+ std::vector<ElfSegment*> segments;
+};
+
+class ElfSegment {
+ public:
+ ElfSegment(Elf_Phdr* phdr);
+
+ unsigned int getType() { return type; }
+ unsigned int getFlags() { return flags; }
+ unsigned int getAlign() { return align; }
+
+ ElfSection* getFirstSection() {
+ return sections.empty() ? nullptr : sections.front();
+ }
+ int getVPDiff() { return v_p_diff; }
+ unsigned int getFileSize();
+ unsigned int getMemSize();
+ unsigned int getOffset();
+ unsigned int getAddr();
+
+ void addSection(ElfSection* section);
+ void removeSection(ElfSection* section);
+
+ std::list<ElfSection*>::iterator begin() { return sections.begin(); }
+ std::list<ElfSection*>::iterator end() { return sections.end(); }
+
+ void clear();
+
+ private:
+ unsigned int type;
+ int v_p_diff; // Difference between physical and virtual address
+ unsigned int flags;
+ unsigned int align;
+ std::list<ElfSection*> sections;
+ // The following are only really used for PT_GNU_RELRO until something
+ // better is found.
+ unsigned int vaddr;
+ unsigned int filesz, memsz;
+};
+
+class Elf_Ehdr : public serializable<Elf_Ehdr_Traits>, public ElfSection {
+ public:
+ Elf_Ehdr(std::ifstream& file, unsigned char ei_class, unsigned char ei_data);
+ void serialize(std::ofstream& file, unsigned char ei_class,
+ unsigned char ei_data) {
+ serializable<Elf_Ehdr_Traits>::serialize(file, ei_class, ei_data);
+ }
+};
+
+class Elf_Phdr : public serializable<Elf_Phdr_Traits> {
+ public:
+ Elf_Phdr(){};
+ Elf_Phdr(std::ifstream& file, unsigned char ei_class, unsigned char ei_data)
+ : serializable<Elf_Phdr_Traits>(file, ei_class, ei_data){};
+ bool contains(ElfSection* section) {
+ unsigned int size = section->getSize();
+ unsigned int addr = section->getAddr();
+ // This may be biased, but should work in most cases
+ if ((section->getFlags() & SHF_ALLOC) == 0) return false;
+ // Special case for PT_DYNAMIC. Eventually, this should
+ // be better handled than special cases
+ if ((p_type == PT_DYNAMIC) && (section->getType() != SHT_DYNAMIC))
+ return false;
+ // Special case for PT_TLS.
+ if ((p_type == PT_TLS) && !(section->getFlags() & SHF_TLS)) return false;
+ return (addr >= p_vaddr) && (addr + size <= p_vaddr + p_memsz);
+ }
+};
+
+typedef serializable<Elf_Dyn_Traits> Elf_Dyn;
+
+struct Elf_DynValue {
+ unsigned int tag;
+ ElfValue* value;
+};
+
+class ElfDynamic_Section : public ElfSection {
+ public:
+ ElfDynamic_Section(Elf_Shdr& s, std::ifstream* file, Elf* parent);
+ ~ElfDynamic_Section();
+
+ void serialize(std::ofstream& file, unsigned char ei_class,
+ unsigned char ei_data);
+
+ ElfValue* getValueForType(unsigned int tag);
+ ElfSection* getSectionForType(unsigned int tag);
+ bool setValueForType(unsigned int tag, ElfValue* val);
+
+ private:
+ std::vector<Elf_DynValue> dyns;
+};
+
+typedef serializable<Elf_Sym_Traits> Elf_Sym;
+
+struct Elf_SymValue {
+ const char* name;
+ unsigned char info;
+ unsigned char other;
+ ElfLocation value;
+ unsigned int size;
+ bool defined;
+};
+
+#define STT(type) (1 << STT_##type)
+
+class ElfSymtab_Section : public ElfSection {
+ public:
+ ElfSymtab_Section(Elf_Shdr& s, std::ifstream* file, Elf* parent);
+
+ void serialize(std::ofstream& file, unsigned char ei_class,
+ unsigned char ei_data);
+
+ Elf_SymValue* lookup(const char* name,
+ unsigned int type_filter = STT(OBJECT) | STT(FUNC));
+
+ // private: // Until we have a real API
+ std::vector<Elf_SymValue> syms;
+};
+
+class Elf_Rel : public serializable<Elf_Rel_Traits> {
+ public:
+ Elf_Rel() : serializable<Elf_Rel_Traits>(){};
+
+ Elf_Rel(std::ifstream& file, unsigned char ei_class, unsigned char ei_data)
+ : serializable<Elf_Rel_Traits>(file, ei_class, ei_data){};
+
+ static const unsigned int sh_type = SHT_REL;
+ static const unsigned int d_tag = DT_REL;
+ static const unsigned int d_tag_count = DT_RELCOUNT;
+};
+
+class Elf_Rela : public serializable<Elf_Rela_Traits> {
+ public:
+ Elf_Rela() : serializable<Elf_Rela_Traits>(){};
+
+ Elf_Rela(std::ifstream& file, unsigned char ei_class, unsigned char ei_data)
+ : serializable<Elf_Rela_Traits>(file, ei_class, ei_data){};
+
+ static const unsigned int sh_type = SHT_RELA;
+ static const unsigned int d_tag = DT_RELA;
+ static const unsigned int d_tag_count = DT_RELACOUNT;
+};
+
+template <class Rel>
+class ElfRel_Section : public ElfSection {
+ public:
+ ElfRel_Section(Elf_Shdr& s, std::ifstream* file, Elf* parent)
+ : ElfSection(s, file, parent) {
+ auto pos = file->tellg();
+ file->seekg(shdr.sh_offset);
+ for (unsigned int i = 0; i < s.sh_size / s.sh_entsize; i++) {
+ Rel r(*file, parent->getClass(), parent->getData());
+ rels.push_back(r);
+ }
+ file->seekg(pos);
+ }
+
+ void serialize(std::ofstream& file, unsigned char ei_class,
+ unsigned char ei_data) {
+ for (typename std::vector<Rel>::iterator i = rels.begin(); i != rels.end();
+ ++i)
+ (*i).serialize(file, ei_class, ei_data);
+ }
+ // private: // Until we have a real API
+ std::vector<Rel> rels;
+};
+
+class ElfStrtab_Section : public ElfSection {
+ public:
+ ElfStrtab_Section(Elf_Shdr& s, std::ifstream* file, Elf* parent)
+ : ElfSection(s, file, parent) {
+ table.push_back(table_storage(data, shdr.sh_size));
+ }
+
+ ~ElfStrtab_Section() {
+ for (std::vector<table_storage>::iterator t = table.begin() + 1;
+ t != table.end(); ++t)
+ delete[] t->buf;
+ }
+
+ const char* getStr(unsigned int index);
+
+ const char* getStr(const char* string);
+
+ unsigned int getStrIndex(const char* string);
+
+ void serialize(std::ofstream& file, unsigned char ei_class,
+ unsigned char ei_data);
+
+ private:
+ struct table_storage {
+ unsigned int size, used;
+ char* buf;
+
+ table_storage() : size(4096), used(0), buf(new char[4096]) {}
+ table_storage(const char* data, unsigned int sz)
+ : size(sz), used(sz), buf(const_cast<char*>(data)) {}
+ };
+ std::vector<table_storage> table;
+};
+
+inline unsigned char Elf::getClass() { return ehdr->e_ident[EI_CLASS]; }
+
+inline unsigned char Elf::getData() { return ehdr->e_ident[EI_DATA]; }
+
+inline unsigned char Elf::getType() { return ehdr->e_type; }
+
+inline unsigned char Elf::getMachine() { return ehdr->e_machine; }
+
+inline unsigned int Elf::getSize() {
+ ElfSection* section;
+ for (section = shdr_section /* It's usually not far from the end */;
+ section->getNext() != nullptr; section = section->getNext())
+ ;
+ return section->getOffset() + section->getSize();
+}
+
+inline ElfSegment* ElfSection::getSegmentByType(unsigned int type) {
+ for (std::vector<ElfSegment*>::iterator seg = segments.begin();
+ seg != segments.end(); ++seg)
+ if ((*seg)->getType() == type) return *seg;
+ return nullptr;
+}
+
+inline void ElfSection::insertInSegments(std::vector<ElfSegment*>& segs) {
+ for (std::vector<ElfSegment*>::iterator it = segs.begin(); it != segs.end();
+ ++it) {
+ (*it)->addSection(this);
+ }
+}
+
+inline ElfLocation::ElfLocation(ElfSection* section, unsigned int off,
+ enum position pos)
+ : section(section) {
+ if ((pos == ABSOLUTE) && section)
+ offset = off - section->getAddr();
+ else
+ offset = off;
+}
+
+inline ElfLocation::ElfLocation(unsigned int location, Elf* elf) {
+ section = elf->getSectionAt(location);
+ offset = location - (section ? section->getAddr() : 0);
+}
+
+inline unsigned int ElfLocation::getValue() {
+ return (section ? section->getAddr() : 0) + offset;
+}
+
+inline const char* ElfLocation::getBuffer() {
+ return section ? section->getData() + offset : nullptr;
+}
+
+inline unsigned int ElfSize::getValue() { return section->getSize(); }
+
+inline unsigned int ElfEntSize::getValue() { return section->getEntSize(); }