xref: /aosp_15_r20/external/zucchini/abs32_utils.cc (revision a03ca8b91e029cd15055c20c78c2e087c84792e4)
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 
11 #include "base/check_op.h"
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>
ReadAbs(ConstBufferView image,offset_t offset,uint64_t * value)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>
WriteAbs(offset_t offset,T value,MutableBufferView * image)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 
AbsoluteAddress(Bitness bitness,uint64_t image_base)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 
FromRva(rva_t rva)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 
ToRva() const63 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 
Read(offset_t offset,const ConstBufferView & image)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 
Write(offset_t offset,MutableBufferView * image)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 
Abs32RvaExtractorWin32(ConstBufferView image,AbsoluteAddress && addr,const std::vector<offset_t> & abs32_locations,offset_t lo,offset_t hi)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);
97   auto find_and_check = [this](const std::vector<offset_t>& locations,
98                                offset_t offset) {
99     auto it = std::lower_bound(locations.begin(), locations.end(), offset);
100     // Ensure that |offset| does not straddle a reference body.
101     CHECK(it == locations.begin() || offset - *(it - 1) >= addr_.width());
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 
GetNext()113 std::optional<Abs32RvaExtractorWin32::Unit> Abs32RvaExtractorWin32::GetNext() {
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   }
123   return std::nullopt;
124 }
125 
126 /******** Abs32ReaderWin32 ********/
127 
Abs32ReaderWin32(Abs32RvaExtractorWin32 && abs32_rva_extractor,const AddressTranslator & translator)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 
GetNext()135 std::optional<Reference> Abs32ReaderWin32::GetNext() {
136   for (auto unit = abs32_rva_extractor_.GetNext(); unit.has_value();
137        unit = abs32_rva_extractor_.GetNext()) {
138     offset_t location = unit->location;
139     offset_t unsafe_target = target_rva_to_offset_.Convert(unit->target_rva);
140     if (unsafe_target != kInvalidOffset)
141       return Reference{location, unsafe_target};
142   }
143   return std::nullopt;
144 }
145 
146 /******** Abs32WriterWin32 ********/
147 
Abs32WriterWin32(MutableBufferView image,AbsoluteAddress && addr,const AddressTranslator & translator)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 
PutNext(Reference ref)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 
RemoveUntranslatableAbs32(ConstBufferView image,AbsoluteAddress && addr,const AddressTranslator & translator,std::vector<offset_t> * locations)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 
RemoveOverlappingAbs32Locations(uint32_t width,std::vector<offset_t> * locations)186 size_t RemoveOverlappingAbs32Locations(uint32_t width,
187                                        std::vector<offset_t>* locations) {
188   if (locations->size() <= 1)
189     return 0;
190 
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
212