Skip to content

Commit

Permalink
[Courgette] Add parser for ELF section names.
Browse files Browse the repository at this point in the history
To reduce noise for ELF parsing, we plan to restrict ELF rel32
parsing to ".text" section only. This CL adds ELF section name
parsing, with tests.

Review URL: https://codereview.chromium.org/1900043004

Cr-Commit-Position: refs/heads/master@{#388854}
  • Loading branch information
samuelhuang authored and Commit bot committed Apr 21, 2016
1 parent b6a522d commit 16f01e0
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 4 deletions.
31 changes: 28 additions & 3 deletions courgette/disassembler_elf_32.cc
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,8 @@ bool DisassemblerElf32::ParseHeader() {
section_header_table_.assign(section_header_table_raw,
section_header_table_raw + section_header_table_size_);

// TODO(huangs): Validate offsets of all section headers.

section_header_file_offset_order_ =
GetSectionHeaderFileOffsetOrder(section_header_table_);

Expand All @@ -132,11 +134,18 @@ bool DisassemblerElf32::ParseHeader() {
FileOffsetToPointer(header_->e_phoff));
program_header_table_size_ = header_->e_phnum;

if (header_->e_shstrndx >= header_->e_shnum)
Elf32_Half string_section_id = header_->e_shstrndx;
if (string_section_id >= header_->e_shnum)
return Bad("Out of bounds string section index");

default_string_section_ = reinterpret_cast<const char*>(
SectionBody(static_cast<int>(header_->e_shstrndx)));
default_string_section_ =
reinterpret_cast<const char*>(SectionBody(string_section_id));
default_string_section_size_ = SectionHeader(string_section_id)->sh_size;
// String section may be empty. If nonempty, then last byte must be null.
if (default_string_section_size_ > 0) {
if (default_string_section_[default_string_section_size_ - 1] != '\0')
return Bad("String section does not terminate");
}

if (!UpdateLength())
return Bad("Out of bounds section or segment");
Expand Down Expand Up @@ -206,6 +215,22 @@ bool DisassemblerElf32::UpdateLength() {
return true;
}

CheckBool DisassemblerElf32::SectionName(const Elf32_Shdr& shdr,
std::string* name) const {
DCHECK(name);
size_t string_pos = shdr.sh_name;
if (string_pos == 0) {
// Empty string by convention. Valid even if string section is empty.
name->clear();
} else {
if (string_pos >= default_string_section_size_)
return false;
// Safe because string section must terminate with null.
*name = default_string_section_ + string_pos;
}
return true;
}

CheckBool DisassemblerElf32::IsValidTargetRVA(RVA rva) const {
if (rva == kUnassignedRVA)
return false;
Expand Down
7 changes: 6 additions & 1 deletion courgette/disassembler_elf_32.h
Original file line number Diff line number Diff line change
Expand Up @@ -105,9 +105,13 @@ class DisassemblerElf32 : public Disassembler {
}

const uint8_t* SectionBody(Elf32_Half id) const {
// TODO(huangs): Assert that section does not have SHT_NOBITS.
return FileOffsetToPointer(SectionHeader(id)->sh_offset);
}

// Gets the |name| of section |shdr|. Returns true on success.
CheckBool SectionName(const Elf32_Shdr& shdr, std::string* name) const;

// Misc Segment Helpers

Elf32_Half ProgramSegmentHeaderCount() const {
Expand Down Expand Up @@ -174,8 +178,9 @@ class DisassemblerElf32 : public Disassembler {
const Elf32_Phdr* program_header_table_;
Elf32_Half program_header_table_size_;

// Pointer to section names.
// Pointer to string table containing section names.
const char* default_string_section_;
size_t default_string_section_size_;

std::vector<RVA> abs32_locations_;
ScopedVector<TypedRVA> rel32_locations_;
Expand Down
22 changes: 22 additions & 0 deletions courgette/disassembler_elf_32_x86_unittest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@

#include "courgette/disassembler_elf_32_x86.h"

#include <ctype.h>
#include <stddef.h>
#include <stdint.h>

#include <algorithm>
#include <memory>
#include <set>
#include <string>
#include <vector>

Expand All @@ -35,6 +37,24 @@ class TestDisassemblerElf32X86 : public DisassemblerElf32X86 {
EXPECT_EQ(static_cast<size_t>(SectionHeaderCount()), file_offsets.size());
EXPECT_TRUE(std::is_sorted(file_offsets.begin(), file_offsets.end()));
}

void TestSectionName() {
std::set<std::string> name_set;
for (const Elf32_Shdr& section_header : section_header_table_) {
std::string name;
EXPECT_TRUE(SectionName(section_header, &name));
// Ensure |name| is unique and is printable (may be empty though).
EXPECT_EQ(0U, name_set.count(name));
EXPECT_TRUE(std::all_of(name.begin(), name.end(), ::isprint));
name_set.insert(name);
}
// Check for existence of a few common sections.
EXPECT_EQ(1U, name_set.count(".text"));
EXPECT_EQ(1U, name_set.count(".data"));
EXPECT_EQ(1U, name_set.count(".rodata"));
EXPECT_EQ(1U, name_set.count(".bss"));
EXPECT_EQ(1U, name_set.count(".shstrtab"));
}
};

class DisassemblerElf32X86Test : public BaseTest {
Expand Down Expand Up @@ -104,6 +124,8 @@ void DisassemblerElf32X86Test::TestExe(const char* file_name,
EXPECT_FALSE(found_match);

disassembler->TestSectionHeaderFileOffsetOrder();

disassembler->TestSectionName();
}

} // namespace
Expand Down

0 comments on commit 16f01e0

Please sign in to comment.