xref: /aosp_15_r20/external/zucchini/reloc_elf.cc (revision a03ca8b91e029cd15055c20c78c2e087c84792e4)
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>
8 
9 #include "base/logging.h"
10 #include "components/zucchini/algorithm.h"
11 
12 namespace zucchini {
13 
14 /******** RelocReaderElf ********/
15 
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 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 
GetRelocationTarget(elf::Elf32_Rel rel) const69 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 
GetRelocationTarget(elf::Elf64_Rel rel) const78 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 
GetNext()92 std::optional<Reference> RelocReaderElf::GetNext() {
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())
101         return std::nullopt;
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_)
106         return std::nullopt;
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;
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;
127     // |target| will be used to obtain abs32 references, so we must ensure that
128     // it lies inside |image_|.
129     if (!image_.covers({target, WidthOf(bitness_)}))
130       continue;
131     offset_t location = cursor_;
132     cursor_ += cur_entry_size;
133     return Reference{location, target};
134   }
135   return std::nullopt;
136 }
137 
138 /******** RelocWriterElf ********/
139 
RelocWriterElf(MutableBufferView image,Bitness bitness,const AddressTranslator & translator)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 
PutNext(Reference ref)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
164