text: // Copyright 2013 The Chromium Authors. All rights reserved.
  // Use of this source code is governed by a BSD-style license that can be
  // found in the LICENSE file.
  
  #include "courgette/disassembler_elf_32.h"
  
  #include <algorithm>
  #include <string>
  #include <vector>
  
  #include "base/basictypes.h"
  #include "base/logging.h"
  #include "base/memory/scoped_vector.h"
  
  #include "courgette/assembly_program.h"
  #include "courgette/courgette.h"
  #include "courgette/encoded_program.h"
  
  namespace courgette {
  
  DisassemblerElf32::DisassemblerElf32(const void* start, size_t length)
    : Disassembler(start, length),
      header_(NULL),
      section_header_table_(NULL),
      section_header_table_size_(0),
      program_header_table_(NULL),
      program_header_table_size_(0),
      default_string_section_(NULL) {
  }
  
  bool DisassemblerElf32::ParseHeader() {
    if (length() < sizeof(Elf32_Ehdr))
      return Bad("Too small");
  
    header_ = (Elf32_Ehdr *)start();
  
    // Have magic for elf header?
    if (header_->e_ident[0] != 0x7f ||
        header_->e_ident[1] != 'E' ||
        header_->e_ident[2] != 'L' ||
        header_->e_ident[3] != 'F')
      return Bad("No Magic Number");
  
    if (header_->e_type != ET_EXEC &&
        header_->e_type != ET_DYN)
      return Bad("Not an executable file or shared library");
  
    if (header_->e_machine != ElfEM())
      return Bad("Not a supported architecture");
  
    if (header_->e_version != 1)
      return Bad("Unknown file version");
  
    if (header_->e_shentsize != sizeof(Elf32_Shdr))
      return Bad("Unexpected section header size");
  
    if (!IsArrayInBounds(header_->e_shoff, header_->e_shnum, sizeof(Elf32_Shdr)))
      return Bad("Out of bounds section header table");
  
    section_header_table_ = (Elf32_Shdr *)OffsetToPointer(header_->e_shoff);
    section_header_table_size_ = header_->e_shnum;
  
    if (!IsArrayInBounds(header_->e_phoff, header_->e_phnum, sizeof(Elf32_Phdr)))
      return Bad("Out of bounds program header table");
  
    program_header_table_ = (Elf32_Phdr *)OffsetToPointer(header_->e_phoff);
    program_header_table_size_ = header_->e_phnum;
  
    if (header_->e_shstrndx >= header_->e_shnum)
      return Bad("Out of bounds string section index");
  
    default_string_section_ = (const char *)SectionBody((int)header_->e_shstrndx);
  
    if (!UpdateLength()) {
      return Bad("Out of bounds section or segment");
    }
  
    return Good();
  }
  
  bool DisassemblerElf32::Disassemble(AssemblyProgram* target) {
    if (!ok())
      return false;
  
    // The Image Base is always 0 for ELF Executables
    target->set_image_base(0);
  
    if (!ParseAbs32Relocs())
      return false;
  
    if (!ParseRel32RelocsFromSections())
      return false;
  
    if (!ParseFile(target))
      return false;
  
    target->DefaultAssignIndexes();
  
    return true;
  }
  
  bool DisassemblerElf32::UpdateLength() {
    Elf32_Off result = 0;
  
    // Find the end of the last section
    for (int section_id = 0; section_id < SectionHeaderCount(); section_id++) {
      const Elf32_Shdr *section_header = SectionHeader(section_id);
  
      if (section_header->sh_type == SHT_NOBITS)
        continue;
  
      if (!IsArrayInBounds(section_header->sh_offset, section_header->sh_size, 1))
        return false;
  
      Elf32_Off section_end = section_header->sh_offset + section_header->sh_size;
      result = std::max(result, section_end);
    }
  
    // Find the end of the last segment
    for (int i = 0; i < ProgramSegmentHeaderCount(); i++) {
      const Elf32_Phdr *segment_header = ProgramSegmentHeader(i);
  
      if (!IsArrayInBounds(segment_header->p_offset, segment_header->p_filesz, 1))
        return false;
  
      Elf32_Off segment_end = segment_header->p_offset + segment_header->p_filesz;
      result = std::max(result, segment_end);
    }
  
    Elf32_Off section_table_end = header_->e_shoff +
                                  (header_->e_shnum * sizeof(Elf32_Shdr));
    result = std::max(result, section_table_end);
  
    Elf32_Off segment_table_end = header_->e_phoff +
                                  (header_->e_phnum * sizeof(Elf32_Phdr));
    result = std::max(result, segment_table_end);
  
    ReduceLength(result);
    return true;
  }
  
  CheckBool DisassemblerElf32::IsValidRVA(RVA rva) const {
  
    // It's valid if it's contained in any program segment
    for (int i = 0; i < ProgramSegmentHeaderCount(); i++) {
      const Elf32_Phdr *segment_header = ProgramSegmentHeader(i);
  
      if (segment_header->p_type != PT_LOAD)
        continue;
  
      Elf32_Addr begin = segment_header->p_vaddr;
      Elf32_Addr end = segment_header->p_vaddr + segment_header->p_memsz;
  
      if (rva >= begin && rva < end)
        return true;
    }
  
    return false;
  }
  
  // Returns RVA for an in memory address, or NULL.
  CheckBool DisassemblerElf32::RVAToFileOffset(Elf32_Addr addr,
                                                  size_t* result) const {
  
    for (int i = 0; i < ProgramSegmentHeaderCount(); i++) {
      Elf32_Addr begin = ProgramSegmentMemoryBegin(i);
      Elf32_Addr end = begin + ProgramSegmentMemorySize(i);
  
      if (addr >= begin  && addr < end) {
        Elf32_Addr offset = addr - begin;
  
        if (offset < ProgramSegmentFileSize(i)) {
          *result = ProgramSegmentFileOffset(i) + offset;
          return true;
        }
      }
    }
  
    return false;
  }
  
  RVA DisassemblerElf32::FileOffsetToRVA(size_t offset) const {
    // File offsets can be 64 bit values, but we are dealing with 32
    // bit executables and so only need to support 32bit file sizes.
    uint32 offset32 = (uint32)offset;
  
    for (int i = 0; i < SectionHeaderCount(); i++) {
  
      const Elf32_Shdr *section_header = SectionHeader(i);
  
      // These can appear to have a size in the file, but don't.
      if (section_header->sh_type == SHT_NOBITS)
        continue;
  
      Elf32_Off section_begin = section_header->sh_offset;
      Elf32_Off section_end = section_begin + section_header->sh_size;
  
      if (offset32 >= section_begin && offset32 < section_end) {
        return section_header->sh_addr + (offset32 - section_begin);
      }
    }
  
    return 0;
  }
  
  CheckBool DisassemblerElf32::RVAsToOffsets(std::vector<RVA>* rvas,
                                             std::vector<size_t>* offsets) {
    offsets->clear();
  
    for (std::vector<RVA>::iterator rva = rvas->begin();
         rva != rvas->end();
         rva++) {
  
      size_t offset;
  
      if (!RVAToFileOffset(*rva, &offset))
        return false;
  
      offsets->push_back(offset);
    }
  
    return true;
  }
  
  CheckBool DisassemblerElf32::RVAsToOffsets(ScopedVector<TypedRVA>* rvas) {
    for (ScopedVector<TypedRVA>::iterator rva = rvas->begin();
         rva != rvas->end();
         rva++) {
  
      size_t offset;
  
      if (!RVAToFileOffset((*rva)->rva(), &offset))
        return false;
  
      (*rva)->set_offset(offset);
    }
  
    return true;
  }
  
  CheckBool DisassemblerElf32::ParseFile(AssemblyProgram* program) {
    // Walk all the bytes in the file, whether or not in a section.
    uint32 file_offset = 0;
  
    std::vector<size_t> abs_offsets;
  
    if (!RVAsToOffsets(&abs32_locations_, &abs_offsets))
      return false;
  
    if (!RVAsToOffsets(&rel32_locations_))
      return false;
  
    std::vector<size_t>::iterator current_abs_offset = abs_offsets.begin();
    ScopedVector<TypedRVA>::iterator current_rel = rel32_locations_.begin();
  
    std::vector<size_t>::iterator end_abs_offset = abs_offsets.end();
    ScopedVector<TypedRVA>::iterator end_rel = rel32_locations_.end();
  
    for (int section_id = 0;
         section_id < SectionHeaderCount();
         section_id++) {
  
      const Elf32_Shdr *section_header = SectionHeader(section_id);
  
      if (section_header->sh_type == SHT_NOBITS)
        continue;
  
      if (!ParseSimpleRegion(file_offset,
                             section_header->sh_offset,
                             program))
        return false;
      file_offset = section_header->sh_offset;
  
      switch (section_header->sh_type) {
        case SHT_REL:
          if (!ParseRelocationSection(section_header, program))
            return false;
          file_offset = section_header->sh_offset + section_header->sh_size;
          break;
        case SHT_PROGBITS:
          if (!ParseProgbitsSection(section_header,
                                    &current_abs_offset, end_abs_offset,
                                    &current_rel, end_rel,
                                    program))
            return false;
          file_offset = section_header->sh_offset + section_header->sh_size;
          break;
        case SHT_INIT_ARRAY:
          // Fall through
        case SHT_FINI_ARRAY:
          while (current_abs_offset != end_abs_offset &&
                 *current_abs_offset >= section_header->sh_offset &&
                 *current_abs_offset <
                 (section_header->sh_offset + section_header->sh_size)) {
            // Skip any abs_offsets appear in the unsupported INIT_ARRAY section
            VLOG(1) << "Skipping relocation entry for unsupported section: " <<
              section_header->sh_type;
            current_abs_offset++;
          }
          break;
        default:
          if (current_abs_offset != end_abs_offset &&
                 *current_abs_offset >= section_header->sh_offset &&
                 *current_abs_offset <
                 (section_header->sh_offset + section_header->sh_size))
            VLOG(1) << "Relocation address in unrecognized ELF section: " << \
              section_header->sh_type;
        break;
      }
    }
  
    // Rest of the file past the last section
    if (!ParseSimpleRegion(file_offset,
                           length(),
                           program))
      return false;
  
    // Make certain we consume all of the relocations as expected
    return (current_abs_offset == end_abs_offset);
  }
  
  CheckBool DisassemblerElf32::ParseProgbitsSection(
      const Elf32_Shdr *section_header,
      std::vector<size_t>::iterator* current_abs_offset,
      std::vector<size_t>::iterator end_abs_offset,
      ScopedVector<TypedRVA>::iterator* current_rel,
      ScopedVector<TypedRVA>::iterator end_rel,
      AssemblyProgram* program) {
  
    // Walk all the bytes in the file, whether or not in a section.
    size_t file_offset = section_header->sh_offset;
    size_t section_end = section_header->sh_offset + section_header->sh_size;
  
    Elf32_Addr origin = section_header->sh_addr;
    size_t origin_offset = section_header->sh_offset;
    if (!program->EmitOriginInstruction(origin))
      return false;
  
    while (file_offset < section_end) {
  
      if (*current_abs_offset != end_abs_offset &&
          file_offset > **current_abs_offset)
        return false;
  
      while (*current_rel != end_rel &&
             file_offset > (**current_rel)->get_offset()) {
        (*current_rel)++;
      }
  
      size_t next_relocation = section_end;
  
      if (*current_abs_offset != end_abs_offset &&
          next_relocation > **current_abs_offset)
        next_relocation = **current_abs_offset;
  
      // Rel offsets are heuristically derived, and might (incorrectly) overlap
      // an Abs value, or the end of the section, so +3 to make sure there is
      // room for the full 4 byte value.
      if (*current_rel != end_rel &&
          next_relocation > ((**current_rel)->get_offset() + 3))
        next_relocation = (**current_rel)->get_offset();
  
      if (next_relocation > file_offset) {
        if (!ParseSimpleRegion(file_offset, next_relocation, program))
          return false;
  
        file_offset = next_relocation;
        continue;
      }
  
      if (*current_abs_offset != end_abs_offset &&
          file_offset == **current_abs_offset) {
  
        const uint8* p = OffsetToPointer(file_offset);
        RVA target_rva = Read32LittleEndian(p);
  
        if (!program->EmitAbs32(program->FindOrMakeAbs32Label(target_rva)))
          return false;
        file_offset += sizeof(RVA);
        (*current_abs_offset)++;
        continue;
      }
  
      if (*current_rel != end_rel &&
          file_offset == (**current_rel)->get_offset()) {
  
        uint32 relative_target = (**current_rel)->relative_target();
        // This cast is for 64 bit systems, and is only safe because we
        // are working on 32 bit executables.
        RVA target_rva = (RVA)(origin + (file_offset - origin_offset) +
                               relative_target);
  
        if (! (**current_rel)->EmitInstruction(program, target_rva))
          return false;
        file_offset += (**current_rel)->op_size();
        (*current_rel)++;
        continue;
      }
    }
  
    // Rest of the section (if any)
    return ParseSimpleRegion(file_offset, section_end, program);
  }
  
  CheckBool DisassemblerElf32::ParseSimpleRegion(
      size_t start_file_offset,
      size_t end_file_offset,
      AssemblyProgram* program) {
    // Callers don't guarantee start < end
    if (start_file_offset >= end_file_offset) return true;
  
    const size_t len = end_file_offset - start_file_offset;
  
    if (!program->EmitBytesInstruction(OffsetToPointer(start_file_offset), len))
      return false;
  
    return true;
  }
  
  CheckBool DisassemblerElf32::ParseAbs32Relocs() {
    abs32_locations_.clear();
  
    // Loop through sections for relocation sections
    for (int section_id = 0; section_id < SectionHeaderCount(); section_id++) {
      const Elf32_Shdr *section_header = SectionHeader(section_id);
  
      if (section_header->sh_type == SHT_REL) {
  
        Elf32_Rel *relocs_table = (Elf32_Rel *)SectionBody(section_id);
  
        int relocs_table_count = section_header->sh_size /
                                 section_header->sh_entsize;
  
        // Elf32_Word relocation_section_id = section_header->sh_info;
  
        // Loop through relocation objects in the relocation section
        for (int rel_id = 0; rel_id < relocs_table_count; rel_id++) {
          RVA rva;
  
          // Quite a few of these conversions fail, and we simply skip
          // them, that's okay.
          if (RelToRVA(relocs_table[rel_id], &rva) && CheckSection(rva))
            abs32_locations_.push_back(rva);
        }
      }
    }
  
    std::sort(abs32_locations_.begin(), abs32_locations_.end());
    return true;
  }
  
  CheckBool DisassemblerElf32::CheckSection(RVA rva) {
    size_t offset;
  
    if (!RVAToFileOffset(rva, &offset)) {
      return false;
    }
  
    for (int section_id = 0;
         section_id < SectionHeaderCount();
         section_id++) {
  
      const Elf32_Shdr *section_header = SectionHeader(section_id);
  
      if (offset >= section_header->sh_offset &&
          offset < (section_header->sh_offset + section_header->sh_size)) {
        switch (section_header->sh_type) {
          case SHT_REL:
            // Fall-through
          case SHT_PROGBITS:
            return true;
        }
      }
    }
  
    return false;
  }
  
  CheckBool DisassemblerElf32::ParseRel32RelocsFromSections() {
  
    rel32_locations_.clear();
  
    // Loop through sections for relocation sections
    for (int section_id = 0;
         section_id < SectionHeaderCount();
         section_id++) {
  
      const Elf32_Shdr *section_header = SectionHeader(section_id);
  
      if (section_header->sh_type != SHT_PROGBITS)
        continue;
  
      if (!ParseRel32RelocsFromSection(section_header))
        return false;
    }
  
    std::sort(rel32_locations_.begin(),
              rel32_locations_.end(),
              TypedRVA::IsLessThan);
    return true;
  }
  
  }  // namespace courgette
  