1*a03ca8b9SKrzysztof Kosiński // Copyright 2017 The Chromium Authors. All rights reserved.
2*a03ca8b9SKrzysztof Kosiński // Use of this source code is governed by a BSD-style license that can be
3*a03ca8b9SKrzysztof Kosiński // found in the LICENSE file.
4*a03ca8b9SKrzysztof Kosiński
5*a03ca8b9SKrzysztof Kosiński #include "components/zucchini/reloc_win32.h"
6*a03ca8b9SKrzysztof Kosiński
7*a03ca8b9SKrzysztof Kosiński #include <algorithm>
8*a03ca8b9SKrzysztof Kosiński #include <tuple>
9*a03ca8b9SKrzysztof Kosiński #include <utility>
10*a03ca8b9SKrzysztof Kosiński
11*a03ca8b9SKrzysztof Kosiński #include "base/logging.h"
12*a03ca8b9SKrzysztof Kosiński #include "base/numerics/safe_conversions.h"
13*a03ca8b9SKrzysztof Kosiński #include "components/zucchini/algorithm.h"
14*a03ca8b9SKrzysztof Kosiński #include "components/zucchini/io_utils.h"
15*a03ca8b9SKrzysztof Kosiński #include "components/zucchini/type_win_pe.h"
16*a03ca8b9SKrzysztof Kosiński
17*a03ca8b9SKrzysztof Kosiński namespace zucchini {
18*a03ca8b9SKrzysztof Kosiński
19*a03ca8b9SKrzysztof Kosiński /******** RelocUnitWin32 ********/
20*a03ca8b9SKrzysztof Kosiński
21*a03ca8b9SKrzysztof Kosiński RelocUnitWin32::RelocUnitWin32() = default;
RelocUnitWin32(uint8_t type_in,offset_t location_in,rva_t target_rva_in)22*a03ca8b9SKrzysztof Kosiński RelocUnitWin32::RelocUnitWin32(uint8_t type_in,
23*a03ca8b9SKrzysztof Kosiński offset_t location_in,
24*a03ca8b9SKrzysztof Kosiński rva_t target_rva_in)
25*a03ca8b9SKrzysztof Kosiński : type(type_in), location(location_in), target_rva(target_rva_in) {}
26*a03ca8b9SKrzysztof Kosiński
operator ==(const RelocUnitWin32 & a,const RelocUnitWin32 & b)27*a03ca8b9SKrzysztof Kosiński bool operator==(const RelocUnitWin32& a, const RelocUnitWin32& b) {
28*a03ca8b9SKrzysztof Kosiński return std::tie(a.type, a.location, a.target_rva) ==
29*a03ca8b9SKrzysztof Kosiński std::tie(b.type, b.location, b.target_rva);
30*a03ca8b9SKrzysztof Kosiński }
31*a03ca8b9SKrzysztof Kosiński
32*a03ca8b9SKrzysztof Kosiński /******** RelocRvaReaderWin32 ********/
33*a03ca8b9SKrzysztof Kosiński
34*a03ca8b9SKrzysztof Kosiński // static
FindRelocBlocks(ConstBufferView image,BufferRegion reloc_region,std::vector<offset_t> * reloc_block_offsets)35*a03ca8b9SKrzysztof Kosiński bool RelocRvaReaderWin32::FindRelocBlocks(
36*a03ca8b9SKrzysztof Kosiński ConstBufferView image,
37*a03ca8b9SKrzysztof Kosiński BufferRegion reloc_region,
38*a03ca8b9SKrzysztof Kosiński std::vector<offset_t>* reloc_block_offsets) {
39*a03ca8b9SKrzysztof Kosiński CHECK_LT(reloc_region.size, kOffsetBound);
40*a03ca8b9SKrzysztof Kosiński ConstBufferView reloc_data = image[reloc_region];
41*a03ca8b9SKrzysztof Kosiński reloc_block_offsets->clear();
42*a03ca8b9SKrzysztof Kosiński while (reloc_data.size() >= sizeof(pe::RelocHeader)) {
43*a03ca8b9SKrzysztof Kosiński reloc_block_offsets->push_back(
44*a03ca8b9SKrzysztof Kosiński base::checked_cast<offset_t>(reloc_data.begin() - image.begin()));
45*a03ca8b9SKrzysztof Kosiński auto size = reloc_data.read<pe::RelocHeader>(0).size;
46*a03ca8b9SKrzysztof Kosiński // |size| must be aligned to 4-bytes.
47*a03ca8b9SKrzysztof Kosiński if (size < sizeof(pe::RelocHeader) || size % 4 || size > reloc_data.size())
48*a03ca8b9SKrzysztof Kosiński return false;
49*a03ca8b9SKrzysztof Kosiński reloc_data.remove_prefix(size);
50*a03ca8b9SKrzysztof Kosiński }
51*a03ca8b9SKrzysztof Kosiński return reloc_data.empty(); // Fail if trailing data exist.
52*a03ca8b9SKrzysztof Kosiński }
53*a03ca8b9SKrzysztof Kosiński
RelocRvaReaderWin32(ConstBufferView image,BufferRegion reloc_region,const std::vector<offset_t> & reloc_block_offsets,offset_t lo,offset_t hi)54*a03ca8b9SKrzysztof Kosiński RelocRvaReaderWin32::RelocRvaReaderWin32(
55*a03ca8b9SKrzysztof Kosiński ConstBufferView image,
56*a03ca8b9SKrzysztof Kosiński BufferRegion reloc_region,
57*a03ca8b9SKrzysztof Kosiński const std::vector<offset_t>& reloc_block_offsets,
58*a03ca8b9SKrzysztof Kosiński offset_t lo,
59*a03ca8b9SKrzysztof Kosiński offset_t hi)
60*a03ca8b9SKrzysztof Kosiński : image_(image) {
61*a03ca8b9SKrzysztof Kosiński CHECK_LE(lo, hi);
62*a03ca8b9SKrzysztof Kosiński lo = base::checked_cast<offset_t>(reloc_region.InclusiveClamp(lo));
63*a03ca8b9SKrzysztof Kosiński hi = base::checked_cast<offset_t>(reloc_region.InclusiveClamp(hi));
64*a03ca8b9SKrzysztof Kosiński end_it_ = image_.begin() + hi;
65*a03ca8b9SKrzysztof Kosiński
66*a03ca8b9SKrzysztof Kosiński // By default, get GetNext() to produce empty output.
67*a03ca8b9SKrzysztof Kosiński cur_reloc_units_ = BufferSource(end_it_, 0);
68*a03ca8b9SKrzysztof Kosiński if (reloc_block_offsets.empty())
69*a03ca8b9SKrzysztof Kosiński return;
70*a03ca8b9SKrzysztof Kosiński
71*a03ca8b9SKrzysztof Kosiński // Find the block that contains |lo|.
72*a03ca8b9SKrzysztof Kosiński auto block_it = std::upper_bound(reloc_block_offsets.begin(),
73*a03ca8b9SKrzysztof Kosiński reloc_block_offsets.end(), lo);
74*a03ca8b9SKrzysztof Kosiński DCHECK(block_it != reloc_block_offsets.begin());
75*a03ca8b9SKrzysztof Kosiński --block_it;
76*a03ca8b9SKrzysztof Kosiński
77*a03ca8b9SKrzysztof Kosiński // Initialize |cur_reloc_units_| and |rva_hi_bits_|.
78*a03ca8b9SKrzysztof Kosiński if (!LoadRelocBlock(image_.begin() + *block_it))
79*a03ca8b9SKrzysztof Kosiński return; // Nothing left.
80*a03ca8b9SKrzysztof Kosiński
81*a03ca8b9SKrzysztof Kosiński // Skip |cur_reloc_units_| to |lo|, truncating up.
82*a03ca8b9SKrzysztof Kosiński offset_t cur_reloc_units_offset =
83*a03ca8b9SKrzysztof Kosiński base::checked_cast<offset_t>(cur_reloc_units_.begin() - image_.begin());
84*a03ca8b9SKrzysztof Kosiński if (lo > cur_reloc_units_offset) {
85*a03ca8b9SKrzysztof Kosiński offset_t delta =
86*a03ca8b9SKrzysztof Kosiński AlignCeil<offset_t>(lo - cur_reloc_units_offset, kRelocUnitSize);
87*a03ca8b9SKrzysztof Kosiński cur_reloc_units_.Skip(delta);
88*a03ca8b9SKrzysztof Kosiński }
89*a03ca8b9SKrzysztof Kosiński }
90*a03ca8b9SKrzysztof Kosiński
91*a03ca8b9SKrzysztof Kosiński RelocRvaReaderWin32::RelocRvaReaderWin32(RelocRvaReaderWin32&&) = default;
92*a03ca8b9SKrzysztof Kosiński
93*a03ca8b9SKrzysztof Kosiński RelocRvaReaderWin32::~RelocRvaReaderWin32() = default;
94*a03ca8b9SKrzysztof Kosiński
95*a03ca8b9SKrzysztof Kosiński // Unrolls a nested loop: outer = reloc blocks and inner = reloc entries.
GetNext()96*a03ca8b9SKrzysztof Kosiński std::optional<RelocUnitWin32> RelocRvaReaderWin32::GetNext() {
97*a03ca8b9SKrzysztof Kosiński // "Outer loop" to find non-empty reloc block.
98*a03ca8b9SKrzysztof Kosiński while (cur_reloc_units_.Remaining() < kRelocUnitSize) {
99*a03ca8b9SKrzysztof Kosiński if (!LoadRelocBlock(cur_reloc_units_.end()))
100*a03ca8b9SKrzysztof Kosiński return std::nullopt;
101*a03ca8b9SKrzysztof Kosiński }
102*a03ca8b9SKrzysztof Kosiński if (end_it_ - cur_reloc_units_.begin() < kRelocUnitSize)
103*a03ca8b9SKrzysztof Kosiński return std::nullopt;
104*a03ca8b9SKrzysztof Kosiński // "Inner loop" to extract single reloc unit.
105*a03ca8b9SKrzysztof Kosiński offset_t location =
106*a03ca8b9SKrzysztof Kosiński base::checked_cast<offset_t>(cur_reloc_units_.begin() - image_.begin());
107*a03ca8b9SKrzysztof Kosiński uint16_t entry = cur_reloc_units_.read<uint16_t>(0);
108*a03ca8b9SKrzysztof Kosiński uint8_t type = static_cast<uint8_t>(entry >> 12);
109*a03ca8b9SKrzysztof Kosiński rva_t rva = rva_hi_bits_ + (entry & 0xFFF);
110*a03ca8b9SKrzysztof Kosiński cur_reloc_units_.Skip(kRelocUnitSize);
111*a03ca8b9SKrzysztof Kosiński return RelocUnitWin32{type, location, rva};
112*a03ca8b9SKrzysztof Kosiński }
113*a03ca8b9SKrzysztof Kosiński
LoadRelocBlock(ConstBufferView::const_iterator block_begin)114*a03ca8b9SKrzysztof Kosiński bool RelocRvaReaderWin32::LoadRelocBlock(
115*a03ca8b9SKrzysztof Kosiński ConstBufferView::const_iterator block_begin) {
116*a03ca8b9SKrzysztof Kosiński ConstBufferView header_buf(block_begin, sizeof(pe::RelocHeader));
117*a03ca8b9SKrzysztof Kosiński if (header_buf.end() >= end_it_ ||
118*a03ca8b9SKrzysztof Kosiński end_it_ - header_buf.end() < kRelocUnitSize) {
119*a03ca8b9SKrzysztof Kosiński return false;
120*a03ca8b9SKrzysztof Kosiński }
121*a03ca8b9SKrzysztof Kosiński const auto& header = header_buf.read<pe::RelocHeader>(0);
122*a03ca8b9SKrzysztof Kosiński rva_hi_bits_ = header.rva_hi;
123*a03ca8b9SKrzysztof Kosiński uint32_t block_size = header.size;
124*a03ca8b9SKrzysztof Kosiński if (block_size < sizeof(pe::RelocHeader))
125*a03ca8b9SKrzysztof Kosiński return false;
126*a03ca8b9SKrzysztof Kosiński if ((block_size - sizeof(pe::RelocHeader)) % kRelocUnitSize != 0)
127*a03ca8b9SKrzysztof Kosiński return false;
128*a03ca8b9SKrzysztof Kosiński cur_reloc_units_ = BufferSource(block_begin, block_size);
129*a03ca8b9SKrzysztof Kosiński cur_reloc_units_.Skip(sizeof(pe::RelocHeader));
130*a03ca8b9SKrzysztof Kosiński return true;
131*a03ca8b9SKrzysztof Kosiński }
132*a03ca8b9SKrzysztof Kosiński
133*a03ca8b9SKrzysztof Kosiński /******** RelocReaderWin32 ********/
134*a03ca8b9SKrzysztof Kosiński
RelocReaderWin32(RelocRvaReaderWin32 && reloc_rva_reader,uint16_t reloc_type,offset_t offset_bound,const AddressTranslator & translator)135*a03ca8b9SKrzysztof Kosiński RelocReaderWin32::RelocReaderWin32(RelocRvaReaderWin32&& reloc_rva_reader,
136*a03ca8b9SKrzysztof Kosiński uint16_t reloc_type,
137*a03ca8b9SKrzysztof Kosiński offset_t offset_bound,
138*a03ca8b9SKrzysztof Kosiński const AddressTranslator& translator)
139*a03ca8b9SKrzysztof Kosiński : reloc_rva_reader_(std::move(reloc_rva_reader)),
140*a03ca8b9SKrzysztof Kosiński reloc_type_(reloc_type),
141*a03ca8b9SKrzysztof Kosiński offset_bound_(offset_bound),
142*a03ca8b9SKrzysztof Kosiński entry_rva_to_offset_(translator) {}
143*a03ca8b9SKrzysztof Kosiński
144*a03ca8b9SKrzysztof Kosiński RelocReaderWin32::~RelocReaderWin32() = default;
145*a03ca8b9SKrzysztof Kosiński
146*a03ca8b9SKrzysztof Kosiński // ReferenceReader:
GetNext()147*a03ca8b9SKrzysztof Kosiński std::optional<Reference> RelocReaderWin32::GetNext() {
148*a03ca8b9SKrzysztof Kosiński for (std::optional<RelocUnitWin32> unit = reloc_rva_reader_.GetNext();
149*a03ca8b9SKrzysztof Kosiński unit.has_value(); unit = reloc_rva_reader_.GetNext()) {
150*a03ca8b9SKrzysztof Kosiński if (unit->type != reloc_type_)
151*a03ca8b9SKrzysztof Kosiński continue;
152*a03ca8b9SKrzysztof Kosiński offset_t target = entry_rva_to_offset_.Convert(unit->target_rva);
153*a03ca8b9SKrzysztof Kosiński if (target == kInvalidOffset)
154*a03ca8b9SKrzysztof Kosiński continue;
155*a03ca8b9SKrzysztof Kosiński // Ensure that |target| (abs32 reference) lies entirely within the image.
156*a03ca8b9SKrzysztof Kosiński if (target >= offset_bound_)
157*a03ca8b9SKrzysztof Kosiński continue;
158*a03ca8b9SKrzysztof Kosiński offset_t location = unit->location;
159*a03ca8b9SKrzysztof Kosiński return Reference{location, target};
160*a03ca8b9SKrzysztof Kosiński }
161*a03ca8b9SKrzysztof Kosiński return std::nullopt;
162*a03ca8b9SKrzysztof Kosiński }
163*a03ca8b9SKrzysztof Kosiński
164*a03ca8b9SKrzysztof Kosiński /******** RelocWriterWin32 ********/
165*a03ca8b9SKrzysztof Kosiński
RelocWriterWin32(uint16_t reloc_type,MutableBufferView image,BufferRegion reloc_region,const std::vector<offset_t> & reloc_block_offsets,const AddressTranslator & translator)166*a03ca8b9SKrzysztof Kosiński RelocWriterWin32::RelocWriterWin32(
167*a03ca8b9SKrzysztof Kosiński uint16_t reloc_type,
168*a03ca8b9SKrzysztof Kosiński MutableBufferView image,
169*a03ca8b9SKrzysztof Kosiński BufferRegion reloc_region,
170*a03ca8b9SKrzysztof Kosiński const std::vector<offset_t>& reloc_block_offsets,
171*a03ca8b9SKrzysztof Kosiński const AddressTranslator& translator)
172*a03ca8b9SKrzysztof Kosiński : reloc_type_(reloc_type),
173*a03ca8b9SKrzysztof Kosiński image_(image),
174*a03ca8b9SKrzysztof Kosiński reloc_region_(reloc_region),
175*a03ca8b9SKrzysztof Kosiński reloc_block_offsets_(reloc_block_offsets),
176*a03ca8b9SKrzysztof Kosiński target_offset_to_rva_(translator) {}
177*a03ca8b9SKrzysztof Kosiński
178*a03ca8b9SKrzysztof Kosiński RelocWriterWin32::~RelocWriterWin32() = default;
179*a03ca8b9SKrzysztof Kosiński
PutNext(Reference ref)180*a03ca8b9SKrzysztof Kosiński void RelocWriterWin32::PutNext(Reference ref) {
181*a03ca8b9SKrzysztof Kosiński DCHECK_GE(ref.location, reloc_region_.lo());
182*a03ca8b9SKrzysztof Kosiński DCHECK_LT(ref.location, reloc_region_.hi());
183*a03ca8b9SKrzysztof Kosiński auto block_it = std::upper_bound(reloc_block_offsets_.begin(),
184*a03ca8b9SKrzysztof Kosiński reloc_block_offsets_.end(), ref.location);
185*a03ca8b9SKrzysztof Kosiński --block_it;
186*a03ca8b9SKrzysztof Kosiński rva_t rva_hi_bits = image_.read<pe::RelocHeader>(*block_it).rva_hi;
187*a03ca8b9SKrzysztof Kosiński rva_t target_rva = target_offset_to_rva_.Convert(ref.target);
188*a03ca8b9SKrzysztof Kosiński rva_t rva_lo_bits = (target_rva - rva_hi_bits) & 0xFFF;
189*a03ca8b9SKrzysztof Kosiński if (target_rva != rva_hi_bits + rva_lo_bits) {
190*a03ca8b9SKrzysztof Kosiński LOG(ERROR) << "Invalid RVA at " << AsHex<8>(ref.location) << ".";
191*a03ca8b9SKrzysztof Kosiński return;
192*a03ca8b9SKrzysztof Kosiński }
193*a03ca8b9SKrzysztof Kosiński image_.write<uint16_t>(ref.location, rva_lo_bits | (reloc_type_ << 12));
194*a03ca8b9SKrzysztof Kosiński }
195*a03ca8b9SKrzysztof Kosiński
196*a03ca8b9SKrzysztof Kosiński } // namespace zucchini
197