Skip to content

Commit

Permalink
[Courgette] Make ELF section header sorting less intrusive.
Browse files Browse the repository at this point in the history
Recently we start to sort ELF section headers by file offset. However,
directly sorting the section headers is intrusive because it scrambles
section ids (order in array). This CL adds a layer of section id lookup,
so we can visit section headers in file offset order without affecting
section ids. Also updated tests.

BUG=604542

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

Cr-Commit-Position: refs/heads/master@{#388290}
  • Loading branch information
samuelhuang authored and Commit bot committed Apr 19, 2016
1 parent 43b1d01 commit 6918930
Show file tree
Hide file tree
Showing 3 changed files with 54 additions and 21 deletions.
40 changes: 23 additions & 17 deletions courgette/disassembler_elf_32.cc
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,20 @@ namespace courgette {

namespace {

// Initializes |section_header_table| by copying |section_header_table_size|
// entries from |section_header_table_raw|, and sorting by |sh_offset|.
void SortSectionHeader(const Elf32_Shdr* section_header_table_raw,
Elf32_Half section_header_table_size,
std::vector<Elf32_Shdr>* section_header_table) {
section_header_table->assign(section_header_table_raw,
section_header_table_raw + section_header_table_size);
auto comp = [](const Elf32_Shdr& header1, const Elf32_Shdr& header2) {
return header1.sh_offset < header2.sh_offset;
// Sorts |section_headers| by file offset and stores the resulting permutation
// of section ids in |order|.
std::vector<Elf32_Half> GetSectionHeaderFileOffsetOrder(
const std::vector<Elf32_Shdr>& section_headers) {
size_t size = section_headers.size();
std::vector<Elf32_Half> order(size);
for (size_t i = 0; i < size; ++i)
order[i] = static_cast<Elf32_Half>(i);

auto comp = [&](int idx1, int idx2) {
return section_headers[idx1].sh_offset < section_headers[idx2].sh_offset;
};
std::stable_sort(
section_header_table->begin(), section_header_table->end(), comp);
std::stable_sort(order.begin(), order.end(), comp);
return order;
}

} // namespace
Expand All @@ -44,8 +46,8 @@ RVA DisassemblerElf32::FileOffsetToRVA(FileOffset offset) const {
// executables and so only need to support 32-bit file sizes.
uint32_t offset32 = static_cast<uint32_t>(offset);

for (Elf32_Half section_id = 0; section_id < SectionHeaderCount();
++section_id) {
// Visit section headers ordered by file offset.
for (Elf32_Half section_id : section_header_file_offset_order_) {
const Elf32_Shdr* section_header = SectionHeader(section_id);
// These can appear to have a size in the file, but don't.
if (section_header->sh_type == SHT_NOBITS)
Expand Down Expand Up @@ -112,12 +114,16 @@ bool DisassemblerElf32::ParseHeader() {
if (!IsArrayInBounds(header_->e_shoff, header_->e_shnum, sizeof(Elf32_Shdr)))
return Bad("Out of bounds section header table");

// Extract |section_header_table_|, ordered by section id.
const Elf32_Shdr* section_header_table_raw =
reinterpret_cast<const Elf32_Shdr*>(
FileOffsetToPointer(header_->e_shoff));
section_header_table_size_ = header_->e_shnum;
SortSectionHeader(section_header_table_raw, section_header_table_size_,
&section_header_table_);
section_header_table_.assign(section_header_table_raw,
section_header_table_raw + section_header_table_size_);

section_header_file_offset_order_ =
GetSectionHeaderFileOffsetOrder(section_header_table_);

if (!IsArrayInBounds(header_->e_phoff, header_->e_phnum, sizeof(Elf32_Phdr)))
return Bad("Out of bounds program header table");
Expand Down Expand Up @@ -264,8 +270,8 @@ CheckBool DisassemblerElf32::ParseFile(AssemblyProgram* program) {
std::vector<FileOffset>::iterator end_abs_offset = abs_offsets.end();
ScopedVector<TypedRVA>::iterator end_rel = rel32_locations_.end();

for (Elf32_Half section_id = 0; section_id < SectionHeaderCount();
++section_id) {
// Visit section headers ordered by file offset.
for (Elf32_Half section_id : section_header_file_offset_order_) {
const Elf32_Shdr* section_header = SectionHeader(section_id);

if (section_header->sh_type == SHT_NOBITS)
Expand Down
10 changes: 8 additions & 2 deletions courgette/disassembler_elf_32.h
Original file line number Diff line number Diff line change
Expand Up @@ -162,13 +162,19 @@ class DisassemblerElf32 : public Disassembler {
CheckBool ParseRel32RelocsFromSections() WARN_UNUSED_RESULT;

const Elf32_Ehdr* header_;
std::vector<Elf32_Shdr> section_header_table_;

Elf32_Half section_header_table_size_;

// Section header table, ordered by section id.
std::vector<Elf32_Shdr> section_header_table_;

// An ordering of |section_header_table_|, sorted by file offset.
std::vector<Elf32_Half> section_header_file_offset_order_;

const Elf32_Phdr* program_header_table_;
Elf32_Half program_header_table_size_;

// Section header for default
// Pointer to section names.
const char* default_string_section_;

std::vector<RVA> abs32_locations_;
Expand Down
25 changes: 23 additions & 2 deletions courgette/disassembler_elf_32_x86_unittest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include <algorithm>
#include <memory>
#include <string>
#include <vector>

#include "courgette/assembly_program.h"
#include "courgette/base_test_unittest.h"
Expand All @@ -19,6 +20,23 @@ namespace courgette {

namespace {

class TestDisassemblerElf32X86 : public DisassemblerElf32X86 {
public:
TestDisassemblerElf32X86(const void* start, size_t length)
: DisassemblerElf32X86(start, length) { }
~TestDisassemblerElf32X86() override { }

void TestSectionHeaderFileOffsetOrder() {
std::vector<FileOffset> file_offsets;
for (Elf32_Half section_id : section_header_file_offset_order_) {
const Elf32_Shdr* section_header = SectionHeader(section_id);
file_offsets.push_back(section_header->sh_offset);
}
EXPECT_EQ(static_cast<size_t>(SectionHeaderCount()), file_offsets.size());
EXPECT_TRUE(std::is_sorted(file_offsets.begin(), file_offsets.end()));
}
};

class DisassemblerElf32X86Test : public BaseTest {
public:
void TestExe(const char* file_name,
Expand All @@ -32,8 +50,8 @@ void DisassemblerElf32X86Test::TestExe(const char* file_name,
using TypedRVA = DisassemblerElf32::TypedRVA;
std::string file1 = FileContents(file_name);

std::unique_ptr<DisassemblerElf32X86> disassembler(
new DisassemblerElf32X86(file1.c_str(), file1.length()));
std::unique_ptr<TestDisassemblerElf32X86> disassembler(
new TestDisassemblerElf32X86(file1.c_str(), file1.length()));

bool can_parse_header = disassembler->ParseHeader();
EXPECT_TRUE(can_parse_header);
Expand Down Expand Up @@ -84,12 +102,15 @@ void DisassemblerElf32X86Test::TestExe(const char* file_name,
}
}
EXPECT_FALSE(found_match);

disassembler->TestSectionHeaderFileOffsetOrder();
}

} // namespace

TEST_F(DisassemblerElf32X86Test, All) {
TestExe("elf-32-1", 200, 3441);
TestExe("elf-32-high-bss", 0, 13);
}

} // namespace courgette

0 comments on commit 6918930

Please sign in to comment.