#include "common/linux/synth_elf.h" #include <assert.h> #include <elf.h> #include <stdio.h> #include <string.h> #include "common/linux/elf_gnu_compat.h" #include "common/using_std_string.h" namespace google_breakpad { namespace synth_elf { ELF::ELF(uint16_t machine, uint8_t file_class, Endianness endianness) : Section(endianness), addr_size_(file_class == ELFCLASS64 ? 8 : 4), program_count_(0), program_header_table_(endianness), section_count_(0), section_header_table_(endianness), section_header_strings_(endianness) { // Could add support for more machine types here if needed. assert(machine == EM_386 || machine == EM_X86_64 || machine == EM_ARM); assert(file_class == ELFCLASS32 || file_class == ELFCLASS64); start() = 0; // Add ELF header // e_ident // EI_MAG0...EI_MAG3 D8(ELFMAG0); D8(ELFMAG1); D8(ELFMAG2); D8(ELFMAG3); // EI_CLASS D8(file_class); // EI_DATA D8(endianness == kLittleEndian ? ELFDATA2LSB : ELFDATA2MSB); // EI_VERSION D8(EV_CURRENT); // EI_OSABI D8(ELFOSABI_SYSV); // EI_ABIVERSION D8(0); // EI_PAD Append(7, 0); assert(Size() == EI_NIDENT); // e_type D16(ET_EXEC); //TODO: allow passing ET_DYN? // e_machine D16(machine); // e_version D32(EV_CURRENT); // e_entry Append(endianness, addr_size_, 0); // e_phoff Append(endianness, addr_size_, program_header_label_); // e_shoff Append(endianness, addr_size_, section_header_label_); // e_flags D32(0); // e_ehsize D16(addr_size_ == 8 ? sizeof(Elf64_Ehdr) : sizeof(Elf32_Ehdr)); // e_phentsize D16(addr_size_ == 8 ? sizeof(Elf64_Phdr) : sizeof(Elf32_Phdr)); // e_phnum D16(program_count_label_); // e_shentsize D16(addr_size_ == 8 ? sizeof(Elf64_Shdr) : sizeof(Elf32_Shdr)); // e_shnum D16(section_count_label_); // e_shstrndx D16(section_header_string_index_); // Add an empty section for SHN_UNDEF. Section shn_undef; AddSection("", shn_undef, SHT_NULL); } int ELF::AddSection(const string& name, const Section& section, uint32_t type, uint32_t flags, uint64_t addr, uint32_t link, uint64_t entsize, uint64_t offset) { Label offset_label; Label string_label(section_header_strings_.Add(name)); size_t size = section.Size(); int index = section_count_; ++section_count_; section_header_table_ // sh_name .D32(string_label) // sh_type .D32(type) // sh_flags .Append(endianness(), addr_size_, flags) // sh_addr .Append(endianness(), addr_size_, addr) // sh_offset .Append(endianness(), addr_size_, offset_label) // sh_size .Append(endianness(), addr_size_, size) // sh_link .D32(link) // sh_info .D32(0) // sh_addralign .Append(endianness(), addr_size_, 0) // sh_entsize .Append(endianness(), addr_size_, entsize); sections_.push_back(ElfSection(section, type, addr, offset, offset_label, size)); return index; } void ELF::AppendSection(ElfSection §ion) { // NULL and NOBITS sections have no content, so they // don't need to be written to the file. if (section.type_ == SHT_NULL) { section.offset_label_ = 0; } else if (section.type_ == SHT_NOBITS) { section.offset_label_ = section.offset_; } else { Mark(§ion.offset_label_); Append(section); Align(4); } } void ELF::AddSegment(int start, int end, uint32_t type, uint32_t flags) { assert(start > 0); assert(size_t(start) < sections_.size()); assert(end > 0); assert(size_t(end) < sections_.size()); ++program_count_; // p_type program_header_table_.D32(type); if (addr_size_ == 8) { // p_flags program_header_table_.D32(flags); } size_t filesz = 0; size_t memsz = 0; bool prev_was_nobits = false; for (int i = start; i <= end; ++i) { size_t size = sections_[i].size_; if (sections_[i].type_ != SHT_NOBITS) { assert(!prev_was_nobits); // non SHT_NOBITS sections are 4-byte aligned (see AddSection) size = (size + 3) & ~3; filesz += size; } else { prev_was_nobits = true; } memsz += size; } program_header_table_ // p_offset .Append(endianness(), addr_size_, sections_[start].offset_label_) // p_vaddr .Append(endianness(), addr_size_, sections_[start].addr_) // p_paddr .Append(endianness(), addr_size_, sections_[start].addr_) // p_filesz .Append(endianness(), addr_size_, filesz) // p_memsz .Append(endianness(), addr_size_, memsz); if (addr_size_ == 4) { // p_flags program_header_table_.D32(flags); } // p_align program_header_table_.Append(endianness(), addr_size_, 0); } void ELF::Finish() { // Add the section header string table at the end. section_header_string_index_ = section_count_; //printf(".shstrtab size: %ld\n", section_header_strings_.Size()); AddSection(".shstrtab", section_header_strings_, SHT_STRTAB); //printf("section_count_: %ld, sections_.size(): %ld\n", // section_count_, sections_.size()); if (program_count_) { Mark(&program_header_label_); Append(program_header_table_); } else { program_header_label_ = 0; } for (vector<ElfSection>::iterator it = sections_.begin(); it < sections_.end(); ++it) { AppendSection(*it); } section_count_label_ = section_count_; program_count_label_ = program_count_; // Section header table starts here. Mark(§ion_header_label_); Append(section_header_table_); } SymbolTable::SymbolTable(Endianness endianness, size_t addr_size, StringTable& table) : Section(endianness), addr_size_(addr_size), table_(table) { assert(addr_size_ == 4 || addr_size_ == 8); } void SymbolTable::AddSymbol(const string& name, uint32_t value, uint32_t size, unsigned info, uint16_t shndx) { assert(addr_size_ == 4); D32(table_.Add(name)); D32(value); D32(size); D8(info); D8(0); // other D16(shndx); } void SymbolTable::AddSymbol(const string& name, uint64_t value, uint64_t size, unsigned info, uint16_t shndx) { assert(addr_size_ == 8); D32(table_.Add(name)); D8(info); D8(0); // other D16(shndx); D64(value); D64(size); } void Notes::AddNote(int type, const string &name, const uint8_t* desc_bytes, size_t desc_size) { // Elf32_Nhdr and Elf64_Nhdr are exactly the same. Elf32_Nhdr note_header; memset(¬e_header, 0, sizeof(note_header)); note_header.n_namesz = name.length() + 1; note_header.n_descsz = desc_size; note_header.n_type = type; Append(reinterpret_cast<const uint8_t*>(¬e_header), sizeof(note_header)); AppendCString(name); Align(4); Append(desc_bytes, desc_size); Align(4); } } // namespace synth_elf } // namespace google_breakpad