OLD | NEW |
1 // Copyright 2014 The Crashpad Authors. All rights reserved. | 1 // Copyright 2014 The Crashpad Authors. All rights reserved. |
2 // | 2 // |
3 // Licensed under the Apache License, Version 2.0 (the "License"); | 3 // Licensed under the Apache License, Version 2.0 (the "License"); |
4 // you may not use this file except in compliance with 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 | 5 // You may obtain a copy of the License at |
6 // | 6 // |
7 // http://www.apache.org/licenses/LICENSE-2.0 | 7 // http://www.apache.org/licenses/LICENSE-2.0 |
8 // | 8 // |
9 // Unless required by applicable law or agreed to in writing, software | 9 // Unless required by applicable law or agreed to in writing, software |
10 // distributed under the License is distributed on an "AS IS" BASIS, | 10 // distributed under the License is distributed on an "AS IS" BASIS, |
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
12 // See the License for the specific language governing permissions and | 12 // See the License for the specific language governing permissions and |
13 // limitations under the License. | 13 // limitations under the License. |
14 | 14 |
15 #include "util/mac/mach_o_image_reader.h" | 15 #include "util/mac/mach_o_image_reader.h" |
16 | 16 |
17 #include <mach-o/loader.h> | 17 #include <mach-o/loader.h> |
18 #include <mach-o/nlist.h> | 18 #include <mach-o/nlist.h> |
19 #include <string.h> | 19 #include <string.h> |
20 | 20 |
21 #include <limits> | 21 #include <limits> |
22 #include <vector> | 22 #include <vector> |
23 | 23 |
24 #include "base/logging.h" | 24 #include "base/logging.h" |
25 #include "base/strings/stringprintf.h" | 25 #include "base/strings/stringprintf.h" |
26 #include "util/mac/checked_mach_address_range.h" | 26 #include "util/mac/checked_mach_address_range.h" |
27 #include "util/mac/mach_o_image_segment_reader.h" | 27 #include "util/mac/mach_o_image_segment_reader.h" |
| 28 #include "util/mac/mach_o_image_symbol_table_reader.h" |
28 #include "util/mac/process_reader.h" | 29 #include "util/mac/process_reader.h" |
29 | 30 |
30 namespace { | 31 namespace { |
31 | 32 |
32 const uint32_t kInvalidSegmentIndex = std::numeric_limits<uint32_t>::max(); | 33 const uint32_t kInvalidSegmentIndex = std::numeric_limits<uint32_t>::max(); |
33 | 34 |
34 } // namespace | 35 } // namespace |
35 | 36 |
36 namespace crashpad { | 37 namespace crashpad { |
37 | 38 |
38 MachOImageReader::MachOImageReader() | 39 MachOImageReader::MachOImageReader() |
39 : segments_(), | 40 : segments_(), |
40 segment_map_(), | 41 segment_map_(), |
41 module_info_(), | 42 module_info_(), |
42 dylinker_name_(), | 43 dylinker_name_(), |
43 uuid_(), | 44 uuid_(), |
44 address_(0), | 45 address_(0), |
45 size_(0), | 46 size_(0), |
46 slide_(0), | 47 slide_(0), |
47 source_version_(0), | 48 source_version_(0), |
48 symtab_command_(), | 49 symtab_command_(), |
49 dysymtab_command_(), | 50 dysymtab_command_(), |
| 51 symbol_table_(), |
50 id_dylib_command_(), | 52 id_dylib_command_(), |
51 process_reader_(NULL), | 53 process_reader_(NULL), |
52 file_type_(0), | 54 file_type_(0), |
53 initialized_() { | 55 initialized_(), |
| 56 symbol_table_initialized_() { |
54 } | 57 } |
55 | 58 |
56 MachOImageReader::~MachOImageReader() { | 59 MachOImageReader::~MachOImageReader() { |
57 } | 60 } |
58 | 61 |
59 bool MachOImageReader::Initialize(ProcessReader* process_reader, | 62 bool MachOImageReader::Initialize(ProcessReader* process_reader, |
60 mach_vm_address_t address, | 63 mach_vm_address_t address, |
61 const std::string& name) { | 64 const std::string& name) { |
62 INITIALIZATION_STATE_SET_INITIALIZING(initialized_); | 65 INITIALIZATION_STATE_SET_INITIALIZING(initialized_); |
63 | 66 |
(...skipping 153 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
217 } | 220 } |
218 | 221 |
219 for (size_t reader_index = 0; | 222 for (size_t reader_index = 0; |
220 reader_index < arraysize(kLoadCommandReaders); | 223 reader_index < arraysize(kLoadCommandReaders); |
221 ++reader_index) { | 224 ++reader_index) { |
222 if (load_command.cmd != kLoadCommandReaders[reader_index].command) { | 225 if (load_command.cmd != kLoadCommandReaders[reader_index].command) { |
223 continue; | 226 continue; |
224 } | 227 } |
225 | 228 |
226 if (load_command.cmdsize < kLoadCommandReaders[reader_index].size) { | 229 if (load_command.cmdsize < kLoadCommandReaders[reader_index].size) { |
227 LOG(WARNING) | 230 LOG(WARNING) << base::StringPrintf( |
228 << base::StringPrintf( | 231 "load command cmdsize 0x%x insufficient for 0x%zx", |
229 "load command cmdsize 0x%x insufficient for 0x%zx", | 232 load_command.cmdsize, |
230 load_command.cmdsize, | 233 kLoadCommandReaders[reader_index].size) |
231 kLoadCommandReaders[reader_index].size) | 234 << load_command_info; |
232 << load_command_info; | |
233 return false; | 235 return false; |
234 } | 236 } |
235 | 237 |
236 if (kLoadCommandReaders[reader_index].singleton) { | 238 if (kLoadCommandReaders[reader_index].singleton) { |
237 if (singleton_indices[reader_index] != kInvalidSegmentIndex) { | 239 if (singleton_indices[reader_index] != kInvalidSegmentIndex) { |
238 LOG(WARNING) << "duplicate load command at " | 240 LOG(WARNING) << "duplicate load command at " |
239 << singleton_indices[reader_index] | 241 << singleton_indices[reader_index] << load_command_info; |
240 << load_command_info; | |
241 return false; | 242 return false; |
242 } | 243 } |
243 | 244 |
244 singleton_indices[reader_index] = load_command_index; | 245 singleton_indices[reader_index] = load_command_index; |
245 } | 246 } |
246 | 247 |
247 if (!((this)->*(kLoadCommandReaders[reader_index].function))( | 248 if (!((this)->*(kLoadCommandReaders[reader_index].function))( |
248 load_command_address, load_command_info)) { | 249 load_command_address, load_command_info)) { |
249 return false; | 250 return false; |
250 } | 251 } |
(...skipping 91 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
342 | 343 |
343 if (address) { | 344 if (address) { |
344 *address = section->addr + (segment->SegmentSlides() ? slide_ : 0); | 345 *address = section->addr + (segment->SegmentSlides() ? slide_ : 0); |
345 } | 346 } |
346 | 347 |
347 return section; | 348 return section; |
348 } | 349 } |
349 | 350 |
350 const process_types::section* MachOImageReader::GetSectionAtIndex( | 351 const process_types::section* MachOImageReader::GetSectionAtIndex( |
351 size_t index, | 352 size_t index, |
| 353 const MachOImageSegmentReader** containing_segment, |
352 mach_vm_address_t* address) const { | 354 mach_vm_address_t* address) const { |
353 INITIALIZATION_STATE_DCHECK_VALID(initialized_); | 355 INITIALIZATION_STATE_DCHECK_VALID(initialized_); |
354 | 356 |
355 COMPILE_ASSERT(NO_SECT == 0, no_sect_must_be_zero); | 357 COMPILE_ASSERT(NO_SECT == 0, no_sect_must_be_zero); |
356 if (index == NO_SECT) { | 358 if (index == NO_SECT) { |
357 LOG(WARNING) << "section index " << index << " out of range"; | 359 LOG(WARNING) << "section index " << index << " out of range"; |
358 return NULL; | 360 return NULL; |
359 } | 361 } |
360 | 362 |
361 // Switch to a more comfortable 0-based index. | 363 // Switch to a more comfortable 0-based index. |
362 size_t local_index = index - 1; | 364 size_t local_index = index - 1; |
363 | 365 |
364 for (const MachOImageSegmentReader* segment : segments_) { | 366 for (const MachOImageSegmentReader* segment : segments_) { |
365 size_t nsects = segment->nsects(); | 367 size_t nsects = segment->nsects(); |
366 if (local_index < nsects) { | 368 if (local_index < nsects) { |
367 const process_types::section* section = | 369 const process_types::section* section = |
368 segment->GetSectionAtIndex(local_index); | 370 segment->GetSectionAtIndex(local_index); |
369 | 371 |
| 372 if (containing_segment) { |
| 373 *containing_segment = segment; |
| 374 } |
370 if (address) { | 375 if (address) { |
371 *address = section->addr + (segment->SegmentSlides() ? slide_ : 0); | 376 *address = section->addr + (segment->SegmentSlides() ? slide_ : 0); |
372 } | 377 } |
373 | 378 |
374 return section; | 379 return section; |
375 } | 380 } |
376 | 381 |
377 local_index -= nsects; | 382 local_index -= nsects; |
378 } | 383 } |
379 | 384 |
380 LOG(WARNING) << "section index " << index << " out of range"; | 385 LOG(WARNING) << "section index " << index << " out of range"; |
381 return NULL; | 386 return NULL; |
382 } | 387 } |
383 | 388 |
| 389 bool MachOImageReader::LookUpExternalDefinedSymbol( |
| 390 const std::string& name, |
| 391 mach_vm_address_t* value) const { |
| 392 INITIALIZATION_STATE_DCHECK_VALID(initialized_); |
| 393 |
| 394 if (symbol_table_initialized_.is_uninitialized()) { |
| 395 InitializeSymbolTable(); |
| 396 } |
| 397 |
| 398 if (!symbol_table_initialized_.is_valid() || !symbol_table_) { |
| 399 return false; |
| 400 } |
| 401 |
| 402 const MachOImageSymbolTableReader::SymbolInformation* symbol_info = |
| 403 symbol_table_->LookUpExternalDefinedSymbol(name); |
| 404 if (!symbol_info) { |
| 405 return false; |
| 406 } |
| 407 |
| 408 if (symbol_info->section == NO_SECT) { |
| 409 // This is an absolute (N_ABS) symbol, which requires no further validation |
| 410 // or processing. |
| 411 *value = symbol_info->value; |
| 412 return true; |
| 413 } |
| 414 |
| 415 // This is a symbol defined in a particular section, so make sure that it’s |
| 416 // valid for that section and fix it up for any “slide” as needed. |
| 417 |
| 418 mach_vm_address_t section_address; |
| 419 const MachOImageSegmentReader* segment; |
| 420 const process_types::section* section = |
| 421 GetSectionAtIndex(symbol_info->section, &segment, §ion_address); |
| 422 if (!section) { |
| 423 return false; |
| 424 } |
| 425 |
| 426 mach_vm_address_t slid_value = |
| 427 symbol_info->value + (segment->SegmentSlides() ? slide_ : 0); |
| 428 |
| 429 // The __mh_execute_header (_MH_EXECUTE_SYM) symbol is weird. In |
| 430 // position-independent executables, it shows up in the symbol table as a |
| 431 // symbol in section 1, although it’s not really in that section. It points to |
| 432 // the mach_header[_64], which is the beginning of the __TEXT segment, and the |
| 433 // __text section normally begins after the load commands in the __TEXT |
| 434 // segment. The range check below will fail for this symbol, because it’s not |
| 435 // really in the section it claims to be in. See Xcode 5.1 |
| 436 // ld64-236.3/src/ld/OutputFile.cpp ld::tool::OutputFile::buildSymbolTable(). |
| 437 // There, ld takes symbols that refer to anything in the mach_header[_64] and |
| 438 // marks them as being in section 1. Here, section 1 is treated in this same |
| 439 // special way as long as it’s in the __TEXT segment that begins at the start |
| 440 // of the image, which is normally the case, and as long as the symbol’s value |
| 441 // is the base of the image. |
| 442 // |
| 443 // This only happens for PIE executables, because __mh_execute_header needs |
| 444 // to slide. In non-PIE executables, __mh_execute_header is an absolute |
| 445 // symbol. |
| 446 CheckedMachAddressRange section_range( |
| 447 process_reader_, section_address, section->size); |
| 448 if (!section_range.ContainsValue(slid_value) && |
| 449 !(symbol_info->section == 1 && segment->Name() == SEG_TEXT && |
| 450 slid_value == Address())) { |
| 451 std::string section_name_full = |
| 452 MachOImageSegmentReader::SegmentAndSectionNameString(section->segname, |
| 453 section->sectname); |
| 454 LOG(WARNING) << base::StringPrintf( |
| 455 "symbol %s (0x%llx) outside of section %s (0x%llx + " |
| 456 "0x%llx)", |
| 457 name.c_str(), |
| 458 slid_value, |
| 459 section_name_full.c_str(), |
| 460 section_address, |
| 461 section->size) << module_info_; |
| 462 return false; |
| 463 } |
| 464 |
| 465 *value = slid_value; |
| 466 return true; |
| 467 } |
| 468 |
384 uint32_t MachOImageReader::DylibVersion() const { | 469 uint32_t MachOImageReader::DylibVersion() const { |
385 INITIALIZATION_STATE_DCHECK_VALID(initialized_); | 470 INITIALIZATION_STATE_DCHECK_VALID(initialized_); |
386 DCHECK_EQ(FileType(), static_cast<uint32_t>(MH_DYLIB)); | 471 DCHECK_EQ(FileType(), static_cast<uint32_t>(MH_DYLIB)); |
387 | 472 |
388 if (id_dylib_command_) { | 473 if (id_dylib_command_) { |
389 return id_dylib_command_->dylib_current_version; | 474 return id_dylib_command_->dylib_current_version; |
390 } | 475 } |
391 | 476 |
392 // In case this was a weird dylib without an LC_ID_DYLIB command. | 477 // In case this was a weird dylib without an LC_ID_DYLIB command. |
393 return 0; | 478 return 0; |
(...skipping 176 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
570 return true; | 655 return true; |
571 } | 656 } |
572 | 657 |
573 bool MachOImageReader::ReadUnexpectedCommand( | 658 bool MachOImageReader::ReadUnexpectedCommand( |
574 mach_vm_address_t load_command_address, | 659 mach_vm_address_t load_command_address, |
575 const std::string& load_command_info) { | 660 const std::string& load_command_info) { |
576 LOG(WARNING) << "unexpected load command" << load_command_info; | 661 LOG(WARNING) << "unexpected load command" << load_command_info; |
577 return false; | 662 return false; |
578 } | 663 } |
579 | 664 |
| 665 void MachOImageReader::InitializeSymbolTable() const { |
| 666 DCHECK(symbol_table_initialized_.is_uninitialized()); |
| 667 symbol_table_initialized_.set_invalid(); |
| 668 |
| 669 if (!symtab_command_) { |
| 670 // It’s technically valid for there to be no LC_SYMTAB, and in that case, |
| 671 // any symbol lookups should fail. Mark the symbol table as valid, and |
| 672 // LookUpExternalDefinedSymbol() will understand what it means when this is |
| 673 // valid but symbol_table_ is not present. |
| 674 symbol_table_initialized_.set_valid(); |
| 675 return; |
| 676 } |
| 677 |
| 678 // Find the __LINKEDIT segment. Technically, the symbol table can be in any |
| 679 // mapped segment, but by convention, it’s in the one named __LINKEDIT. |
| 680 mach_vm_address_t linkedit_address; |
| 681 mach_vm_size_t linkedit_size; |
| 682 const MachOImageSegmentReader* linkedit_segment = |
| 683 GetSegmentByName(SEG_LINKEDIT, &linkedit_address, &linkedit_size); |
| 684 if (!linkedit_segment) { |
| 685 LOG(WARNING) << "no " SEG_LINKEDIT " segment"; |
| 686 return; |
| 687 } |
| 688 |
| 689 symbol_table_.reset(new MachOImageSymbolTableReader()); |
| 690 if (!symbol_table_->Initialize(process_reader_, |
| 691 symtab_command_.get(), |
| 692 dysymtab_command_.get(), |
| 693 linkedit_segment, |
| 694 linkedit_address, |
| 695 linkedit_size, |
| 696 module_info_)) { |
| 697 symbol_table_.reset(); |
| 698 return; |
| 699 } |
| 700 |
| 701 symbol_table_initialized_.set_valid(); |
| 702 } |
| 703 |
580 } // namespace crashpad | 704 } // namespace crashpad |
OLD | NEW |