// 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 #include #include #include #include #include "base/numerics/safe_conversions.h" #include "components/zucchini/address_translator.h" #include "components/zucchini/algorithm.h" #include "components/zucchini/disassembler_elf.h" #include "components/zucchini/image_utils.h" #include "components/zucchini/test_utils.h" #include "components/zucchini/type_elf.h" #include "testing/gtest/include/gtest/gtest.h" namespace zucchini { namespace { template SectionDimensionsElf MakeSectionDimensions(const BufferRegion& region, offset_t entry_size) { using sh_offset_t = decltype(Elf_Shdr::sh_offset); using sh_size_t = decltype(Elf_Shdr::sh_size); using sh_entsize_t = decltype(Elf_Shdr::sh_entsize); return SectionDimensionsElf{Elf_Shdr{ 0, // sh_name 0, // sh_type 0, // sh_flags 0, // sh_addr // sh_offset base::checked_cast(region.offset), // sh_size base::checked_cast(region.size), 0, // sh_link 0, // sh_info 0, // sh_addralign // sh_entsize base::checked_cast(entry_size), }}; } // Helper to manipulate an image with one or more relocation tables. template class FakeImageWithReloc { public: using ElfIntelTraits = ELF_INTEL_TRAITS; struct RelocSpec { offset_t start; std::vector data; }; FakeImageWithReloc(size_t image_size, rva_t base_rva, const std::vector& reloc_specs) : image_data_(image_size, 0xFF), mutable_image_(&image_data_[0], image_data_.size()) { translator_.Initialize({{0, static_cast(image_size), base_rva, static_cast(image_size)}}); // Set up test image with reloc sections. for (const RelocSpec& reloc_spec : reloc_specs) { BufferRegion reloc_region = {reloc_spec.start, reloc_spec.data.size()}; std::copy(reloc_spec.data.begin(), reloc_spec.data.end(), image_data_.begin() + reloc_region.lo()); section_dimensions_.emplace_back( MakeSectionDimensions( reloc_region, ElfIntelTraits::kVAWidth)); reloc_regions_.push_back(reloc_region); } } std::vector ExtractRelocReferences() { const size_t image_size = image_data_.size(); ConstBufferView image = {image_data_.data(), image_size}; // Make RelocReaderElf. auto reader = std::make_unique( image, ElfIntelTraits::kBitness, section_dimensions_, ElfIntelTraits::kRelType, 0, image_size, translator_); // Read all references and check. std::vector refs; for (absl::optional ref = reader->GetNext(); ref.has_value(); ref = reader->GetNext()) { refs.push_back(ref.value()); } return refs; } std::unique_ptr MakeRelocWriter() { return std::move(std::make_unique( mutable_image_, ElfIntelTraits::kBitness, translator_)); } std::vector GetRawRelocData(int reloc_index) { BufferRegion reloc_region = reloc_regions_[reloc_index]; return Sub(image_data_, reloc_region.lo(), reloc_region.hi()); } private: std::vector image_data_; MutableBufferView mutable_image_; std::vector reloc_regions_; std::vector section_dimensions_; AddressTranslator translator_; }; } // namespace TEST(RelocElfTest, ReadWrite32) { // Set up mock image: Size = 0x3000, .reloc at 0x600. RVA is 0x40000 + offset. constexpr size_t kImageSize = 0x3000; constexpr rva_t kBaseRva = 0x40000; constexpr offset_t kRelocStart0 = 0x600; // "C0 10 04 00 08 00 00 00" represents // (r_sym, r_type, r_offset) = (0x000000, 0x08, 0x000410C0). // r_type = 0x08 = R_386_RELATIVE, and so |r_offset| is an RVA 0x000410C0. // Zucchini does not care about |r_sym|. std::vector reloc_data0 = ParseHexString( "C0 10 04 00 08 00 00 00 " // R_386_RELATIVE. "F8 10 04 00 08 AB CD EF " // R_386_RELATIVE. "00 10 04 00 00 AB CD EF " // R_386_NONE. "00 10 04 00 07 AB CD EF"); // R_386_JMP_SLOT. constexpr offset_t kRelocStart1 = 0x620; std::vector reloc_data1 = ParseHexString( "BC 20 04 00 08 00 00 00 " // R_386_RELATIVE. "A0 20 04 00 08 AB CD EF"); // R_386_RELATIVE. FakeImageWithReloc fake_image( kImageSize, kBaseRva, {{kRelocStart0, reloc_data0}, {kRelocStart1, reloc_data1}}); // Only R_386_RELATIVE references are extracted. Targets are translated from // address (e.g., 0x000420BC) to offset (e.g., 0x20BC). std::vector exp_refs{ {0x600, 0x10C0}, {0x608, 0x10F8}, {0x620, 0x20BC}, {0x628, 0x20A0}}; EXPECT_EQ(exp_refs, fake_image.ExtractRelocReferences()); // Write reference, extract bytes and check. std::unique_ptr writer = fake_image.MakeRelocWriter(); writer->PutNext({0x608, 0x1F83}); std::vector exp_reloc_data0 = ParseHexString( "C0 10 04 00 08 00 00 00 " // R_386_RELATIVE. "83 1F 04 00 08 AB CD EF " // R_386_RELATIVE (address modified). "00 10 04 00 00 AB CD EF " // R_386_NONE. "00 10 04 00 07 AB CD EF"); // R_386_JMP_SLOT. EXPECT_EQ(exp_reloc_data0, fake_image.GetRawRelocData(0)); writer->PutNext({0x628, 0x2950}); std::vector exp_reloc_data1 = ParseHexString( "BC 20 04 00 08 00 00 00 " // R_386_RELATIVE. "50 29 04 00 08 AB CD EF"); // R_386_RELATIVE (address modified). EXPECT_EQ(exp_reloc_data1, fake_image.GetRawRelocData(1)); } TEST(RelocElfTest, Limit32) { constexpr size_t kImageSize = 0x3000; constexpr offset_t kBaseRva = 0x40000; constexpr offset_t kRelocStart = 0x600; // All R_386_RELATIVE. std::vector reloc_data = ParseHexString( // Strictly within file. "00 00 04 00 08 00 00 00 " "00 10 04 00 08 00 00 00 " "F0 2F 04 00 08 00 00 00 " "F8 2F 04 00 08 00 00 00 " "FC 2F 04 00 08 00 00 00 " // Straddles end of file. "FD 2F 04 00 08 00 00 00 " "FE 2F 04 00 08 00 00 00 " "FF 2F 04 00 08 00 00 00 " // Beyond end of file. "00 30 04 00 08 00 00 00 " "01 30 04 00 08 00 00 00 " "FC FF FF 7F 08 00 00 00 " "FE FF FF 7F 08 00 00 00 " "00 00 00 80 08 00 00 00 " "FC FF FF FF 08 00 00 00 " "FF FF FF FF 08 00 00 00 " // Another good reference. "34 12 04 00 08 00 00 00"); FakeImageWithReloc fake_image(kImageSize, kBaseRva, {{kRelocStart, reloc_data}}); std::vector exp_refs{{0x600, 0x0000}, {0x608, 0x1000}, {0x610, 0x2FF0}, {0x618, 0x2FF8}, {0x620, 0x2FFC}, {0x678, 0x1234}}; EXPECT_EQ(exp_refs, fake_image.ExtractRelocReferences()); } TEST(RelocElfTest, Limit64) { constexpr size_t kImageSize = 0x3000; constexpr offset_t kBaseRva = 0x40000; constexpr offset_t kRelocStart = 0x600; // All R_X86_64_RELATIVE. std::vector reloc_data = ParseHexString( // Strictly within file. "00 00 04 00 00 00 00 00 08 00 00 00 00 00 00 00 " "00 10 04 00 00 00 00 00 08 00 00 00 00 00 00 00 " "F0 2F 04 00 00 00 00 00 08 00 00 00 00 00 00 00 " "F4 2F 04 00 00 00 00 00 08 00 00 00 00 00 00 00 " "F8 2F 04 00 00 00 00 00 08 00 00 00 00 00 00 00 " // Straddles end of file. "F9 2F 04 00 00 00 00 00 08 00 00 00 00 00 00 00 " "FC 2F 04 00 00 00 00 00 08 00 00 00 00 00 00 00 " "FF 2F 04 00 00 00 00 00 08 00 00 00 00 00 00 00 " // Beyond end of file. "00 30 04 00 00 00 00 00 08 00 00 00 00 00 00 00 " "01 30 04 00 00 00 00 00 08 00 00 00 00 00 00 00 " "FC FF FF 7F 00 00 00 00 08 00 00 00 00 00 00 00 " "FE FF FF 7F 00 00 00 00 08 00 00 00 00 00 00 00 " "00 00 00 80 00 00 00 00 08 00 00 00 00 00 00 00 " "FC FF FF FF 00 00 00 00 08 00 00 00 00 00 00 00 " "FF FF FF FF 00 00 00 00 08 00 00 00 00 00 00 00 " "00 00 04 00 01 00 00 00 08 00 00 00 00 00 00 00 " "FF FF FF FF FF FF FF FF 08 00 00 00 00 00 00 00 " "F8 FF FF FF FF FF FF FF 08 00 00 00 00 00 00 00 " // Another good reference. "34 12 04 00 00 00 00 00 08 00 00 00 00 00 00 00"); FakeImageWithReloc fake_image(kImageSize, kBaseRva, {{kRelocStart, reloc_data}}); std::vector exp_refs{{0x600, 0x0000}, {0x610, 0x1000}, {0x620, 0x2FF0}, {0x630, 0x2FF4}, {0x640, 0x2FF8}, {0x720, 0x1234}}; EXPECT_EQ(exp_refs, fake_image.ExtractRelocReferences()); } } // namespace zucchini