| // 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/address_translator.h" |
| |
| #include <algorithm> |
| #include <utility> |
| |
| namespace zucchini { |
| |
| /******** AddressTranslator::OffsetToRvaCache ********/ |
| |
| AddressTranslator::OffsetToRvaCache::OffsetToRvaCache( |
| const AddressTranslator& translator) |
| : translator_(translator) {} |
| |
| rva_t AddressTranslator::OffsetToRvaCache::Convert(offset_t offset) const { |
| if (offset >= translator_.fake_offset_begin_) { |
| // Rely on |translator_| to handle this special case. |
| return translator_.OffsetToRva(offset); |
| } |
| if (cached_unit_ && cached_unit_->CoversOffset(offset)) |
| return cached_unit_->OffsetToRvaUnsafe(offset); |
| const AddressTranslator::Unit* unit = translator_.OffsetToUnit(offset); |
| if (!unit) |
| return kInvalidRva; |
| cached_unit_ = unit; |
| return unit->OffsetToRvaUnsafe(offset); |
| } |
| |
| /******** AddressTranslator::RvaToOffsetCache ********/ |
| |
| AddressTranslator::RvaToOffsetCache::RvaToOffsetCache( |
| const AddressTranslator& translator) |
| : translator_(translator) {} |
| |
| bool AddressTranslator::RvaToOffsetCache::IsValid(rva_t rva) const { |
| if (!cached_unit_ || !cached_unit_->CoversRva(rva)) { |
| const AddressTranslator::Unit* unit = translator_.RvaToUnit(rva); |
| if (!unit) |
| return false; |
| cached_unit_ = unit; |
| } |
| return true; |
| } |
| |
| offset_t AddressTranslator::RvaToOffsetCache::Convert(rva_t rva) const { |
| if (!cached_unit_ || !cached_unit_->CoversRva(rva)) { |
| const AddressTranslator::Unit* unit = translator_.RvaToUnit(rva); |
| if (!unit) |
| return kInvalidOffset; |
| cached_unit_ = unit; |
| } |
| return cached_unit_->RvaToOffsetUnsafe(rva, translator_.fake_offset_begin_); |
| } |
| |
| /******** AddressTranslator ********/ |
| |
| AddressTranslator::AddressTranslator() = default; |
| |
| AddressTranslator::~AddressTranslator() = default; |
| |
| AddressTranslator::Status AddressTranslator::Initialize( |
| std::vector<Unit>&& units) { |
| for (Unit& unit : units) { |
| // Check for overflows and fail if found. |
| if (!RangeIsBounded<offset_t>(unit.offset_begin, unit.offset_size, |
| kOffsetBound) || |
| !RangeIsBounded<rva_t>(unit.rva_begin, unit.rva_size, kRvaBound)) { |
| return kErrorOverflow; |
| } |
| // If |rva_size < offset_size|: Just shrink |offset_size| to accommodate. |
| unit.offset_size = std::min(unit.offset_size, unit.rva_size); |
| // Now |rva_size >= offset_size|. Note that |rva_size > offset_size| is |
| // allowed; these lead to dangling RVA. |
| } |
| |
| // Remove all empty units. |
| units.erase(std::remove_if(units.begin(), units.end(), |
| [](const Unit& unit) { return unit.IsEmpty(); }), |
| units.end()); |
| |
| // Sort |units| by RVA, then uniquefy. |
| std::sort(units.begin(), units.end(), [](const Unit& a, const Unit& b) { |
| return std::tie(a.rva_begin, a.rva_size) < |
| std::tie(b.rva_begin, b.rva_size); |
| }); |
| units.erase(std::unique(units.begin(), units.end()), units.end()); |
| |
| // Scan for RVA range overlaps, validate, and merge wherever possible. |
| if (units.size() > 1) { |
| // Traverse with two iterators: |slow| stays behind and modifies Units that |
| // absorb all overlapping (or tangent if suitable) Units; |fast| explores |
| // new Units as candidates for consistency checks and potential merge into |
| // |slow|. |
| auto slow = units.begin(); |
| |
| // All |it| with |slow| < |it| < |fast| contain garbage. |
| for (auto fast = slow + 1; fast != units.end(); ++fast) { |
| // Comment notation: S = slow offset, F = fast offset, O = overlap offset, |
| // s = slow RVA, f = fast RVA, o = overlap RVA. |
| DCHECK_GE(fast->rva_begin, slow->rva_begin); |
| if (slow->rva_end() < fast->rva_begin) { |
| // ..ssssss..ffffff..: Disjoint: Can advance |slow|. |
| *(++slow) = *fast; |
| continue; |
| } |
| |
| // ..ssssffff..: Tangent: Merge is optional. |
| // ..sssooofff.. / ..sssooosss..: Overlap: Merge is required. |
| bool merge_is_optional = slow->rva_end() == fast->rva_begin; |
| |
| // Check whether |fast| and |slow| have identical RVA -> offset shift. |
| // If not, then merge cannot be resolved. Examples: |
| // ..ssssffff.. -> ..SSSSFFFF..: Good, can merge. |
| // ..ssssffff.. -> ..SSSS..FFFF..: Non-fatal: don't merge. |
| // ..ssssffff.. -> ..FFFF..SSSS..: Non-fatal: don't merge. |
| // ..ssssffff.. -> ..SSOOFF..: Fatal: Ignore for now (handled later). |
| // ..sssooofff.. -> ..SSSOOOFFF..: Good, can merge. |
| // ..sssooofff.. -> ..SSSSSOFFFFF..: Fatal. |
| // ..sssooofff.. -> ..FFOOOOSS..: Fatal. |
| // ..sssooofff.. -> ..SSSOOOF..: Good, notice |fast| has dangling RVAs. |
| // ..oooooo.. -> ..OOOOOO..: Good, can merge. |
| if (fast->offset_begin < slow->offset_begin || |
| fast->offset_begin - slow->offset_begin != |
| fast->rva_begin - slow->rva_begin) { |
| if (merge_is_optional) { |
| *(++slow) = *fast; |
| continue; |
| } |
| return kErrorBadOverlap; |
| } |
| |
| // Check whether dangling RVAs (if they exist) are consistent. Examples: |
| // ..sssooofff.. -> ..SSSOOOF..: Good, can merge. |
| // ..sssooosss.. -> ..SSSOOOS..: Good, can merge. |
| // ..sssooofff.. -> ..SSSOO..: Good, can merge. |
| // ..sssooofff.. -> ..SSSOFFF..: Fatal. |
| // ..sssooosss.. -> ..SSSOOFFFF..: Fatal. |
| // ..oooooo.. -> ..OOO..: Good, can merge. |
| // Idea of check: Suppose |fast| has dangling RVA, then |
| // |[fast->rva_start, fast->rva_start + fast->offset_start)| -> |
| // |[fast->offset_start, **fast->offset_end()**)|, with remaining RVA |
| // mapping to fake offsets. This means |fast->offset_end()| must be >= |
| // |slow->offset_end()|, and failure to do so resluts in error. The |
| // argument for |slow| havng dangling RVA is symmetric. |
| if ((fast->HasDanglingRva() && fast->offset_end() < slow->offset_end()) || |
| (slow->HasDanglingRva() && slow->offset_end() < fast->offset_end())) { |
| if (merge_is_optional) { |
| *(++slow) = *fast; |
| continue; |
| } |
| return kErrorBadOverlapDanglingRva; |
| } |
| |
| // Merge |fast| into |slow|. |
| slow->rva_size = |
| std::max(slow->rva_size, fast->rva_end() - slow->rva_begin); |
| slow->offset_size = |
| std::max(slow->offset_size, fast->offset_end() - slow->offset_begin); |
| } |
| ++slow; |
| units.erase(slow, units.end()); |
| } |
| |
| // After resolving RVA overlaps, any offset overlap would imply error. |
| std::sort(units.begin(), units.end(), [](const Unit& a, const Unit& b) { |
| return a.offset_begin < b.offset_begin; |
| }); |
| |
| if (units.size() > 1) { |
| auto previous = units.begin(); |
| for (auto current = previous + 1; current != units.end(); ++current) { |
| if (previous->offset_end() > current->offset_begin) |
| return kErrorBadOverlap; |
| previous = current; |
| } |
| } |
| |
| // For to fake offset heuristics: Compute exclusive upper bounds for offsets |
| // and RVAs. |
| offset_t offset_bound = 0; |
| rva_t rva_bound = 0; |
| for (const Unit& unit : units) { |
| offset_bound = std::max(offset_bound, unit.offset_end()); |
| rva_bound = std::max(rva_bound, unit.rva_end()); |
| } |
| |
| // Compute pessimistic range and see if it still fits within space of valid |
| // offsets. This limits image size to one half of |kOffsetBound|, and is a |
| // main drawback for the current heuristic to convert dangling RVA to fake |
| // offsets. |
| if (!RangeIsBounded(offset_bound, rva_bound, kOffsetBound)) |
| return kErrorFakeOffsetBeginTooLarge; |
| |
| // Success. Store results. |units| is currently sorted by offset, so assign. |
| units_sorted_by_offset_.assign(units.begin(), units.end()); |
| |
| // Sort |units| by RVA, and just store it directly |
| std::sort(units.begin(), units.end(), [](const Unit& a, const Unit& b) { |
| return a.rva_begin < b.rva_begin; |
| }); |
| units_sorted_by_rva_ = std::move(units); |
| |
| fake_offset_begin_ = offset_bound; |
| return kSuccess; |
| } |
| |
| rva_t AddressTranslator::OffsetToRva(offset_t offset) const { |
| if (offset >= fake_offset_begin_) { |
| // Handle dangling RVA: First shift it to regular RVA space. |
| rva_t rva = offset - fake_offset_begin_; |
| // If result is indeed a dangling RVA, return it; else return |kInvalidRva|. |
| const Unit* unit = RvaToUnit(rva); |
| return (unit && unit->HasDanglingRva() && unit->CoversDanglingRva(rva)) |
| ? rva |
| : kInvalidRva; |
| } |
| const Unit* unit = OffsetToUnit(offset); |
| return unit ? unit->OffsetToRvaUnsafe(offset) : kInvalidRva; |
| } |
| |
| offset_t AddressTranslator::RvaToOffset(rva_t rva) const { |
| const Unit* unit = RvaToUnit(rva); |
| // This also handles dangling RVA. |
| return unit ? unit->RvaToOffsetUnsafe(rva, fake_offset_begin_) |
| : kInvalidOffset; |
| } |
| |
| const AddressTranslator::Unit* AddressTranslator::OffsetToUnit( |
| offset_t offset) const { |
| // Finds first Unit with |offset_begin| > |offset|, rewind by 1 to find the |
| // last Unit with |offset_begin| >= |offset| (if it exists). |
| auto it = std::upper_bound( |
| units_sorted_by_offset_.begin(), units_sorted_by_offset_.end(), offset, |
| [](offset_t a, const Unit& b) { return a < b.offset_begin; }); |
| if (it == units_sorted_by_offset_.begin()) |
| return nullptr; |
| --it; |
| return it->CoversOffset(offset) ? &(*it) : nullptr; |
| } |
| |
| const AddressTranslator::Unit* AddressTranslator::RvaToUnit(rva_t rva) const { |
| auto it = std::upper_bound( |
| units_sorted_by_rva_.begin(), units_sorted_by_rva_.end(), rva, |
| [](rva_t a, const Unit& b) { return a < b.rva_begin; }); |
| if (it == units_sorted_by_rva_.begin()) |
| return nullptr; |
| --it; |
| return it->CoversRva(rva) ? &(*it) : nullptr; |
| } |
| |
| } // namespace zucchini |