Etienne Pierre-Doray | a88cad0 | 2018-07-25 20:16:02 +0000 | [diff] [blame] | 1 | // Copyright 2018 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 | |
| 5 | #include "components/zucchini/reloc_elf.h" |
| 6 | |
| 7 | #include <algorithm> |
Etienne Pierre-Doray | a88cad0 | 2018-07-25 20:16:02 +0000 | [diff] [blame] | 8 | |
| 9 | #include "base/logging.h" |
Etienne Pierre-Doray | a88cad0 | 2018-07-25 20:16:02 +0000 | [diff] [blame] | 10 | #include "components/zucchini/algorithm.h" |
| 11 | |
| 12 | namespace zucchini { |
| 13 | |
| 14 | /******** RelocReaderElf ********/ |
| 15 | |
| 16 | RelocReaderElf::RelocReaderElf( |
| 17 | ConstBufferView image, |
| 18 | Bitness bitness, |
| 19 | const std::vector<SectionDimensionsElf>& reloc_section_dims, |
| 20 | uint32_t rel_type, |
| 21 | offset_t lo, |
| 22 | offset_t hi, |
| 23 | const AddressTranslator& translator) |
| 24 | : image_(image), |
| 25 | bitness_(bitness), |
| 26 | rel_type_(rel_type), |
| 27 | reloc_section_dimensions_(reloc_section_dims), |
| 28 | hi_(hi), |
| 29 | target_rva_to_offset_(translator) { |
| 30 | DCHECK(bitness_ == kBit32 || bitness_ == kBit64); |
| 31 | |
| 32 | // Find the relocation section at or right before |lo|. |
| 33 | cur_section_dimensions_ = std::upper_bound( |
| 34 | reloc_section_dimensions_.begin(), reloc_section_dimensions_.end(), lo); |
| 35 | if (cur_section_dimensions_ != reloc_section_dimensions_.begin()) |
| 36 | --cur_section_dimensions_; |
| 37 | |
| 38 | // |lo| and |hi_| do not cut across a reloc reference (e.g., |
| 39 | // Elf_Rel::r_offset), but may cut across a reloc struct (e.g. Elf_Rel)! |
| 40 | // GetNext() emits all reloc references in |[lo, hi_)|, but needs to examine |
| 41 | // the entire reloc struct for context. Knowing that |r_offset| is the first |
| 42 | // entry in a reloc struct, |cursor_| and |hi_| are adjusted by the following: |
| 43 | // - If |lo| is in a reloc section, then |cursor_| is chosen, as |lo| aligned |
| 44 | // up to the next reloc struct, to exclude reloc struct that |lo| may cut |
| 45 | // across. |
| 46 | // - If |hi_| is in a reloc section, then align it up, to include reloc struct |
| 47 | // that |hi_| may cut across. |
| 48 | cursor_ = |
| 49 | base::checked_cast<offset_t>(cur_section_dimensions_->region.offset); |
| 50 | if (cursor_ < lo) |
| 51 | cursor_ += |
| 52 | AlignCeil<offset_t>(lo - cursor_, cur_section_dimensions_->entry_size); |
| 53 | |
| 54 | auto end_section = std::upper_bound(reloc_section_dimensions_.begin(), |
| 55 | reloc_section_dimensions_.end(), hi_); |
| 56 | if (end_section != reloc_section_dimensions_.begin()) { |
| 57 | --end_section; |
| 58 | if (hi_ - end_section->region.offset < end_section->region.size) { |
| 59 | offset_t end_region_offset = |
| 60 | base::checked_cast<offset_t>(end_section->region.offset); |
| 61 | hi_ = end_region_offset + AlignCeil<offset_t>(hi_ - end_region_offset, |
| 62 | end_section->entry_size); |
| 63 | } |
| 64 | } |
| 65 | } |
| 66 | |
| 67 | RelocReaderElf::~RelocReaderElf() = default; |
| 68 | |
| 69 | rva_t RelocReaderElf::GetRelocationTarget(elf::Elf32_Rel rel) const { |
| 70 | // The least significant byte of |rel.r_info| is the type. The other 3 bytes |
| 71 | // store the symbol, which we ignore. |
| 72 | uint32_t type = static_cast<uint32_t>(rel.r_info & 0xFF); |
| 73 | if (type == rel_type_) |
| 74 | return rel.r_offset; |
| 75 | return kInvalidRva; |
| 76 | } |
| 77 | |
| 78 | rva_t RelocReaderElf::GetRelocationTarget(elf::Elf64_Rel rel) const { |
| 79 | // The least significant 4 bytes of |rel.r_info| is the type. The other 4 |
| 80 | // bytes store the symbol, which we ignore. |
| 81 | uint32_t type = static_cast<uint32_t>(rel.r_info & 0xFFFFFFFF); |
| 82 | if (type == rel_type_) { |
| 83 | // Assume |rel.r_offset| fits within 32-bit integer. |
| 84 | if ((rel.r_offset & 0xFFFFFFFF) == rel.r_offset) |
| 85 | return static_cast<rva_t>(rel.r_offset); |
| 86 | // Otherwise output warning. |
| 87 | LOG(WARNING) << "Warning: Skipping r_offset whose value exceeds 32-bits."; |
| 88 | } |
| 89 | return kInvalidRva; |
| 90 | } |
| 91 | |
Anton Bikineev | 1a96551 | 2021-05-15 22:35:36 +0000 | [diff] [blame] | 92 | absl::optional<Reference> RelocReaderElf::GetNext() { |
Etienne Pierre-Doray | a88cad0 | 2018-07-25 20:16:02 +0000 | [diff] [blame] | 93 | offset_t cur_entry_size = cur_section_dimensions_->entry_size; |
| 94 | offset_t cur_section_dimensions_end = |
| 95 | base::checked_cast<offset_t>(cur_section_dimensions_->region.hi()); |
| 96 | |
| 97 | for (; cursor_ + cur_entry_size <= hi_; cursor_ += cur_entry_size) { |
| 98 | while (cursor_ >= cur_section_dimensions_end) { |
| 99 | ++cur_section_dimensions_; |
| 100 | if (cur_section_dimensions_ == reloc_section_dimensions_.end()) |
Anton Bikineev | 1a96551 | 2021-05-15 22:35:36 +0000 | [diff] [blame] | 101 | return absl::nullopt; |
Etienne Pierre-Doray | a88cad0 | 2018-07-25 20:16:02 +0000 | [diff] [blame] | 102 | cur_entry_size = cur_section_dimensions_->entry_size; |
| 103 | cursor_ = |
| 104 | base::checked_cast<offset_t>(cur_section_dimensions_->region.offset); |
| 105 | if (cursor_ + cur_entry_size > hi_) |
Anton Bikineev | 1a96551 | 2021-05-15 22:35:36 +0000 | [diff] [blame] | 106 | return absl::nullopt; |
Etienne Pierre-Doray | a88cad0 | 2018-07-25 20:16:02 +0000 | [diff] [blame] | 107 | cur_section_dimensions_end = |
| 108 | base::checked_cast<offset_t>(cur_section_dimensions_->region.hi()); |
| 109 | } |
| 110 | rva_t target_rva = kInvalidRva; |
| 111 | // TODO(huangs): Fix RELA sections: Need to process |r_addend|. |
| 112 | switch (bitness_) { |
| 113 | case kBit32: |
| 114 | target_rva = GetRelocationTarget(image_.read<elf::Elf32_Rel>(cursor_)); |
| 115 | break; |
| 116 | case kBit64: |
| 117 | target_rva = GetRelocationTarget(image_.read<elf::Elf64_Rel>(cursor_)); |
| 118 | break; |
| 119 | } |
| 120 | if (target_rva == kInvalidRva) |
| 121 | continue; |
Samuel Huang | 21bbdef | 2020-01-23 15:58:26 +0000 | [diff] [blame] | 122 | // TODO(huangs): Make the check more strict: The reference body should not |
| 123 | // straddle section boundary. |
| 124 | offset_t target = target_rva_to_offset_.Convert(target_rva); |
| 125 | if (target == kInvalidOffset) |
| 126 | continue; |
Etienne Pierre-Doray | a88cad0 | 2018-07-25 20:16:02 +0000 | [diff] [blame] | 127 | // |target| will be used to obtain abs32 references, so we must ensure that |
| 128 | // it lies inside |image_|. |
Samuel Huang | 21bbdef | 2020-01-23 15:58:26 +0000 | [diff] [blame] | 129 | if (!image_.covers({target, WidthOf(bitness_)})) |
Etienne Pierre-Doray | a88cad0 | 2018-07-25 20:16:02 +0000 | [diff] [blame] | 130 | continue; |
| 131 | offset_t location = cursor_; |
| 132 | cursor_ += cur_entry_size; |
| 133 | return Reference{location, target}; |
| 134 | } |
Anton Bikineev | 1a96551 | 2021-05-15 22:35:36 +0000 | [diff] [blame] | 135 | return absl::nullopt; |
Etienne Pierre-Doray | a88cad0 | 2018-07-25 20:16:02 +0000 | [diff] [blame] | 136 | } |
| 137 | |
| 138 | /******** RelocWriterElf ********/ |
| 139 | |
| 140 | RelocWriterElf::RelocWriterElf(MutableBufferView image, |
| 141 | Bitness bitness, |
| 142 | const AddressTranslator& translator) |
| 143 | : image_(image), bitness_(bitness), target_offset_to_rva_(translator) { |
| 144 | DCHECK(bitness_ == kBit32 || bitness_ == kBit64); |
| 145 | } |
| 146 | |
| 147 | RelocWriterElf::~RelocWriterElf() = default; |
| 148 | |
| 149 | void RelocWriterElf::PutNext(Reference ref) { |
| 150 | switch (bitness_) { |
| 151 | case kBit32: |
| 152 | image_.modify<elf::Elf32_Rel>(ref.location).r_offset = |
| 153 | target_offset_to_rva_.Convert(ref.target); |
| 154 | break; |
| 155 | case kBit64: |
| 156 | image_.modify<elf::Elf64_Rel>(ref.location).r_offset = |
| 157 | target_offset_to_rva_.Convert(ref.target); |
| 158 | break; |
| 159 | } |
| 160 | // Leave |reloc.r_info| alone. |
| 161 | } |
| 162 | |
| 163 | } // namespace zucchini |