| // Copyright 2017 The Chromium Authors | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| #ifndef COMPONENTS_ZUCCHINI_REL32_FINDER_H_ | |
| #define COMPONENTS_ZUCCHINI_REL32_FINDER_H_ | |
| #include <stddef.h> | |
| #include <deque> | |
| #include "components/zucchini/address_translator.h" | |
| #include "components/zucchini/arm_utils.h" | |
| #include "components/zucchini/buffer_view.h" | |
| #include "components/zucchini/image_utils.h" | |
| namespace zucchini { | |
| // See README.md for definitions on abs32 and rel32 references. The following | |
| // are assumed: | |
| // * Abs32 reference bodies have fixed widths. | |
| // * Rel32 locations can be identified by heuristically disassembling machine | |
| // code, and errors are tolerated. | |
| // * The collection all abs32 and rel32 reference bodies do not overlap. | |
| // A class to visit non-empty contiguous gaps in |region| that lie outside of | |
| // |abs32_locations| elements, each with a body that spans |abs32_width_| bytes. | |
| // For example, given: | |
| // region = [base_ + 4, base_ + 26), | |
| // abs32_locations = {2, 6, 15, 20, 27}, | |
| // abs32_width_ = 4, | |
| // the following is obtained: | |
| // 111111111122222222223 -> offsets | |
| // 0123456789012345678901234567890 | |
| // ....**********************..... -> region = * | |
| // ^ ^ ^ ^ ^ -> abs32 locations | |
| // aaaaaaaa aaaa aaaa aaaa -> abs32 bodies | |
| // ....------*****----*----**..... -> regions excluding abs32 -> 3 gaps | |
| // The resulting gaps (non-empty, so [6, 6) is excluded) are: | |
| // [10, 15), [19, 20), [24, 26). | |
| // These gaps can then be passed to Rel32Finder (below) to find rel32 references | |
| // with bodies that are guaranteed to not overlap with any abs32 bodies. | |
| class Abs32GapFinder { | |
| public: | |
| // |abs32_locations| is a sorted list of non-overlapping abs32 locations in | |
| // |image|, each spanning |abs32_width| bytes. Gaps are searched in |region|, | |
| // which must be part of |image|. | |
| Abs32GapFinder(ConstBufferView image, | |
| ConstBufferView region, | |
| const std::deque<offset_t>& abs32_locations, | |
| size_t abs32_width); | |
| Abs32GapFinder(const Abs32GapFinder&) = delete; | |
| const Abs32GapFinder& operator=(const Abs32GapFinder&) = delete; | |
| ~Abs32GapFinder(); | |
| // Searches for the next available gap, and returns successfulness. | |
| bool FindNext(); | |
| // Returns the cached result from the last successful FindNext(). | |
| ConstBufferView GetGap() const { return gap_; } | |
| private: | |
| const ConstBufferView::const_iterator base_; | |
| const ConstBufferView::const_iterator region_end_; | |
| ConstBufferView::const_iterator cur_lo_; | |
| const std::deque<offset_t>::const_iterator abs32_end_; | |
| std::deque<offset_t>::const_iterator abs32_cur_; | |
| const size_t abs32_width_; | |
| ConstBufferView gap_; | |
| }; | |
| // A class to scan regions within an image to find successive rel32 references. | |
| // Architecture-specific parsing and result extraction are delegated to | |
| // inherited classes (say, Rel32Finder_Impl). Sample extraction loop, combined | |
| // with Abs32GapFinder usage: | |
| // | |
| // Abs32GapFinder gap_finder(...); | |
| // Rel32Finder_Impl finder(...); | |
| // while (gap_finder.FindNext()) { | |
| // rel_finder.SetRegion(gap_finder.GetGap()); | |
| // while (rel_finder.FindNext()) { | |
| // auto rel32 = rel_finder.GetRel32(); // In Rel32Finder_Impl. | |
| // if (architecture_specific_validation(rel32)) { | |
| // rel_finder.Accept(); | |
| // // Store rel32. | |
| // } | |
| // } | |
| // } | |
| class Rel32Finder { | |
| public: | |
| Rel32Finder(ConstBufferView image, const AddressTranslator& translator); | |
| Rel32Finder(const Rel32Finder&) = delete; | |
| const Rel32Finder& operator=(const Rel32Finder&) = delete; | |
| virtual ~Rel32Finder(); | |
| // Assigns the scan |region| for rel32 references to enable FindNext() use. | |
| void SetRegion(ConstBufferView region); | |
| // Scans for the next rel32 reference, and returns whether any is found, so a | |
| // "while" loop can be used for iterative rel32 extraction. The results are | |
| // cached in Rel32Finder_Impl and obtained by Rel32Finder_Impl::GetRel32(). | |
| bool FindNext(); | |
| // When a rel32 reference is found, the caller needs to decide whether to keep | |
| // the result (perhaps following more validation). If it decides to keep the | |
| // result, then it must call Accept(), so the next call to FindNext() can skip | |
| // the accepted rel32 reference. | |
| void Accept(); | |
| // Accessors for unittest. | |
| ConstBufferView::const_iterator accept_it() const { return accept_it_; } | |
| ConstBufferView region() const { return region_; } | |
| protected: | |
| // Alternatives for where to continue the next scan when a rel32 reference is | |
| // found. nulls indicate that no rel32 references remain. | |
| struct NextIterators { | |
| // The next iterator if the caller does not call Accept(). | |
| ConstBufferView::const_iterator reject; | |
| // The next iterator if the caller calls Accept(). | |
| ConstBufferView::const_iterator accept; | |
| }; | |
| // Detects and extracts architecture-specific rel32 reference. For each one | |
| // found, the implementation should cache the necessary data to be retrieved | |
| // via accessors. Returns a NextIterators that stores alternatives for where | |
| // to continue the scan. If no rel32 reference is found then the returned | |
| // NextIterators are nulls. | |
| virtual NextIterators Scan(ConstBufferView region) = 0; | |
| const ConstBufferView image_; | |
| AddressTranslator::OffsetToRvaCache offset_to_rva_; | |
| private: | |
| ConstBufferView region_; | |
| ConstBufferView::const_iterator accept_it_ = nullptr; | |
| }; | |
| // Parsing for X86 or X64: we perform naive scan for opcodes that have rel32 as | |
| // an argument, and disregard instruction alignment. | |
| class Rel32FinderIntel : public Rel32Finder { | |
| public: | |
| Rel32FinderIntel(const Rel32FinderIntel&) = delete; | |
| const Rel32FinderIntel& operator=(const Rel32FinderIntel&) = delete; | |
| // Struct to store GetRel32() results. | |
| struct Result { | |
| offset_t location; | |
| rva_t target_rva; | |
| // Some references must have their target in the same section as location, | |
| // which we use this to heuristically reject rel32 reference candidates. | |
| // When true, this constraint is relaxed. | |
| bool can_point_outside_section; | |
| }; | |
| using Rel32Finder::Rel32Finder; | |
| // Returns the cached result from the last successful FindNext(). | |
| const Result& GetRel32() { return rel32_; } | |
| protected: | |
| // Helper for Scan() that also assigns |rel32_|. | |
| Rel32Finder::NextIterators SetResult(ConstBufferView::const_iterator cursor, | |
| uint32_t code_size, | |
| bool can_point_outside_section); | |
| // Cached results. | |
| Result rel32_; | |
| // Rel32Finder: | |
| NextIterators Scan(ConstBufferView region) override = 0; | |
| }; | |
| // X86 instructions. | |
| class Rel32FinderX86 : public Rel32FinderIntel { | |
| public: | |
| using Rel32FinderIntel::Rel32FinderIntel; | |
| Rel32FinderX86(const Rel32FinderX86&) = delete; | |
| const Rel32FinderX86& operator=(const Rel32FinderX86&) = delete; | |
| private: | |
| // Rel32Finder: | |
| NextIterators Scan(ConstBufferView region) override; | |
| }; | |
| // X64 instructions. | |
| class Rel32FinderX64 : public Rel32FinderIntel { | |
| public: | |
| using Rel32FinderIntel::Rel32FinderIntel; | |
| Rel32FinderX64(const Rel32FinderX64&) = delete; | |
| const Rel32FinderX64& operator=(const Rel32FinderX64&) = delete; | |
| private: | |
| // Rel32Finder: | |
| NextIterators Scan(ConstBufferView region) override; | |
| }; | |
| // Base class for ARM (AArch32 and AArch64) instructions. | |
| template <typename ADDR_TYPE> | |
| class Rel32FinderArm : public Rel32Finder { | |
| public: | |
| struct Result { | |
| offset_t location; | |
| rva_t target_rva; | |
| ADDR_TYPE type; | |
| // For testing. | |
| bool operator==(const Result& other) const { | |
| return location == other.location && target_rva == other.target_rva && | |
| type == other.type; | |
| } | |
| }; | |
| Rel32FinderArm(ConstBufferView image, const AddressTranslator& translator); | |
| Rel32FinderArm(const Rel32FinderArm&) = delete; | |
| const Rel32FinderArm& operator=(const Rel32FinderArm&) = delete; | |
| ~Rel32FinderArm() override; | |
| // Helper for Scan*() that also assigns |rel32_|. | |
| NextIterators SetResult(Result&& result, | |
| ConstBufferView::const_iterator cursor, | |
| int instr_size); | |
| // SetResult() for end of scan. | |
| NextIterators SetEmptyResult(); | |
| protected: | |
| // Cached results. | |
| Result rel32_; | |
| }; | |
| // AArch32 instructions. | |
| class Rel32FinderAArch32 | |
| : public Rel32FinderArm<AArch32Rel32Translator::AddrType> { | |
| public: | |
| Rel32FinderAArch32(ConstBufferView image, | |
| const AddressTranslator& translator, | |
| bool is_thumb2); | |
| Rel32FinderAArch32(const Rel32FinderAArch32&) = delete; | |
| const Rel32FinderAArch32& operator=(const Rel32FinderAArch32&) = delete; | |
| ~Rel32FinderAArch32() override; | |
| const Result& GetRel32() const { return rel32_; } | |
| private: | |
| // Rel32 extraction, assuming segment is in ARM mode. | |
| NextIterators ScanA32(ConstBufferView region); | |
| // Rel32 extraction, assuming segment is in THUMB2 mode. | |
| NextIterators ScanT32(ConstBufferView region); | |
| // Rel32Finder: | |
| NextIterators Scan(ConstBufferView region) override; | |
| // Indicates whether segment is in THUMB2 or ARM mod. In general this can | |
| // change throughout a section. However, currently we assume that this is | |
| // constant for an entire section. | |
| const bool is_thumb2_; | |
| }; | |
| // AArch64 instructions. | |
| class Rel32FinderAArch64 | |
| : public Rel32FinderArm<AArch64Rel32Translator::AddrType> { | |
| public: | |
| Rel32FinderAArch64(ConstBufferView image, | |
| const AddressTranslator& translator); | |
| Rel32FinderAArch64(const Rel32FinderAArch64&) = delete; | |
| const Rel32FinderAArch64& operator=(const Rel32FinderAArch64&) = delete; | |
| ~Rel32FinderAArch64() override; | |
| const Result& GetRel32() const { return rel32_; } | |
| private: | |
| // Rel32Finder: | |
| NextIterators Scan(ConstBufferView region) override; | |
| }; | |
| } // namespace zucchini | |
| #endif // COMPONENTS_ZUCCHINI_REL32_FINDER_H_ |