Chromium Code Reviews| 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 |