text: // Copyright 2017 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 "components/zucchini/element_detection.h"
  
  #include <utility>
  
  #include "base/logging.h"
  #include "components/zucchini/buildflags.h"
  #include "components/zucchini/disassembler.h"
  #include "components/zucchini/disassembler_no_op.h"
  
  #if BUILDFLAG(ENABLE_DEX)
  #include "components/zucchini/disassembler_dex.h"
  #endif  // BUILDFLAG(ENABLE_DEX)
  
  #if BUILDFLAG(ENABLE_ELF)
  #include "components/zucchini/disassembler_elf.h"
  #endif  // BUILDFLAG(ENABLE_ELF)
  
  #if BUILDFLAG(ENABLE_WIN)
  #include "components/zucchini/disassembler_win32.h"
  #endif  // BUILDFLAG(ENABLE_WIN)
  
  #if BUILDFLAG(ENABLE_ZTF)
  #include "components/zucchini/disassembler_ztf.h"
  #endif  // BUILDFLAG(ENABLE_ZTF)
  
  namespace zucchini {
  
  namespace {
  
  // Impose a minimal program size to eliminate pathological cases.
  enum : size_t { kMinProgramSize = 16 };
  
  }  // namespace
  
  /******** Utility Functions ********/
  
  std::unique_ptr<Disassembler> MakeDisassemblerWithoutFallback(
      ConstBufferView image) {
  #if BUILDFLAG(ENABLE_WIN)
    if (DisassemblerWin32X86::QuickDetect(image)) {
      auto disasm = Disassembler::Make<DisassemblerWin32X86>(image);
      if (disasm && disasm->size() >= kMinProgramSize)
        return disasm;
    }
  
    if (DisassemblerWin32X64::QuickDetect(image)) {
      auto disasm = Disassembler::Make<DisassemblerWin32X64>(image);
      if (disasm && disasm->size() >= kMinProgramSize)
        return disasm;
    }
  #endif  // BUILDFLAG(ENABLE_WIN)
  
  #if BUILDFLAG(ENABLE_ELF)
    if (DisassemblerElfX86::QuickDetect(image)) {
      auto disasm = Disassembler::Make<DisassemblerElfX86>(image);
      if (disasm && disasm->size() >= kMinProgramSize)
        return disasm;
    }
  
    if (DisassemblerElfX64::QuickDetect(image)) {
      auto disasm = Disassembler::Make<DisassemblerElfX64>(image);
      if (disasm && disasm->size() >= kMinProgramSize)
        return disasm;
    }
  #endif  // BUILDFLAG(ENABLE_ELF)
  
  #if BUILDFLAG(ENABLE_DEX)
    if (DisassemblerDex::QuickDetect(image)) {
      auto disasm = Disassembler::Make<DisassemblerDex>(image);
      if (disasm && disasm->size() >= kMinProgramSize)
        return disasm;
    }
  #endif  // BUILDFLAG(ENABLE_DEX)
  
  #if BUILDFLAG(ENABLE_ZTF)
    if (DisassemblerZtf::QuickDetect(image)) {
      // This disallows very short examples like "ZTxtxtZ\n" in ensemble patching.
      auto disasm = Disassembler::Make<DisassemblerZtf>(image);
      if (disasm && disasm->size() >= kMinProgramSize)
        return disasm;
    }
  #endif  // BUILDFLAG(ENABLE_ZTF)
  
    return nullptr;
  }
  
  std::unique_ptr<Disassembler> MakeDisassemblerOfType(ConstBufferView image,
                                                       ExecutableType exe_type) {
    switch (exe_type) {
  #if BUILDFLAG(ENABLE_WIN)
      case kExeTypeWin32X86:
        return Disassembler::Make<DisassemblerWin32X86>(image);
      case kExeTypeWin32X64:
        return Disassembler::Make<DisassemblerWin32X64>(image);
  #endif  // BUILDFLAG(ENABLE_WIN)
  #if BUILDFLAG(ENABLE_ELF)
      case kExeTypeElfX86:
        return Disassembler::Make<DisassemblerElfX86>(image);
      case kExeTypeElfX64:
        return Disassembler::Make<DisassemblerElfX64>(image);
  #endif  // BUILDFLAG(ENABLE_ELF)
  #if BUILDFLAG(ENABLE_DEX)
      case kExeTypeDex:
        return Disassembler::Make<DisassemblerDex>(image);
  #endif  // BUILDFLAG(ENABLE_DEX)
  #if BUILDFLAG(ENABLE_ZTF)
      case kExeTypeZtf:
        return Disassembler::Make<DisassemblerZtf>(image);
  #endif  // BUILDFLAG(ENABLE_ZTF)
      case kExeTypeNoOp:
        return Disassembler::Make<DisassemblerNoOp>(image);
      default:
        // If an architecture is disabled then null is handled gracefully.
        return nullptr;
    }
  }
  
  base::Optional<Element> DetectElementFromDisassembler(ConstBufferView image) {
    std::unique_ptr<Disassembler> disasm = MakeDisassemblerWithoutFallback(image);
    if (disasm)
      return Element({0, disasm->size()}, disasm->GetExeType());
    return base::nullopt;
  }
  
  /******** ProgramScanner ********/
  
  ElementFinder::ElementFinder(ConstBufferView image, ElementDetector&& detector)
      : image_(image), detector_(std::move(detector)) {}
  
  ElementFinder::~ElementFinder() = default;
  
  base::Optional<Element> ElementFinder::GetNext() {
    for (; pos_ < image_.size(); ++pos_) {
      ConstBufferView test_image =
          ConstBufferView::FromRange(image_.begin() + pos_, image_.end());
      base::Optional<Element> element = detector_.Run(test_image);
      if (element) {
        element->offset += pos_;
        pos_ = element->EndOffset();
        return element;
      }
    }
    return base::nullopt;
  }
  
  }  // namespace zucchini
  