blob: a7d1b38207ae37da8601c32e0b91bba5d32d01e9 [file] [log] [blame]
Etienne Pierre-Doraya88cad02018-07-25 20:16:02 +00001// 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-Doraya88cad02018-07-25 20:16:02 +00008
9#include "base/logging.h"
Etienne Pierre-Doraya88cad02018-07-25 20:16:02 +000010#include "components/zucchini/algorithm.h"
11
12namespace zucchini {
13
14/******** RelocReaderElf ********/
15
16RelocReaderElf::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
67RelocReaderElf::~RelocReaderElf() = default;
68
69rva_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
78rva_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 Bikineev1a965512021-05-15 22:35:36 +000092absl::optional<Reference> RelocReaderElf::GetNext() {
Etienne Pierre-Doraya88cad02018-07-25 20:16:02 +000093 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 Bikineev1a965512021-05-15 22:35:36 +0000101 return absl::nullopt;
Etienne Pierre-Doraya88cad02018-07-25 20:16:02 +0000102 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 Bikineev1a965512021-05-15 22:35:36 +0000106 return absl::nullopt;
Etienne Pierre-Doraya88cad02018-07-25 20:16:02 +0000107 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 Huang21bbdef2020-01-23 15:58:26 +0000122 // 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-Doraya88cad02018-07-25 20:16:02 +0000127 // |target| will be used to obtain abs32 references, so we must ensure that
128 // it lies inside |image_|.
Samuel Huang21bbdef2020-01-23 15:58:26 +0000129 if (!image_.covers({target, WidthOf(bitness_)}))
Etienne Pierre-Doraya88cad02018-07-25 20:16:02 +0000130 continue;
131 offset_t location = cursor_;
132 cursor_ += cur_entry_size;
133 return Reference{location, target};
134 }
Anton Bikineev1a965512021-05-15 22:35:36 +0000135 return absl::nullopt;
Etienne Pierre-Doraya88cad02018-07-25 20:16:02 +0000136}
137
138/******** RelocWriterElf ********/
139
140RelocWriterElf::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
147RelocWriterElf::~RelocWriterElf() = default;
148
149void 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