blob: c025a076d3c59b835b2c853d7a91a0f13a1d02c8 [file] [log] [blame]
huangs062026a12017-10-05 16:26:051// 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 "chrome/installer/zucchini/reloc_utils.h"
6
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"
13#include "chrome/installer/zucchini/algorithm.h"
14#include "chrome/installer/zucchini/io_utils.h"
15#include "chrome/installer/zucchini/type_win_pe.h"
16
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)) {
43 reloc_block_offsets->push_back(reloc_data.begin() - image.begin());
44 auto size = reloc_data.read<pe::RelocHeader>(0).size;
45 // |size| must be aligned to 4-bytes.
46 if (size < sizeof(pe::RelocHeader) || size % 4 || size > reloc_data.size())
47 return false;
48 reloc_data.remove_prefix(size);
49 }
50 return reloc_data.empty(); // Fail if trailing data exist.
51}
52
53RelocRvaReaderWin32::RelocRvaReaderWin32(
54 ConstBufferView image,
55 BufferRegion reloc_region,
56 const std::vector<offset_t>& reloc_block_offsets,
57 offset_t lo,
58 offset_t hi)
59 : image_(image) {
60 CHECK_LE(lo, hi);
61 lo = base::checked_cast<offset_t>(reloc_region.InclusiveClamp(lo));
62 hi = base::checked_cast<offset_t>(reloc_region.InclusiveClamp(hi));
63 end_it_ = image_.begin() + hi;
64
65 // By default, get GetNext() to produce empty output.
66 cur_reloc_units_ = BufferSource(end_it_, 0);
67 if (reloc_block_offsets.empty())
68 return;
69
70 // Find the block that contains |lo|.
71 auto block_it = std::upper_bound(reloc_block_offsets.begin(),
72 reloc_block_offsets.end(), lo);
73 DCHECK(block_it != reloc_block_offsets.begin());
74 --block_it;
75
76 // Initialize |cur_reloc_units_| and |rva_hi_bits_|.
77 if (!LoadRelocBlock(image_.begin() + *block_it))
78 return; // Nothing left.
79
80 // Skip |cur_reloc_units_| to |lo|, truncating up.
81 offset_t cur_reloc_units_offset = cur_reloc_units_.begin() - image_.begin();
82 if (lo > cur_reloc_units_offset) {
83 offset_t delta =
84 ceil<offset_t>(lo - cur_reloc_units_offset, kRelocUnitSize);
85 cur_reloc_units_.Skip(delta);
86 }
87}
88
89RelocRvaReaderWin32::RelocRvaReaderWin32(RelocRvaReaderWin32&&) = default;
90
91RelocRvaReaderWin32::~RelocRvaReaderWin32() = default;
92
93// Unrolls a nested loop: outer = reloc blocks and inner = reloc entries.
94base::Optional<RelocUnitWin32> RelocRvaReaderWin32::GetNext() {
95 // "Outer loop" to find non-empty reloc block.
96 while (cur_reloc_units_.Remaining() < kRelocUnitSize) {
97 if (!LoadRelocBlock(cur_reloc_units_.end()))
98 return base::nullopt;
99 }
100 if (end_it_ - cur_reloc_units_.begin() < kRelocUnitSize)
101 return base::nullopt;
102 // "Inner loop" to extract single reloc unit.
103 offset_t location = cur_reloc_units_.begin() - image_.begin();
104 uint16_t entry = cur_reloc_units_.read<uint16_t>(0);
105 uint8_t type = static_cast<uint8_t>(entry >> 12);
106 rva_t rva = rva_hi_bits_ + (entry & 0xFFF);
107 cur_reloc_units_.Skip(kRelocUnitSize);
108 return RelocUnitWin32{type, location, rva};
109}
110
111bool RelocRvaReaderWin32::LoadRelocBlock(
112 ConstBufferView::const_iterator block_begin) {
113 ConstBufferView header_buf(block_begin, sizeof(pe::RelocHeader));
114 if (header_buf.end() >= end_it_ ||
115 end_it_ - header_buf.end() < kRelocUnitSize) {
116 return false;
117 }
118 const auto& header = header_buf.read<pe::RelocHeader>(0);
119 rva_hi_bits_ = header.rva_hi;
120 uint32_t block_size = header.size;
121 DCHECK_GE(block_size, sizeof(pe::RelocHeader));
122 cur_reloc_units_ = BufferSource(block_begin, block_size);
123 cur_reloc_units_.Skip(sizeof(pe::RelocHeader));
124 return true;
125}
126
127/******** RelocReaderWin32 ********/
128
129RelocReaderWin32::RelocReaderWin32(RelocRvaReaderWin32&& reloc_rva_reader,
130 uint16_t reloc_type,
131 offset_t offset_bound,
132 const AddressTranslator& translator)
133 : reloc_rva_reader_(std::move(reloc_rva_reader)),
134 reloc_type_(reloc_type),
135 offset_bound_(offset_bound),
136 entry_rva_to_offset_(translator) {}
137
138RelocReaderWin32::~RelocReaderWin32() = default;
139
140// ReferenceReader:
141base::Optional<Reference> RelocReaderWin32::GetNext() {
142 for (base::Optional<RelocUnitWin32> unit = reloc_rva_reader_.GetNext();
143 unit.has_value(); unit = reloc_rva_reader_.GetNext()) {
144 if (unit->type != reloc_type_)
145 continue;
146 offset_t target = entry_rva_to_offset_.Convert(unit->target_rva);
147 if (target == kInvalidOffset)
148 continue;
149 offset_t location = unit->location;
150 if (IsMarked(target)) {
151 LOG(WARNING) << "Warning: Skipping mark-aliased reloc target: "
152 << AsHex<8>(location) << " -> " << AsHex<8>(target) << ".";
153 continue;
154 }
155 // Ensures the target (abs32 reference) lies entirely within the image.
156 if (target >= offset_bound_)
157 continue;
158 return Reference{location, target};
159 }
160 return base::nullopt;
161}
162
163/******** RelocWriterWin32 ********/
164
165RelocWriterWin32::RelocWriterWin32(
166 uint16_t reloc_type,
167 MutableBufferView image,
168 BufferRegion reloc_region,
169 const std::vector<offset_t>& reloc_block_offsets,
170 const AddressTranslator& translator)
171 : reloc_type_(reloc_type),
172 image_(image),
173 reloc_region_(reloc_region),
174 reloc_block_offsets_(reloc_block_offsets),
175 target_offset_to_rva_(translator) {}
176
177RelocWriterWin32::~RelocWriterWin32() = default;
178
179void RelocWriterWin32::PutNext(Reference ref) {
180 DCHECK_GE(ref.location, reloc_region_.lo());
181 DCHECK_LT(ref.location, reloc_region_.hi());
182 auto block_it = std::upper_bound(reloc_block_offsets_.begin(),
183 reloc_block_offsets_.end(), ref.location);
184 --block_it;
185 rva_t rva_hi_bits = image_.read<pe::RelocHeader>(*block_it).rva_hi;
186 rva_t target_rva = target_offset_to_rva_.Convert(ref.target);
187 rva_t rva_lo_bits = target_rva - rva_hi_bits;
188 DCHECK_EQ(rva_lo_bits & 0xFFF, rva_lo_bits);
189 image_.write<uint16_t>(ref.location,
190 (rva_lo_bits & 0xFFF) | (reloc_type_ << 12));
191}
192
193} // namespace zucchini