1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167
|
// Copyright 2018 The Chromium Authors
// 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 <stddef.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;
}
std::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 std::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 std::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 std::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_.write<decltype(elf::Elf32_Rel::r_offset)>(
ref.location + offsetof(elf::Elf32_Rel, r_offset),
target_offset_to_rva_.Convert(ref.target));
break;
case kBit64:
image_.write<decltype(elf::Elf64_Rel::r_offset)>(
ref.location + offsetof(elf::Elf64_Rel, r_offset),
target_offset_to_rva_.Convert(ref.target));
break;
}
// Leave |reloc.r_info| alone.
}
} // namespace zucchini
|