164 lines
6.0 KiB
C++
164 lines
6.0 KiB
C++
// Copyright 2018 The Chromium Authors. All rights reserved.
|
|
// 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 <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;
|
|
}
|
|
|
|
absl::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 absl::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 absl::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 absl::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_.modify<elf::Elf32_Rel>(ref.location).r_offset =
|
|
target_offset_to_rva_.Convert(ref.target);
|
|
break;
|
|
case kBit64:
|
|
image_.modify<elf::Elf64_Rel>(ref.location).r_offset =
|
|
target_offset_to_rva_.Convert(ref.target);
|
|
break;
|
|
}
|
|
// Leave |reloc.r_info| alone.
|
|
}
|
|
|
|
} // namespace zucchini
|