Etienne Pierre-Doray | a88cad0 | 2018-07-25 20:16:02 +0000 | [diff] [blame] | 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 <stdint.h> |
| 8 | |
| 9 | #include <algorithm> |
| 10 | #include <memory> |
Samuel Huang | 21bbdef | 2020-01-23 15:58:26 +0000 | [diff] [blame] | 11 | #include <utility> |
Etienne Pierre-Doray | a88cad0 | 2018-07-25 20:16:02 +0000 | [diff] [blame] | 12 | #include <vector> |
| 13 | |
| 14 | #include "base/numerics/safe_conversions.h" |
| 15 | #include "components/zucchini/address_translator.h" |
| 16 | #include "components/zucchini/algorithm.h" |
Samuel Huang | 21bbdef | 2020-01-23 15:58:26 +0000 | [diff] [blame] | 17 | #include "components/zucchini/disassembler_elf.h" |
Etienne Pierre-Doray | a88cad0 | 2018-07-25 20:16:02 +0000 | [diff] [blame] | 18 | #include "components/zucchini/image_utils.h" |
| 19 | #include "components/zucchini/test_utils.h" |
| 20 | #include "components/zucchini/type_elf.h" |
| 21 | #include "testing/gtest/include/gtest/gtest.h" |
| 22 | |
| 23 | namespace zucchini { |
| 24 | |
| 25 | namespace { |
| 26 | |
| 27 | template <class Elf_Shdr> |
| 28 | SectionDimensionsElf MakeSectionDimensions(const BufferRegion& region, |
| 29 | offset_t entry_size) { |
| 30 | using sh_offset_t = decltype(Elf_Shdr::sh_offset); |
| 31 | using sh_size_t = decltype(Elf_Shdr::sh_size); |
| 32 | using sh_entsize_t = decltype(Elf_Shdr::sh_entsize); |
| 33 | return SectionDimensionsElf{Elf_Shdr{ |
| 34 | 0, // sh_name |
| 35 | 0, // sh_type |
| 36 | 0, // sh_flags |
| 37 | 0, // sh_addr |
| 38 | // sh_offset |
| 39 | base::checked_cast<sh_offset_t>(region.offset), |
| 40 | // sh_size |
| 41 | base::checked_cast<sh_size_t>(region.size), |
| 42 | 0, // sh_link |
| 43 | 0, // sh_info |
| 44 | 0, // sh_addralign |
| 45 | // sh_entsize |
| 46 | base::checked_cast<sh_entsize_t>(entry_size), |
| 47 | }}; |
| 48 | } |
| 49 | |
Samuel Huang | 21bbdef | 2020-01-23 15:58:26 +0000 | [diff] [blame] | 50 | // Helper to manipulate an image with one or more relocation tables. |
Samuel Huang | 3e1f64d | 2021-08-04 00:58:50 +0000 | [diff] [blame] | 51 | template <class ELF_INTEL_TRAITS> |
Samuel Huang | 21bbdef | 2020-01-23 15:58:26 +0000 | [diff] [blame] | 52 | class FakeImageWithReloc { |
| 53 | public: |
Samuel Huang | 3e1f64d | 2021-08-04 00:58:50 +0000 | [diff] [blame] | 54 | using ElfIntelTraits = ELF_INTEL_TRAITS; |
Samuel Huang | 21bbdef | 2020-01-23 15:58:26 +0000 | [diff] [blame] | 55 | struct RelocSpec { |
| 56 | offset_t start; |
| 57 | std::vector<uint8_t> data; |
| 58 | }; |
| 59 | |
| 60 | FakeImageWithReloc(size_t image_size, |
| 61 | rva_t base_rva, |
| 62 | const std::vector<RelocSpec>& reloc_specs) |
| 63 | : image_data_(image_size, 0xFF), |
| 64 | mutable_image_(&image_data_[0], image_data_.size()) { |
Peter Kasting | 3865f0b | 2021-06-09 19:27:03 +0000 | [diff] [blame] | 65 | translator_.Initialize({{0, static_cast<offset_t>(image_size), base_rva, |
| 66 | static_cast<rva_t>(image_size)}}); |
Samuel Huang | 21bbdef | 2020-01-23 15:58:26 +0000 | [diff] [blame] | 67 | // Set up test image with reloc sections. |
| 68 | for (const RelocSpec& reloc_spec : reloc_specs) { |
| 69 | BufferRegion reloc_region = {reloc_spec.start, reloc_spec.data.size()}; |
| 70 | std::copy(reloc_spec.data.begin(), reloc_spec.data.end(), |
| 71 | image_data_.begin() + reloc_region.lo()); |
| 72 | section_dimensions_.emplace_back( |
| 73 | MakeSectionDimensions<typename ElfIntelTraits::Elf_Shdr>( |
| 74 | reloc_region, ElfIntelTraits::kVAWidth)); |
| 75 | reloc_regions_.push_back(reloc_region); |
| 76 | } |
| 77 | } |
| 78 | |
| 79 | std::vector<Reference> ExtractRelocReferences() { |
| 80 | const size_t image_size = image_data_.size(); |
| 81 | ConstBufferView image = {image_data_.data(), image_size}; |
| 82 | |
| 83 | // Make RelocReaderElf. |
| 84 | auto reader = std::make_unique<RelocReaderElf>( |
| 85 | image, ElfIntelTraits::kBitness, section_dimensions_, |
| 86 | ElfIntelTraits::kRelType, 0, image_size, translator_); |
| 87 | |
| 88 | // Read all references and check. |
| 89 | std::vector<Reference> refs; |
Anton Bikineev | 1a96551 | 2021-05-15 22:35:36 +0000 | [diff] [blame] | 90 | for (absl::optional<Reference> ref = reader->GetNext(); ref.has_value(); |
Samuel Huang | 21bbdef | 2020-01-23 15:58:26 +0000 | [diff] [blame] | 91 | ref = reader->GetNext()) { |
| 92 | refs.push_back(ref.value()); |
| 93 | } |
| 94 | return refs; |
| 95 | } |
| 96 | |
| 97 | std::unique_ptr<RelocWriterElf> MakeRelocWriter() { |
| 98 | return std::move(std::make_unique<RelocWriterElf>( |
| 99 | mutable_image_, ElfIntelTraits::kBitness, translator_)); |
| 100 | } |
| 101 | |
| 102 | std::vector<uint8_t> GetRawRelocData(int reloc_index) { |
| 103 | BufferRegion reloc_region = reloc_regions_[reloc_index]; |
| 104 | return Sub(image_data_, reloc_region.lo(), reloc_region.hi()); |
| 105 | } |
| 106 | |
| 107 | private: |
| 108 | std::vector<uint8_t> image_data_; |
| 109 | MutableBufferView mutable_image_; |
| 110 | std::vector<BufferRegion> reloc_regions_; |
| 111 | std::vector<SectionDimensionsElf> section_dimensions_; |
| 112 | AddressTranslator translator_; |
| 113 | }; |
| 114 | |
Etienne Pierre-Doray | a88cad0 | 2018-07-25 20:16:02 +0000 | [diff] [blame] | 115 | } // namespace |
| 116 | |
| 117 | TEST(RelocElfTest, ReadWrite32) { |
| 118 | // Set up mock image: Size = 0x3000, .reloc at 0x600. RVA is 0x40000 + offset. |
Samuel Huang | 21bbdef | 2020-01-23 15:58:26 +0000 | [diff] [blame] | 119 | constexpr size_t kImageSize = 0x3000; |
Etienne Pierre-Doray | a88cad0 | 2018-07-25 20:16:02 +0000 | [diff] [blame] | 120 | constexpr rva_t kBaseRva = 0x40000; |
Samuel Huang | 21bbdef | 2020-01-23 15:58:26 +0000 | [diff] [blame] | 121 | |
| 122 | constexpr offset_t kRelocStart0 = 0x600; |
Etienne Pierre-Doray | a88cad0 | 2018-07-25 20:16:02 +0000 | [diff] [blame] | 123 | // "C0 10 04 00 08 00 00 00" represents |
| 124 | // (r_sym, r_type, r_offset) = (0x000000, 0x08, 0x000410C0). |
Samuel Huang | 21bbdef | 2020-01-23 15:58:26 +0000 | [diff] [blame] | 125 | // r_type = 0x08 = R_386_RELATIVE, and so |r_offset| is an RVA 0x000410C0. |
| 126 | // Zucchini does not care about |r_sym|. |
| 127 | std::vector<uint8_t> reloc_data0 = ParseHexString( |
Samuel Huang | 2dedd30 | 2019-01-08 18:37:09 +0000 | [diff] [blame] | 128 | "C0 10 04 00 08 00 00 00 " // R_386_RELATIVE. |
| 129 | "F8 10 04 00 08 AB CD EF " // R_386_RELATIVE. |
| 130 | "00 10 04 00 00 AB CD EF " // R_386_NONE. |
| 131 | "00 10 04 00 07 AB CD EF"); // R_386_JMP_SLOT. |
Etienne Pierre-Doray | a88cad0 | 2018-07-25 20:16:02 +0000 | [diff] [blame] | 132 | |
Samuel Huang | 21bbdef | 2020-01-23 15:58:26 +0000 | [diff] [blame] | 133 | constexpr offset_t kRelocStart1 = 0x620; |
| 134 | std::vector<uint8_t> reloc_data1 = ParseHexString( |
Samuel Huang | 2dedd30 | 2019-01-08 18:37:09 +0000 | [diff] [blame] | 135 | "BC 20 04 00 08 00 00 00 " // R_386_RELATIVE. |
| 136 | "A0 20 04 00 08 AB CD EF"); // R_386_RELATIVE. |
Etienne Pierre-Doray | a88cad0 | 2018-07-25 20:16:02 +0000 | [diff] [blame] | 137 | |
Samuel Huang | 21bbdef | 2020-01-23 15:58:26 +0000 | [diff] [blame] | 138 | FakeImageWithReloc<Elf32IntelTraits> fake_image( |
| 139 | kImageSize, kBaseRva, |
| 140 | {{kRelocStart0, reloc_data0}, {kRelocStart1, reloc_data1}}); |
Etienne Pierre-Doray | a88cad0 | 2018-07-25 20:16:02 +0000 | [diff] [blame] | 141 | |
Etienne Pierre-Doray | a88cad0 | 2018-07-25 20:16:02 +0000 | [diff] [blame] | 142 | // Only R_386_RELATIVE references are extracted. Targets are translated from |
| 143 | // address (e.g., 0x000420BC) to offset (e.g., 0x20BC). |
| 144 | std::vector<Reference> exp_refs{ |
| 145 | {0x600, 0x10C0}, {0x608, 0x10F8}, {0x620, 0x20BC}, {0x628, 0x20A0}}; |
Samuel Huang | 21bbdef | 2020-01-23 15:58:26 +0000 | [diff] [blame] | 146 | EXPECT_EQ(exp_refs, fake_image.ExtractRelocReferences()); |
Etienne Pierre-Doray | a88cad0 | 2018-07-25 20:16:02 +0000 | [diff] [blame] | 147 | |
| 148 | // Write reference, extract bytes and check. |
Samuel Huang | 21bbdef | 2020-01-23 15:58:26 +0000 | [diff] [blame] | 149 | std::unique_ptr<RelocWriterElf> writer = fake_image.MakeRelocWriter(); |
Etienne Pierre-Doray | a88cad0 | 2018-07-25 20:16:02 +0000 | [diff] [blame] | 150 | |
| 151 | writer->PutNext({0x608, 0x1F83}); |
Samuel Huang | 21bbdef | 2020-01-23 15:58:26 +0000 | [diff] [blame] | 152 | std::vector<uint8_t> exp_reloc_data0 = ParseHexString( |
Samuel Huang | 2dedd30 | 2019-01-08 18:37:09 +0000 | [diff] [blame] | 153 | "C0 10 04 00 08 00 00 00 " // R_386_RELATIVE. |
| 154 | "83 1F 04 00 08 AB CD EF " // R_386_RELATIVE (address modified). |
| 155 | "00 10 04 00 00 AB CD EF " // R_386_NONE. |
| 156 | "00 10 04 00 07 AB CD EF"); // R_386_JMP_SLOT. |
Samuel Huang | 21bbdef | 2020-01-23 15:58:26 +0000 | [diff] [blame] | 157 | EXPECT_EQ(exp_reloc_data0, fake_image.GetRawRelocData(0)); |
Etienne Pierre-Doray | a88cad0 | 2018-07-25 20:16:02 +0000 | [diff] [blame] | 158 | |
| 159 | writer->PutNext({0x628, 0x2950}); |
Samuel Huang | 21bbdef | 2020-01-23 15:58:26 +0000 | [diff] [blame] | 160 | std::vector<uint8_t> exp_reloc_data1 = ParseHexString( |
Samuel Huang | 2dedd30 | 2019-01-08 18:37:09 +0000 | [diff] [blame] | 161 | "BC 20 04 00 08 00 00 00 " // R_386_RELATIVE. |
| 162 | "50 29 04 00 08 AB CD EF"); // R_386_RELATIVE (address modified). |
Samuel Huang | 21bbdef | 2020-01-23 15:58:26 +0000 | [diff] [blame] | 163 | EXPECT_EQ(exp_reloc_data1, fake_image.GetRawRelocData(1)); |
| 164 | } |
| 165 | |
| 166 | TEST(RelocElfTest, Limit32) { |
| 167 | constexpr size_t kImageSize = 0x3000; |
| 168 | constexpr offset_t kBaseRva = 0x40000; |
| 169 | constexpr offset_t kRelocStart = 0x600; |
| 170 | // All R_386_RELATIVE. |
| 171 | std::vector<uint8_t> reloc_data = ParseHexString( |
| 172 | // Strictly within file. |
| 173 | "00 00 04 00 08 00 00 00 " |
| 174 | "00 10 04 00 08 00 00 00 " |
| 175 | "F0 2F 04 00 08 00 00 00 " |
| 176 | "F8 2F 04 00 08 00 00 00 " |
| 177 | "FC 2F 04 00 08 00 00 00 " |
| 178 | // Straddles end of file. |
| 179 | "FD 2F 04 00 08 00 00 00 " |
| 180 | "FE 2F 04 00 08 00 00 00 " |
| 181 | "FF 2F 04 00 08 00 00 00 " |
| 182 | // Beyond end of file. |
| 183 | "00 30 04 00 08 00 00 00 " |
| 184 | "01 30 04 00 08 00 00 00 " |
| 185 | "FC FF FF 7F 08 00 00 00 " |
| 186 | "FE FF FF 7F 08 00 00 00 " |
| 187 | "00 00 00 80 08 00 00 00 " |
| 188 | "FC FF FF FF 08 00 00 00 " |
| 189 | "FF FF FF FF 08 00 00 00 " |
| 190 | // Another good reference. |
| 191 | "34 12 04 00 08 00 00 00"); |
| 192 | |
| 193 | FakeImageWithReloc<Elf32IntelTraits> fake_image(kImageSize, kBaseRva, |
| 194 | {{kRelocStart, reloc_data}}); |
| 195 | |
| 196 | std::vector<Reference> exp_refs{{0x600, 0x0000}, {0x608, 0x1000}, |
| 197 | {0x610, 0x2FF0}, {0x618, 0x2FF8}, |
| 198 | {0x620, 0x2FFC}, {0x678, 0x1234}}; |
| 199 | EXPECT_EQ(exp_refs, fake_image.ExtractRelocReferences()); |
| 200 | } |
| 201 | |
| 202 | TEST(RelocElfTest, Limit64) { |
| 203 | constexpr size_t kImageSize = 0x3000; |
| 204 | constexpr offset_t kBaseRva = 0x40000; |
| 205 | |
| 206 | constexpr offset_t kRelocStart = 0x600; |
| 207 | // All R_X86_64_RELATIVE. |
| 208 | std::vector<uint8_t> reloc_data = ParseHexString( |
| 209 | // Strictly within file. |
| 210 | "00 00 04 00 00 00 00 00 08 00 00 00 00 00 00 00 " |
| 211 | "00 10 04 00 00 00 00 00 08 00 00 00 00 00 00 00 " |
| 212 | "F0 2F 04 00 00 00 00 00 08 00 00 00 00 00 00 00 " |
| 213 | "F4 2F 04 00 00 00 00 00 08 00 00 00 00 00 00 00 " |
| 214 | "F8 2F 04 00 00 00 00 00 08 00 00 00 00 00 00 00 " |
| 215 | // Straddles end of file. |
| 216 | "F9 2F 04 00 00 00 00 00 08 00 00 00 00 00 00 00 " |
| 217 | "FC 2F 04 00 00 00 00 00 08 00 00 00 00 00 00 00 " |
| 218 | "FF 2F 04 00 00 00 00 00 08 00 00 00 00 00 00 00 " |
| 219 | // Beyond end of file. |
| 220 | "00 30 04 00 00 00 00 00 08 00 00 00 00 00 00 00 " |
| 221 | "01 30 04 00 00 00 00 00 08 00 00 00 00 00 00 00 " |
| 222 | "FC FF FF 7F 00 00 00 00 08 00 00 00 00 00 00 00 " |
| 223 | "FE FF FF 7F 00 00 00 00 08 00 00 00 00 00 00 00 " |
| 224 | "00 00 00 80 00 00 00 00 08 00 00 00 00 00 00 00 " |
| 225 | "FC FF FF FF 00 00 00 00 08 00 00 00 00 00 00 00 " |
| 226 | "FF FF FF FF 00 00 00 00 08 00 00 00 00 00 00 00 " |
| 227 | "00 00 04 00 01 00 00 00 08 00 00 00 00 00 00 00 " |
| 228 | "FF FF FF FF FF FF FF FF 08 00 00 00 00 00 00 00 " |
| 229 | "F8 FF FF FF FF FF FF FF 08 00 00 00 00 00 00 00 " |
| 230 | // Another good reference. |
| 231 | "34 12 04 00 00 00 00 00 08 00 00 00 00 00 00 00"); |
| 232 | |
| 233 | FakeImageWithReloc<Elf64IntelTraits> fake_image(kImageSize, kBaseRva, |
| 234 | {{kRelocStart, reloc_data}}); |
| 235 | |
| 236 | std::vector<Reference> exp_refs{{0x600, 0x0000}, {0x610, 0x1000}, |
| 237 | {0x620, 0x2FF0}, {0x630, 0x2FF4}, |
| 238 | {0x640, 0x2FF8}, {0x720, 0x1234}}; |
| 239 | EXPECT_EQ(exp_refs, fake_image.ExtractRelocReferences()); |
Etienne Pierre-Doray | a88cad0 | 2018-07-25 20:16:02 +0000 | [diff] [blame] | 240 | } |
| 241 | |
| 242 | } // namespace zucchini |