123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196 |
- // Copyright 2017 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
- #include "components/zucchini/reloc_win32.h"
- #include <algorithm>
- #include <tuple>
- #include <utility>
- #include "base/logging.h"
- #include "base/numerics/safe_conversions.h"
- #include "components/zucchini/algorithm.h"
- #include "components/zucchini/io_utils.h"
- #include "components/zucchini/type_win_pe.h"
- namespace zucchini {
- /******** RelocUnitWin32 ********/
- RelocUnitWin32::RelocUnitWin32() = default;
- RelocUnitWin32::RelocUnitWin32(uint8_t type_in,
- offset_t location_in,
- rva_t target_rva_in)
- : type(type_in), location(location_in), target_rva(target_rva_in) {}
- bool operator==(const RelocUnitWin32& a, const RelocUnitWin32& b) {
- return std::tie(a.type, a.location, a.target_rva) ==
- std::tie(b.type, b.location, b.target_rva);
- }
- /******** RelocRvaReaderWin32 ********/
- // static
- bool RelocRvaReaderWin32::FindRelocBlocks(
- ConstBufferView image,
- BufferRegion reloc_region,
- std::vector<offset_t>* reloc_block_offsets) {
- CHECK_LT(reloc_region.size, kOffsetBound);
- ConstBufferView reloc_data = image[reloc_region];
- reloc_block_offsets->clear();
- while (reloc_data.size() >= sizeof(pe::RelocHeader)) {
- reloc_block_offsets->push_back(
- base::checked_cast<offset_t>(reloc_data.begin() - image.begin()));
- auto size = reloc_data.read<pe::RelocHeader>(0).size;
- // |size| must be aligned to 4-bytes.
- if (size < sizeof(pe::RelocHeader) || size % 4 || size > reloc_data.size())
- return false;
- reloc_data.remove_prefix(size);
- }
- return reloc_data.empty(); // Fail if trailing data exist.
- }
- RelocRvaReaderWin32::RelocRvaReaderWin32(
- ConstBufferView image,
- BufferRegion reloc_region,
- const std::vector<offset_t>& reloc_block_offsets,
- offset_t lo,
- offset_t hi)
- : image_(image) {
- CHECK_LE(lo, hi);
- lo = base::checked_cast<offset_t>(reloc_region.InclusiveClamp(lo));
- hi = base::checked_cast<offset_t>(reloc_region.InclusiveClamp(hi));
- end_it_ = image_.begin() + hi;
- // By default, get GetNext() to produce empty output.
- cur_reloc_units_ = BufferSource(end_it_, 0);
- if (reloc_block_offsets.empty())
- return;
- // Find the block that contains |lo|.
- auto block_it = std::upper_bound(reloc_block_offsets.begin(),
- reloc_block_offsets.end(), lo);
- DCHECK(block_it != reloc_block_offsets.begin());
- --block_it;
- // Initialize |cur_reloc_units_| and |rva_hi_bits_|.
- if (!LoadRelocBlock(image_.begin() + *block_it))
- return; // Nothing left.
- // Skip |cur_reloc_units_| to |lo|, truncating up.
- offset_t cur_reloc_units_offset =
- base::checked_cast<offset_t>(cur_reloc_units_.begin() - image_.begin());
- if (lo > cur_reloc_units_offset) {
- offset_t delta =
- AlignCeil<offset_t>(lo - cur_reloc_units_offset, kRelocUnitSize);
- cur_reloc_units_.Skip(delta);
- }
- }
- RelocRvaReaderWin32::RelocRvaReaderWin32(RelocRvaReaderWin32&&) = default;
- RelocRvaReaderWin32::~RelocRvaReaderWin32() = default;
- // Unrolls a nested loop: outer = reloc blocks and inner = reloc entries.
- absl::optional<RelocUnitWin32> RelocRvaReaderWin32::GetNext() {
- // "Outer loop" to find non-empty reloc block.
- while (cur_reloc_units_.Remaining() < kRelocUnitSize) {
- if (!LoadRelocBlock(cur_reloc_units_.end()))
- return absl::nullopt;
- }
- if (end_it_ - cur_reloc_units_.begin() < kRelocUnitSize)
- return absl::nullopt;
- // "Inner loop" to extract single reloc unit.
- offset_t location =
- base::checked_cast<offset_t>(cur_reloc_units_.begin() - image_.begin());
- uint16_t entry = cur_reloc_units_.read<uint16_t>(0);
- uint8_t type = static_cast<uint8_t>(entry >> 12);
- rva_t rva = rva_hi_bits_ + (entry & 0xFFF);
- cur_reloc_units_.Skip(kRelocUnitSize);
- return RelocUnitWin32{type, location, rva};
- }
- bool RelocRvaReaderWin32::LoadRelocBlock(
- ConstBufferView::const_iterator block_begin) {
- ConstBufferView header_buf(block_begin, sizeof(pe::RelocHeader));
- if (header_buf.end() >= end_it_ ||
- end_it_ - header_buf.end() < kRelocUnitSize) {
- return false;
- }
- const auto& header = header_buf.read<pe::RelocHeader>(0);
- rva_hi_bits_ = header.rva_hi;
- uint32_t block_size = header.size;
- if (block_size < sizeof(pe::RelocHeader))
- return false;
- if ((block_size - sizeof(pe::RelocHeader)) % kRelocUnitSize != 0)
- return false;
- cur_reloc_units_ = BufferSource(block_begin, block_size);
- cur_reloc_units_.Skip(sizeof(pe::RelocHeader));
- return true;
- }
- /******** RelocReaderWin32 ********/
- RelocReaderWin32::RelocReaderWin32(RelocRvaReaderWin32&& reloc_rva_reader,
- uint16_t reloc_type,
- offset_t offset_bound,
- const AddressTranslator& translator)
- : reloc_rva_reader_(std::move(reloc_rva_reader)),
- reloc_type_(reloc_type),
- offset_bound_(offset_bound),
- entry_rva_to_offset_(translator) {}
- RelocReaderWin32::~RelocReaderWin32() = default;
- // ReferenceReader:
- absl::optional<Reference> RelocReaderWin32::GetNext() {
- for (absl::optional<RelocUnitWin32> unit = reloc_rva_reader_.GetNext();
- unit.has_value(); unit = reloc_rva_reader_.GetNext()) {
- if (unit->type != reloc_type_)
- continue;
- offset_t target = entry_rva_to_offset_.Convert(unit->target_rva);
- if (target == kInvalidOffset)
- continue;
- // Ensure that |target| (abs32 reference) lies entirely within the image.
- if (target >= offset_bound_)
- continue;
- offset_t location = unit->location;
- return Reference{location, target};
- }
- return absl::nullopt;
- }
- /******** RelocWriterWin32 ********/
- RelocWriterWin32::RelocWriterWin32(
- uint16_t reloc_type,
- MutableBufferView image,
- BufferRegion reloc_region,
- const std::vector<offset_t>& reloc_block_offsets,
- const AddressTranslator& translator)
- : reloc_type_(reloc_type),
- image_(image),
- reloc_region_(reloc_region),
- reloc_block_offsets_(reloc_block_offsets),
- target_offset_to_rva_(translator) {}
- RelocWriterWin32::~RelocWriterWin32() = default;
- void RelocWriterWin32::PutNext(Reference ref) {
- DCHECK_GE(ref.location, reloc_region_.lo());
- DCHECK_LT(ref.location, reloc_region_.hi());
- auto block_it = std::upper_bound(reloc_block_offsets_.begin(),
- reloc_block_offsets_.end(), ref.location);
- --block_it;
- rva_t rva_hi_bits = image_.read<pe::RelocHeader>(*block_it).rva_hi;
- rva_t target_rva = target_offset_to_rva_.Convert(ref.target);
- rva_t rva_lo_bits = (target_rva - rva_hi_bits) & 0xFFF;
- if (target_rva != rva_hi_bits + rva_lo_bits) {
- LOG(ERROR) << "Invalid RVA at " << AsHex<8>(ref.location) << ".";
- return;
- }
- image_.write<uint16_t>(ref.location, rva_lo_bits | (reloc_type_ << 12));
- }
- } // namespace zucchini
|