diff options
Diffstat (limited to 'reloc_utils.cc')
-rw-r--r-- | reloc_utils.cc | 193 |
1 files changed, 193 insertions, 0 deletions
diff --git a/reloc_utils.cc b/reloc_utils.cc new file mode 100644 index 0000000..d21a0d3 --- /dev/null +++ b/reloc_utils.cc @@ -0,0 +1,193 @@ +// 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_utils.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(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 = cur_reloc_units_.begin() - image_.begin(); + if (lo > cur_reloc_units_offset) { + offset_t delta = + ceil<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. +base::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 base::nullopt; + } + if (end_it_ - cur_reloc_units_.begin() < kRelocUnitSize) + return base::nullopt; + // "Inner loop" to extract single reloc unit. + offset_t location = 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; + DCHECK_GE(block_size, sizeof(pe::RelocHeader)); + 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: +base::Optional<Reference> RelocReaderWin32::GetNext() { + for (base::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; + offset_t location = unit->location; + if (IsMarked(target)) { + LOG(WARNING) << "Warning: Skipping mark-aliased reloc target: " + << AsHex<8>(location) << " -> " << AsHex<8>(target) << "."; + continue; + } + // Ensures the target (abs32 reference) lies entirely within the image. + if (target >= offset_bound_) + continue; + return Reference{location, target}; + } + return base::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; + DCHECK_EQ(rva_lo_bits & 0xFFF, rva_lo_bits); + image_.write<uint16_t>(ref.location, + (rva_lo_bits & 0xFFF) | (reloc_type_ << 12)); +} + +} // namespace zucchini |