aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--abs32_utils.cc32
-rw-r--r--abs32_utils.h9
-rw-r--r--abs32_utils_unittest.cc49
-rw-r--r--address_translator.cc2
-rw-r--r--address_translator.h3
-rw-r--r--address_translator_unittest.cc30
-rw-r--r--disassembler_elf.cc51
-rw-r--r--disassembler_win32.cc20
-rw-r--r--reloc_win32.cc2
9 files changed, 163 insertions, 35 deletions
diff --git a/abs32_utils.cc b/abs32_utils.cc
index bf6526b..91bdb61 100644
--- a/abs32_utils.cc
+++ b/abs32_utils.cc
@@ -94,11 +94,11 @@ Abs32RvaExtractorWin32::Abs32RvaExtractorWin32(
offset_t hi)
: image_(image), addr_(std::move(addr)) {
CHECK_LE(lo, hi);
- auto find_and_check = [&addr](const std::vector<offset_t>& locations,
- offset_t offset) {
+ auto find_and_check = [this](const std::vector<offset_t>& locations,
+ offset_t offset) {
auto it = std::lower_bound(locations.begin(), locations.end(), offset);
- // Ensure |offset| does not straddle a reference body.
- CHECK(it == locations.begin() || offset - *(it - 1) >= addr.width());
+ // Ensure that |offset| does not straddle a reference body.
+ CHECK(it == locations.begin() || offset - *(it - 1) >= addr_.width());
return it;
};
cur_abs32_ = find_and_check(abs32_locations, lo);
@@ -136,11 +136,8 @@ base::Optional<Reference> Abs32ReaderWin32::GetNext() {
for (auto unit = abs32_rva_extractor_.GetNext(); unit.has_value();
unit = abs32_rva_extractor_.GetNext()) {
offset_t location = unit->location;
- // |target| will not be dereferenced, so we don't worry about it
- // exceeding |image_.size()| (in fact, there are valid cases where it
- // does).
offset_t unsafe_target = target_rva_to_offset_.Convert(unit->target_rva);
- if (unsafe_target < kOffsetBound)
+ if (unsafe_target != kInvalidOffset)
return Reference{location, unsafe_target};
}
return base::nullopt;
@@ -167,6 +164,25 @@ void Abs32WriterWin32::PutNext(Reference ref) {
/******** Exported Functions ********/
+size_t RemoveUntranslatableAbs32(ConstBufferView image,
+ AbsoluteAddress&& addr,
+ const AddressTranslator& translator,
+ std::vector<offset_t>* locations) {
+ AddressTranslator::RvaToOffsetCache target_rva_checker(translator);
+ Abs32RvaExtractorWin32 extractor(image, std::move(addr), *locations, 0,
+ image.size());
+ Abs32ReaderWin32 reader(std::move(extractor), translator);
+ std::vector<offset_t>::iterator write_it = locations->begin();
+ // |reader| reads |locations| while |write_it| modifies it. However, there's
+ // no conflict since read occurs before write, and can skip ahead.
+ for (auto ref = reader.GetNext(); ref.has_value(); ref = reader.GetNext())
+ *(write_it++) = ref->location;
+ DCHECK(write_it <= locations->end());
+ size_t num_removed = locations->end() - write_it;
+ locations->erase(write_it, locations->end());
+ return num_removed;
+}
+
size_t RemoveOverlappingAbs32Locations(uint32_t width,
std::vector<offset_t>* locations) {
if (locations->size() <= 1)
diff --git a/abs32_utils.h b/abs32_utils.h
index a97aa38..5bc9cf2 100644
--- a/abs32_utils.h
+++ b/abs32_utils.h
@@ -45,7 +45,7 @@ class AbsoluteAddress {
// the write and returns true. On failure (invalid |offset|), returns false.
bool Write(offset_t offset, MutableBufferView* image);
- size_t width() const { return WidthOf(bitness_); }
+ uint32_t width() const { return WidthOf(bitness_); }
// Exposing |value_| for testing.
uint64_t* mutable_value() { return &value_; }
@@ -126,6 +126,13 @@ class Abs32WriterWin32 : public ReferenceWriter {
DISALLOW_COPY_AND_ASSIGN(Abs32WriterWin32);
};
+// Given a list of abs32 |locations|, removes all elements whose targets cannot
+// be translated. Returns the number of elements removed.
+size_t RemoveUntranslatableAbs32(ConstBufferView image,
+ AbsoluteAddress&& addr,
+ const AddressTranslator& translator,
+ std::vector<offset_t>* locations);
+
// Given a sorted list of abs32 |locations|, removes all elements whose body
// (with |width| given) overlaps with the body of a previous element.
size_t RemoveOverlappingAbs32Locations(uint32_t width,
diff --git a/abs32_utils_unittest.cc b/abs32_utils_unittest.cc
index a8f6917..77a2599 100644
--- a/abs32_utils_unittest.cc
+++ b/abs32_utils_unittest.cc
@@ -11,6 +11,7 @@
#include <utility>
#include "base/numerics/safe_conversions.h"
+#include "components/zucchini/address_translator.h"
#include "components/zucchini/image_utils.h"
#include "components/zucchini/test_utils.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -52,7 +53,7 @@ TEST(Abs32UtilsTest, AbsoluteAddress32) {
EXPECT_TRUE(addr32.Read(0x8U, image32));
EXPECT_EQ(kInvalidRva, addr32.ToRva()); // Underflow.
EXPECT_TRUE(addr32.Read(0xCU, image32));
- EXPECT_EQ(kInvalidRva, addr32.ToRva()); // Translated RVA would be to large.
+ EXPECT_EQ(kInvalidRva, addr32.ToRva()); // Translated RVA would be too large.
EXPECT_TRUE(addr32.Read(0x10U, image32));
EXPECT_EQ(kInvalidRva, addr32.ToRva()); // Underflow (boundary case).
@@ -432,6 +433,52 @@ TEST(Abs32UtilsTest, Win32Write64) {
EXPECT_EQ(expected_data64, data64);
}
+TEST(Abs32UtilsTest, RemoveUntranslatableAbs32) {
+ Bitness kBitness = kBit32;
+ uint64_t kImageBase = 0x2BCD0000;
+
+ // Valid RVAs: [0x00001A00, 0x00001A28) and [0x00003A00, 0x00004000).
+ // Valid AVAs: [0x2BCD1A00, 0x2BCD1A28) and [0x2BCD3A00, 0x2BCD4000).
+ // Notice that the second section has has dangling RVA.
+ AddressTranslator translator;
+ ASSERT_EQ(AddressTranslator::kSuccess,
+ translator.Initialize(
+ {{0x04, +0x28, 0x1A00, +0x28}, {0x30, +0x30, 0x3A00, +0x600}}));
+
+ std::vector<uint8_t> data = ParseHexString(
+ "FF FF FF FF 0B 3A CD 2B 00 00 00 04 3A CD 2B 00 "
+ "FC 3F CD 2B 14 1A CD 2B 44 00 00 00 CC 00 00 00 "
+ "00 00 55 00 00 00 1E 1A CD 2B 00 99 FF FF FF FF "
+ "10 3A CD 2B 22 00 00 00 00 00 00 11 00 00 00 00 "
+ "66 00 00 00 28 1A CD 2B 00 00 CD 2B 27 1A CD 2B "
+ "FF 39 CD 2B 00 00 00 00 18 1A CD 2B 00 00 00 00 "
+ "FF FF FF FF FF FF FF FF");
+ MutableBufferView image(data.data(), data.size());
+
+ const offset_t kAbs1 = 0x04; // a:2BCD3A0B = r:3A0B = o:3B
+ const offset_t kAbs2 = 0x0B; // a:2BCD3A04 = r:3A04 = o:34
+ const offset_t kAbs3 = 0x10; // a:2BCD3FFF = r:3FFF (dangling)
+ const offset_t kAbs4 = 0x14; // a:2BCD1A14 = r:1A14 = o:18
+ const offset_t kAbs5 = 0x26; // a:2BCD1A1E = r:1A1E = o:22
+ const offset_t kAbs6 = 0x30; // a:2BCD3A10 = r:3A10 = 0x40
+ const offset_t kAbs7 = 0x44; // a:2BCD1A28 = r:1A28 (bad: sentinel)
+ const offset_t kAbs8 = 0x48; // a:2BCD0000 = r:0000 (bad: not covered)
+ const offset_t kAbs9 = 0x4C; // a:2BCD1A27 = r:1A27 = 0x2B
+ const offset_t kAbsA = 0x50; // a:2BCD39FF (bad: not covered)
+ const offset_t kAbsB = 0x54; // a:00000000 (bad: underflow)
+ const offset_t kAbsC = 0x58; // a:2BCD1A18 = r:1A18 = 0x1C
+
+ std::vector<offset_t> locations = {kAbs1, kAbs2, kAbs3, kAbs4, kAbs5, kAbs6,
+ kAbs7, kAbs8, kAbs9, kAbsA, kAbsB, kAbsC};
+ std::vector<offset_t> exp_locations = {kAbs1, kAbs2, kAbs3, kAbs4,
+ kAbs5, kAbs6, kAbs9, kAbsC};
+ size_t exp_num_removed = locations.size() - exp_locations.size();
+ size_t num_removed = RemoveUntranslatableAbs32(image, {kBitness, kImageBase},
+ translator, &locations);
+ EXPECT_EQ(exp_num_removed, num_removed);
+ EXPECT_EQ(exp_locations, locations);
+}
+
TEST(Abs32UtilsTest, RemoveOverlappingAbs32Locations) {
// Make |width| a state to reduce repetition.
uint32_t width = WidthOf(kBit32);
diff --git a/address_translator.cc b/address_translator.cc
index 5b388da..20f19b2 100644
--- a/address_translator.cc
+++ b/address_translator.cc
@@ -38,6 +38,8 @@ AddressTranslator::RvaToOffsetCache::RvaToOffsetCache(
: translator_(translator) {}
bool AddressTranslator::RvaToOffsetCache::IsValid(rva_t rva) const {
+ if (rva == kInvalidRva)
+ return false;
if (!cached_unit_ || !cached_unit_->CoversRva(rva)) {
const AddressTranslator::Unit* unit = translator_.RvaToUnit(rva);
if (!unit)
diff --git a/address_translator.h b/address_translator.h
index 821b9ad..5666b99 100644
--- a/address_translator.h
+++ b/address_translator.h
@@ -59,7 +59,7 @@ namespace zucchini {
using rva_t = uint32_t;
// Divide by 2 to match |kOffsetBound|.
constexpr rva_t kRvaBound = static_cast<rva_t>(-1) / 2;
-constexpr rva_t kInvalidRva = static_cast<rva_t>(-1);
+constexpr rva_t kInvalidRva = static_cast<rva_t>(-2);
// A utility to translate between offsets and RVAs in an image.
class AddressTranslator {
@@ -133,6 +133,7 @@ class AddressTranslator {
explicit RvaToOffsetCache(const AddressTranslator& translator);
bool IsValid(rva_t rva) const;
+
offset_t Convert(rva_t rva) const;
private:
diff --git a/address_translator_unittest.cc b/address_translator_unittest.cc
index 0aeff77..efa2f14 100644
--- a/address_translator_unittest.cc
+++ b/address_translator_unittest.cc
@@ -553,4 +553,34 @@ TEST(AddressTranslatorTest, Merge) {
} while (std::next_permutation(test_case2.begin(), test_case2.end()));
}
+TEST(AddressTranslatorTest, RvaToOffsetCache_IsValid) {
+ AddressTranslator translator;
+ // Notice that the second section has dangling RVA.
+ ASSERT_EQ(AddressTranslator::kSuccess,
+ translator.Initialize(
+ {{0x04, +0x28, 0x1A00, +0x28}, {0x30, +0x10, 0x3A00, +0x30}}));
+ AddressTranslator::RvaToOffsetCache rva_checker(translator);
+
+ EXPECT_FALSE(rva_checker.IsValid(kInvalidRva));
+
+ for (int i = 0; i < 0x28; ++i)
+ EXPECT_TRUE(rva_checker.IsValid(0x1A00 + i));
+ EXPECT_FALSE(rva_checker.IsValid(0x1A00 + 0x28));
+ EXPECT_FALSE(rva_checker.IsValid(0x1A00 + 0x29));
+ EXPECT_FALSE(rva_checker.IsValid(0x1A00 - 1));
+ EXPECT_FALSE(rva_checker.IsValid(0x1A00 - 2));
+
+ for (int i = 0; i < 0x30; ++i)
+ EXPECT_TRUE(rva_checker.IsValid(0x3A00 + i));
+ EXPECT_FALSE(rva_checker.IsValid(0x3A00 + 0x30));
+ EXPECT_FALSE(rva_checker.IsValid(0x3A00 + 0x31));
+ EXPECT_FALSE(rva_checker.IsValid(0x3A00 - 1));
+ EXPECT_FALSE(rva_checker.IsValid(0x3A00 - 2));
+
+ EXPECT_FALSE(rva_checker.IsValid(0));
+ EXPECT_FALSE(rva_checker.IsValid(0x10));
+ EXPECT_FALSE(rva_checker.IsValid(0x7FFFFFFFU));
+ EXPECT_FALSE(rva_checker.IsValid(0xFFFFFFFFU));
+}
+
} // namespace zucchini
diff --git a/disassembler_elf.cc b/disassembler_elf.cc
index 0f6d3a0..be01c4c 100644
--- a/disassembler_elf.cc
+++ b/disassembler_elf.cc
@@ -19,6 +19,8 @@ namespace zucchini {
namespace {
+constexpr uint64_t kElfImageBase = 0;
+
// Determines whether |section| is a reloc section.
template <class Traits>
bool IsRelocSection(const typename Traits::Elf_Shdr& section) {
@@ -288,23 +290,33 @@ template <class Traits>
void DisassemblerElf<Traits>::GetAbs32FromRelocSections() {
constexpr int kAbs32Width = Traits::kVAWidth;
DCHECK(abs32_locations_.empty());
- auto relocs = MakeReadRelocs(0, offset_t(size()));
- for (auto ref = relocs->GetNext(); ref; ref = relocs->GetNext()) {
- // Reject null targets and targets outside |image_|. Note that here we
- // assume abs32 targets are never "fake offsets".
- if (ref->target > 0 && image_.covers({ref->target, kAbs32Width}))
- abs32_locations_.push_back(ref->target);
- }
- abs32_locations_.shrink_to_fit();
+
+ // Read reloc targets as preliminary abs32 locations.
+ std::unique_ptr<ReferenceReader> relocs = MakeReadRelocs(0, offset_t(size()));
+ for (auto ref = relocs->GetNext(); ref.has_value(); ref = relocs->GetNext())
+ abs32_locations_.push_back(ref->target);
+
std::sort(abs32_locations_.begin(), abs32_locations_.end());
+ // Abs32 references must have targets translatable to offsets. Remove those
+ // that are unable to do so.
+ // TODO(huangs): Investigate whether passing |Traits::kBitness| is correct:
+ // Some architectures using ELF might have 4-byte long abs32 body regardless
+ // of bitness.
+ size_t num_untranslatable =
+ RemoveUntranslatableAbs32(image_, {Traits::kBitness, kElfImageBase},
+ translator_, &abs32_locations_);
+ LOG_IF(WARNING, num_untranslatable) << "Removed " << num_untranslatable
+ << " untranslatable abs32 references.";
+
// Abs32 reference bodies must not overlap. If found, simply remove them.
- size_t num_removed =
+ size_t num_overlapping =
RemoveOverlappingAbs32Locations(kAbs32Width, &abs32_locations_);
- if (num_removed) {
- LOG(WARNING) << "Warning: Found and removed " << num_removed
- << " abs32 locations with overlapping bodies.";
- }
+ LOG_IF(WARNING, num_overlapping)
+ << "Removed " << num_overlapping
+ << " abs32 references with overlapping bodies.";
+
+ abs32_locations_.shrink_to_fit();
}
template <class Traits>
@@ -392,9 +404,12 @@ template <class Traits>
std::unique_ptr<ReferenceReader> DisassemblerElfIntel<Traits>::MakeReadAbs32(
offset_t lo,
offset_t hi) {
- Abs32RvaExtractorWin32 abs_rva_extractor(this->image_,
- AbsoluteAddress(Traits::kBitness, 0),
- this->abs32_locations_, lo, hi);
+ // TODO(huangs): Don't use Abs32RvaExtractorWin32 here; use new class that
+ // caters to different ELF architectures (e.g., abs32 in AArch64 are 4 bytes
+ // long, not 8 bytes long).
+ Abs32RvaExtractorWin32 abs_rva_extractor(
+ this->image_, AbsoluteAddress(Traits::kBitness, kElfImageBase),
+ this->abs32_locations_, lo, hi);
return std::make_unique<Abs32ReaderWin32>(std::move(abs_rva_extractor),
this->translator_);
}
@@ -402,8 +417,10 @@ std::unique_ptr<ReferenceReader> DisassemblerElfIntel<Traits>::MakeReadAbs32(
template <class Traits>
std::unique_ptr<ReferenceWriter> DisassemblerElfIntel<Traits>::MakeWriteAbs32(
MutableBufferView image) {
+ // TODO(huangs): For AArch64, see if |Traits::kBitness| should be used here?
return std::make_unique<Abs32WriterWin32>(
- image, AbsoluteAddress(Traits::kBitness, 0), this->translator_);
+ image, AbsoluteAddress(Traits::kBitness, kElfImageBase),
+ this->translator_);
}
template <class Traits>
diff --git a/disassembler_win32.cc b/disassembler_win32.cc
index b3f568b..9067ad7 100644
--- a/disassembler_win32.cc
+++ b/disassembler_win32.cc
@@ -316,20 +316,28 @@ bool DisassemblerWin32<Traits>::ParseAndStoreAbs32() {
return true;
has_parsed_abs32_ = true;
- ParseAndStoreRelocBlocks();
-
+ // Read reloc targets as preliminary abs32 locations.
std::unique_ptr<ReferenceReader> relocs = MakeReadRelocs(0, offset_t(size()));
for (auto ref = relocs->GetNext(); ref.has_value(); ref = relocs->GetNext())
abs32_locations_.push_back(ref->target);
- abs32_locations_.shrink_to_fit();
std::sort(abs32_locations_.begin(), abs32_locations_.end());
+ // Abs32 references must have targets translatable to offsets. Remove those
+ // that are unable to do so.
+ size_t num_untranslatable = RemoveUntranslatableAbs32(
+ image_, {Traits::kBitness, image_base_}, translator_, &abs32_locations_);
+ LOG_IF(WARNING, num_untranslatable) << "Removed " << num_untranslatable
+ << " untranslatable abs32 references.";
+
// Abs32 reference bodies must not overlap. If found, simply remove them.
- size_t num_removed =
+ size_t num_overlapping =
RemoveOverlappingAbs32Locations(Traits::kVAWidth, &abs32_locations_);
- LOG_IF(WARNING, num_removed) << "Found and removed " << num_removed
- << " abs32 locations with overlapping bodies.";
+ LOG_IF(WARNING, num_overlapping)
+ << "Removed " << num_overlapping
+ << " abs32 references with overlapping bodies.";
+
+ abs32_locations_.shrink_to_fit();
return true;
}
diff --git a/reloc_win32.cc b/reloc_win32.cc
index dcf40bb..6da48f2 100644
--- a/reloc_win32.cc
+++ b/reloc_win32.cc
@@ -152,7 +152,7 @@ base::Optional<Reference> RelocReaderWin32::GetNext() {
offset_t target = entry_rva_to_offset_.Convert(unit->target_rva);
if (target == kInvalidOffset)
continue;
- // Ensures the target (abs32 reference) lies entirely within the image.
+ // Ensure that |target| (abs32 reference) lies entirely within the image.
if (target >= offset_bound_)
continue;
offset_t location = unit->location;