xref: /aosp_15_r20/external/zucchini/reloc_win32.cc (revision a03ca8b91e029cd15055c20c78c2e087c84792e4)
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