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