OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2014 The Crashpad Authors. All rights reserved. | |
2 // | |
3 // Licensed under the Apache License, Version 2.0 (the "License"); | |
4 // you may not use this file except in compliance with the License. | |
5 // You may obtain a copy of the License at | |
6 // | |
7 // http://www.apache.org/licenses/LICENSE-2.0 | |
8 // | |
9 // Unless required by applicable law or agreed to in writing, software | |
10 // distributed under the License is distributed on an "AS IS" BASIS, | |
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
12 // See the License for the specific language governing permissions and | |
13 // limitations under the License. | |
14 | |
15 #include "util/mac/mach_o_image_reader.h" | |
16 | |
17 #include <dlfcn.h> | |
18 #include <mach-o/dyld_images.h> | |
19 #include <mach-o/getsect.h> | |
20 #include <mach-o/loader.h> | |
21 #include <stdint.h> | |
22 #include <string.h> | |
23 | |
24 #include "base/strings/stringprintf.h" | |
25 #include "build/build_config.h" | |
26 #include "gtest/gtest.h" | |
27 #include "util/mac/mach_o_image_segment_reader.h" | |
28 #include "util/mac/process_reader.h" | |
29 #include "util/mac/process_types.h" | |
30 #include "util/misc/uuid.h" | |
31 | |
32 extern "C" { | |
33 const struct dyld_all_image_infos* _dyld_get_all_image_infos(); | |
Robert Sesek
2014/09/04 13:18:42
Move this to a shared header? I've seen it before
| |
34 } // extern "C" | |
35 | |
36 namespace { | |
37 | |
38 using namespace crashpad; | |
39 | |
40 // Native types and constants, in cases where the 32-bit and 64-bit versions | |
41 // are different. | |
42 #if defined(ARCH_CPU_64_BITS) | |
43 typedef mach_header_64 MachHeader; | |
44 const uint32_t kMachMagic = MH_MAGIC_64; | |
45 typedef segment_command_64 SegmentCommand; | |
46 const uint32_t kSegmentCommand = LC_SEGMENT_64; | |
47 typedef section_64 Section; | |
48 #else | |
49 typedef mach_header MachHeader; | |
50 const uint32_t kMachMagic = MH_MAGIC; | |
51 typedef segment_command SegmentCommand; | |
52 const uint32_t kSegmentCommand = LC_SEGMENT; | |
53 typedef section Section; | |
54 #endif | |
55 | |
56 #if defined(ARCH_CPU_X86_64) | |
57 const int kCPUType = CPU_TYPE_X86_64; | |
58 #elif defined(ARCH_CPU_X86) | |
59 const int kCPUType = CPU_TYPE_X86; | |
60 #endif | |
61 | |
62 // Verifies that |expect_section| and |actual_section| agree. | |
63 void ExpectSection(const Section* expect_section, | |
64 const process_types::section* actual_section) { | |
65 ASSERT_TRUE(expect_section); | |
66 ASSERT_TRUE(actual_section); | |
67 | |
68 EXPECT_EQ( | |
69 MachOImageSegmentReader::SectionNameString(expect_section->sectname), | |
70 MachOImageSegmentReader::SectionNameString(actual_section->sectname)); | |
71 EXPECT_EQ( | |
72 MachOImageSegmentReader::SegmentNameString(expect_section->segname), | |
73 MachOImageSegmentReader::SegmentNameString(actual_section->segname)); | |
74 EXPECT_EQ(expect_section->addr, actual_section->addr); | |
75 EXPECT_EQ(expect_section->size, actual_section->size); | |
76 EXPECT_EQ(expect_section->offset, actual_section->offset); | |
77 EXPECT_EQ(expect_section->align, actual_section->align); | |
78 EXPECT_EQ(expect_section->reloff, actual_section->reloff); | |
79 EXPECT_EQ(expect_section->nreloc, actual_section->nreloc); | |
80 EXPECT_EQ(expect_section->flags, actual_section->flags); | |
81 EXPECT_EQ(expect_section->reserved1, actual_section->reserved1); | |
82 EXPECT_EQ(expect_section->reserved2, actual_section->reserved2); | |
83 } | |
84 | |
85 // Verifies that |expect_segment| is a valid Mach-O segment load command for the | |
86 // current system by checking its |cmd| field. Then, verifies that the | |
87 // information in |actual_segment| matches that in |expect_segment|. The | |
88 // |segname|, |vmaddr|, |vmsize|, and |fileoff| fields are examined. Each | |
89 // section within the segment is also examined by calling ExpectSection(). | |
90 // Access to each section via both MachOImageSegmentReader::GetSectionByName() | |
91 // and MachOImageReader::GetSectionByName() is verified, expecting that each | |
92 // call produces the same section. Segment and section data addresses are | |
93 // verified against data obtained by calling getsegmentdata() and | |
94 // getsectiondata(). The segment is checked to make sure that it behaves | |
95 // correctly when attempting to look up a nonexistent section by name. | |
96 // |section_index| is used to track the last-used section index in an image on | |
97 // entry, and is reset to the last-used section index on return after the | |
98 // sections are processed. This is used to test that | |
99 // MachOImageReader::GetSectionAtIndex() returns the correct result. | |
100 void ExpectSegmentCommand(const SegmentCommand* expect_segment, | |
101 const MachHeader* expect_image, | |
102 const MachOImageSegmentReader* actual_segment, | |
103 mach_vm_address_t actual_segment_address, | |
104 mach_vm_size_t actual_segment_size, | |
105 const MachOImageReader* actual_image, | |
106 size_t* section_index) { | |
107 ASSERT_TRUE(expect_segment); | |
108 ASSERT_TRUE(actual_segment); | |
109 | |
110 EXPECT_EQ(kSegmentCommand, expect_segment->cmd); | |
111 | |
112 std::string segment_name = actual_segment->Name(); | |
113 EXPECT_EQ(MachOImageSegmentReader::SegmentNameString(expect_segment->segname), | |
114 segment_name); | |
115 EXPECT_EQ(expect_segment->vmaddr, actual_segment->vmaddr()); | |
116 EXPECT_EQ(expect_segment->vmsize, actual_segment->vmsize()); | |
117 EXPECT_EQ(expect_segment->fileoff, actual_segment->fileoff()); | |
118 | |
119 if (actual_segment->SegmentSlides()) { | |
120 EXPECT_EQ(actual_segment_address, | |
121 actual_segment->vmaddr() + actual_image->Slide()); | |
122 | |
123 unsigned long expect_segment_size; | |
124 const uint8_t* expect_segment_data = getsegmentdata( | |
125 expect_image, segment_name.c_str(), &expect_segment_size); | |
126 mach_vm_address_t expect_segment_address = | |
127 reinterpret_cast<mach_vm_address_t>(expect_segment_data); | |
128 EXPECT_EQ(expect_segment_address, actual_segment_address); | |
129 EXPECT_EQ(expect_segment_size, actual_segment->vmsize()); | |
130 EXPECT_EQ(actual_segment->vmsize(), actual_segment_size); | |
131 } else { | |
132 // getsegmentdata() doesn’t return appropriate data for the __PAGEZERO | |
133 // segment because getsegmentdata() always adjusts for slide, but the | |
134 // __PAGEZERO segment never slides, it just grows. Skip the getsegmentdata() | |
135 // check for that segment according to the same rules that the kernel uses | |
136 // to identify __PAGEZERO. See 10.9.4 xnu-2422.110.17/bsd/kern/mach_loader.c | |
137 // load_segment(). | |
138 EXPECT_EQ(actual_segment_address, actual_segment->vmaddr()); | |
139 EXPECT_EQ(actual_segment->vmsize() + actual_image->Slide(), | |
140 actual_segment_size); | |
141 } | |
142 | |
143 ASSERT_EQ(expect_segment->nsects, actual_segment->nsects()); | |
144 | |
145 // Make sure that the expected load command is big enough for the number of | |
146 // sections that it claims to have, and set up a pointer to its first section | |
147 // structure. | |
148 ASSERT_EQ(sizeof(*expect_segment) + expect_segment->nsects * sizeof(Section), | |
149 expect_segment->cmdsize); | |
150 const Section* expect_sections = | |
151 reinterpret_cast<const Section*>(&expect_segment[1]); | |
152 | |
153 for (size_t index = 0; index < actual_segment->nsects(); ++index) { | |
154 const Section* expect_section = &expect_sections[index]; | |
155 const process_types::section* actual_section = | |
156 actual_segment->GetSectionAtIndex(index); | |
157 ExpectSection(&expect_sections[index], actual_section); | |
158 if (testing::Test::HasFatalFailure()) { | |
159 return; | |
160 } | |
161 | |
162 // Make sure that the section is accessible by GetSectionByName as well. | |
163 std::string section_name = | |
164 MachOImageSegmentReader::SectionNameString(expect_section->sectname); | |
165 const process_types::section* actual_section_by_name = | |
166 actual_segment->GetSectionByName(section_name); | |
167 EXPECT_EQ(actual_section, actual_section_by_name); | |
168 | |
169 // Make sure that the section is accessible by the parent MachOImageReader’s | |
170 // GetSectionByName. | |
171 mach_vm_address_t actual_section_address; | |
172 const process_types::section* actual_section_from_image_by_name = | |
173 actual_image->GetSectionByName( | |
174 segment_name, section_name, &actual_section_address); | |
175 EXPECT_EQ(actual_section, actual_section_from_image_by_name); | |
176 | |
177 if (actual_segment->SegmentSlides()) { | |
178 EXPECT_EQ(actual_section_address, | |
179 actual_section->addr + actual_image->Slide()); | |
180 | |
181 unsigned long expect_section_size; | |
182 const uint8_t* expect_section_data = getsectiondata(expect_image, | |
183 segment_name.c_str(), | |
184 section_name.c_str(), | |
185 &expect_section_size); | |
186 mach_vm_address_t expect_section_address = | |
187 reinterpret_cast<mach_vm_address_t>(expect_section_data); | |
188 EXPECT_EQ(expect_section_address, actual_section_address); | |
189 EXPECT_EQ(expect_section_size, actual_section->size); | |
190 } else { | |
191 EXPECT_EQ(actual_section_address, actual_section->addr); | |
192 } | |
193 | |
194 // Test the parent MachOImageReader’s GetSectionAtIndex as well. | |
195 mach_vm_address_t actual_section_address_at_index; | |
196 const process_types::section* actual_section_from_image_at_index = | |
197 actual_image->GetSectionAtIndex(++(*section_index), | |
198 &actual_section_address_at_index); | |
199 EXPECT_EQ(actual_section, actual_section_from_image_at_index); | |
200 EXPECT_EQ(actual_section_address, actual_section_address_at_index); | |
201 } | |
202 | |
203 EXPECT_EQ(NULL, actual_segment->GetSectionByName("NoSuchSection")); | |
204 } | |
205 | |
206 // Walks through the load commands of |expect_image|, finding all of the | |
207 // expected segment commands. For each expected segment command, calls | |
208 // actual_image->GetSegmentByName() to obtain an actual segment command, and | |
209 // calls ExpectSegmentCommand() to compare the expected and actual segments. A | |
210 // series of by-name lookups is also performed on the segment to ensure that it | |
211 // behaves correctly when attempting to look up segment and section names that | |
212 // are not present. |test_section_indices| should be true to test | |
213 // MachOImageReader::GetSectionAtIndex() using out-of-range section indices. | |
214 // This should be tested for at least one module, but it’s very noisy in terms | |
215 // of logging output, so this knob is provided to suppress this portion of the | |
216 // test when looping over all modules. | |
217 void ExpectSegmentCommands(const MachHeader* expect_image, | |
218 const MachOImageReader* actual_image, | |
219 bool test_section_index_bounds) { | |
220 ASSERT_TRUE(expect_image); | |
221 ASSERT_TRUE(actual_image); | |
222 | |
223 const char* commands_base = reinterpret_cast<const char*>(&expect_image[1]); | |
224 uint32_t position = 0; | |
225 size_t section_index = 0; | |
226 for (uint32_t index = 0; index < expect_image->ncmds; ++index) { | |
227 ASSERT_LT(position, expect_image->sizeofcmds); | |
228 const load_command* command = | |
229 reinterpret_cast<const load_command*>(&commands_base[position]); | |
230 ASSERT_LE(position + command->cmdsize, expect_image->sizeofcmds); | |
231 if (command->cmd == kSegmentCommand) { | |
232 const SegmentCommand* expect_segment = | |
233 reinterpret_cast<const SegmentCommand*>(command); | |
234 std::string segment_name = | |
235 MachOImageSegmentReader::SegmentNameString(expect_segment->segname); | |
236 mach_vm_address_t actual_segment_address; | |
237 mach_vm_size_t actual_segment_size; | |
238 const MachOImageSegmentReader* actual_segment = | |
239 actual_image->GetSegmentByName( | |
240 segment_name, &actual_segment_address, &actual_segment_size); | |
241 ExpectSegmentCommand(expect_segment, | |
242 expect_image, | |
243 actual_segment, | |
244 actual_segment_address, | |
245 actual_segment_size, | |
246 actual_image, | |
247 §ion_index); | |
248 if (testing::Test::HasFatalFailure()) { | |
249 return; | |
250 } | |
251 } | |
252 position += command->cmdsize; | |
253 } | |
254 EXPECT_EQ(expect_image->sizeofcmds, position); | |
255 | |
256 if (test_section_index_bounds) { | |
257 // GetSectionAtIndex uses a 1-based index. Make sure that the range is | |
258 // correct. | |
259 EXPECT_EQ(NULL, actual_image->GetSectionAtIndex(0, NULL)); | |
260 EXPECT_EQ(NULL, actual_image->GetSectionAtIndex(section_index + 1, NULL)); | |
261 } | |
262 | |
263 // Make sure that by-name lookups for names that don’t exist work properly: | |
264 // they should return NULL. | |
265 EXPECT_FALSE(actual_image->GetSegmentByName("NoSuchSegment", NULL, NULL)); | |
266 EXPECT_FALSE( | |
267 actual_image->GetSectionByName("NoSuchSegment", "NoSuchSection", NULL)); | |
268 | |
269 // Make sure that there’s a __TEXT segment so that this can do a valid test of | |
270 // a section that doesn’t exist within a segment that does. | |
271 EXPECT_TRUE(actual_image->GetSegmentByName(SEG_TEXT, NULL, NULL)); | |
272 EXPECT_FALSE(actual_image->GetSectionByName(SEG_TEXT, "NoSuchSection", NULL)); | |
273 | |
274 // Similarly, make sure that a section name that exists in one segment isn’t | |
275 // accidentally found during a lookup for that section in a different segment. | |
276 EXPECT_TRUE(actual_image->GetSectionByName(SEG_TEXT, SECT_TEXT, NULL)); | |
277 EXPECT_FALSE( | |
278 actual_image->GetSectionByName("NoSuchSegment", SECT_TEXT, NULL)); | |
279 EXPECT_FALSE(actual_image->GetSectionByName(SEG_DATA, SECT_TEXT, NULL)); | |
280 | |
281 // The __LINKEDIT segment normally does exist but doesn’t have any sections. | |
282 EXPECT_FALSE( | |
283 actual_image->GetSectionByName(SEG_LINKEDIT, "NoSuchSection", NULL)); | |
284 EXPECT_FALSE(actual_image->GetSectionByName(SEG_LINKEDIT, SECT_TEXT, NULL)); | |
285 } | |
286 | |
287 // Verifies that |expect_image| is a vaild Mach-O header for the current system | |
288 // by checking its |magic| and |cputype| fields. Then, verifies that the | |
289 // information in |actual_image| matches that in |expect_image|. The |filetype| | |
290 // field is examined, and actual_image->Address() is compared to | |
291 // |expect_image_address|. Various other attributes of |actual_image| are | |
292 // sanity-checked depending on the Mach-O file type. Finally, | |
293 // ExpectSegmentCommands() is called to verify all that all of the segments | |
294 // match; |test_section_index_bounds| is used as an argument to that function. | |
295 void ExpectMachImage(const MachHeader* expect_image, | |
296 mach_vm_address_t expect_image_address, | |
297 const MachOImageReader* actual_image, | |
298 bool test_section_index_bounds) { | |
299 ASSERT_TRUE(expect_image); | |
300 ASSERT_TRUE(actual_image); | |
301 | |
302 EXPECT_EQ(kMachMagic, expect_image->magic); | |
303 EXPECT_EQ(kCPUType, expect_image->cputype); | |
304 | |
305 EXPECT_EQ(expect_image->filetype, actual_image->FileType()); | |
306 EXPECT_EQ(expect_image_address, actual_image->Address()); | |
307 | |
308 mach_vm_address_t actual_text_segment_address; | |
309 mach_vm_size_t actual_text_segment_size; | |
310 const MachOImageSegmentReader* actual_text_segment = | |
311 actual_image->GetSegmentByName( | |
312 SEG_TEXT, &actual_text_segment_address, &actual_text_segment_size); | |
313 ASSERT_TRUE(actual_text_segment); | |
314 EXPECT_EQ(expect_image_address, actual_text_segment_address); | |
315 EXPECT_EQ(actual_image->Size(), actual_text_segment_size); | |
316 EXPECT_EQ(expect_image_address - actual_text_segment->vmaddr(), | |
317 actual_image->Slide()); | |
318 | |
319 uint32_t file_type = actual_image->FileType(); | |
320 EXPECT_TRUE(file_type == MH_EXECUTE || file_type == MH_DYLIB || | |
321 file_type == MH_DYLINKER || file_type == MH_BUNDLE); | |
322 | |
323 if (file_type == MH_EXECUTE || file_type == MH_DYLINKER) { | |
324 EXPECT_EQ("/usr/lib/dyld", actual_image->DylinkerName()); | |
325 } | |
326 | |
327 // For these, just don’t crash or anything. | |
328 if (file_type == MH_DYLIB) { | |
329 actual_image->DylibVersion(); | |
330 } | |
331 actual_image->SourceVersion(); | |
332 UUID uuid; | |
333 actual_image->UUID(&uuid); | |
334 | |
335 ExpectSegmentCommands(expect_image, actual_image, test_section_index_bounds); | |
336 } | |
337 | |
338 TEST(MachOImageReader, Self_MainExecutable) { | |
339 ProcessReader process_reader; | |
340 ASSERT_TRUE(process_reader.Initialize(mach_task_self())); | |
341 | |
342 const MachHeader* mh_execute_header = reinterpret_cast<MachHeader*>( | |
343 dlsym(RTLD_MAIN_ONLY, "_mh_execute_header")); | |
344 ASSERT_NE(static_cast<void*>(NULL), mh_execute_header); | |
345 mach_vm_address_t mh_execute_header_address = | |
346 reinterpret_cast<mach_vm_address_t>(mh_execute_header); | |
347 | |
348 MachOImageReader image_reader; | |
349 ASSERT_TRUE(image_reader.Initialize( | |
350 &process_reader, mh_execute_header_address, "mh_execute_header")); | |
351 | |
352 EXPECT_EQ(static_cast<uint32_t>(MH_EXECUTE), image_reader.FileType()); | |
353 | |
354 ExpectMachImage( | |
355 mh_execute_header, mh_execute_header_address, &image_reader, true); | |
356 } | |
357 | |
358 TEST(MachOImageReader, Self_DyldImages) { | |
359 ProcessReader process_reader; | |
360 ASSERT_TRUE(process_reader.Initialize(mach_task_self())); | |
361 | |
362 const struct dyld_all_image_infos* dyld_image_infos = | |
363 _dyld_get_all_image_infos(); | |
364 ASSERT_GE(dyld_image_infos->version, 1u); | |
365 ASSERT_TRUE(dyld_image_infos->infoArray); | |
366 | |
367 for (uint32_t index = 0; index < dyld_image_infos->infoArrayCount; ++index) { | |
368 const dyld_image_info* dyld_image = &dyld_image_infos->infoArray[index]; | |
369 SCOPED_TRACE(base::StringPrintf( | |
370 "index %u, image %s", index, dyld_image->imageFilePath)); | |
371 | |
372 // dyld_image_info::imageLoadAddress is poorly-declared: it’s declared as | |
373 // const mach_header* in both 32-bit and 64-bit environments, but in the | |
374 // 64-bit environment, it should be const mach_header_64*. | |
375 const MachHeader* mach_header = | |
376 reinterpret_cast<const MachHeader*>(dyld_image->imageLoadAddress); | |
377 mach_vm_address_t image_address = | |
378 reinterpret_cast<mach_vm_address_t>(mach_header); | |
379 | |
380 MachOImageReader image_reader; | |
381 ASSERT_TRUE(image_reader.Initialize( | |
382 &process_reader, image_address, dyld_image->imageFilePath)); | |
383 | |
384 uint32_t file_type = image_reader.FileType(); | |
385 if (index == 0) { | |
386 EXPECT_EQ(static_cast<uint32_t>(MH_EXECUTE), file_type); | |
387 } else { | |
388 EXPECT_TRUE(file_type == MH_DYLIB || file_type == MH_BUNDLE); | |
389 } | |
390 | |
391 ExpectMachImage(mach_header, image_address, &image_reader, false); | |
392 if (Test::HasFatalFailure()) { | |
393 return; | |
394 } | |
395 } | |
396 | |
397 // Now that all of the modules have been verified, make sure that dyld itself | |
398 // can be read properly too. | |
399 if (dyld_image_infos->version >= 2) { | |
400 SCOPED_TRACE("dyld"); | |
401 | |
402 // dyld_all_image_infos::dyldImageLoadAddress is poorly-declared too. | |
403 const MachHeader* mach_header = reinterpret_cast<const MachHeader*>( | |
404 dyld_image_infos->dyldImageLoadAddress); | |
405 mach_vm_address_t image_address = | |
406 reinterpret_cast<mach_vm_address_t>(mach_header); | |
407 | |
408 MachOImageReader image_reader; | |
409 ASSERT_TRUE( | |
410 image_reader.Initialize(&process_reader, image_address, "dyld")); | |
411 | |
412 EXPECT_EQ(static_cast<uint32_t>(MH_DYLINKER), image_reader.FileType()); | |
413 | |
414 ExpectMachImage(mach_header, image_address, &image_reader, false); | |
415 if (Test::HasFatalFailure()) { | |
416 return; | |
417 } | |
418 } | |
419 | |
420 // If dyld is new enough to record UUIDs, check the UUID of any module that | |
421 // it says has one. Note that dyld doesn’t record UUIDs of anything that | |
422 // loaded out of the shared cache, but it should at least have a UUID for the | |
423 // main executable if it has one. | |
424 if (dyld_image_infos->version >= 8 && dyld_image_infos->uuidArray) { | |
425 for (uint32_t index = 0; | |
426 index < dyld_image_infos->uuidArrayCount; | |
427 ++index) { | |
428 const dyld_uuid_info* dyld_image = &dyld_image_infos->uuidArray[index]; | |
429 SCOPED_TRACE(base::StringPrintf("uuid index %u", index)); | |
430 | |
431 // dyld_uuid_info::imageLoadAddress is poorly-declared too. | |
432 const MachHeader* mach_header = | |
433 reinterpret_cast<const MachHeader*>(dyld_image->imageLoadAddress); | |
434 mach_vm_address_t image_address = | |
435 reinterpret_cast<mach_vm_address_t>(mach_header); | |
436 | |
437 MachOImageReader image_reader; | |
438 ASSERT_TRUE( | |
439 image_reader.Initialize(&process_reader, image_address, "uuid")); | |
440 | |
441 ExpectMachImage(mach_header, image_address, &image_reader, false); | |
442 | |
443 UUID expected_uuid; | |
444 expected_uuid.InitializeFromBytes(dyld_image->imageUUID); | |
445 UUID actual_uuid; | |
446 image_reader.UUID(&actual_uuid); | |
447 EXPECT_EQ(0, memcmp(&actual_uuid, &expected_uuid, sizeof(UUID))); | |
Robert Sesek
2014/09/04 13:18:42
Might make sense to add UUID::operator==.
| |
448 } | |
449 } | |
450 } | |
451 | |
452 } // namespace | |
OLD | NEW |