| 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 |