| // Copyright 2018 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_elf.h" |
| |
| #include <algorithm> |
| |
| #include "base/logging.h" |
| #include "components/zucchini/algorithm.h" |
| |
| namespace zucchini { |
| |
| /******** RelocReaderElf ********/ |
| |
| RelocReaderElf::RelocReaderElf( |
| ConstBufferView image, |
| Bitness bitness, |
| const std::vector<SectionDimensionsElf>& reloc_section_dims, |
| uint32_t rel_type, |
| offset_t lo, |
| offset_t hi, |
| const AddressTranslator& translator) |
| : image_(image), |
| bitness_(bitness), |
| rel_type_(rel_type), |
| reloc_section_dimensions_(reloc_section_dims), |
| hi_(hi), |
| target_rva_to_offset_(translator) { |
| DCHECK(bitness_ == kBit32 || bitness_ == kBit64); |
| |
| // Find the relocation section at or right before |lo|. |
| cur_section_dimensions_ = std::upper_bound( |
| reloc_section_dimensions_.begin(), reloc_section_dimensions_.end(), lo); |
| if (cur_section_dimensions_ != reloc_section_dimensions_.begin()) |
| --cur_section_dimensions_; |
| |
| // |lo| and |hi_| do not cut across a reloc reference (e.g., |
| // Elf_Rel::r_offset), but may cut across a reloc struct (e.g. Elf_Rel)! |
| // GetNext() emits all reloc references in |[lo, hi_)|, but needs to examine |
| // the entire reloc struct for context. Knowing that |r_offset| is the first |
| // entry in a reloc struct, |cursor_| and |hi_| are adjusted by the following: |
| // - If |lo| is in a reloc section, then |cursor_| is chosen, as |lo| aligned |
| // up to the next reloc struct, to exclude reloc struct that |lo| may cut |
| // across. |
| // - If |hi_| is in a reloc section, then align it up, to include reloc struct |
| // that |hi_| may cut across. |
| cursor_ = |
| base::checked_cast<offset_t>(cur_section_dimensions_->region.offset); |
| if (cursor_ < lo) |
| cursor_ += |
| AlignCeil<offset_t>(lo - cursor_, cur_section_dimensions_->entry_size); |
| |
| auto end_section = std::upper_bound(reloc_section_dimensions_.begin(), |
| reloc_section_dimensions_.end(), hi_); |
| if (end_section != reloc_section_dimensions_.begin()) { |
| --end_section; |
| if (hi_ - end_section->region.offset < end_section->region.size) { |
| offset_t end_region_offset = |
| base::checked_cast<offset_t>(end_section->region.offset); |
| hi_ = end_region_offset + AlignCeil<offset_t>(hi_ - end_region_offset, |
| end_section->entry_size); |
| } |
| } |
| } |
| |
| RelocReaderElf::~RelocReaderElf() = default; |
| |
| rva_t RelocReaderElf::GetRelocationTarget(elf::Elf32_Rel rel) const { |
| // The least significant byte of |rel.r_info| is the type. The other 3 bytes |
| // store the symbol, which we ignore. |
| uint32_t type = static_cast<uint32_t>(rel.r_info & 0xFF); |
| if (type == rel_type_) |
| return rel.r_offset; |
| return kInvalidRva; |
| } |
| |
| rva_t RelocReaderElf::GetRelocationTarget(elf::Elf64_Rel rel) const { |
| // The least significant 4 bytes of |rel.r_info| is the type. The other 4 |
| // bytes store the symbol, which we ignore. |
| uint32_t type = static_cast<uint32_t>(rel.r_info & 0xFFFFFFFF); |
| if (type == rel_type_) { |
| // Assume |rel.r_offset| fits within 32-bit integer. |
| if ((rel.r_offset & 0xFFFFFFFF) == rel.r_offset) |
| return static_cast<rva_t>(rel.r_offset); |
| // Otherwise output warning. |
| LOG(WARNING) << "Warning: Skipping r_offset whose value exceeds 32-bits."; |
| } |
| return kInvalidRva; |
| } |
| |
| absl::optional<Reference> RelocReaderElf::GetNext() { |
| offset_t cur_entry_size = cur_section_dimensions_->entry_size; |
| offset_t cur_section_dimensions_end = |
| base::checked_cast<offset_t>(cur_section_dimensions_->region.hi()); |
| |
| for (; cursor_ + cur_entry_size <= hi_; cursor_ += cur_entry_size) { |
| while (cursor_ >= cur_section_dimensions_end) { |
| ++cur_section_dimensions_; |
| if (cur_section_dimensions_ == reloc_section_dimensions_.end()) |
| return absl::nullopt; |
| cur_entry_size = cur_section_dimensions_->entry_size; |
| cursor_ = |
| base::checked_cast<offset_t>(cur_section_dimensions_->region.offset); |
| if (cursor_ + cur_entry_size > hi_) |
| return absl::nullopt; |
| cur_section_dimensions_end = |
| base::checked_cast<offset_t>(cur_section_dimensions_->region.hi()); |
| } |
| rva_t target_rva = kInvalidRva; |
| // TODO(huangs): Fix RELA sections: Need to process |r_addend|. |
| switch (bitness_) { |
| case kBit32: |
| target_rva = GetRelocationTarget(image_.read<elf::Elf32_Rel>(cursor_)); |
| break; |
| case kBit64: |
| target_rva = GetRelocationTarget(image_.read<elf::Elf64_Rel>(cursor_)); |
| break; |
| } |
| if (target_rva == kInvalidRva) |
| continue; |
| // TODO(huangs): Make the check more strict: The reference body should not |
| // straddle section boundary. |
| offset_t target = target_rva_to_offset_.Convert(target_rva); |
| if (target == kInvalidOffset) |
| continue; |
| // |target| will be used to obtain abs32 references, so we must ensure that |
| // it lies inside |image_|. |
| if (!image_.covers({target, WidthOf(bitness_)})) |
| continue; |
| offset_t location = cursor_; |
| cursor_ += cur_entry_size; |
| return Reference{location, target}; |
| } |
| return absl::nullopt; |
| } |
| |
| /******** RelocWriterElf ********/ |
| |
| RelocWriterElf::RelocWriterElf(MutableBufferView image, |
| Bitness bitness, |
| const AddressTranslator& translator) |
| : image_(image), bitness_(bitness), target_offset_to_rva_(translator) { |
| DCHECK(bitness_ == kBit32 || bitness_ == kBit64); |
| } |
| |
| RelocWriterElf::~RelocWriterElf() = default; |
| |
| void RelocWriterElf::PutNext(Reference ref) { |
| switch (bitness_) { |
| case kBit32: |
| image_.modify<elf::Elf32_Rel>(ref.location).r_offset = |
| target_offset_to_rva_.Convert(ref.target); |
| break; |
| case kBit64: |
| image_.modify<elf::Elf64_Rel>(ref.location).r_offset = |
| target_offset_to_rva_.Convert(ref.target); |
| break; |
| } |
| // Leave |reloc.r_info| alone. |
| } |
| |
| } // namespace zucchini |