blob: bebb5035a2a29d5069e07d0af1955239242bf671 [file] [log] [blame]
Avi Drissman8ba1bad2022-09-13 19:22:361// Copyright 2017 The Chromium Authors
huangs062026a12017-10-05 16:26:052// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
Etienne Pierre-dorayefbbccf2018-07-24 15:42:335#include "components/zucchini/reloc_win32.h"
huangs062026a12017-10-05 16:26:056
7#include <algorithm>
8#include <tuple>
9#include <utility>
10
Samuel Huangff0bc472017-10-26 06:55:1511#include "base/logging.h"
huangs062026a12017-10-05 16:26:0512#include "base/numerics/safe_conversions.h"
Samuel Huang577ef6c2018-03-13 18:19:3413#include "components/zucchini/algorithm.h"
14#include "components/zucchini/io_utils.h"
15#include "components/zucchini/type_win_pe.h"
huangs062026a12017-10-05 16:26:0516
huangs062026a12017-10-05 16:26:0517namespace zucchini {
18
19/******** RelocUnitWin32 ********/
20
21RelocUnitWin32::RelocUnitWin32() = default;
22RelocUnitWin32::RelocUnitWin32(uint8_t type_in,
23 offset_t location_in,
24 rva_t target_rva_in)
25 : type(type_in), location(location_in), target_rva(target_rva_in) {}
26
27bool operator==(const RelocUnitWin32& a, const RelocUnitWin32& b) {
28 return std::tie(a.type, a.location, a.target_rva) ==
29 std::tie(b.type, b.location, b.target_rva);
30}
31
32/******** RelocRvaReaderWin32 ********/
33
34// static
35bool RelocRvaReaderWin32::FindRelocBlocks(
36 ConstBufferView image,
37 BufferRegion reloc_region,
38 std::vector<offset_t>* reloc_block_offsets) {
39 CHECK_LT(reloc_region.size, kOffsetBound);
40 ConstBufferView reloc_data = image[reloc_region];
41 reloc_block_offsets->clear();
42 while (reloc_data.size() >= sizeof(pe::RelocHeader)) {
Etienne Pierre-dorayf6c70a22018-09-11 13:40:4643 reloc_block_offsets->push_back(
44 base::checked_cast<offset_t>(reloc_data.begin() - image.begin()));
huangs062026a12017-10-05 16:26:0545 auto size = reloc_data.read<pe::RelocHeader>(0).size;
46 // |size| must be aligned to 4-bytes.
47 if (size < sizeof(pe::RelocHeader) || size % 4 || size > reloc_data.size())
48 return false;
49 reloc_data.remove_prefix(size);
50 }
51 return reloc_data.empty(); // Fail if trailing data exist.
52}
53
54RelocRvaReaderWin32::RelocRvaReaderWin32(
55 ConstBufferView image,
56 BufferRegion reloc_region,
57 const std::vector<offset_t>& reloc_block_offsets,
58 offset_t lo,
59 offset_t hi)
60 : image_(image) {
61 CHECK_LE(lo, hi);
62 lo = base::checked_cast<offset_t>(reloc_region.InclusiveClamp(lo));
63 hi = base::checked_cast<offset_t>(reloc_region.InclusiveClamp(hi));
64 end_it_ = image_.begin() + hi;
65
66 // By default, get GetNext() to produce empty output.
67 cur_reloc_units_ = BufferSource(end_it_, 0);
68 if (reloc_block_offsets.empty())
69 return;
70
71 // Find the block that contains |lo|.
72 auto block_it = std::upper_bound(reloc_block_offsets.begin(),
73 reloc_block_offsets.end(), lo);
74 DCHECK(block_it != reloc_block_offsets.begin());
75 --block_it;
76
77 // Initialize |cur_reloc_units_| and |rva_hi_bits_|.
78 if (!LoadRelocBlock(image_.begin() + *block_it))
79 return; // Nothing left.
80
81 // Skip |cur_reloc_units_| to |lo|, truncating up.
Etienne Pierre-dorayf6c70a22018-09-11 13:40:4682 offset_t cur_reloc_units_offset =
83 base::checked_cast<offset_t>(cur_reloc_units_.begin() - image_.begin());
huangs062026a12017-10-05 16:26:0584 if (lo > cur_reloc_units_offset) {
85 offset_t delta =
Samuel Huang2c50b5af2018-06-13 17:47:3986 AlignCeil<offset_t>(lo - cur_reloc_units_offset, kRelocUnitSize);
Samuel Huangc019fe92023-05-31 04:14:2187 // Okay if this empties |cur_reloc_units_|.
huangs062026a12017-10-05 16:26:0588 cur_reloc_units_.Skip(delta);
89 }
90}
91
92RelocRvaReaderWin32::RelocRvaReaderWin32(RelocRvaReaderWin32&&) = default;
93
94RelocRvaReaderWin32::~RelocRvaReaderWin32() = default;
95
96// Unrolls a nested loop: outer = reloc blocks and inner = reloc entries.
Anton Bikineev1156b5f2021-05-15 22:35:3697absl::optional<RelocUnitWin32> RelocRvaReaderWin32::GetNext() {
huangs062026a12017-10-05 16:26:0598 // "Outer loop" to find non-empty reloc block.
99 while (cur_reloc_units_.Remaining() < kRelocUnitSize) {
100 if (!LoadRelocBlock(cur_reloc_units_.end()))
Anton Bikineev1156b5f2021-05-15 22:35:36101 return absl::nullopt;
huangs062026a12017-10-05 16:26:05102 }
103 if (end_it_ - cur_reloc_units_.begin() < kRelocUnitSize)
Anton Bikineev1156b5f2021-05-15 22:35:36104 return absl::nullopt;
huangs062026a12017-10-05 16:26:05105 // "Inner loop" to extract single reloc unit.
Etienne Pierre-dorayf6c70a22018-09-11 13:40:46106 offset_t location =
107 base::checked_cast<offset_t>(cur_reloc_units_.begin() - image_.begin());
huangs062026a12017-10-05 16:26:05108 uint16_t entry = cur_reloc_units_.read<uint16_t>(0);
109 uint8_t type = static_cast<uint8_t>(entry >> 12);
110 rva_t rva = rva_hi_bits_ + (entry & 0xFFF);
Samuel Huangc019fe92023-05-31 04:14:21111 if (!cur_reloc_units_.Skip(kRelocUnitSize)) {
112 return absl::nullopt;
113 }
huangs062026a12017-10-05 16:26:05114 return RelocUnitWin32{type, location, rva};
115}
116
117bool RelocRvaReaderWin32::LoadRelocBlock(
118 ConstBufferView::const_iterator block_begin) {
119 ConstBufferView header_buf(block_begin, sizeof(pe::RelocHeader));
120 if (header_buf.end() >= end_it_ ||
121 end_it_ - header_buf.end() < kRelocUnitSize) {
122 return false;
123 }
124 const auto& header = header_buf.read<pe::RelocHeader>(0);
125 rva_hi_bits_ = header.rva_hi;
126 uint32_t block_size = header.size;
Calder Kitagawa97544482018-04-05 12:56:50127 if (block_size < sizeof(pe::RelocHeader))
128 return false;
129 if ((block_size - sizeof(pe::RelocHeader)) % kRelocUnitSize != 0)
130 return false;
huangs062026a12017-10-05 16:26:05131 cur_reloc_units_ = BufferSource(block_begin, block_size);
Samuel Huangc019fe92023-05-31 04:14:21132 cur_reloc_units_.Skip(sizeof(pe::RelocHeader)); // Always succeeds.
huangs062026a12017-10-05 16:26:05133 return true;
134}
135
136/******** RelocReaderWin32 ********/
137
138RelocReaderWin32::RelocReaderWin32(RelocRvaReaderWin32&& reloc_rva_reader,
139 uint16_t reloc_type,
140 offset_t offset_bound,
141 const AddressTranslator& translator)
142 : reloc_rva_reader_(std::move(reloc_rva_reader)),
143 reloc_type_(reloc_type),
144 offset_bound_(offset_bound),
145 entry_rva_to_offset_(translator) {}
146
147RelocReaderWin32::~RelocReaderWin32() = default;
148
149// ReferenceReader:
Anton Bikineev1156b5f2021-05-15 22:35:36150absl::optional<Reference> RelocReaderWin32::GetNext() {
151 for (absl::optional<RelocUnitWin32> unit = reloc_rva_reader_.GetNext();
huangs062026a12017-10-05 16:26:05152 unit.has_value(); unit = reloc_rva_reader_.GetNext()) {
153 if (unit->type != reloc_type_)
154 continue;
155 offset_t target = entry_rva_to_offset_.Convert(unit->target_rva);
156 if (target == kInvalidOffset)
157 continue;
Samuel Huangb6d108f2018-10-10 15:48:10158 // Ensure that |target| (abs32 reference) lies entirely within the image.
huangs062026a12017-10-05 16:26:05159 if (target >= offset_bound_)
160 continue;
Calder Kitagawa79c5e35452018-04-10 20:03:08161 offset_t location = unit->location;
huangs062026a12017-10-05 16:26:05162 return Reference{location, target};
163 }
Anton Bikineev1156b5f2021-05-15 22:35:36164 return absl::nullopt;
huangs062026a12017-10-05 16:26:05165}
166
167/******** RelocWriterWin32 ********/
168
169RelocWriterWin32::RelocWriterWin32(
170 uint16_t reloc_type,
171 MutableBufferView image,
172 BufferRegion reloc_region,
173 const std::vector<offset_t>& reloc_block_offsets,
174 const AddressTranslator& translator)
175 : reloc_type_(reloc_type),
176 image_(image),
177 reloc_region_(reloc_region),
178 reloc_block_offsets_(reloc_block_offsets),
179 target_offset_to_rva_(translator) {}
180
181RelocWriterWin32::~RelocWriterWin32() = default;
182
183void RelocWriterWin32::PutNext(Reference ref) {
184 DCHECK_GE(ref.location, reloc_region_.lo());
185 DCHECK_LT(ref.location, reloc_region_.hi());
Ali Hijazia709b48b2022-11-09 01:27:44186 auto block_it = std::upper_bound(reloc_block_offsets_->begin(),
187 reloc_block_offsets_->end(), ref.location);
huangs062026a12017-10-05 16:26:05188 --block_it;
189 rva_t rva_hi_bits = image_.read<pe::RelocHeader>(*block_it).rva_hi;
190 rva_t target_rva = target_offset_to_rva_.Convert(ref.target);
Calder Kitagawae8d1c972018-07-09 18:51:54191 rva_t rva_lo_bits = (target_rva - rva_hi_bits) & 0xFFF;
192 if (target_rva != rva_hi_bits + rva_lo_bits) {
193 LOG(ERROR) << "Invalid RVA at " << AsHex<8>(ref.location) << ".";
194 return;
195 }
196 image_.write<uint16_t>(ref.location, rva_lo_bits | (reloc_type_ << 12));
huangs062026a12017-10-05 16:26:05197}
198
199} // namespace zucchini