diff --git a/src/common/linux/synth_elf.cc b/src/common/linux/synth_elf.cc index 66ba60a3..1a741e55 100644 --- a/src/common/linux/synth_elf.cc +++ b/src/common/linux/synth_elf.cc @@ -20,6 +20,7 @@ ELF::ELF(uint16_t machine, : 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) { @@ -115,7 +116,8 @@ int ELF::AddSection(const string& name, const Section& section, // sh_entsize .Append(endianness(), addr_size_, entsize); - sections_.push_back(ElfSection(section, type, offset, offset_label)); + sections_.push_back(ElfSection(section, type, addr, offset, offset_label, + size)); return index; } @@ -133,6 +135,58 @@ void ELF::AppendSection(ElfSection §ion) { } } +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_; @@ -140,14 +194,19 @@ void ELF::Finish() { 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::iterator it = sections_.begin(); it < sections_.end(); ++it) { AppendSection(*it); } section_count_label_ = section_count_; program_count_label_ = program_count_; - // TODO: allow adding entries to program header table - program_header_label_ = 0; // Section header table starts here. Mark(§ion_header_label_); diff --git a/src/common/linux/synth_elf.h b/src/common/linux/synth_elf.h index a2fb0dc0..9a6c0316 100644 --- a/src/common/linux/synth_elf.h +++ b/src/common/linux/synth_elf.h @@ -104,6 +104,10 @@ class ELF : public Section { uint32_t type, uint32_t flags = 0, uint64_t addr = 0, uint32_t link = 0, uint64_t entsize = 0, uint64_t offset = 0); + // Add a segment containing from section index start to section index end. + // The indexes must have been gotten from AddSection. + void AddSegment(int start, int end, uint32_t type, uint32_t flags = 0); + // Write out all data. GetContents may be used after this. void Finish(); @@ -116,6 +120,8 @@ class ELF : public Section { // Number of entries in the program header table. int program_count_; Label program_count_label_; + // The program header table itself. + Section program_header_table_; // Offset to the section header table. Label section_header_label_; @@ -133,15 +139,17 @@ class ELF : public Section { // Record of an added section struct ElfSection : public Section { - ElfSection(const Section& section, uint32_t type, uint32_t offset, - Label offset_label) - : Section(section), type_(type), offset_(offset) - , offset_label_(offset_label) { + ElfSection(const Section& section, uint32_t type, uint32_t addr, + uint32_t offset, Label offset_label, uint32_t size) + : Section(section), type_(type), addr_(addr), offset_(offset) + , offset_label_(offset_label), size_(size) { } uint32_t type_; + uint32_t addr_; uint32_t offset_; Label offset_label_; + uint32_t size_; }; vector sections_; diff --git a/src/common/linux/synth_elf_unittest.cc b/src/common/linux/synth_elf_unittest.cc index b1586151..94ff5052 100644 --- a/src/common/linux/synth_elf_unittest.cc +++ b/src/common/linux/synth_elf_unittest.cc @@ -42,6 +42,7 @@ using google_breakpad::ElfClass32; using google_breakpad::ElfClass64; using google_breakpad::synth_elf::ELF; +using google_breakpad::synth_elf::Section; using google_breakpad::synth_elf::StringTable; using google_breakpad::synth_elf::SymbolTable; using google_breakpad::test_assembler::Endianness; @@ -260,4 +261,112 @@ TYPED_TEST(BasicElf, EmptyLE) { EXPECT_EQ(0U, shdr[1].sh_entsize); } +TYPED_TEST(BasicElf, BasicLE) { + typedef typename TypeParam::Ehdr Ehdr; + typedef typename TypeParam::Phdr Phdr; + typedef typename TypeParam::Shdr Shdr; + const size_t kStringTableSize = sizeof("\0.text\0.bss\0.shstrtab"); + const size_t kStringTableAlign = 4 - kStringTableSize % 4; + const size_t kExpectedSize = sizeof(Ehdr) + + // Four sections, SHT_NULL + the section header string table + + // 4096 bytes of the size-aligned .text section + one program header. + sizeof(Phdr) + 4 * sizeof(Shdr) + 4096 + + kStringTableSize + kStringTableAlign; + + // It doesn't really matter that the machine type is right for the class. + ELF elf(EM_386, TypeParam::kClass, kLittleEndian); + Section text(kLittleEndian); + text.Append(4094, 0); + int text_idx = elf.AddSection(".text", text, SHT_PROGBITS); + Section bss(kLittleEndian); + bss.Append(16, 0); + int bss_idx = elf.AddSection(".bss", bss, SHT_NOBITS); + elf.AddSegment(text_idx, bss_idx, PT_LOAD); + elf.Finish(); + EXPECT_EQ(kExpectedSize, elf.Size()); + + string contents; + ASSERT_TRUE(elf.GetContents(&contents)); + ASSERT_EQ(kExpectedSize, contents.size()); + const Ehdr* header = + reinterpret_cast(contents.data()); + const uint8_t kIdent[] = { + ELFMAG0, ELFMAG1, ELFMAG2, ELFMAG3, + TypeParam::kClass, ELFDATA2LSB, EV_CURRENT, ELFOSABI_SYSV, + 0, 0, 0, 0, 0, 0, 0, 0 + }; + EXPECT_EQ(0, memcmp(kIdent, header->e_ident, sizeof(kIdent))); + EXPECT_EQ(ET_EXEC, header->e_type); + EXPECT_EQ(EM_386, header->e_machine); + EXPECT_EQ(static_cast(EV_CURRENT), header->e_version); + EXPECT_EQ(0U, header->e_entry); + EXPECT_EQ(sizeof(Ehdr), header->e_phoff); + EXPECT_EQ(sizeof(Ehdr) + sizeof(Phdr) + 4096 + kStringTableSize + + kStringTableAlign, header->e_shoff); + EXPECT_EQ(0U, header->e_flags); + EXPECT_EQ(sizeof(Ehdr), header->e_ehsize); + EXPECT_EQ(sizeof(Phdr), header->e_phentsize); + EXPECT_EQ(1, header->e_phnum); + EXPECT_EQ(sizeof(Shdr), header->e_shentsize); + EXPECT_EQ(4, header->e_shnum); + EXPECT_EQ(3, header->e_shstrndx); + + const Shdr* shdr = + reinterpret_cast(contents.data() + header->e_shoff); + EXPECT_EQ(0U, shdr[0].sh_name); + EXPECT_EQ(static_cast(SHT_NULL), shdr[0].sh_type); + EXPECT_EQ(0U, shdr[0].sh_flags); + EXPECT_EQ(0U, shdr[0].sh_addr); + EXPECT_EQ(0U, shdr[0].sh_offset); + EXPECT_EQ(0U, shdr[0].sh_size); + EXPECT_EQ(0U, shdr[0].sh_link); + EXPECT_EQ(0U, shdr[0].sh_info); + EXPECT_EQ(0U, shdr[0].sh_addralign); + EXPECT_EQ(0U, shdr[0].sh_entsize); + + EXPECT_EQ(1U, shdr[1].sh_name); + EXPECT_EQ(static_cast(SHT_PROGBITS), shdr[1].sh_type); + EXPECT_EQ(0U, shdr[1].sh_flags); + EXPECT_EQ(0U, shdr[1].sh_addr); + EXPECT_EQ(sizeof(Ehdr) + sizeof(Phdr), shdr[1].sh_offset); + EXPECT_EQ(4094U, shdr[1].sh_size); + EXPECT_EQ(0U, shdr[1].sh_link); + EXPECT_EQ(0U, shdr[1].sh_info); + EXPECT_EQ(0U, shdr[1].sh_addralign); + EXPECT_EQ(0U, shdr[1].sh_entsize); + + EXPECT_EQ(sizeof("\0.text"), shdr[2].sh_name); + EXPECT_EQ(static_cast(SHT_NOBITS), shdr[2].sh_type); + EXPECT_EQ(0U, shdr[2].sh_flags); + EXPECT_EQ(0U, shdr[2].sh_addr); + EXPECT_EQ(0U, shdr[2].sh_offset); + EXPECT_EQ(16U, shdr[2].sh_size); + EXPECT_EQ(0U, shdr[2].sh_link); + EXPECT_EQ(0U, shdr[2].sh_info); + EXPECT_EQ(0U, shdr[2].sh_addralign); + EXPECT_EQ(0U, shdr[2].sh_entsize); + + EXPECT_EQ(sizeof("\0.text\0.bss"), shdr[3].sh_name); + EXPECT_EQ(static_cast(SHT_STRTAB), shdr[3].sh_type); + EXPECT_EQ(0U, shdr[3].sh_flags); + EXPECT_EQ(0U, shdr[3].sh_addr); + EXPECT_EQ(sizeof(Ehdr) + sizeof(Phdr) + 4096, shdr[3].sh_offset); + EXPECT_EQ(kStringTableSize, shdr[3].sh_size); + EXPECT_EQ(0U, shdr[3].sh_link); + EXPECT_EQ(0U, shdr[3].sh_info); + EXPECT_EQ(0U, shdr[3].sh_addralign); + EXPECT_EQ(0U, shdr[3].sh_entsize); + + const Phdr* phdr = + reinterpret_cast(contents.data() + header->e_phoff); + EXPECT_EQ(static_cast(PT_LOAD), phdr->p_type); + EXPECT_EQ(sizeof(Ehdr) + sizeof(Phdr), phdr->p_offset); + EXPECT_EQ(0U, phdr->p_vaddr); + EXPECT_EQ(0U, phdr->p_paddr); + EXPECT_EQ(4096U, phdr->p_filesz); + EXPECT_EQ(4096U + 16U, phdr->p_memsz); + EXPECT_EQ(0U, phdr->p_flags); + EXPECT_EQ(0U, phdr->p_align); +} + #endif // defined(__i386__) || defined(__x86_64__)