aboutsummaryrefslogtreecommitdiff
path: root/abs32_utils.cc
blob: ad1c85e382d0a3eeeac7b3dad95646d75b3b8785 (plain)
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. 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/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::vector<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::vector<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;

absl::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 absl::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;

absl::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 absl::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::vector<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::vector<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::vector<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