toolkit/crashreporter/google-breakpad/src/common/mac/macho_reader_unittest.cc

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 // Copyright (c) 2010 Google Inc.
michael@0 2 // All rights reserved.
michael@0 3 //
michael@0 4 // Redistribution and use in source and binary forms, with or without
michael@0 5 // modification, are permitted provided that the following conditions are
michael@0 6 // met:
michael@0 7 //
michael@0 8 // * Redistributions of source code must retain the above copyright
michael@0 9 // notice, this list of conditions and the following disclaimer.
michael@0 10 // * Redistributions in binary form must reproduce the above
michael@0 11 // copyright notice, this list of conditions and the following disclaimer
michael@0 12 // in the documentation and/or other materials provided with the
michael@0 13 // distribution.
michael@0 14 // * Neither the name of Google Inc. nor the names of its
michael@0 15 // contributors may be used to endorse or promote products derived from
michael@0 16 // this software without specific prior written permission.
michael@0 17 //
michael@0 18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
michael@0 19 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
michael@0 20 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
michael@0 21 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
michael@0 22 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
michael@0 23 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
michael@0 24 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
michael@0 25 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
michael@0 26 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
michael@0 27 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
michael@0 28 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
michael@0 29
michael@0 30 // Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
michael@0 31
michael@0 32 // macho_reader_unittest.cc: Unit tests for google_breakpad::Mach_O::FatReader
michael@0 33 // and google_breakpad::Mach_O::Reader.
michael@0 34
michael@0 35 #include <map>
michael@0 36 #include <string>
michael@0 37 #include <vector>
michael@0 38
michael@0 39 #include "breakpad_googletest_includes.h"
michael@0 40 #include "common/mac/macho_reader.h"
michael@0 41 #include "common/test_assembler.h"
michael@0 42
michael@0 43 namespace mach_o = google_breakpad::mach_o;
michael@0 44 namespace test_assembler = google_breakpad::test_assembler;
michael@0 45
michael@0 46 using mach_o::FatReader;
michael@0 47 using mach_o::FileFlags;
michael@0 48 using mach_o::FileType;
michael@0 49 using mach_o::LoadCommandType;
michael@0 50 using mach_o::Reader;
michael@0 51 using mach_o::Section;
michael@0 52 using mach_o::SectionMap;
michael@0 53 using mach_o::Segment;
michael@0 54 using test_assembler::Endianness;
michael@0 55 using test_assembler::Label;
michael@0 56 using test_assembler::kBigEndian;
michael@0 57 using test_assembler::kLittleEndian;
michael@0 58 using test_assembler::kUnsetEndian;
michael@0 59 using google_breakpad::ByteBuffer;
michael@0 60 using std::map;
michael@0 61 using std::string;
michael@0 62 using std::vector;
michael@0 63 using testing::AllOf;
michael@0 64 using testing::DoAll;
michael@0 65 using testing::Field;
michael@0 66 using testing::InSequence;
michael@0 67 using testing::Matcher;
michael@0 68 using testing::Return;
michael@0 69 using testing::SaveArg;
michael@0 70 using testing::Test;
michael@0 71 using testing::_;
michael@0 72
michael@0 73
michael@0 74 // Mock classes for the reader's various reporters and handlers.
michael@0 75
michael@0 76 class MockFatReaderReporter: public FatReader::Reporter {
michael@0 77 public:
michael@0 78 MockFatReaderReporter(const string &filename)
michael@0 79 : FatReader::Reporter(filename) { }
michael@0 80 MOCK_METHOD0(BadHeader, void());
michael@0 81 MOCK_METHOD0(MisplacedObjectFile, void());
michael@0 82 MOCK_METHOD0(TooShort, void());
michael@0 83 };
michael@0 84
michael@0 85 class MockReaderReporter: public Reader::Reporter {
michael@0 86 public:
michael@0 87 MockReaderReporter(const string &filename) : Reader::Reporter(filename) { }
michael@0 88 MOCK_METHOD0(BadHeader, void());
michael@0 89 MOCK_METHOD4(CPUTypeMismatch, void(cpu_type_t cpu_type,
michael@0 90 cpu_subtype_t cpu_subtype,
michael@0 91 cpu_type_t expected_cpu_type,
michael@0 92 cpu_subtype_t expected_cpu_subtype));
michael@0 93 MOCK_METHOD0(HeaderTruncated, void());
michael@0 94 MOCK_METHOD0(LoadCommandRegionTruncated, void());
michael@0 95 MOCK_METHOD3(LoadCommandsOverrun, void(size_t claimed, size_t i,
michael@0 96 LoadCommandType type));
michael@0 97 MOCK_METHOD2(LoadCommandTooShort, void(size_t i, LoadCommandType type));
michael@0 98 MOCK_METHOD1(SectionsMissing, void(const string &name));
michael@0 99 MOCK_METHOD1(MisplacedSegmentData, void(const string &name));
michael@0 100 MOCK_METHOD2(MisplacedSectionData, void(const string &section,
michael@0 101 const string &segment));
michael@0 102 MOCK_METHOD0(MisplacedSymbolTable, void());
michael@0 103 MOCK_METHOD1(UnsupportedCPUType, void(cpu_type_t cpu_type));
michael@0 104 };
michael@0 105
michael@0 106 class MockLoadCommandHandler: public Reader::LoadCommandHandler {
michael@0 107 public:
michael@0 108 MOCK_METHOD2(UnknownCommand, bool(LoadCommandType, const ByteBuffer &));
michael@0 109 MOCK_METHOD1(SegmentCommand, bool(const Segment &));
michael@0 110 MOCK_METHOD2(SymtabCommand, bool(const ByteBuffer &, const ByteBuffer &));
michael@0 111 };
michael@0 112
michael@0 113 class MockSectionHandler: public Reader::SectionHandler {
michael@0 114 public:
michael@0 115 MOCK_METHOD1(HandleSection, bool(const Section &section));
michael@0 116 };
michael@0 117
michael@0 118
michael@0 119 // Tests for mach_o::FatReader.
michael@0 120
michael@0 121 // Since the effect of these functions is to write to stderr, the
michael@0 122 // results of these tests must be inspected by hand.
michael@0 123 TEST(FatReaderReporter, BadHeader) {
michael@0 124 FatReader::Reporter reporter("filename");
michael@0 125 reporter.BadHeader();
michael@0 126 }
michael@0 127
michael@0 128 TEST(FatReaderReporter, MisplacedObjectFile) {
michael@0 129 FatReader::Reporter reporter("filename");
michael@0 130 reporter.MisplacedObjectFile();
michael@0 131 }
michael@0 132
michael@0 133 TEST(FatReaderReporter, TooShort) {
michael@0 134 FatReader::Reporter reporter("filename");
michael@0 135 reporter.TooShort();
michael@0 136 }
michael@0 137
michael@0 138 TEST(MachOReaderReporter, BadHeader) {
michael@0 139 Reader::Reporter reporter("filename");
michael@0 140 reporter.BadHeader();
michael@0 141 }
michael@0 142
michael@0 143 TEST(MachOReaderReporter, CPUTypeMismatch) {
michael@0 144 Reader::Reporter reporter("filename");
michael@0 145 reporter.CPUTypeMismatch(CPU_TYPE_I386, CPU_SUBTYPE_X86_ALL,
michael@0 146 CPU_TYPE_POWERPC, CPU_SUBTYPE_POWERPC_ALL);
michael@0 147 }
michael@0 148
michael@0 149 TEST(MachOReaderReporter, HeaderTruncated) {
michael@0 150 Reader::Reporter reporter("filename");
michael@0 151 reporter.HeaderTruncated();
michael@0 152 }
michael@0 153
michael@0 154 TEST(MachOReaderReporter, LoadCommandRegionTruncated) {
michael@0 155 Reader::Reporter reporter("filename");
michael@0 156 reporter.LoadCommandRegionTruncated();
michael@0 157 }
michael@0 158
michael@0 159 TEST(MachOReaderReporter, LoadCommandsOverrun) {
michael@0 160 Reader::Reporter reporter("filename");
michael@0 161 reporter.LoadCommandsOverrun(10, 9, LC_DYSYMTAB);
michael@0 162 reporter.LoadCommandsOverrun(10, 9, 0);
michael@0 163 }
michael@0 164
michael@0 165 TEST(MachOReaderReporter, LoadCommandTooShort) {
michael@0 166 Reader::Reporter reporter("filename");
michael@0 167 reporter.LoadCommandTooShort(11, LC_SYMTAB);
michael@0 168 }
michael@0 169
michael@0 170 TEST(MachOReaderReporter, SectionsMissing) {
michael@0 171 Reader::Reporter reporter("filename");
michael@0 172 reporter.SectionsMissing("segment name");
michael@0 173 }
michael@0 174
michael@0 175 TEST(MachOReaderReporter, MisplacedSegmentData) {
michael@0 176 Reader::Reporter reporter("filename");
michael@0 177 reporter.MisplacedSegmentData("segment name");
michael@0 178 }
michael@0 179
michael@0 180 TEST(MachOReaderReporter, MisplacedSectionData) {
michael@0 181 Reader::Reporter reporter("filename");
michael@0 182 reporter.MisplacedSectionData("section name", "segment name");
michael@0 183 }
michael@0 184
michael@0 185 TEST(MachOReaderReporter, MisplacedSymbolTable) {
michael@0 186 Reader::Reporter reporter("filename");
michael@0 187 reporter.MisplacedSymbolTable();
michael@0 188 }
michael@0 189
michael@0 190 TEST(MachOReaderReporter, UnsupportedCPUType) {
michael@0 191 Reader::Reporter reporter("filename");
michael@0 192 reporter.UnsupportedCPUType(CPU_TYPE_HPPA);
michael@0 193 }
michael@0 194
michael@0 195 struct FatReaderFixture {
michael@0 196 FatReaderFixture()
michael@0 197 : fat(kBigEndian),
michael@0 198 reporter("reporter filename"),
michael@0 199 reader(&reporter), object_files(), object_files_size() {
michael@0 200 EXPECT_CALL(reporter, BadHeader()).Times(0);
michael@0 201 EXPECT_CALL(reporter, TooShort()).Times(0);
michael@0 202
michael@0 203 // here, start, and Mark are file offsets in 'fat'.
michael@0 204 fat.start() = 0;
michael@0 205 }
michael@0 206 // Append a 'fat_arch' entry to 'fat', with the given field values.
michael@0 207 void AppendFatArch(cpu_type_t type, cpu_subtype_t subtype,
michael@0 208 Label offset, Label size, uint32_t align) {
michael@0 209 fat
michael@0 210 .B32(type)
michael@0 211 .B32(subtype)
michael@0 212 .B32(offset)
michael@0 213 .B32(size)
michael@0 214 .B32(align);
michael@0 215 }
michael@0 216 // Append |n| dummy 'fat_arch' entries to 'fat'. The cpu type and
michael@0 217 // subtype have unrealistic values.
michael@0 218 void AppendDummyArchEntries(int n) {
michael@0 219 for (int i = 0; i < n; i++)
michael@0 220 AppendFatArch(0xb68ad617, 0x715a0840, 0, 0, 1);
michael@0 221 }
michael@0 222 void ReadFat(bool expect_parse_success = true) {
michael@0 223 ASSERT_TRUE(fat.GetContents(&contents));
michael@0 224 fat_bytes = reinterpret_cast<const uint8_t *>(contents.data());
michael@0 225 if (expect_parse_success) {
michael@0 226 EXPECT_TRUE(reader.Read(fat_bytes, contents.size()));
michael@0 227 object_files = reader.object_files(&object_files_size);
michael@0 228 }
michael@0 229 else
michael@0 230 EXPECT_FALSE(reader.Read(fat_bytes, contents.size()));
michael@0 231 }
michael@0 232 test_assembler::Section fat;
michael@0 233 MockFatReaderReporter reporter;
michael@0 234 FatReader reader;
michael@0 235 string contents;
michael@0 236 const uint8_t *fat_bytes;
michael@0 237 const struct fat_arch *object_files;
michael@0 238 size_t object_files_size;
michael@0 239 };
michael@0 240
michael@0 241 class FatReaderTest: public FatReaderFixture, public Test { };
michael@0 242
michael@0 243 TEST_F(FatReaderTest, BadMagic) {
michael@0 244 EXPECT_CALL(reporter, BadHeader()).Times(1);
michael@0 245 fat
michael@0 246 .B32(0xcafed00d) // magic number (incorrect)
michael@0 247 .B32(10); // number of architectures
michael@0 248 AppendDummyArchEntries(10);
michael@0 249 ReadFat(false);
michael@0 250 }
michael@0 251
michael@0 252 TEST_F(FatReaderTest, HeaderTooShort) {
michael@0 253 EXPECT_CALL(reporter, TooShort()).Times(1);
michael@0 254 fat
michael@0 255 .B32(0xcafebabe); // magic number
michael@0 256 ReadFat(false);
michael@0 257 }
michael@0 258
michael@0 259 TEST_F(FatReaderTest, ObjectListTooShort) {
michael@0 260 EXPECT_CALL(reporter, TooShort()).Times(1);
michael@0 261 fat
michael@0 262 .B32(0xcafebabe) // magic number
michael@0 263 .B32(10); // number of architectures
michael@0 264 AppendDummyArchEntries(9); // nine dummy architecture entries...
michael@0 265 fat // and a tenth, missing a byte at the end
michael@0 266 .B32(0x3d46c8fc) // cpu type
michael@0 267 .B32(0x8a7bfb01) // cpu subtype
michael@0 268 .B32(0) // offset
michael@0 269 .B32(0) // size
michael@0 270 .Append(3, '*'); // one byte short of a four-byte alignment
michael@0 271 ReadFat(false);
michael@0 272 }
michael@0 273
michael@0 274 TEST_F(FatReaderTest, DataTooShort) {
michael@0 275 EXPECT_CALL(reporter, MisplacedObjectFile()).Times(1);
michael@0 276 Label arch_data;
michael@0 277 fat
michael@0 278 .B32(0xcafebabe) // magic number
michael@0 279 .B32(1); // number of architectures
michael@0 280 AppendFatArch(0xb4d4a366, 0x4ba4f525, arch_data, 40, 0);
michael@0 281 fat
michael@0 282 .Mark(&arch_data) // file data begins here
michael@0 283 .Append(30, '*'); // only 30 bytes, not 40 as header claims
michael@0 284 ReadFat(false);
michael@0 285 }
michael@0 286
michael@0 287 TEST_F(FatReaderTest, NoObjectFiles) {
michael@0 288 fat
michael@0 289 .B32(0xcafebabe) // magic number
michael@0 290 .B32(0); // number of architectures
michael@0 291 ReadFat();
michael@0 292 EXPECT_EQ(0U, object_files_size);
michael@0 293 }
michael@0 294
michael@0 295 TEST_F(FatReaderTest, OneObjectFile) {
michael@0 296 Label obj1_offset;
michael@0 297 fat
michael@0 298 .B32(0xcafebabe) // magic number
michael@0 299 .B32(1); // number of architectures
michael@0 300 // First object file list entry
michael@0 301 AppendFatArch(0x5e3a6e91, 0x52ccd852, obj1_offset, 0x42, 0x355b15b2);
michael@0 302 // First object file data
michael@0 303 fat
michael@0 304 .Mark(&obj1_offset)
michael@0 305 .Append(0x42, '*'); // dummy contents
michael@0 306 ReadFat();
michael@0 307 ASSERT_EQ(1U, object_files_size);
michael@0 308 EXPECT_EQ(0x5e3a6e91, object_files[0].cputype);
michael@0 309 EXPECT_EQ(0x52ccd852, object_files[0].cpusubtype);
michael@0 310 EXPECT_EQ(obj1_offset.Value(), object_files[0].offset);
michael@0 311 EXPECT_EQ(0x42U, object_files[0].size);
michael@0 312 EXPECT_EQ(0x355b15b2U, object_files[0].align);
michael@0 313 }
michael@0 314
michael@0 315 TEST_F(FatReaderTest, ThreeObjectFiles) {
michael@0 316 Label obj1, obj2, obj3;
michael@0 317 fat
michael@0 318 .B32(0xcafebabe) // magic number
michael@0 319 .B32(3); // number of architectures
michael@0 320 // Three object file list entries.
michael@0 321 AppendFatArch(0x0cb92c30, 0x6a159a71, obj1, 0xfb4, 0x2615dbe8);
michael@0 322 AppendFatArch(0x0f3f1cbb, 0x6c55e90f, obj2, 0xc31, 0x83af6ffd);
michael@0 323 AppendFatArch(0x3717276d, 0x10ecdc84, obj3, 0x4b3, 0x035267d7);
michael@0 324 fat
michael@0 325 // First object file data
michael@0 326 .Mark(&obj1)
michael@0 327 .Append(0xfb4, '*') // dummy contents
michael@0 328 // Second object file data
michael@0 329 .Mark(&obj2)
michael@0 330 .Append(0xc31, '%') // dummy contents
michael@0 331 // Third object file data
michael@0 332 .Mark(&obj3)
michael@0 333 .Append(0x4b3, '^'); // dummy contents
michael@0 334
michael@0 335 ReadFat();
michael@0 336
michael@0 337 ASSERT_EQ(3U, object_files_size);
michael@0 338
michael@0 339 // First object file.
michael@0 340 EXPECT_EQ(0x0cb92c30, object_files[0].cputype);
michael@0 341 EXPECT_EQ(0x6a159a71, object_files[0].cpusubtype);
michael@0 342 EXPECT_EQ(obj1.Value(), object_files[0].offset);
michael@0 343 EXPECT_EQ(0xfb4U, object_files[0].size);
michael@0 344 EXPECT_EQ(0x2615dbe8U, object_files[0].align);
michael@0 345
michael@0 346 // Second object file.
michael@0 347 EXPECT_EQ(0x0f3f1cbb, object_files[1].cputype);
michael@0 348 EXPECT_EQ(0x6c55e90f, object_files[1].cpusubtype);
michael@0 349 EXPECT_EQ(obj2.Value(), object_files[1].offset);
michael@0 350 EXPECT_EQ(0xc31U, object_files[1].size);
michael@0 351 EXPECT_EQ(0x83af6ffdU, object_files[1].align);
michael@0 352
michael@0 353 // Third object file.
michael@0 354 EXPECT_EQ(0x3717276d, object_files[2].cputype);
michael@0 355 EXPECT_EQ(0x10ecdc84, object_files[2].cpusubtype);
michael@0 356 EXPECT_EQ(obj3.Value(), object_files[2].offset);
michael@0 357 EXPECT_EQ(0x4b3U, object_files[2].size);
michael@0 358 EXPECT_EQ(0x035267d7U, object_files[2].align);
michael@0 359 }
michael@0 360
michael@0 361 TEST_F(FatReaderTest, BigEndianMachO32) {
michael@0 362 fat.set_endianness(kBigEndian);
michael@0 363 fat
michael@0 364 .D32(0xfeedface) // Mach-O file magic number
michael@0 365 .D32(0x1a9d0518) // cpu type
michael@0 366 .D32(0x1b779357) // cpu subtype
michael@0 367 .D32(0x009df67e) // file type
michael@0 368 .D32(0) // no load commands
michael@0 369 .D32(0) // the load commands occupy no bytes
michael@0 370 .D32(0x21987a99); // flags
michael@0 371
michael@0 372 ReadFat();
michael@0 373
michael@0 374 // FatReader should treat a Mach-O file as if it were a fat binary file
michael@0 375 // containing one object file --- the whole thing.
michael@0 376 ASSERT_EQ(1U, object_files_size);
michael@0 377 EXPECT_EQ(0x1a9d0518, object_files[0].cputype);
michael@0 378 EXPECT_EQ(0x1b779357, object_files[0].cpusubtype);
michael@0 379 EXPECT_EQ(0U, object_files[0].offset);
michael@0 380 EXPECT_EQ(contents.size(), object_files[0].size);
michael@0 381 }
michael@0 382
michael@0 383 TEST_F(FatReaderTest, BigEndianMachO64) {
michael@0 384 fat.set_endianness(kBigEndian);
michael@0 385 fat
michael@0 386 .D32(0xfeedfacf) // Mach-O 64-bit file magic number
michael@0 387 .D32(0x5aff8487) // cpu type
michael@0 388 .D32(0x4c6a57f7) // cpu subtype
michael@0 389 .D32(0x4392d2c8) // file type
michael@0 390 .D32(0) // no load commands
michael@0 391 .D32(0) // the load commands occupy no bytes
michael@0 392 .D32(0x1b033eea); // flags
michael@0 393
michael@0 394 ReadFat();
michael@0 395
michael@0 396 // FatReader should treat a Mach-O file as if it were a fat binary file
michael@0 397 // containing one object file --- the whole thing.
michael@0 398 ASSERT_EQ(1U, object_files_size);
michael@0 399 EXPECT_EQ(0x5aff8487, object_files[0].cputype);
michael@0 400 EXPECT_EQ(0x4c6a57f7, object_files[0].cpusubtype);
michael@0 401 EXPECT_EQ(0U, object_files[0].offset);
michael@0 402 EXPECT_EQ(contents.size(), object_files[0].size);
michael@0 403 }
michael@0 404
michael@0 405 TEST_F(FatReaderTest, LittleEndianMachO32) {
michael@0 406 fat.set_endianness(kLittleEndian);
michael@0 407 fat
michael@0 408 .D32(0xfeedface) // Mach-O file magic number
michael@0 409 .D32(0x1a9d0518) // cpu type
michael@0 410 .D32(0x1b779357) // cpu subtype
michael@0 411 .D32(0x009df67e) // file type
michael@0 412 .D32(0) // no load commands
michael@0 413 .D32(0) // the load commands occupy no bytes
michael@0 414 .D32(0x21987a99); // flags
michael@0 415
michael@0 416 ReadFat();
michael@0 417
michael@0 418 // FatReader should treat a Mach-O file as if it were a fat binary file
michael@0 419 // containing one object file --- the whole thing.
michael@0 420 ASSERT_EQ(1U, object_files_size);
michael@0 421 EXPECT_EQ(0x1a9d0518, object_files[0].cputype);
michael@0 422 EXPECT_EQ(0x1b779357, object_files[0].cpusubtype);
michael@0 423 EXPECT_EQ(0U, object_files[0].offset);
michael@0 424 EXPECT_EQ(contents.size(), object_files[0].size);
michael@0 425 }
michael@0 426
michael@0 427 TEST_F(FatReaderTest, LittleEndianMachO64) {
michael@0 428 fat.set_endianness(kLittleEndian);
michael@0 429 fat
michael@0 430 .D32(0xfeedfacf) // Mach-O 64-bit file magic number
michael@0 431 .D32(0x5aff8487) // cpu type
michael@0 432 .D32(0x4c6a57f7) // cpu subtype
michael@0 433 .D32(0x4392d2c8) // file type
michael@0 434 .D32(0) // no load commands
michael@0 435 .D32(0) // the load commands occupy no bytes
michael@0 436 .D32(0x1b033eea); // flags
michael@0 437
michael@0 438 ReadFat();
michael@0 439
michael@0 440 // FatReader should treat a Mach-O file as if it were a fat binary file
michael@0 441 // containing one object file --- the whole thing.
michael@0 442 ASSERT_EQ(1U, object_files_size);
michael@0 443 EXPECT_EQ(0x5aff8487, object_files[0].cputype);
michael@0 444 EXPECT_EQ(0x4c6a57f7, object_files[0].cpusubtype);
michael@0 445 EXPECT_EQ(0U, object_files[0].offset);
michael@0 446 EXPECT_EQ(contents.size(), object_files[0].size);
michael@0 447 }
michael@0 448
michael@0 449 TEST_F(FatReaderTest, IncompleteMach) {
michael@0 450 fat.set_endianness(kLittleEndian);
michael@0 451 fat
michael@0 452 .D32(0xfeedfacf) // Mach-O 64-bit file magic number
michael@0 453 .D32(0x5aff8487); // cpu type
michael@0 454 // Truncated!
michael@0 455
michael@0 456 EXPECT_CALL(reporter, TooShort()).WillOnce(Return());
michael@0 457
michael@0 458 ReadFat(false);
michael@0 459 }
michael@0 460
michael@0 461
michael@0 462 // General mach_o::Reader tests.
michael@0 463
michael@0 464 // Dynamically scoped configuration data.
michael@0 465 class WithConfiguration {
michael@0 466 public:
michael@0 467 // Establish the given parameters as the default for SizedSections
michael@0 468 // created within the dynamic scope of this instance.
michael@0 469 WithConfiguration(Endianness endianness, size_t word_size)
michael@0 470 : endianness_(endianness), word_size_(word_size), saved_(current_) {
michael@0 471 current_ = this;
michael@0 472 }
michael@0 473 ~WithConfiguration() { current_ = saved_; }
michael@0 474 static Endianness endianness() {
michael@0 475 assert(current_);
michael@0 476 return current_->endianness_;
michael@0 477 }
michael@0 478 static size_t word_size() {
michael@0 479 assert(current_);
michael@0 480 return current_->word_size_;
michael@0 481 }
michael@0 482
michael@0 483 private:
michael@0 484 // The innermost WithConfiguration in whose dynamic scope we are
michael@0 485 // currently executing.
michael@0 486 static WithConfiguration *current_;
michael@0 487
michael@0 488 // The innermost WithConfiguration whose dynamic scope encloses this
michael@0 489 // WithConfiguration.
michael@0 490 Endianness endianness_;
michael@0 491 size_t word_size_;
michael@0 492 WithConfiguration *saved_;
michael@0 493 };
michael@0 494
michael@0 495 WithConfiguration *WithConfiguration::current_ = NULL;
michael@0 496
michael@0 497 // A test_assembler::Section with a size that we can cite. The start(),
michael@0 498 // Here() and Mark() member functions of a SizedSection always represent
michael@0 499 // offsets within the overall file.
michael@0 500 class SizedSection: public test_assembler::Section {
michael@0 501 public:
michael@0 502 // Construct a section of the given endianness and word size.
michael@0 503 explicit SizedSection(Endianness endianness, size_t word_size)
michael@0 504 : test_assembler::Section(endianness), word_size_(word_size) {
michael@0 505 assert(word_size_ == 32 || word_size_ == 64);
michael@0 506 }
michael@0 507 SizedSection()
michael@0 508 : test_assembler::Section(WithConfiguration::endianness()),
michael@0 509 word_size_(WithConfiguration::word_size()) {
michael@0 510 assert(word_size_ == 32 || word_size_ == 64);
michael@0 511 }
michael@0 512
michael@0 513 // Access/set this section's word size.
michael@0 514 size_t word_size() const { return word_size_; }
michael@0 515 void set_word_size(size_t word_size) {
michael@0 516 assert(word_size_ == 32 || word_size_ == 64);
michael@0 517 word_size_ = word_size;
michael@0 518 }
michael@0 519
michael@0 520 // Return a label representing the size this section will have when it
michael@0 521 // is Placed in some containing section.
michael@0 522 Label final_size() const { return final_size_; }
michael@0 523
michael@0 524 // Append SECTION to the end of this section, and call its Finish member.
michael@0 525 // Return a reference to this section.
michael@0 526 SizedSection &Place(SizedSection *section) {
michael@0 527 assert(section->endianness() == endianness());
michael@0 528 section->Finish();
michael@0 529 section->start() = Here();
michael@0 530 test_assembler::Section::Append(*section);
michael@0 531 return *this;
michael@0 532 }
michael@0 533
michael@0 534 protected:
michael@0 535 // Mark this section's contents as complete. For plain SizedSections, we
michael@0 536 // set SECTION's start to its position in this section, and its final_size
michael@0 537 // label to its current size. Derived classes can extend this as needed
michael@0 538 // for their additional semantics.
michael@0 539 virtual void Finish() {
michael@0 540 final_size_ = Size();
michael@0 541 }
michael@0 542
michael@0 543 // The word size for this data: either 32 or 64.
michael@0 544 size_t word_size_;
michael@0 545
michael@0 546 private:
michael@0 547 // This section's final size, set when we are placed in some other
michael@0 548 // SizedSection.
michael@0 549 Label final_size_;
michael@0 550 };
michael@0 551
michael@0 552 // A SizedSection that is loaded into memory at a particular address.
michael@0 553 class LoadedSection: public SizedSection {
michael@0 554 public:
michael@0 555 explicit LoadedSection(Label address = Label()) : address_(address) { }
michael@0 556
michael@0 557 // Return a label representing this section's address.
michael@0 558 Label address() const { return address_; }
michael@0 559
michael@0 560 // Placing a loaded section within a loaded section sets the relationship
michael@0 561 // between their addresses.
michael@0 562 LoadedSection &Place(LoadedSection *section) {
michael@0 563 section->address() = address() + Size();
michael@0 564 SizedSection::Place(section);
michael@0 565 return *this;
michael@0 566 }
michael@0 567
michael@0 568 protected:
michael@0 569 // The address at which this section's contents will be loaded.
michael@0 570 Label address_;
michael@0 571 };
michael@0 572
michael@0 573 // A SizedSection representing a segment load command.
michael@0 574 class SegmentLoadCommand: public SizedSection {
michael@0 575 public:
michael@0 576 SegmentLoadCommand() : section_count_(0) { }
michael@0 577
michael@0 578 // Append a segment load command header with the given characteristics.
michael@0 579 // The load command will refer to CONTENTS, which must be Placed in the
michael@0 580 // file separately, at the desired position. Return a reference to this
michael@0 581 // section.
michael@0 582 SegmentLoadCommand &Header(const string &name, const LoadedSection &contents,
michael@0 583 uint32_t maxprot, uint32_t initprot,
michael@0 584 uint32_t flags) {
michael@0 585 assert(contents.word_size() == word_size());
michael@0 586 D32(word_size() == 32 ? LC_SEGMENT : LC_SEGMENT_64);
michael@0 587 D32(final_size());
michael@0 588 AppendCString(name, 16);
michael@0 589 Append(endianness(), word_size() / 8, contents.address());
michael@0 590 Append(endianness(), word_size() / 8, vmsize_);
michael@0 591 Append(endianness(), word_size() / 8, contents.start());
michael@0 592 Append(endianness(), word_size() / 8, contents.final_size());
michael@0 593 D32(maxprot);
michael@0 594 D32(initprot);
michael@0 595 D32(final_section_count_);
michael@0 596 D32(flags);
michael@0 597
michael@0 598 content_final_size_ = contents.final_size();
michael@0 599
michael@0 600 return *this;
michael@0 601 }
michael@0 602
michael@0 603 // Return a label representing the size of this segment when loaded into
michael@0 604 // memory. If this label is still undefined by the time we place this
michael@0 605 // segment, it defaults to the final size of the segment's in-file
michael@0 606 // contents. Return a reference to this load command.
michael@0 607 Label &vmsize() { return vmsize_; }
michael@0 608
michael@0 609 // Add a section entry with the given characteristics to this segment
michael@0 610 // load command. Return a reference to this. The section entry will refer
michael@0 611 // to CONTENTS, which must be Placed in the segment's contents
michael@0 612 // separately, at the desired position.
michael@0 613 SegmentLoadCommand &AppendSectionEntry(const string &section_name,
michael@0 614 const string &segment_name,
michael@0 615 uint32_t alignment, uint32_t flags,
michael@0 616 const LoadedSection &contents) {
michael@0 617 AppendCString(section_name, 16);
michael@0 618 AppendCString(segment_name, 16);
michael@0 619 Append(endianness(), word_size() / 8, contents.address());
michael@0 620 Append(endianness(), word_size() / 8, contents.final_size());
michael@0 621 D32(contents.start());
michael@0 622 D32(alignment);
michael@0 623 D32(0); // relocations start
michael@0 624 D32(0); // relocations size
michael@0 625 D32(flags);
michael@0 626 D32(0x93656b95); // reserved1
michael@0 627 D32(0xc35a2473); // reserved2
michael@0 628 if (word_size() == 64)
michael@0 629 D32(0x70284b95); // reserved3
michael@0 630
michael@0 631 section_count_++;
michael@0 632
michael@0 633 return *this;
michael@0 634 }
michael@0 635
michael@0 636 protected:
michael@0 637 void Finish() {
michael@0 638 final_section_count_ = section_count_;
michael@0 639 if (!vmsize_.IsKnownConstant())
michael@0 640 vmsize_ = content_final_size_;
michael@0 641 SizedSection::Finish();
michael@0 642 }
michael@0 643
michael@0 644 private:
michael@0 645 // The number of sections that have been added to this segment so far.
michael@0 646 size_t section_count_;
michael@0 647
michael@0 648 // A label representing the final number of sections this segment will hold.
michael@0 649 Label final_section_count_;
michael@0 650
michael@0 651 // The size of the contents for this segment present in the file.
michael@0 652 Label content_final_size_;
michael@0 653
michael@0 654 // A label representing the size of this segment when loaded; this can be
michael@0 655 // larger than the size of its file contents, the difference being
michael@0 656 // zero-filled. If not set explicitly by calling set_vmsize, this is set
michael@0 657 // equal to the size of the contents.
michael@0 658 Label vmsize_;
michael@0 659 };
michael@0 660
michael@0 661 // A SizedSection holding a list of Mach-O load commands.
michael@0 662 class LoadCommands: public SizedSection {
michael@0 663 public:
michael@0 664 LoadCommands() : command_count_(0) { }
michael@0 665
michael@0 666 // Return a label representing the final load command count.
michael@0 667 Label final_command_count() const { return final_command_count_; }
michael@0 668
michael@0 669 // Increment the command count; return a reference to this section.
michael@0 670 LoadCommands &CountCommand() {
michael@0 671 command_count_++;
michael@0 672 return *this;
michael@0 673 }
michael@0 674
michael@0 675 // Place COMMAND, containing a load command, at the end of this section.
michael@0 676 // Return a reference to this section.
michael@0 677 LoadCommands &Place(SizedSection *section) {
michael@0 678 SizedSection::Place(section);
michael@0 679 CountCommand();
michael@0 680 return *this;
michael@0 681 }
michael@0 682
michael@0 683 protected:
michael@0 684 // Mark this load command list as complete.
michael@0 685 void Finish() {
michael@0 686 SizedSection::Finish();
michael@0 687 final_command_count_ = command_count_;
michael@0 688 }
michael@0 689
michael@0 690 private:
michael@0 691 // The number of load commands we have added to this file so far.
michael@0 692 size_t command_count_;
michael@0 693
michael@0 694 // A label representing the final command count.
michael@0 695 Label final_command_count_;
michael@0 696 };
michael@0 697
michael@0 698 // A SizedSection holding the contents of a Mach-O file. Within a
michael@0 699 // MachOFile, the start, Here, and Mark members refer to file offsets.
michael@0 700 class MachOFile: public SizedSection {
michael@0 701 public:
michael@0 702 MachOFile() {
michael@0 703 start() = 0;
michael@0 704 }
michael@0 705
michael@0 706 // Create a Mach-O file header using the given characteristics and load
michael@0 707 // command list. This Places COMMANDS immediately after the header.
michael@0 708 // Return a reference to this section.
michael@0 709 MachOFile &Header(LoadCommands *commands,
michael@0 710 cpu_type_t cpu_type = CPU_TYPE_X86,
michael@0 711 cpu_subtype_t cpu_subtype = CPU_SUBTYPE_I386_ALL,
michael@0 712 FileType file_type = MH_EXECUTE,
michael@0 713 uint32_t file_flags = (MH_TWOLEVEL |
michael@0 714 MH_DYLDLINK |
michael@0 715 MH_NOUNDEFS)) {
michael@0 716 D32(word_size() == 32 ? 0xfeedface : 0xfeedfacf); // magic number
michael@0 717 D32(cpu_type); // cpu type
michael@0 718 D32(cpu_subtype); // cpu subtype
michael@0 719 D32(file_type); // file type
michael@0 720 D32(commands->final_command_count()); // number of load commands
michael@0 721 D32(commands->final_size()); // their size in bytes
michael@0 722 D32(file_flags); // flags
michael@0 723 if (word_size() == 64)
michael@0 724 D32(0x55638b90); // reserved
michael@0 725 Place(commands);
michael@0 726 return *this;
michael@0 727 }
michael@0 728 };
michael@0 729
michael@0 730
michael@0 731 struct ReaderFixture {
michael@0 732 ReaderFixture()
michael@0 733 : reporter("reporter filename"),
michael@0 734 reader(&reporter) {
michael@0 735 EXPECT_CALL(reporter, BadHeader()).Times(0);
michael@0 736 EXPECT_CALL(reporter, CPUTypeMismatch(_, _, _, _)).Times(0);
michael@0 737 EXPECT_CALL(reporter, HeaderTruncated()).Times(0);
michael@0 738 EXPECT_CALL(reporter, LoadCommandRegionTruncated()).Times(0);
michael@0 739 EXPECT_CALL(reporter, LoadCommandsOverrun(_, _, _)).Times(0);
michael@0 740 EXPECT_CALL(reporter, LoadCommandTooShort(_, _)).Times(0);
michael@0 741 EXPECT_CALL(reporter, SectionsMissing(_)).Times(0);
michael@0 742 EXPECT_CALL(reporter, MisplacedSegmentData(_)).Times(0);
michael@0 743 EXPECT_CALL(reporter, MisplacedSectionData(_, _)).Times(0);
michael@0 744 EXPECT_CALL(reporter, MisplacedSymbolTable()).Times(0);
michael@0 745 EXPECT_CALL(reporter, UnsupportedCPUType(_)).Times(0);
michael@0 746
michael@0 747 EXPECT_CALL(load_command_handler, UnknownCommand(_, _)).Times(0);
michael@0 748 EXPECT_CALL(load_command_handler, SegmentCommand(_)).Times(0);
michael@0 749 }
michael@0 750
michael@0 751 void ReadFile(MachOFile *file,
michael@0 752 bool expect_parse_success,
michael@0 753 cpu_type_t expected_cpu_type,
michael@0 754 cpu_subtype_t expected_cpu_subtype) {
michael@0 755 ASSERT_TRUE(file->GetContents(&file_contents));
michael@0 756 file_bytes = reinterpret_cast<const uint8_t *>(file_contents.data());
michael@0 757 if (expect_parse_success) {
michael@0 758 EXPECT_TRUE(reader.Read(file_bytes,
michael@0 759 file_contents.size(),
michael@0 760 expected_cpu_type,
michael@0 761 expected_cpu_subtype));
michael@0 762 } else {
michael@0 763 EXPECT_FALSE(reader.Read(file_bytes,
michael@0 764 file_contents.size(),
michael@0 765 expected_cpu_type,
michael@0 766 expected_cpu_subtype));
michael@0 767 }
michael@0 768 }
michael@0 769
michael@0 770 string file_contents;
michael@0 771 const uint8_t *file_bytes;
michael@0 772 MockReaderReporter reporter;
michael@0 773 Reader reader;
michael@0 774 MockLoadCommandHandler load_command_handler;
michael@0 775 MockSectionHandler section_handler;
michael@0 776 };
michael@0 777
michael@0 778 class ReaderTest: public ReaderFixture, public Test { };
michael@0 779
michael@0 780 TEST_F(ReaderTest, BadMagic) {
michael@0 781 WithConfiguration config(kLittleEndian, 32);
michael@0 782 const cpu_type_t kCPUType = 0x46b760df;
michael@0 783 const cpu_subtype_t kCPUSubType = 0x76a0e7f7;
michael@0 784 MachOFile file;
michael@0 785 file
michael@0 786 .D32(0x67bdebe1) // Not a proper magic number.
michael@0 787 .D32(kCPUType) // cpu type
michael@0 788 .D32(kCPUSubType) // cpu subtype
michael@0 789 .D32(0x149fc717) // file type
michael@0 790 .D32(0) // no load commands
michael@0 791 .D32(0) // they occupy no bytes
michael@0 792 .D32(0x80e71d64) // flags
michael@0 793 .D32(0); // reserved
michael@0 794 EXPECT_CALL(reporter, BadHeader()).WillOnce(Return());
michael@0 795 ReadFile(&file, false, CPU_TYPE_ANY, kCPUSubType);
michael@0 796 }
michael@0 797
michael@0 798 TEST_F(ReaderTest, MismatchedMagic) {
michael@0 799 WithConfiguration config(kLittleEndian, 32);
michael@0 800 const cpu_type_t kCPUType = CPU_TYPE_I386;
michael@0 801 const cpu_subtype_t kCPUSubType = CPU_SUBTYPE_X86_ALL;
michael@0 802 MachOFile file;
michael@0 803 file
michael@0 804 .D32(MH_CIGAM) // Right magic, but winds up wrong
michael@0 805 // due to bitswapping
michael@0 806 .D32(kCPUType) // cpu type
michael@0 807 .D32(kCPUSubType) // cpu subtype
michael@0 808 .D32(0x149fc717) // file type
michael@0 809 .D32(0) // no load commands
michael@0 810 .D32(0) // they occupy no bytes
michael@0 811 .D32(0x80e71d64) // flags
michael@0 812 .D32(0); // reserved
michael@0 813 EXPECT_CALL(reporter, BadHeader()).WillOnce(Return());
michael@0 814 ReadFile(&file, false, kCPUType, kCPUSubType);
michael@0 815 }
michael@0 816
michael@0 817 TEST_F(ReaderTest, ShortMagic) {
michael@0 818 WithConfiguration config(kBigEndian, 32);
michael@0 819 MachOFile file;
michael@0 820 file
michael@0 821 .D16(0xfeed); // magic number
michael@0 822 // truncated!
michael@0 823 EXPECT_CALL(reporter, HeaderTruncated()).WillOnce(Return());
michael@0 824 ReadFile(&file, false, CPU_TYPE_ANY, 0);
michael@0 825 }
michael@0 826
michael@0 827 TEST_F(ReaderTest, ShortHeader) {
michael@0 828 WithConfiguration config(kBigEndian, 32);
michael@0 829 const cpu_type_t kCPUType = CPU_TYPE_ANY;
michael@0 830 const cpu_subtype_t kCPUSubType = 0x76a0e7f7;
michael@0 831 MachOFile file;
michael@0 832 file
michael@0 833 .D32(0xfeedface) // magic number
michael@0 834 .D32(kCPUType) // cpu type
michael@0 835 .D32(kCPUSubType) // cpu subtype
michael@0 836 .D32(0x149fc717) // file type
michael@0 837 .D32(0) // no load commands
michael@0 838 .D32(0); // they occupy no bytes
michael@0 839 EXPECT_CALL(reporter, HeaderTruncated()).WillOnce(Return());
michael@0 840 ReadFile(&file, false, CPU_TYPE_ANY, kCPUSubType);
michael@0 841 }
michael@0 842
michael@0 843 TEST_F(ReaderTest, MismatchedCPU) {
michael@0 844 WithConfiguration config(kBigEndian, 32);
michael@0 845 const cpu_type_t kCPUType = CPU_TYPE_I386;
michael@0 846 const cpu_subtype_t kCPUSubType = CPU_SUBTYPE_X86_ALL;
michael@0 847 MachOFile file;
michael@0 848 file
michael@0 849 .D32(MH_MAGIC) // Right magic for PPC (once bitswapped)
michael@0 850 .D32(kCPUType) // cpu type
michael@0 851 .D32(kCPUSubType) // cpu subtype
michael@0 852 .D32(0x149fc717) // file type
michael@0 853 .D32(0) // no load commands
michael@0 854 .D32(0) // they occupy no bytes
michael@0 855 .D32(0x80e71d64) // flags
michael@0 856 .D32(0); // reserved
michael@0 857 EXPECT_CALL(reporter,
michael@0 858 CPUTypeMismatch(CPU_TYPE_I386, CPU_SUBTYPE_X86_ALL,
michael@0 859 CPU_TYPE_POWERPC, CPU_SUBTYPE_POWERPC_ALL))
michael@0 860 .WillOnce(Return());
michael@0 861 ReadFile(&file, false, CPU_TYPE_POWERPC, CPU_SUBTYPE_POWERPC_ALL);
michael@0 862 }
michael@0 863
michael@0 864 TEST_F(ReaderTest, LittleEndian32Bit) {
michael@0 865 WithConfiguration config(kLittleEndian, 32);
michael@0 866 const cpu_type_t kCPUType = 0x46b760df;
michael@0 867 const cpu_subtype_t kCPUSubType = 0x76a0e7f7;
michael@0 868 MachOFile file;
michael@0 869 file
michael@0 870 .D32(0xfeedface) // magic number
michael@0 871 .D32(kCPUType) // cpu type
michael@0 872 .D32(kCPUSubType) // cpu subtype
michael@0 873 .D32(0x149fc717) // file type
michael@0 874 .D32(0) // no load commands
michael@0 875 .D32(0) // they occupy no bytes
michael@0 876 .D32(0x80e71d64) // flags
michael@0 877 .D32(0); // reserved
michael@0 878 ReadFile(&file, true, CPU_TYPE_ANY, kCPUSubType);
michael@0 879 EXPECT_FALSE(reader.bits_64());
michael@0 880 EXPECT_FALSE(reader.big_endian());
michael@0 881 EXPECT_EQ(kCPUType, reader.cpu_type());
michael@0 882 EXPECT_EQ(kCPUSubType, reader.cpu_subtype());
michael@0 883 EXPECT_EQ(FileType(0x149fc717), reader.file_type());
michael@0 884 EXPECT_EQ(FileFlags(0x80e71d64), reader.flags());
michael@0 885 }
michael@0 886
michael@0 887 TEST_F(ReaderTest, LittleEndian64Bit) {
michael@0 888 WithConfiguration config(kLittleEndian, 64);
michael@0 889 const cpu_type_t kCPUType = 0x46b760df;
michael@0 890 const cpu_subtype_t kCPUSubType = 0x76a0e7f7;
michael@0 891 MachOFile file;
michael@0 892 file
michael@0 893 .D32(0xfeedfacf) // magic number
michael@0 894 .D32(kCPUType) // cpu type
michael@0 895 .D32(kCPUSubType) // cpu subtype
michael@0 896 .D32(0x149fc717) // file type
michael@0 897 .D32(0) // no load commands
michael@0 898 .D32(0) // they occupy no bytes
michael@0 899 .D32(0x80e71d64) // flags
michael@0 900 .D32(0); // reserved
michael@0 901 ReadFile(&file, true, CPU_TYPE_ANY, kCPUSubType);
michael@0 902 EXPECT_TRUE(reader.bits_64());
michael@0 903 EXPECT_FALSE(reader.big_endian());
michael@0 904 EXPECT_EQ(kCPUType, reader.cpu_type());
michael@0 905 EXPECT_EQ(kCPUSubType, reader.cpu_subtype());
michael@0 906 EXPECT_EQ(FileType(0x149fc717), reader.file_type());
michael@0 907 EXPECT_EQ(FileFlags(0x80e71d64), reader.flags());
michael@0 908 }
michael@0 909
michael@0 910 TEST_F(ReaderTest, BigEndian32Bit) {
michael@0 911 WithConfiguration config(kBigEndian, 32);
michael@0 912 const cpu_type_t kCPUType = 0x46b760df;
michael@0 913 const cpu_subtype_t kCPUSubType = 0x76a0e7f7;
michael@0 914 MachOFile file;
michael@0 915 file
michael@0 916 .D32(0xfeedface) // magic number
michael@0 917 .D32(kCPUType) // cpu type
michael@0 918 .D32(kCPUSubType) // cpu subtype
michael@0 919 .D32(0x149fc717) // file type
michael@0 920 .D32(0) // no load commands
michael@0 921 .D32(0) // they occupy no bytes
michael@0 922 .D32(0x80e71d64) // flags
michael@0 923 .D32(0); // reserved
michael@0 924 ReadFile(&file, true, CPU_TYPE_ANY, kCPUSubType);
michael@0 925 EXPECT_FALSE(reader.bits_64());
michael@0 926 EXPECT_TRUE(reader.big_endian());
michael@0 927 EXPECT_EQ(kCPUType, reader.cpu_type());
michael@0 928 EXPECT_EQ(kCPUSubType, reader.cpu_subtype());
michael@0 929 EXPECT_EQ(FileType(0x149fc717), reader.file_type());
michael@0 930 EXPECT_EQ(FileFlags(0x80e71d64), reader.flags());
michael@0 931 }
michael@0 932
michael@0 933 TEST_F(ReaderTest, BigEndian64Bit) {
michael@0 934 WithConfiguration config(kBigEndian, 64);
michael@0 935 const cpu_type_t kCPUType = 0x46b760df;
michael@0 936 const cpu_subtype_t kCPUSubType = 0x76a0e7f7;
michael@0 937 MachOFile file;
michael@0 938 file
michael@0 939 .D32(0xfeedfacf) // magic number
michael@0 940 .D32(kCPUType) // cpu type
michael@0 941 .D32(kCPUSubType) // cpu subtype
michael@0 942 .D32(0x149fc717) // file type
michael@0 943 .D32(0) // no load commands
michael@0 944 .D32(0) // they occupy no bytes
michael@0 945 .D32(0x80e71d64) // flags
michael@0 946 .D32(0); // reserved
michael@0 947 ReadFile(&file, true, CPU_TYPE_ANY, kCPUSubType);
michael@0 948 EXPECT_TRUE(reader.bits_64());
michael@0 949 EXPECT_TRUE(reader.big_endian());
michael@0 950 EXPECT_EQ(kCPUType, reader.cpu_type());
michael@0 951 EXPECT_EQ(kCPUSubType, reader.cpu_subtype());
michael@0 952 EXPECT_EQ(FileType(0x149fc717), reader.file_type());
michael@0 953 EXPECT_EQ(FileFlags(0x80e71d64), reader.flags());
michael@0 954 }
michael@0 955
michael@0 956
michael@0 957 // Load command tests.
michael@0 958
michael@0 959 class LoadCommand: public ReaderFixture, public Test { };
michael@0 960
michael@0 961 TEST_F(LoadCommand, RegionTruncated) {
michael@0 962 WithConfiguration config(kBigEndian, 64);
michael@0 963 const cpu_type_t kCPUType = 0x46b760df;
michael@0 964 const cpu_subtype_t kCPUSubType = 0x76a0e7f7;
michael@0 965 MachOFile file;
michael@0 966 file
michael@0 967 .D32(0xfeedfacf) // magic number
michael@0 968 .D32(kCPUType) // cpu type
michael@0 969 .D32(kCPUSubType) // cpu subtype
michael@0 970 .D32(0x149fc717) // file type
michael@0 971 .D32(1) // one load command
michael@0 972 .D32(40) // occupying 40 bytes
michael@0 973 .D32(0x80e71d64) // flags
michael@0 974 .D32(0) // reserved
michael@0 975 .Append(20, 0); // load command region, not as long as
michael@0 976 // Mach-O header promised
michael@0 977
michael@0 978 EXPECT_CALL(reporter, LoadCommandRegionTruncated()).WillOnce(Return());
michael@0 979
michael@0 980 ReadFile(&file, false, CPU_TYPE_ANY, kCPUSubType);
michael@0 981 }
michael@0 982
michael@0 983 TEST_F(LoadCommand, None) {
michael@0 984 WithConfiguration config(kLittleEndian, 32);
michael@0 985 LoadCommands load_commands;
michael@0 986 MachOFile file;
michael@0 987 file.Header(&load_commands);
michael@0 988
michael@0 989 ReadFile(&file, true, CPU_TYPE_X86, CPU_SUBTYPE_I386_ALL);
michael@0 990
michael@0 991 EXPECT_FALSE(reader.bits_64());
michael@0 992 EXPECT_FALSE(reader.big_endian());
michael@0 993 EXPECT_EQ(CPU_TYPE_X86, reader.cpu_type());
michael@0 994 EXPECT_EQ(CPU_SUBTYPE_I386_ALL, reader.cpu_subtype());
michael@0 995 EXPECT_EQ(static_cast<uint32_t>(MH_EXECUTE), reader.file_type());
michael@0 996 EXPECT_EQ(FileFlags(MH_TWOLEVEL |
michael@0 997 MH_DYLDLINK |
michael@0 998 MH_NOUNDEFS),
michael@0 999 FileFlags(reader.flags()));
michael@0 1000
michael@0 1001 EXPECT_TRUE(reader.WalkLoadCommands(&load_command_handler));
michael@0 1002 }
michael@0 1003
michael@0 1004 TEST_F(LoadCommand, Unknown) {
michael@0 1005 WithConfiguration config(kBigEndian, 32);
michael@0 1006 LoadCommands load_commands;
michael@0 1007 load_commands
michael@0 1008 .CountCommand()
michael@0 1009 .D32(0x33293d4a) // unknown load command
michael@0 1010 .D32(40) // total size in bytes
michael@0 1011 .Append(32, '*'); // dummy data
michael@0 1012 MachOFile file;
michael@0 1013 file.Header(&load_commands);
michael@0 1014
michael@0 1015 ReadFile(&file, true, CPU_TYPE_ANY, 0);
michael@0 1016
michael@0 1017 EXPECT_FALSE(reader.bits_64());
michael@0 1018 EXPECT_TRUE(reader.big_endian());
michael@0 1019 EXPECT_EQ(CPU_TYPE_X86, reader.cpu_type());
michael@0 1020 EXPECT_EQ(CPU_SUBTYPE_I386_ALL, reader.cpu_subtype());
michael@0 1021 EXPECT_EQ(static_cast<uint32_t>(MH_EXECUTE), reader.file_type());
michael@0 1022 EXPECT_EQ(FileFlags(MH_TWOLEVEL |
michael@0 1023 MH_DYLDLINK |
michael@0 1024 MH_NOUNDEFS),
michael@0 1025 reader.flags());
michael@0 1026
michael@0 1027 ByteBuffer expected;
michael@0 1028 expected.start = file_bytes + load_commands.start().Value();
michael@0 1029 expected.end = expected.start + load_commands.final_size().Value();
michael@0 1030 EXPECT_CALL(load_command_handler, UnknownCommand(0x33293d4a,
michael@0 1031 expected))
michael@0 1032 .WillOnce(Return(true));
michael@0 1033
michael@0 1034 EXPECT_TRUE(reader.WalkLoadCommands(&load_command_handler));
michael@0 1035 }
michael@0 1036
michael@0 1037 TEST_F(LoadCommand, TypeIncomplete) {
michael@0 1038 WithConfiguration config(kLittleEndian, 32);
michael@0 1039 LoadCommands load_commands;
michael@0 1040 load_commands
michael@0 1041 .CountCommand()
michael@0 1042 .Append(3, 0); // load command type, incomplete
michael@0 1043
michael@0 1044 MachOFile file;
michael@0 1045 file.Header(&load_commands);
michael@0 1046
michael@0 1047 ReadFile(&file, true, CPU_TYPE_ANY, 0);
michael@0 1048
michael@0 1049 EXPECT_CALL(reporter, LoadCommandsOverrun(1, 0, 0))
michael@0 1050 .WillOnce(Return());
michael@0 1051 EXPECT_FALSE(reader.WalkLoadCommands(&load_command_handler));
michael@0 1052 }
michael@0 1053
michael@0 1054 TEST_F(LoadCommand, LengthIncomplete) {
michael@0 1055 WithConfiguration config(kBigEndian, 64);
michael@0 1056 LoadCommands load_commands;
michael@0 1057 load_commands
michael@0 1058 .CountCommand()
michael@0 1059 .D32(LC_SEGMENT); // load command
michael@0 1060 // no length
michael@0 1061 MachOFile file;
michael@0 1062 file.Header(&load_commands);
michael@0 1063
michael@0 1064 ReadFile(&file, true, CPU_TYPE_ANY, 0);
michael@0 1065
michael@0 1066 EXPECT_CALL(reporter, LoadCommandsOverrun(1, 0, LC_SEGMENT))
michael@0 1067 .WillOnce(Return());
michael@0 1068 EXPECT_FALSE(reader.WalkLoadCommands(&load_command_handler));
michael@0 1069 }
michael@0 1070
michael@0 1071 TEST_F(LoadCommand, ContentIncomplete) {
michael@0 1072 WithConfiguration config(kLittleEndian, 64);
michael@0 1073 LoadCommands load_commands;
michael@0 1074 load_commands
michael@0 1075 .CountCommand()
michael@0 1076 .D32(LC_SEGMENT) // load command
michael@0 1077 .D32(40) // total size in bytes
michael@0 1078 .Append(28, '*'); // not enough dummy data
michael@0 1079 MachOFile file;
michael@0 1080 file.Header(&load_commands);
michael@0 1081
michael@0 1082 ReadFile(&file, true, CPU_TYPE_ANY, 0);
michael@0 1083
michael@0 1084 EXPECT_CALL(reporter, LoadCommandsOverrun(1, 0, LC_SEGMENT))
michael@0 1085 .WillOnce(Return());
michael@0 1086 EXPECT_FALSE(reader.WalkLoadCommands(&load_command_handler));
michael@0 1087 }
michael@0 1088
michael@0 1089 TEST_F(LoadCommand, SegmentBE32) {
michael@0 1090 WithConfiguration config(kBigEndian, 32);
michael@0 1091 LoadedSection segment;
michael@0 1092 segment.address() = 0x1891139c;
michael@0 1093 segment.Append(42, '*'); // segment contents
michael@0 1094 SegmentLoadCommand segment_command;
michael@0 1095 segment_command
michael@0 1096 .Header("froon", segment, 0x94d6dd22, 0x8bdbc319, 0x990a16dd);
michael@0 1097 segment_command.vmsize() = 0xcb76584fU;
michael@0 1098 LoadCommands load_commands;
michael@0 1099 load_commands.Place(&segment_command);
michael@0 1100 MachOFile file;
michael@0 1101 file
michael@0 1102 .Header(&load_commands)
michael@0 1103 .Place(&segment);
michael@0 1104
michael@0 1105 ReadFile(&file, true, CPU_TYPE_ANY, 0);
michael@0 1106
michael@0 1107 Segment actual_segment;
michael@0 1108 EXPECT_CALL(load_command_handler, SegmentCommand(_))
michael@0 1109 .WillOnce(DoAll(SaveArg<0>(&actual_segment),
michael@0 1110 Return(true)));
michael@0 1111 EXPECT_TRUE(reader.WalkLoadCommands(&load_command_handler));
michael@0 1112
michael@0 1113 EXPECT_EQ(false, actual_segment.bits_64);
michael@0 1114 EXPECT_EQ("froon", actual_segment.name);
michael@0 1115 EXPECT_EQ(0x1891139cU, actual_segment.vmaddr);
michael@0 1116 EXPECT_EQ(0xcb76584fU, actual_segment.vmsize);
michael@0 1117 EXPECT_EQ(0x94d6dd22U, actual_segment.maxprot);
michael@0 1118 EXPECT_EQ(0x8bdbc319U, actual_segment.initprot);
michael@0 1119 EXPECT_EQ(0x990a16ddU, actual_segment.flags);
michael@0 1120 EXPECT_EQ(0U, actual_segment.nsects);
michael@0 1121 EXPECT_EQ(0U, actual_segment.section_list.Size());
michael@0 1122 EXPECT_EQ(segment.final_size().Value(), actual_segment.contents.Size());
michael@0 1123 }
michael@0 1124
michael@0 1125 TEST_F(LoadCommand, SegmentLE32) {
michael@0 1126 WithConfiguration config(kLittleEndian, 32);
michael@0 1127 LoadedSection segment;
michael@0 1128 segment.address() = 0x4b877866;
michael@0 1129 segment.Append(42, '*'); // segment contents
michael@0 1130 SegmentLoadCommand segment_command;
michael@0 1131 segment_command
michael@0 1132 .Header("sixteenprecisely", segment,
michael@0 1133 0x350759ed, 0x6cf5a62e, 0x990a16dd);
michael@0 1134 segment_command.vmsize() = 0xcb76584fU;
michael@0 1135 LoadCommands load_commands;
michael@0 1136 load_commands.Place(&segment_command);
michael@0 1137 MachOFile file;
michael@0 1138 file
michael@0 1139 .Header(&load_commands)
michael@0 1140 .Place(&segment);
michael@0 1141
michael@0 1142 ReadFile(&file, true, CPU_TYPE_ANY, 0);
michael@0 1143
michael@0 1144 Segment actual_segment;
michael@0 1145 EXPECT_CALL(load_command_handler, SegmentCommand(_))
michael@0 1146 .WillOnce(DoAll(SaveArg<0>(&actual_segment),
michael@0 1147 Return(true)));
michael@0 1148 EXPECT_TRUE(reader.WalkLoadCommands(&load_command_handler));
michael@0 1149
michael@0 1150 EXPECT_EQ(false, actual_segment.bits_64);
michael@0 1151 EXPECT_EQ("sixteenprecisely", actual_segment.name);
michael@0 1152 EXPECT_EQ(0x4b877866U, actual_segment.vmaddr);
michael@0 1153 EXPECT_EQ(0xcb76584fU, actual_segment.vmsize);
michael@0 1154 EXPECT_EQ(0x350759edU, actual_segment.maxprot);
michael@0 1155 EXPECT_EQ(0x6cf5a62eU, actual_segment.initprot);
michael@0 1156 EXPECT_EQ(0x990a16ddU, actual_segment.flags);
michael@0 1157 EXPECT_EQ(0U, actual_segment.nsects);
michael@0 1158 EXPECT_EQ(0U, actual_segment.section_list.Size());
michael@0 1159 EXPECT_EQ(segment.final_size().Value(), actual_segment.contents.Size());
michael@0 1160 }
michael@0 1161
michael@0 1162 TEST_F(LoadCommand, SegmentBE64) {
michael@0 1163 WithConfiguration config(kBigEndian, 64);
michael@0 1164 LoadedSection segment;
michael@0 1165 segment.address() = 0x79f484f77009e511ULL;
michael@0 1166 segment.Append(42, '*'); // segment contents
michael@0 1167 SegmentLoadCommand segment_command;
michael@0 1168 segment_command
michael@0 1169 .Header("froon", segment, 0x42b45da5, 0x8bdbc319, 0xb2335220);
michael@0 1170 segment_command.vmsize() = 0x8d92397ce6248abaULL;
michael@0 1171 LoadCommands load_commands;
michael@0 1172 load_commands.Place(&segment_command);
michael@0 1173 MachOFile file;
michael@0 1174 file
michael@0 1175 .Header(&load_commands)
michael@0 1176 .Place(&segment);
michael@0 1177
michael@0 1178 ReadFile(&file, true, CPU_TYPE_ANY, 0);
michael@0 1179
michael@0 1180 Segment actual_segment;
michael@0 1181 EXPECT_CALL(load_command_handler, SegmentCommand(_))
michael@0 1182 .WillOnce(DoAll(SaveArg<0>(&actual_segment),
michael@0 1183 Return(true)));
michael@0 1184 EXPECT_TRUE(reader.WalkLoadCommands(&load_command_handler));
michael@0 1185
michael@0 1186 EXPECT_EQ(true, actual_segment.bits_64);
michael@0 1187 EXPECT_EQ("froon", actual_segment.name);
michael@0 1188 EXPECT_EQ(0x79f484f77009e511ULL, actual_segment.vmaddr);
michael@0 1189 EXPECT_EQ(0x8d92397ce6248abaULL, actual_segment.vmsize);
michael@0 1190 EXPECT_EQ(0x42b45da5U, actual_segment.maxprot);
michael@0 1191 EXPECT_EQ(0x8bdbc319U, actual_segment.initprot);
michael@0 1192 EXPECT_EQ(0xb2335220U, actual_segment.flags);
michael@0 1193 EXPECT_EQ(0U, actual_segment.nsects);
michael@0 1194 EXPECT_EQ(0U, actual_segment.section_list.Size());
michael@0 1195 EXPECT_EQ(segment.final_size().Value(), actual_segment.contents.Size());
michael@0 1196 }
michael@0 1197
michael@0 1198 TEST_F(LoadCommand, SegmentLE64) {
michael@0 1199 WithConfiguration config(kLittleEndian, 64);
michael@0 1200 LoadedSection segment;
michael@0 1201 segment.address() = 0x50c0501dc5922d35ULL;
michael@0 1202 segment.Append(42, '*'); // segment contents
michael@0 1203 SegmentLoadCommand segment_command;
michael@0 1204 segment_command
michael@0 1205 .Header("sixteenprecisely", segment,
michael@0 1206 0x917c339d, 0xdbc446fa, 0xb650b563);
michael@0 1207 segment_command.vmsize() = 0x84ae73e7c75469bfULL;
michael@0 1208 LoadCommands load_commands;
michael@0 1209 load_commands.Place(&segment_command);
michael@0 1210 MachOFile file;
michael@0 1211 file
michael@0 1212 .Header(&load_commands)
michael@0 1213 .Place(&segment);
michael@0 1214
michael@0 1215 ReadFile(&file, true, CPU_TYPE_ANY, 0);
michael@0 1216
michael@0 1217 Segment actual_segment;
michael@0 1218 EXPECT_CALL(load_command_handler, SegmentCommand(_))
michael@0 1219 .WillOnce(DoAll(SaveArg<0>(&actual_segment),
michael@0 1220 Return(true)));
michael@0 1221 EXPECT_TRUE(reader.WalkLoadCommands(&load_command_handler));
michael@0 1222
michael@0 1223 EXPECT_EQ(true, actual_segment.bits_64);
michael@0 1224 EXPECT_EQ("sixteenprecisely", actual_segment.name);
michael@0 1225 EXPECT_EQ(0x50c0501dc5922d35ULL, actual_segment.vmaddr);
michael@0 1226 EXPECT_EQ(0x84ae73e7c75469bfULL, actual_segment.vmsize);
michael@0 1227 EXPECT_EQ(0x917c339dU, actual_segment.maxprot);
michael@0 1228 EXPECT_EQ(0xdbc446faU, actual_segment.initprot);
michael@0 1229 EXPECT_EQ(0xb650b563U, actual_segment.flags);
michael@0 1230 EXPECT_EQ(0U, actual_segment.nsects);
michael@0 1231 EXPECT_EQ(0U, actual_segment.section_list.Size());
michael@0 1232 EXPECT_EQ(segment.final_size().Value(), actual_segment.contents.Size());
michael@0 1233 }
michael@0 1234
michael@0 1235 TEST_F(LoadCommand, SegmentCommandTruncated) {
michael@0 1236 WithConfiguration config(kBigEndian, 32);
michael@0 1237 LoadedSection segment_contents;
michael@0 1238 segment_contents.Append(20, '*'); // lah di dah
michael@0 1239 SizedSection command;
michael@0 1240 command
michael@0 1241 .D32(LC_SEGMENT) // command type
michael@0 1242 .D32(command.final_size()) // command size
michael@0 1243 .AppendCString("too-short", 16) // segment name
michael@0 1244 .D32(0x9c759211) // vmaddr
michael@0 1245 .D32(segment_contents.final_size()) // vmsize
michael@0 1246 .D32(segment_contents.start()) // file offset
michael@0 1247 .D32(segment_contents.final_size()) // file size
michael@0 1248 .D32(0x56f28446) // max protection
michael@0 1249 .D32(0xe7910dcb) // initial protection
michael@0 1250 .D32(0) // no sections
michael@0 1251 .Append(3, 0); // flags (one byte short!)
michael@0 1252 LoadCommands load_commands;
michael@0 1253 load_commands.Place(&command);
michael@0 1254 MachOFile file;
michael@0 1255 file
michael@0 1256 .Header(&load_commands)
michael@0 1257 .Place(&segment_contents);
michael@0 1258
michael@0 1259 ReadFile(&file, true, CPU_TYPE_ANY, 0);
michael@0 1260
michael@0 1261 EXPECT_CALL(reporter, LoadCommandTooShort(0, LC_SEGMENT))
michael@0 1262 .WillOnce(Return());
michael@0 1263
michael@0 1264 EXPECT_FALSE(reader.WalkLoadCommands(&load_command_handler));
michael@0 1265 }
michael@0 1266
michael@0 1267 TEST_F(LoadCommand, SegmentBadContentOffset) {
michael@0 1268 WithConfiguration config(kLittleEndian, 32);
michael@0 1269 // Instead of letting a Place call set the segment's file offset and size,
michael@0 1270 // set them ourselves, to check that the parser catches invalid offsets
michael@0 1271 // instead of handing us bogus pointers.
michael@0 1272 LoadedSection segment;
michael@0 1273 segment.address() = 0x4db5489c;
michael@0 1274 segment.start() = 0x7e189e76; // beyond end of file
michael@0 1275 segment.final_size() = 0x98b9c3ab;
michael@0 1276 SegmentLoadCommand segment_command;
michael@0 1277 segment_command
michael@0 1278 .Header("notmerelyfifteen", segment, 0xcbab25ee, 0x359a20db, 0x68a3933f);
michael@0 1279 LoadCommands load_commands;
michael@0 1280 load_commands.Place(&segment_command);
michael@0 1281 MachOFile file;
michael@0 1282 file.Header(&load_commands);
michael@0 1283
michael@0 1284 ReadFile(&file, true, CPU_TYPE_ANY, 0);
michael@0 1285
michael@0 1286 EXPECT_CALL(reporter, MisplacedSegmentData("notmerelyfifteen"))
michael@0 1287 .WillOnce(Return());
michael@0 1288
michael@0 1289 EXPECT_FALSE(reader.WalkLoadCommands(&load_command_handler));
michael@0 1290 }
michael@0 1291
michael@0 1292 TEST_F(LoadCommand, ThreeLoadCommands) {
michael@0 1293 WithConfiguration config(kBigEndian, 32);
michael@0 1294 LoadedSection seg1, seg2, seg3;
michael@0 1295 SegmentLoadCommand cmd1, cmd2, cmd3;
michael@0 1296
michael@0 1297 seg1.Append(128, '@');
michael@0 1298 seg1.address() = 0xa7f61ef6;
michael@0 1299 cmd1.Header("head", seg1, 0x88bf1cc7, 0x889a26a4, 0xe9b80d87);
michael@0 1300 // Include some dummy data at the end of the load command. Since we
michael@0 1301 // didn't claim to have any sections, the reader should ignore this. But
michael@0 1302 // making sure the commands have different lengths ensures that we're
michael@0 1303 // using the right command's length to advance the LoadCommandIterator.
michael@0 1304 cmd1.Append(128, '!');
michael@0 1305
michael@0 1306 seg2.Append(42, '*');
michael@0 1307 seg2.address() = 0xc70fc909;
michael@0 1308 cmd2.Header("thorax", seg2, 0xde7327f4, 0xfdaf771d, 0x65e74b30);
michael@0 1309 // More dummy data at the end of the load command.
michael@0 1310 cmd2.Append(32, '^');
michael@0 1311
michael@0 1312 seg3.Append(42, '%');
michael@0 1313 seg3.address() = 0x46b3ab05;
michael@0 1314 cmd3.Header("abdomen", seg3, 0x7098b70d, 0x8d8d7728, 0x5131419b);
michael@0 1315 // More dummy data at the end of the load command.
michael@0 1316 cmd3.Append(64, '&');
michael@0 1317
michael@0 1318 LoadCommands load_commands;
michael@0 1319 load_commands.Place(&cmd1).Place(&cmd2).Place(&cmd3);
michael@0 1320
michael@0 1321 MachOFile file;
michael@0 1322 file.Header(&load_commands).Place(&seg1).Place(&seg2).Place(&seg3);
michael@0 1323
michael@0 1324 ReadFile(&file, true, CPU_TYPE_ANY, 0);
michael@0 1325
michael@0 1326 {
michael@0 1327 InSequence s;
michael@0 1328 EXPECT_CALL(load_command_handler,
michael@0 1329 SegmentCommand(Field(&Segment::name, "head")))
michael@0 1330 .WillOnce(Return(true));
michael@0 1331 EXPECT_CALL(load_command_handler,
michael@0 1332 SegmentCommand(Field(&Segment::name, "thorax")))
michael@0 1333 .WillOnce(Return(true));
michael@0 1334 EXPECT_CALL(load_command_handler,
michael@0 1335 SegmentCommand(Field(&Segment::name, "abdomen")))
michael@0 1336 .WillOnce(Return(true));
michael@0 1337 }
michael@0 1338
michael@0 1339 EXPECT_TRUE(reader.WalkLoadCommands(&load_command_handler));
michael@0 1340 }
michael@0 1341
michael@0 1342 static inline Matcher<const Section &> MatchSection(
michael@0 1343 Matcher<bool> bits_64,
michael@0 1344 Matcher<const string &> section_name,
michael@0 1345 Matcher<const string &> segment_name,
michael@0 1346 Matcher<uint64_t> address,
michael@0 1347 Matcher<uint32_t> alignment,
michael@0 1348 Matcher<uint32_t> flags,
michael@0 1349 Matcher<const ByteBuffer &> contents) {
michael@0 1350 return AllOf(AllOf(Field(&Section::bits_64, bits_64),
michael@0 1351 Field(&Section::section_name, section_name),
michael@0 1352 Field(&Section::segment_name, segment_name),
michael@0 1353 Field(&Section::address, address)),
michael@0 1354 AllOf(Field(&Section::align, alignment),
michael@0 1355 Field(&Section::flags, flags),
michael@0 1356 Field(&Section::contents, contents)));
michael@0 1357 }
michael@0 1358
michael@0 1359 static inline Matcher<const Section &> MatchSection(
michael@0 1360 Matcher<bool> bits_64,
michael@0 1361 Matcher<const string &> section_name,
michael@0 1362 Matcher<const string &> segment_name,
michael@0 1363 Matcher<uint64_t> address) {
michael@0 1364 return AllOf(Field(&Section::bits_64, bits_64),
michael@0 1365 Field(&Section::section_name, section_name),
michael@0 1366 Field(&Section::segment_name, segment_name),
michael@0 1367 Field(&Section::address, address));
michael@0 1368 }
michael@0 1369
michael@0 1370 TEST_F(LoadCommand, OneSegmentTwoSections) {
michael@0 1371 WithConfiguration config(kBigEndian, 64);
michael@0 1372
michael@0 1373 // Create some sections with some data.
michael@0 1374 LoadedSection section1, section2;
michael@0 1375 section1.Append("buddha's hand");
michael@0 1376 section2.Append("kumquat");
michael@0 1377
michael@0 1378 // Create a segment to hold them.
michael@0 1379 LoadedSection segment;
michael@0 1380 segment.address() = 0xe1d0eeec;
michael@0 1381 segment.Place(&section2).Place(&section1);
michael@0 1382
michael@0 1383 SegmentLoadCommand segment_command;
michael@0 1384 segment_command
michael@0 1385 .Header("head", segment, 0x92c9568c, 0xa89f2627, 0x4dc7a1e2)
michael@0 1386 .AppendSectionEntry("mandarin", "kishu", 12, 0x8cd4604bU, section1)
michael@0 1387 .AppendSectionEntry("bergamot", "cara cara", 12, 0x98746efaU, section2);
michael@0 1388
michael@0 1389 LoadCommands commands;
michael@0 1390 commands.Place(&segment_command);
michael@0 1391
michael@0 1392 MachOFile file;
michael@0 1393 file.Header(&commands).Place(&segment);
michael@0 1394
michael@0 1395 ReadFile(&file, true, CPU_TYPE_ANY, 0);
michael@0 1396
michael@0 1397 Segment actual_segment;
michael@0 1398 EXPECT_CALL(load_command_handler, SegmentCommand(_))
michael@0 1399 .WillOnce(DoAll(SaveArg<0>(&actual_segment),
michael@0 1400 Return(true)));
michael@0 1401 EXPECT_TRUE(reader.WalkLoadCommands(&load_command_handler));
michael@0 1402
michael@0 1403 {
michael@0 1404 InSequence s;
michael@0 1405 ByteBuffer contents1;
michael@0 1406 contents1.start = file_bytes + section1.start().Value();
michael@0 1407 contents1.end = contents1.start + section1.final_size().Value();
michael@0 1408 EXPECT_EQ("buddha's hand",
michael@0 1409 string(reinterpret_cast<const char *>(contents1.start),
michael@0 1410 contents1.Size()));
michael@0 1411 EXPECT_CALL(section_handler,
michael@0 1412 HandleSection(MatchSection(true, "mandarin", "kishu",
michael@0 1413 section1.address().Value(), 12,
michael@0 1414 0x8cd4604bU, contents1)))
michael@0 1415 .WillOnce(Return(true));
michael@0 1416
michael@0 1417 ByteBuffer contents2;
michael@0 1418 contents2.start = file_bytes + section2.start().Value();
michael@0 1419 contents2.end = contents2.start + section2.final_size().Value();
michael@0 1420 EXPECT_EQ("kumquat",
michael@0 1421 string(reinterpret_cast<const char *>(contents2.start),
michael@0 1422 contents2.Size()));
michael@0 1423 EXPECT_CALL(section_handler,
michael@0 1424 HandleSection(MatchSection(true, "bergamot", "cara cara",
michael@0 1425 section2.address().Value(), 12,
michael@0 1426 0x98746efaU, contents2)))
michael@0 1427 .WillOnce(Return(true));
michael@0 1428 }
michael@0 1429
michael@0 1430 EXPECT_TRUE(reader.WalkSegmentSections(actual_segment, &section_handler));
michael@0 1431 }
michael@0 1432
michael@0 1433 TEST_F(LoadCommand, MisplacedSectionBefore) {
michael@0 1434 WithConfiguration config(kLittleEndian, 64);
michael@0 1435
michael@0 1436 // The segment.
michael@0 1437 LoadedSection segment;
michael@0 1438 segment.address() = 0x696d83cc;
michael@0 1439 segment.Append(10, '0');
michael@0 1440
michael@0 1441 // The contents of the following sections don't matter, because
michael@0 1442 // we're not really going to Place them in segment; we're just going
michael@0 1443 // to set all their labels by hand to get the (impossible)
michael@0 1444 // configurations we want.
michael@0 1445
michael@0 1446 // A section whose starting offset is before that of its section.
michael@0 1447 LoadedSection before;
michael@0 1448 before.Append(10, '1');
michael@0 1449 before.start() = segment.start() - 1;
michael@0 1450 before.address() = segment.address() - 1;
michael@0 1451 before.final_size() = before.Size();
michael@0 1452
michael@0 1453 SegmentLoadCommand command;
michael@0 1454 command
michael@0 1455 .Header("segment", segment, 0x173baa29, 0x8407275d, 0xed8f7057)
michael@0 1456 .AppendSectionEntry("before", "segment", 0, 0x686c6921, before);
michael@0 1457
michael@0 1458 LoadCommands commands;
michael@0 1459 commands.Place(&command);
michael@0 1460
michael@0 1461 MachOFile file;
michael@0 1462 file.Header(&commands).Place(&segment);
michael@0 1463
michael@0 1464 ReadFile(&file, true, CPU_TYPE_ANY, 0);
michael@0 1465
michael@0 1466 Segment actual_segment;
michael@0 1467 EXPECT_TRUE(reader.FindSegment("segment", &actual_segment));
michael@0 1468
michael@0 1469 EXPECT_CALL(reporter, MisplacedSectionData("before", "segment"))
michael@0 1470 .WillOnce(Return());
michael@0 1471 EXPECT_FALSE(reader.WalkSegmentSections(actual_segment, &section_handler));
michael@0 1472 }
michael@0 1473
michael@0 1474 TEST_F(LoadCommand, MisplacedSectionAfter) {
michael@0 1475 WithConfiguration config(kLittleEndian, 64);
michael@0 1476
michael@0 1477 // The segment.
michael@0 1478 LoadedSection segment;
michael@0 1479 segment.address() = 0x696d83cc;
michael@0 1480 segment.Append(10, '0');
michael@0 1481
michael@0 1482 // The contents of the following sections don't matter, because
michael@0 1483 // we're not really going to Place them in segment; we're just going
michael@0 1484 // to set all their labels by hand to get the (impossible)
michael@0 1485 // configurations we want.
michael@0 1486
michael@0 1487 // A section whose starting offset is after the end of its section.
michael@0 1488 LoadedSection after;
michael@0 1489 after.Append(10, '2');
michael@0 1490 after.start() = segment.start() + 11;
michael@0 1491 after.address() = segment.address() + 11;
michael@0 1492 after.final_size() = after.Size();
michael@0 1493
michael@0 1494 SegmentLoadCommand command;
michael@0 1495 command
michael@0 1496 .Header("segment", segment, 0x173baa29, 0x8407275d, 0xed8f7057)
michael@0 1497 .AppendSectionEntry("after", "segment", 0, 0x2ee50124, after);
michael@0 1498
michael@0 1499 LoadCommands commands;
michael@0 1500 commands.Place(&command);
michael@0 1501
michael@0 1502 MachOFile file;
michael@0 1503 file.Header(&commands).Place(&segment);
michael@0 1504
michael@0 1505 ReadFile(&file, true, CPU_TYPE_ANY, 0);
michael@0 1506
michael@0 1507 Segment actual_segment;
michael@0 1508 EXPECT_TRUE(reader.FindSegment("segment", &actual_segment));
michael@0 1509
michael@0 1510 EXPECT_CALL(reporter, MisplacedSectionData("after", "segment"))
michael@0 1511 .WillOnce(Return());
michael@0 1512 EXPECT_FALSE(reader.WalkSegmentSections(actual_segment, &section_handler));
michael@0 1513 }
michael@0 1514
michael@0 1515 TEST_F(LoadCommand, MisplacedSectionTooBig) {
michael@0 1516 WithConfiguration config(kLittleEndian, 64);
michael@0 1517
michael@0 1518 // The segment.
michael@0 1519 LoadedSection segment;
michael@0 1520 segment.address() = 0x696d83cc;
michael@0 1521 segment.Append(10, '0');
michael@0 1522
michael@0 1523 // The contents of the following sections don't matter, because
michael@0 1524 // we're not really going to Place them in segment; we're just going
michael@0 1525 // to set all their labels by hand to get the (impossible)
michael@0 1526 // configurations we want.
michael@0 1527
michael@0 1528 // A section that extends beyond the end of its section.
michael@0 1529 LoadedSection too_big;
michael@0 1530 too_big.Append(10, '3');
michael@0 1531 too_big.start() = segment.start() + 1;
michael@0 1532 too_big.address() = segment.address() + 1;
michael@0 1533 too_big.final_size() = too_big.Size();
michael@0 1534
michael@0 1535 SegmentLoadCommand command;
michael@0 1536 command
michael@0 1537 .Header("segment", segment, 0x173baa29, 0x8407275d, 0xed8f7057)
michael@0 1538 .AppendSectionEntry("too big", "segment", 0, 0x8b53ae5c, too_big);
michael@0 1539
michael@0 1540 LoadCommands commands;
michael@0 1541 commands.Place(&command);
michael@0 1542
michael@0 1543 MachOFile file;
michael@0 1544 file.Header(&commands).Place(&segment);
michael@0 1545
michael@0 1546 ReadFile(&file, true, CPU_TYPE_ANY, 0);
michael@0 1547
michael@0 1548 Segment actual_segment;
michael@0 1549 EXPECT_TRUE(reader.FindSegment("segment", &actual_segment));
michael@0 1550
michael@0 1551 EXPECT_CALL(reporter, MisplacedSectionData("too big", "segment"))
michael@0 1552 .WillOnce(Return());
michael@0 1553 EXPECT_FALSE(reader.WalkSegmentSections(actual_segment, &section_handler));
michael@0 1554 }
michael@0 1555
michael@0 1556
michael@0 1557 // The segments in a .dSYM bundle's Mach-O file have their file offset
michael@0 1558 // and size set to zero, but the sections don't. The reader shouldn't
michael@0 1559 // report an error in this case.
michael@0 1560 TEST_F(LoadCommand, ZappedSegment) {
michael@0 1561 WithConfiguration config(kBigEndian, 32);
michael@0 1562
michael@0 1563 // The segment.
michael@0 1564 LoadedSection segment;
michael@0 1565 segment.address() = 0x696d83cc;
michael@0 1566 segment.start() = 0;
michael@0 1567 segment.final_size() = 0;
michael@0 1568
michael@0 1569 // The section.
michael@0 1570 LoadedSection section;
michael@0 1571 section.address() = segment.address();
michael@0 1572 section.start() = 0;
michael@0 1573 section.final_size() = 1000; // extends beyond its segment
michael@0 1574
michael@0 1575 SegmentLoadCommand command;
michael@0 1576 command
michael@0 1577 .Header("zapped", segment, 0x0861a5cb, 0x68ccff67, 0x0b66255c)
michael@0 1578 .AppendSectionEntry("twitching", "zapped", 0, 0x93b3bd42, section);
michael@0 1579
michael@0 1580 LoadCommands commands;
michael@0 1581 commands.Place(&command);
michael@0 1582
michael@0 1583 MachOFile file;
michael@0 1584 file.Header(&commands);
michael@0 1585
michael@0 1586 ReadFile(&file, true, CPU_TYPE_ANY, 0);
michael@0 1587
michael@0 1588 Segment actual_segment;
michael@0 1589 EXPECT_TRUE(reader.FindSegment("zapped", &actual_segment));
michael@0 1590
michael@0 1591 ByteBuffer zapped_extent(NULL, 0);
michael@0 1592 EXPECT_CALL(section_handler,
michael@0 1593 HandleSection(MatchSection(false, "twitching", "zapped",
michael@0 1594 0x696d83cc, 0, 0x93b3bd42,
michael@0 1595 zapped_extent)))
michael@0 1596 .WillOnce(Return(true));
michael@0 1597
michael@0 1598 EXPECT_TRUE(reader.WalkSegmentSections(actual_segment, &section_handler));
michael@0 1599 }
michael@0 1600
michael@0 1601 TEST_F(LoadCommand, MapSegmentSections) {
michael@0 1602 WithConfiguration config(kLittleEndian, 32);
michael@0 1603
michael@0 1604 // Create some sections with some data.
michael@0 1605 LoadedSection section1, section2, section3, section4;
michael@0 1606 section1.Append("buddha's hand");
michael@0 1607 section2.start() = 0; // Section 2 is an S_ZEROFILL section.
michael@0 1608 section2.final_size() = 0;
michael@0 1609 section3.Append("shasta gold");
michael@0 1610 section4.Append("satsuma");
michael@0 1611
michael@0 1612 // Create two segments to hold them.
michael@0 1613 LoadedSection segment1, segment2;
michael@0 1614 segment1.address() = 0x13e6c8a9;
michael@0 1615 segment1.Place(&section3).Place(&section1);
michael@0 1616 segment2.set_word_size(64);
michael@0 1617 segment2.address() = 0x04d462e2;
michael@0 1618 segment2.Place(&section4);
michael@0 1619 section2.address() = segment2.address() + segment2.Size();
michael@0 1620
michael@0 1621 SegmentLoadCommand segment_command1, segment_command2;
michael@0 1622 segment_command1
michael@0 1623 .Header("head", segment1, 0x67d955a6, 0x7a61c13e, 0xe3e50c64)
michael@0 1624 .AppendSectionEntry("mandarin", "head", 12, 0x5bb565d7, section1)
michael@0 1625 .AppendSectionEntry("bergamot", "head", 12, 0x8620de73, section3);
michael@0 1626 segment_command2.set_word_size(64);
michael@0 1627 segment_command2
michael@0 1628 .Header("thorax", segment2, 0x7aab2419, 0xe908007f, 0x17961d33)
michael@0 1629 .AppendSectionEntry("sixteenprecisely", "thorax",
michael@0 1630 12, S_ZEROFILL, section2)
michael@0 1631 .AppendSectionEntry("cara cara", "thorax", 12, 0xb6c5dd8a, section4);
michael@0 1632
michael@0 1633 LoadCommands commands;
michael@0 1634 commands.Place(&segment_command1).Place(&segment_command2);
michael@0 1635
michael@0 1636 MachOFile file;
michael@0 1637 file.Header(&commands).Place(&segment1).Place(&segment2);
michael@0 1638
michael@0 1639 ReadFile(&file, true, CPU_TYPE_ANY, 0);
michael@0 1640
michael@0 1641 Segment segment;
michael@0 1642 SectionMap section_map;
michael@0 1643
michael@0 1644 EXPECT_FALSE(reader.FindSegment("smoot", &segment));
michael@0 1645
michael@0 1646 ASSERT_TRUE(reader.FindSegment("thorax", &segment));
michael@0 1647 ASSERT_TRUE(reader.MapSegmentSections(segment, &section_map));
michael@0 1648
michael@0 1649 EXPECT_FALSE(section_map.find("sixteenpreciselyandthensome")
michael@0 1650 != section_map.end());
michael@0 1651 EXPECT_FALSE(section_map.find("mandarin") != section_map.end());
michael@0 1652 ASSERT_TRUE(section_map.find("cara cara") != section_map.end());
michael@0 1653 EXPECT_THAT(section_map["cara cara"],
michael@0 1654 MatchSection(true, "cara cara", "thorax", 0x04d462e2));
michael@0 1655 ASSERT_TRUE(section_map.find("sixteenprecisely")
michael@0 1656 != section_map.end());
michael@0 1657 ByteBuffer sixteenprecisely_contents(NULL, 0);
michael@0 1658 EXPECT_THAT(section_map["sixteenprecisely"],
michael@0 1659 MatchSection(true, "sixteenprecisely", "thorax",
michael@0 1660 0x04d462e2 + 7, 12, S_ZEROFILL,
michael@0 1661 sixteenprecisely_contents));
michael@0 1662
michael@0 1663 ASSERT_TRUE(reader.FindSegment("head", &segment));
michael@0 1664 ASSERT_TRUE(reader.MapSegmentSections(segment, &section_map));
michael@0 1665
michael@0 1666 ASSERT_TRUE(section_map.find("mandarin") != section_map.end());
michael@0 1667 EXPECT_THAT(section_map["mandarin"],
michael@0 1668 MatchSection(false, "mandarin", "head", 0x13e6c8a9 + 11));
michael@0 1669 ASSERT_TRUE(section_map.find("bergamot") != section_map.end());
michael@0 1670 EXPECT_THAT(section_map["bergamot"],
michael@0 1671 MatchSection(false, "bergamot", "head", 0x13e6c8a9));
michael@0 1672 }
michael@0 1673
michael@0 1674 TEST_F(LoadCommand, FindSegment) {
michael@0 1675 WithConfiguration config(kBigEndian, 32);
michael@0 1676
michael@0 1677 LoadedSection segment1, segment2, segment3;
michael@0 1678 segment1.address() = 0xb8ae5752;
michael@0 1679 segment1.Append("Some contents!");
michael@0 1680 segment2.address() = 0xd6b0ce83;
michael@0 1681 segment2.Append("Different stuff.");
michael@0 1682 segment3.address() = 0x7374fd2a;
michael@0 1683 segment3.Append("Further materials.");
michael@0 1684
michael@0 1685 SegmentLoadCommand cmd1, cmd2, cmd3;
michael@0 1686 cmd1.Header("first", segment1, 0xfadb6932, 0x175bf529, 0x0de790ad);
michael@0 1687 cmd2.Header("second", segment2, 0xeef716e0, 0xe103a9d7, 0x7d38a8ef);
michael@0 1688 cmd3.Header("third", segment3, 0xe172b39e, 0x86012f07, 0x080ac94d);
michael@0 1689
michael@0 1690 LoadCommands commands;
michael@0 1691 commands.Place(&cmd1).Place(&cmd2).Place(&cmd3);
michael@0 1692
michael@0 1693 MachOFile file;
michael@0 1694 file.Header(&commands).Place(&segment1).Place(&segment2).Place(&segment3);
michael@0 1695
michael@0 1696 ReadFile(&file, true, CPU_TYPE_ANY, 0);
michael@0 1697
michael@0 1698 Segment actual_segment;
michael@0 1699
michael@0 1700 EXPECT_FALSE(reader.FindSegment("murphy", &actual_segment));
michael@0 1701
michael@0 1702 EXPECT_TRUE(reader.FindSegment("second", &actual_segment));
michael@0 1703 EXPECT_EQ(0xd6b0ce83, actual_segment.vmaddr);
michael@0 1704 }
michael@0 1705
michael@0 1706
michael@0 1707 // Symtab tests.
michael@0 1708
michael@0 1709 // A StringAssembler is a class for generating .stabstr sections to present
michael@0 1710 // as input to the STABS parser.
michael@0 1711 class StringAssembler: public SizedSection {
michael@0 1712 public:
michael@0 1713 // Add the string S to this StringAssembler, and return the string's
michael@0 1714 // offset within this compilation unit's strings.
michael@0 1715 size_t Add(const string &s) {
michael@0 1716 size_t offset = Size();
michael@0 1717 AppendCString(s);
michael@0 1718 return offset;
michael@0 1719 }
michael@0 1720 };
michael@0 1721
michael@0 1722 // A SymbolAssembler is a class for generating .stab sections to present as
michael@0 1723 // test input for the STABS parser.
michael@0 1724 class SymbolAssembler: public SizedSection {
michael@0 1725 public:
michael@0 1726 // Create a SymbolAssembler that uses StringAssembler for its strings.
michael@0 1727 explicit SymbolAssembler(StringAssembler *string_assembler)
michael@0 1728 : string_assembler_(string_assembler),
michael@0 1729 entry_count_(0) { }
michael@0 1730
michael@0 1731 // Append a STAB entry to the end of this section with the given
michael@0 1732 // characteristics. NAME is the offset of this entry's name string within
michael@0 1733 // its compilation unit's portion of the .stabstr section; this can be a
michael@0 1734 // value generated by a StringAssembler. Return a reference to this
michael@0 1735 // SymbolAssembler.
michael@0 1736 SymbolAssembler &Symbol(uint8_t type, uint8_t other, Label descriptor,
michael@0 1737 Label value, Label name) {
michael@0 1738 D32(name);
michael@0 1739 D8(type);
michael@0 1740 D8(other);
michael@0 1741 D16(descriptor);
michael@0 1742 Append(endianness(), word_size_ / 8, value);
michael@0 1743 entry_count_++;
michael@0 1744 return *this;
michael@0 1745 }
michael@0 1746
michael@0 1747 // As above, but automatically add NAME to our StringAssembler.
michael@0 1748 SymbolAssembler &Symbol(uint8_t type, uint8_t other, Label descriptor,
michael@0 1749 Label value, const string &name) {
michael@0 1750 return Symbol(type, other, descriptor, value, string_assembler_->Add(name));
michael@0 1751 }
michael@0 1752
michael@0 1753 private:
michael@0 1754 // The strings for our STABS entries.
michael@0 1755 StringAssembler *string_assembler_;
michael@0 1756
michael@0 1757 // The number of entries in this compilation unit so far.
michael@0 1758 size_t entry_count_;
michael@0 1759 };
michael@0 1760
michael@0 1761 class Symtab: public ReaderFixture, public Test { };
michael@0 1762
michael@0 1763 TEST_F(Symtab, Symtab32) {
michael@0 1764 WithConfiguration config(kLittleEndian, 32);
michael@0 1765
michael@0 1766 StringAssembler strings;
michael@0 1767 SymbolAssembler symbols(&strings);
michael@0 1768 symbols
michael@0 1769 .Symbol(0x52, 0x7c, 0x3470, 0x9bb02e7c, "hrududu")
michael@0 1770 .Symbol(0x50, 0x90, 0x7520, 0x1122525d, "Frith");
michael@0 1771
michael@0 1772 SizedSection symtab_command;
michael@0 1773 symtab_command
michael@0 1774 .D32(LC_SYMTAB) // command
michael@0 1775 .D32(symtab_command.final_size()) // size
michael@0 1776 .D32(symbols.start()) // file offset of symbols
michael@0 1777 .D32(2) // symbol count
michael@0 1778 .D32(strings.start()) // file offset of strings
michael@0 1779 .D32(strings.final_size()); // strings size
michael@0 1780
michael@0 1781 LoadCommands load_commands;
michael@0 1782 load_commands.Place(&symtab_command);
michael@0 1783
michael@0 1784 MachOFile file;
michael@0 1785 file.Header(&load_commands).Place(&symbols).Place(&strings);
michael@0 1786
michael@0 1787 ReadFile(&file, true, CPU_TYPE_ANY, 0);
michael@0 1788
michael@0 1789 ByteBuffer symbols_found, strings_found;
michael@0 1790 EXPECT_CALL(load_command_handler, SymtabCommand(_, _))
michael@0 1791 .WillOnce(DoAll(SaveArg<0>(&symbols_found),
michael@0 1792 SaveArg<1>(&strings_found),
michael@0 1793 Return(true)));
michael@0 1794 EXPECT_TRUE(reader.WalkLoadCommands(&load_command_handler));
michael@0 1795
michael@0 1796 EXPECT_EQ(24U, symbols_found.Size());
michael@0 1797 EXPECT_EQ(14U, strings_found.Size());
michael@0 1798 }
michael@0 1799
michael@0 1800 TEST_F(Symtab, Symtab64) {
michael@0 1801 WithConfiguration config(kBigEndian, 64);
michael@0 1802
michael@0 1803 StringAssembler strings;
michael@0 1804 SymbolAssembler symbols(&strings);
michael@0 1805 symbols
michael@0 1806 .Symbol(0xa7, 0xaf, 0x03af, 0x42f3072c74335181ULL, "foo")
michael@0 1807 .Symbol(0xb0, 0x9a, 0x2aa7, 0x2e2d349b3d5744a0ULL, "bar");
michael@0 1808
michael@0 1809 SizedSection symtab_command;
michael@0 1810 symtab_command
michael@0 1811 .D32(LC_SYMTAB) // command
michael@0 1812 .D32(symtab_command.final_size()) // size
michael@0 1813 .D32(symbols.start()) // file offset of symbols
michael@0 1814 .D32(2) // symbol count
michael@0 1815 .D32(strings.start()) // file offset of strings
michael@0 1816 .D32(strings.final_size()); // strings size
michael@0 1817
michael@0 1818 LoadCommands load_commands;
michael@0 1819 load_commands.Place(&symtab_command);
michael@0 1820
michael@0 1821 MachOFile file;
michael@0 1822 file.Header(&load_commands).Place(&symbols).Place(&strings);
michael@0 1823
michael@0 1824 ReadFile(&file, true, CPU_TYPE_ANY, 0);
michael@0 1825
michael@0 1826 ByteBuffer symbols_found, strings_found;
michael@0 1827 EXPECT_CALL(load_command_handler, SymtabCommand(_, _))
michael@0 1828 .WillOnce(DoAll(SaveArg<0>(&symbols_found),
michael@0 1829 SaveArg<1>(&strings_found),
michael@0 1830 Return(true)));
michael@0 1831 EXPECT_TRUE(reader.WalkLoadCommands(&load_command_handler));
michael@0 1832
michael@0 1833 EXPECT_EQ(32U, symbols_found.Size());
michael@0 1834 EXPECT_EQ(8U, strings_found.Size());
michael@0 1835 }
michael@0 1836
michael@0 1837 TEST_F(Symtab, SymtabMisplacedSymbols) {
michael@0 1838 WithConfiguration config(kBigEndian, 32);
michael@0 1839
michael@0 1840 StringAssembler strings;
michael@0 1841 SymbolAssembler symbols(&strings);
michael@0 1842 symbols
michael@0 1843 .Symbol(0xa7, 0xaf, 0x03af, 0x42f3072c74335181ULL, "foo")
michael@0 1844 .Symbol(0xb0, 0x9a, 0x2aa7, 0x2e2d349b3d5744a0ULL, "bar");
michael@0 1845
michael@0 1846 SizedSection symtab_command;
michael@0 1847 symtab_command
michael@0 1848 .D32(LC_SYMTAB) // command
michael@0 1849 .D32(symtab_command.final_size()) // size
michael@0 1850 .D32(symbols.start()) // file offset of symbols
michael@0 1851 .D32(3) // symbol count (too many)
michael@0 1852 .D32(strings.start()) // file offset of strings
michael@0 1853 .D32(strings.final_size()); // strings size
michael@0 1854
michael@0 1855 LoadCommands load_commands;
michael@0 1856 load_commands.Place(&symtab_command);
michael@0 1857
michael@0 1858 MachOFile file;
michael@0 1859 // Put symbols at end, so the excessive length will be noticed.
michael@0 1860 file.Header(&load_commands).Place(&strings).Place(&symbols);
michael@0 1861
michael@0 1862 ReadFile(&file, true, CPU_TYPE_ANY, 0);
michael@0 1863
michael@0 1864 EXPECT_CALL(reporter, MisplacedSymbolTable()).Times(1);
michael@0 1865 EXPECT_FALSE(reader.WalkLoadCommands(&load_command_handler));
michael@0 1866 }
michael@0 1867
michael@0 1868 TEST_F(Symtab, SymtabMisplacedStrings) {
michael@0 1869 WithConfiguration config(kLittleEndian, 32);
michael@0 1870
michael@0 1871 StringAssembler strings;
michael@0 1872 SymbolAssembler symbols(&strings);
michael@0 1873 symbols
michael@0 1874 .Symbol(0xa7, 0xaf, 0x03af, 0x42f3072c74335181ULL, "foo")
michael@0 1875 .Symbol(0xb0, 0x9a, 0x2aa7, 0x2e2d349b3d5744a0ULL, "bar");
michael@0 1876
michael@0 1877 SizedSection symtab_command;
michael@0 1878 symtab_command
michael@0 1879 .D32(LC_SYMTAB) // command
michael@0 1880 .D32(symtab_command.final_size()) // size
michael@0 1881 .D32(symbols.start()) // file offset of symbols
michael@0 1882 .D32(2) // symbol count
michael@0 1883 .D32(strings.start()) // file offset of strings
michael@0 1884 .D32(strings.final_size() + 1); // strings size (too long)
michael@0 1885
michael@0 1886 LoadCommands load_commands;
michael@0 1887 load_commands.Place(&symtab_command);
michael@0 1888
michael@0 1889 MachOFile file;
michael@0 1890 // Put strings at end, so the excessive length will be noticed.
michael@0 1891 file.Header(&load_commands).Place(&symbols).Place(&strings);
michael@0 1892
michael@0 1893 ReadFile(&file, true, CPU_TYPE_ANY, 0);
michael@0 1894
michael@0 1895 EXPECT_CALL(reporter, MisplacedSymbolTable()).Times(1);
michael@0 1896 EXPECT_FALSE(reader.WalkLoadCommands(&load_command_handler));
michael@0 1897 }
michael@0 1898

mercurial