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