Samuel Huang | 06f1ae9 | 2018-03-13 18:19:34 +0000 | [diff] [blame] | 1 | // Copyright 2017 The Chromium Authors. All rights reserved. |
| 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
| 5 | #include "components/zucchini/abs32_utils.h" |
| 6 | |
| 7 | #include <algorithm> |
| 8 | #include <type_traits> |
| 9 | #include <utility> |
| 10 | |
Hans Wennborg | 5a340be | 2020-04-28 11:06:24 +0000 | [diff] [blame] | 11 | #include "base/check_op.h" |
Samuel Huang | 06f1ae9 | 2018-03-13 18:19:34 +0000 | [diff] [blame] | 12 | #include "components/zucchini/io_utils.h" |
| 13 | |
| 14 | namespace zucchini { |
| 15 | |
| 16 | namespace { |
| 17 | |
| 18 | // Templated helper for AbsoluteAddress::Read(). |
| 19 | template <typename T> |
| 20 | bool ReadAbs(ConstBufferView image, offset_t offset, uint64_t* value) { |
| 21 | static_assert(std::is_unsigned<T>::value, "Value type must be unsigned."); |
| 22 | if (!image.can_access<T>(offset)) |
| 23 | return false; |
| 24 | *value = static_cast<uint64_t>(image.read<T>(offset)); |
| 25 | return true; |
| 26 | } |
| 27 | |
| 28 | // Templated helper for AbsoluteAddress::Write(). |
| 29 | template <typename T> |
| 30 | bool WriteAbs(offset_t offset, T value, MutableBufferView* image) { |
| 31 | static_assert(std::is_unsigned<T>::value, "Value type must be unsigned."); |
| 32 | if (!image->can_access<T>(offset)) |
| 33 | return false; |
| 34 | image->write<T>(offset, value); |
| 35 | return true; |
| 36 | } |
| 37 | |
| 38 | } // namespace |
| 39 | |
| 40 | /******** AbsoluteAddress ********/ |
| 41 | |
| 42 | AbsoluteAddress::AbsoluteAddress(Bitness bitness, uint64_t image_base) |
| 43 | : bitness_(bitness), image_base_(image_base), value_(image_base) { |
| 44 | CHECK(bitness_ == kBit64 || image_base_ < 0x100000000ULL); |
| 45 | } |
| 46 | |
| 47 | AbsoluteAddress::AbsoluteAddress(AbsoluteAddress&&) = default; |
| 48 | |
| 49 | AbsoluteAddress::~AbsoluteAddress() = default; |
| 50 | |
| 51 | bool AbsoluteAddress::FromRva(rva_t rva) { |
| 52 | if (rva >= kRvaBound) |
| 53 | return false; |
| 54 | uint64_t value = image_base_ + rva; |
| 55 | // Check overflow, which manifests as |value| "wrapping around", resulting in |
| 56 | // |value| less than |image_base_| (preprocessing needed for 32-bit). |
| 57 | if (((bitness_ == kBit32) ? (value & 0xFFFFFFFFU) : value) < image_base_) |
| 58 | return false; |
| 59 | value_ = value; |
| 60 | return true; |
| 61 | } |
| 62 | |
| 63 | rva_t AbsoluteAddress::ToRva() const { |
| 64 | if (value_ < image_base_) |
| 65 | return kInvalidRva; |
| 66 | uint64_t raw_rva = value_ - image_base_; |
| 67 | if (raw_rva >= kRvaBound) |
| 68 | return kInvalidRva; |
| 69 | return static_cast<rva_t>(raw_rva); |
| 70 | } |
| 71 | |
| 72 | bool AbsoluteAddress::Read(offset_t offset, const ConstBufferView& image) { |
| 73 | // Read raw data; |value_| is not guaranteed to represent a valid RVA. |
| 74 | if (bitness_ == kBit32) |
| 75 | return ReadAbs<uint32_t>(image, offset, &value_); |
| 76 | DCHECK_EQ(kBit64, bitness_); |
| 77 | return ReadAbs<uint64_t>(image, offset, &value_); |
| 78 | } |
| 79 | |
| 80 | bool AbsoluteAddress::Write(offset_t offset, MutableBufferView* image) { |
| 81 | if (bitness_ == kBit32) |
| 82 | return WriteAbs<uint32_t>(offset, static_cast<uint32_t>(value_), image); |
| 83 | DCHECK_EQ(kBit64, bitness_); |
| 84 | return WriteAbs<uint64_t>(offset, value_, image); |
| 85 | } |
| 86 | |
| 87 | /******** Abs32RvaExtractorWin32 ********/ |
| 88 | |
| 89 | Abs32RvaExtractorWin32::Abs32RvaExtractorWin32( |
| 90 | ConstBufferView image, |
| 91 | AbsoluteAddress&& addr, |
| 92 | const std::vector<offset_t>& abs32_locations, |
| 93 | offset_t lo, |
| 94 | offset_t hi) |
| 95 | : image_(image), addr_(std::move(addr)) { |
| 96 | CHECK_LE(lo, hi); |
Samuel Huang | 98dd017 | 2018-10-10 15:48:10 +0000 | [diff] [blame] | 97 | auto find_and_check = [this](const std::vector<offset_t>& locations, |
| 98 | offset_t offset) { |
Samuel Huang | 06f1ae9 | 2018-03-13 18:19:34 +0000 | [diff] [blame] | 99 | auto it = std::lower_bound(locations.begin(), locations.end(), offset); |
Samuel Huang | 98dd017 | 2018-10-10 15:48:10 +0000 | [diff] [blame] | 100 | // Ensure that |offset| does not straddle a reference body. |
| 101 | CHECK(it == locations.begin() || offset - *(it - 1) >= addr_.width()); |
Samuel Huang | 06f1ae9 | 2018-03-13 18:19:34 +0000 | [diff] [blame] | 102 | return it; |
| 103 | }; |
| 104 | cur_abs32_ = find_and_check(abs32_locations, lo); |
| 105 | end_abs32_ = find_and_check(abs32_locations, hi); |
| 106 | } |
| 107 | |
| 108 | Abs32RvaExtractorWin32::Abs32RvaExtractorWin32(Abs32RvaExtractorWin32&&) = |
| 109 | default; |
| 110 | |
| 111 | Abs32RvaExtractorWin32::~Abs32RvaExtractorWin32() = default; |
| 112 | |
Anton Bikineev | 1a96551 | 2021-05-15 22:35:36 +0000 | [diff] [blame] | 113 | absl::optional<Abs32RvaExtractorWin32::Unit> Abs32RvaExtractorWin32::GetNext() { |
Samuel Huang | 06f1ae9 | 2018-03-13 18:19:34 +0000 | [diff] [blame] | 114 | while (cur_abs32_ < end_abs32_) { |
| 115 | offset_t location = *(cur_abs32_++); |
| 116 | if (!addr_.Read(location, image_)) |
| 117 | continue; |
| 118 | rva_t target_rva = addr_.ToRva(); |
| 119 | if (target_rva == kInvalidRva) |
| 120 | continue; |
| 121 | return Unit{location, target_rva}; |
| 122 | } |
Anton Bikineev | 1a96551 | 2021-05-15 22:35:36 +0000 | [diff] [blame] | 123 | return absl::nullopt; |
Samuel Huang | 06f1ae9 | 2018-03-13 18:19:34 +0000 | [diff] [blame] | 124 | } |
| 125 | |
| 126 | /******** Abs32ReaderWin32 ********/ |
| 127 | |
| 128 | Abs32ReaderWin32::Abs32ReaderWin32(Abs32RvaExtractorWin32&& abs32_rva_extractor, |
| 129 | const AddressTranslator& translator) |
| 130 | : abs32_rva_extractor_(std::move(abs32_rva_extractor)), |
| 131 | target_rva_to_offset_(translator) {} |
| 132 | |
| 133 | Abs32ReaderWin32::~Abs32ReaderWin32() = default; |
| 134 | |
Anton Bikineev | 1a96551 | 2021-05-15 22:35:36 +0000 | [diff] [blame] | 135 | absl::optional<Reference> Abs32ReaderWin32::GetNext() { |
Samuel Huang | 06f1ae9 | 2018-03-13 18:19:34 +0000 | [diff] [blame] | 136 | for (auto unit = abs32_rva_extractor_.GetNext(); unit.has_value(); |
| 137 | unit = abs32_rva_extractor_.GetNext()) { |
| 138 | offset_t location = unit->location; |
Etienne Pierre-doray | e57c4e6 | 2018-08-10 17:44:37 +0000 | [diff] [blame] | 139 | offset_t unsafe_target = target_rva_to_offset_.Convert(unit->target_rva); |
Samuel Huang | 98dd017 | 2018-10-10 15:48:10 +0000 | [diff] [blame] | 140 | if (unsafe_target != kInvalidOffset) |
Etienne Pierre-doray | e57c4e6 | 2018-08-10 17:44:37 +0000 | [diff] [blame] | 141 | return Reference{location, unsafe_target}; |
Samuel Huang | 06f1ae9 | 2018-03-13 18:19:34 +0000 | [diff] [blame] | 142 | } |
Anton Bikineev | 1a96551 | 2021-05-15 22:35:36 +0000 | [diff] [blame] | 143 | return absl::nullopt; |
Samuel Huang | 06f1ae9 | 2018-03-13 18:19:34 +0000 | [diff] [blame] | 144 | } |
| 145 | |
| 146 | /******** Abs32WriterWin32 ********/ |
| 147 | |
| 148 | Abs32WriterWin32::Abs32WriterWin32(MutableBufferView image, |
| 149 | AbsoluteAddress&& addr, |
| 150 | const AddressTranslator& translator) |
| 151 | : image_(image), |
| 152 | addr_(std::move(addr)), |
| 153 | target_offset_to_rva_(translator) {} |
| 154 | |
| 155 | Abs32WriterWin32::~Abs32WriterWin32() = default; |
| 156 | |
| 157 | void Abs32WriterWin32::PutNext(Reference ref) { |
| 158 | rva_t target_rva = target_offset_to_rva_.Convert(ref.target); |
| 159 | if (target_rva != kInvalidRva) { |
| 160 | addr_.FromRva(target_rva); |
| 161 | addr_.Write(ref.location, &image_); |
| 162 | } |
| 163 | } |
| 164 | |
| 165 | /******** Exported Functions ********/ |
| 166 | |
Samuel Huang | 98dd017 | 2018-10-10 15:48:10 +0000 | [diff] [blame] | 167 | size_t RemoveUntranslatableAbs32(ConstBufferView image, |
| 168 | AbsoluteAddress&& addr, |
| 169 | const AddressTranslator& translator, |
| 170 | std::vector<offset_t>* locations) { |
| 171 | AddressTranslator::RvaToOffsetCache target_rva_checker(translator); |
| 172 | Abs32RvaExtractorWin32 extractor(image, std::move(addr), *locations, 0, |
| 173 | image.size()); |
| 174 | Abs32ReaderWin32 reader(std::move(extractor), translator); |
| 175 | std::vector<offset_t>::iterator write_it = locations->begin(); |
| 176 | // |reader| reads |locations| while |write_it| modifies it. However, there's |
| 177 | // no conflict since read occurs before write, and can skip ahead. |
| 178 | for (auto ref = reader.GetNext(); ref.has_value(); ref = reader.GetNext()) |
| 179 | *(write_it++) = ref->location; |
| 180 | DCHECK(write_it <= locations->end()); |
| 181 | size_t num_removed = locations->end() - write_it; |
| 182 | locations->erase(write_it, locations->end()); |
| 183 | return num_removed; |
| 184 | } |
| 185 | |
Etienne Pierre-doray | e53806a | 2018-10-05 20:15:13 +0000 | [diff] [blame] | 186 | size_t RemoveOverlappingAbs32Locations(uint32_t width, |
Samuel Huang | 06f1ae9 | 2018-03-13 18:19:34 +0000 | [diff] [blame] | 187 | std::vector<offset_t>* locations) { |
| 188 | if (locations->size() <= 1) |
| 189 | return 0; |
| 190 | |
Samuel Huang | 06f1ae9 | 2018-03-13 18:19:34 +0000 | [diff] [blame] | 191 | auto slow = locations->begin(); |
| 192 | auto fast = locations->begin() + 1; |
| 193 | for (;;) { |
| 194 | // Find next good location. |
| 195 | while (fast != locations->end() && *fast - *slow < width) |
| 196 | ++fast; |
| 197 | // Advance |slow|. For the last iteration this becomes the new sentinel. |
| 198 | ++slow; |
| 199 | if (fast == locations->end()) |
| 200 | break; |
| 201 | // Compactify good locations (potentially overwrite bad locations). |
| 202 | if (slow != fast) |
| 203 | *slow = *fast; |
| 204 | ++fast; |
| 205 | } |
| 206 | size_t num_removed = locations->end() - slow; |
| 207 | locations->erase(slow, locations->end()); |
| 208 | return num_removed; |
| 209 | } |
| 210 | |
| 211 | } // namespace zucchini |