blob: 622b967788f04221c65925baf81c5c3803e6393c [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
Arthur Sonzognib948e672024-07-31 08:29:045#ifdef UNSAFE_BUFFERS_BUILD
6// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
7#pragma allow_unsafe_buffers
8#endif
9
Etienne Pierre-dorayefbbccf2018-07-24 15:42:3310#include "components/zucchini/reloc_win32.h"
huangs062026a12017-10-05 16:26:0511
12#include <algorithm>
13#include <tuple>
14#include <utility>
15
Samuel Huangff0bc472017-10-26 06:55:1516#include "base/logging.h"
huangs062026a12017-10-05 16:26:0517#include "base/numerics/safe_conversions.h"
Samuel Huang577ef6c2018-03-13 18:19:3418#include "components/zucchini/algorithm.h"
19#include "components/zucchini/io_utils.h"
20#include "components/zucchini/type_win_pe.h"
huangs062026a12017-10-05 16:26:0521
huangs062026a12017-10-05 16:26:0522namespace zucchini {
23
24/******** RelocUnitWin32 ********/
25
26RelocUnitWin32::RelocUnitWin32() = default;
27RelocUnitWin32::RelocUnitWin32(uint8_t type_in,
28 offset_t location_in,
29 rva_t target_rva_in)
30 : type(type_in), location(location_in), target_rva(target_rva_in) {}
31
32bool operator==(const RelocUnitWin32& a, const RelocUnitWin32& b) {
33 return std::tie(a.type, a.location, a.target_rva) ==
34 std::tie(b.type, b.location, b.target_rva);
35}
36
37/******** RelocRvaReaderWin32 ********/
38
39// static
40bool RelocRvaReaderWin32::FindRelocBlocks(
41 ConstBufferView image,
42 BufferRegion reloc_region,
43 std::vector<offset_t>* reloc_block_offsets) {
44 CHECK_LT(reloc_region.size, kOffsetBound);
45 ConstBufferView reloc_data = image[reloc_region];
46 reloc_block_offsets->clear();
47 while (reloc_data.size() >= sizeof(pe::RelocHeader)) {
Etienne Pierre-dorayf6c70a22018-09-11 13:40:4648 reloc_block_offsets->push_back(
49 base::checked_cast<offset_t>(reloc_data.begin() - image.begin()));
huangs062026a12017-10-05 16:26:0550 auto size = reloc_data.read<pe::RelocHeader>(0).size;
51 // |size| must be aligned to 4-bytes.
52 if (size < sizeof(pe::RelocHeader) || size % 4 || size > reloc_data.size())
53 return false;
54 reloc_data.remove_prefix(size);
55 }
56 return reloc_data.empty(); // Fail if trailing data exist.
57}
58
59RelocRvaReaderWin32::RelocRvaReaderWin32(
60 ConstBufferView image,
61 BufferRegion reloc_region,
62 const std::vector<offset_t>& reloc_block_offsets,
63 offset_t lo,
64 offset_t hi)
65 : image_(image) {
66 CHECK_LE(lo, hi);
67 lo = base::checked_cast<offset_t>(reloc_region.InclusiveClamp(lo));
68 hi = base::checked_cast<offset_t>(reloc_region.InclusiveClamp(hi));
69 end_it_ = image_.begin() + hi;
70
71 // By default, get GetNext() to produce empty output.
72 cur_reloc_units_ = BufferSource(end_it_, 0);
73 if (reloc_block_offsets.empty())
74 return;
75
76 // Find the block that contains |lo|.
77 auto block_it = std::upper_bound(reloc_block_offsets.begin(),
78 reloc_block_offsets.end(), lo);
79 DCHECK(block_it != reloc_block_offsets.begin());
80 --block_it;
81
82 // Initialize |cur_reloc_units_| and |rva_hi_bits_|.
83 if (!LoadRelocBlock(image_.begin() + *block_it))
84 return; // Nothing left.
85
86 // Skip |cur_reloc_units_| to |lo|, truncating up.
Etienne Pierre-dorayf6c70a22018-09-11 13:40:4687 offset_t cur_reloc_units_offset =
88 base::checked_cast<offset_t>(cur_reloc_units_.begin() - image_.begin());
huangs062026a12017-10-05 16:26:0589 if (lo > cur_reloc_units_offset) {
90 offset_t delta =
Samuel Huang2c50b5af2018-06-13 17:47:3991 AlignCeil<offset_t>(lo - cur_reloc_units_offset, kRelocUnitSize);
Samuel Huangc019fe92023-05-31 04:14:2192 // Okay if this empties |cur_reloc_units_|.
huangs062026a12017-10-05 16:26:0593 cur_reloc_units_.Skip(delta);
94 }
95}
96
97RelocRvaReaderWin32::RelocRvaReaderWin32(RelocRvaReaderWin32&&) = default;
98
99RelocRvaReaderWin32::~RelocRvaReaderWin32() = default;
100
101// Unrolls a nested loop: outer = reloc blocks and inner = reloc entries.
Arthur Sonzognic571efb2024-01-26 20:26:18102std::optional<RelocUnitWin32> RelocRvaReaderWin32::GetNext() {
huangs062026a12017-10-05 16:26:05103 // "Outer loop" to find non-empty reloc block.
104 while (cur_reloc_units_.Remaining() < kRelocUnitSize) {
105 if (!LoadRelocBlock(cur_reloc_units_.end()))
Arthur Sonzognic571efb2024-01-26 20:26:18106 return std::nullopt;
huangs062026a12017-10-05 16:26:05107 }
108 if (end_it_ - cur_reloc_units_.begin() < kRelocUnitSize)
Arthur Sonzognic571efb2024-01-26 20:26:18109 return std::nullopt;
huangs062026a12017-10-05 16:26:05110 // "Inner loop" to extract single reloc unit.
Etienne Pierre-dorayf6c70a22018-09-11 13:40:46111 offset_t location =
112 base::checked_cast<offset_t>(cur_reloc_units_.begin() - image_.begin());
huangs062026a12017-10-05 16:26:05113 uint16_t entry = cur_reloc_units_.read<uint16_t>(0);
114 uint8_t type = static_cast<uint8_t>(entry >> 12);
115 rva_t rva = rva_hi_bits_ + (entry & 0xFFF);
Samuel Huangc019fe92023-05-31 04:14:21116 if (!cur_reloc_units_.Skip(kRelocUnitSize)) {
Arthur Sonzognic571efb2024-01-26 20:26:18117 return std::nullopt;
Samuel Huangc019fe92023-05-31 04:14:21118 }
huangs062026a12017-10-05 16:26:05119 return RelocUnitWin32{type, location, rva};
120}
121
122bool RelocRvaReaderWin32::LoadRelocBlock(
123 ConstBufferView::const_iterator block_begin) {
124 ConstBufferView header_buf(block_begin, sizeof(pe::RelocHeader));
125 if (header_buf.end() >= end_it_ ||
126 end_it_ - header_buf.end() < kRelocUnitSize) {
127 return false;
128 }
129 const auto& header = header_buf.read<pe::RelocHeader>(0);
130 rva_hi_bits_ = header.rva_hi;
131 uint32_t block_size = header.size;
Calder Kitagawa97544482018-04-05 12:56:50132 if (block_size < sizeof(pe::RelocHeader))
133 return false;
134 if ((block_size - sizeof(pe::RelocHeader)) % kRelocUnitSize != 0)
135 return false;
huangs062026a12017-10-05 16:26:05136 cur_reloc_units_ = BufferSource(block_begin, block_size);
Samuel Huangc019fe92023-05-31 04:14:21137 cur_reloc_units_.Skip(sizeof(pe::RelocHeader)); // Always succeeds.
huangs062026a12017-10-05 16:26:05138 return true;
139}
140
141/******** RelocReaderWin32 ********/