reloc_win32.cc 6.8 KB

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