diff options
Diffstat (limited to '')
-rw-r--r-- | build/unix/elfhack/elfxx.h | 691 |
1 files changed, 691 insertions, 0 deletions
diff --git a/build/unix/elfhack/elfxx.h b/build/unix/elfhack/elfxx.h new file mode 100644 index 0000000000..c309e88c72 --- /dev/null +++ b/build/unix/elfhack/elfxx.h @@ -0,0 +1,691 @@ +/* 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 <iostream> +#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::Type32 { + public: + serializable(){}; + serializable(const typename T::Type32& p) : T::Type32(p){}; + + private: + template <typename R> + void init(const char* buf, size_t len, 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, 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, char ei_class, 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, char ei_class, 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, char ei_class, 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, char ei_class, 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(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(unsigned int offset); + + ElfSegment* getSegmentByType(unsigned int type, ElfSegment* last = nullptr); + + ElfDynamic_Section* getDynSection(); + + void normalize(); + void write(std::ofstream& file); + + char getClass(); + char getData(); + char getType(); + 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; } + unsigned int getAddr(); + unsigned int 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(); + } + } + + unsigned int 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, char ei_class, 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, char ei_class, char ei_data); + void serialize(std::ofstream& file, char ei_class, 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, char ei_class, 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, char ei_class, 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, char ei_class, 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, char ei_class, 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, char ei_class, 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, char ei_class, 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, char ei_class, 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 char Elf::getClass() { return ehdr->e_ident[EI_CLASS]; } + +inline char Elf::getData() { return ehdr->e_ident[EI_DATA]; } + +inline char Elf::getType() { return ehdr->e_type; } + +inline 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(); } |