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