Index: util/mac/mach_o_image_reader.cc |
diff --git a/util/mac/mach_o_image_reader.cc b/util/mac/mach_o_image_reader.cc |
index 243976ee2a39f767f010b4409385b4b11ebbb26a..7e8a1e28328cce091be6e781856af3b378f7f5b7 100644 |
--- a/util/mac/mach_o_image_reader.cc |
+++ b/util/mac/mach_o_image_reader.cc |
@@ -25,6 +25,7 @@ |
#include "base/strings/stringprintf.h" |
#include "util/mac/checked_mach_address_range.h" |
#include "util/mac/mach_o_image_segment_reader.h" |
+#include "util/mac/mach_o_image_symbol_table_reader.h" |
#include "util/mac/process_reader.h" |
namespace { |
@@ -47,10 +48,12 @@ MachOImageReader::MachOImageReader() |
source_version_(0), |
symtab_command_(), |
dysymtab_command_(), |
+ symbol_table_(), |
id_dylib_command_(), |
process_reader_(NULL), |
file_type_(0), |
- initialized_() { |
+ initialized_(), |
+ symbol_table_initialized_() { |
} |
MachOImageReader::~MachOImageReader() { |
@@ -224,20 +227,18 @@ bool MachOImageReader::Initialize(ProcessReader* process_reader, |
} |
if (load_command.cmdsize < kLoadCommandReaders[reader_index].size) { |
- LOG(WARNING) |
- << base::StringPrintf( |
- "load command cmdsize 0x%x insufficient for 0x%zx", |
- load_command.cmdsize, |
- kLoadCommandReaders[reader_index].size) |
- << load_command_info; |
+ LOG(WARNING) << base::StringPrintf( |
+ "load command cmdsize 0x%x insufficient for 0x%zx", |
+ load_command.cmdsize, |
+ kLoadCommandReaders[reader_index].size) |
+ << load_command_info; |
return false; |
} |
if (kLoadCommandReaders[reader_index].singleton) { |
if (singleton_indices[reader_index] != kInvalidSegmentIndex) { |
LOG(WARNING) << "duplicate load command at " |
- << singleton_indices[reader_index] |
- << load_command_info; |
+ << singleton_indices[reader_index] << load_command_info; |
return false; |
} |
@@ -349,6 +350,7 @@ const process_types::section* MachOImageReader::GetSectionByName( |
const process_types::section* MachOImageReader::GetSectionAtIndex( |
size_t index, |
+ const MachOImageSegmentReader** containing_segment, |
mach_vm_address_t* address) const { |
INITIALIZATION_STATE_DCHECK_VALID(initialized_); |
@@ -367,6 +369,9 @@ const process_types::section* MachOImageReader::GetSectionAtIndex( |
const process_types::section* section = |
segment->GetSectionAtIndex(local_index); |
+ if (containing_segment) { |
+ *containing_segment = segment; |
+ } |
if (address) { |
*address = section->addr + (segment->SegmentSlides() ? slide_ : 0); |
} |
@@ -381,6 +386,86 @@ const process_types::section* MachOImageReader::GetSectionAtIndex( |
return NULL; |
} |
+bool MachOImageReader::LookUpExternalDefinedSymbol( |
+ const std::string& name, |
+ mach_vm_address_t* value) const { |
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_); |
+ |
+ if (symbol_table_initialized_.is_uninitialized()) { |
+ InitializeSymbolTable(); |
+ } |
+ |
+ if (!symbol_table_initialized_.is_valid() || !symbol_table_) { |
+ return false; |
+ } |
+ |
+ const MachOImageSymbolTableReader::SymbolInformation* symbol_info = |
+ symbol_table_->LookUpExternalDefinedSymbol(name); |
+ if (!symbol_info) { |
+ return false; |
+ } |
+ |
+ if (symbol_info->section == NO_SECT) { |
+ // This is an absolute (N_ABS) symbol, which requires no further validation |
+ // or processing. |
+ *value = symbol_info->value; |
+ return true; |
+ } |
+ |
+ // This is a symbol defined in a particular section, so make sure that it’s |
+ // valid for that section and fix it up for any “slide” as needed. |
+ |
+ mach_vm_address_t section_address; |
+ const MachOImageSegmentReader* segment; |
+ const process_types::section* section = |
+ GetSectionAtIndex(symbol_info->section, &segment, §ion_address); |
+ if (!section) { |
+ return false; |
+ } |
+ |
+ mach_vm_address_t slid_value = |
+ symbol_info->value + (segment->SegmentSlides() ? slide_ : 0); |
+ |
+ // The __mh_execute_header (_MH_EXECUTE_SYM) symbol is weird. In |
+ // position-independent executables, it shows up in the symbol table as a |
+ // symbol in section 1, although it’s not really in that section. It points to |
+ // the mach_header[_64], which is the beginning of the __TEXT segment, and the |
+ // __text section normally begins after the load commands in the __TEXT |
+ // segment. The range check below will fail for this symbol, because it’s not |
+ // really in the section it claims to be in. See Xcode 5.1 |
+ // ld64-236.3/src/ld/OutputFile.cpp ld::tool::OutputFile::buildSymbolTable(). |
+ // There, ld takes symbols that refer to anything in the mach_header[_64] and |
+ // marks them as being in section 1. Here, section 1 is treated in this same |
+ // special way as long as it’s in the __TEXT segment that begins at the start |
+ // of the image, which is normally the case, and as long as the symbol’s value |
+ // is the base of the image. |
+ // |
+ // This only happens for PIE executables, because __mh_execute_header needs |
+ // to slide. In non-PIE executables, __mh_execute_header is an absolute |
+ // symbol. |
+ CheckedMachAddressRange section_range( |
+ process_reader_, section_address, section->size); |
+ if (!section_range.ContainsValue(slid_value) && |
+ !(symbol_info->section == 1 && segment->Name() == SEG_TEXT && |
+ slid_value == Address())) { |
+ std::string section_name_full = |
+ MachOImageSegmentReader::SegmentAndSectionNameString(section->segname, |
+ section->sectname); |
+ LOG(WARNING) << base::StringPrintf( |
+ "symbol %s (0x%llx) outside of section %s (0x%llx + " |
+ "0x%llx)", |
+ name.c_str(), |
+ slid_value, |
+ section_name_full.c_str(), |
+ section_address, |
+ section->size) << module_info_; |
+ return false; |
+ } |
+ |
+ *value = slid_value; |
+ return true; |
+} |
+ |
uint32_t MachOImageReader::DylibVersion() const { |
INITIALIZATION_STATE_DCHECK_VALID(initialized_); |
DCHECK_EQ(FileType(), static_cast<uint32_t>(MH_DYLIB)); |
@@ -577,4 +662,43 @@ bool MachOImageReader::ReadUnexpectedCommand( |
return false; |
} |
+void MachOImageReader::InitializeSymbolTable() const { |
+ DCHECK(symbol_table_initialized_.is_uninitialized()); |
+ symbol_table_initialized_.set_invalid(); |
+ |
+ if (!symtab_command_) { |
+ // It’s technically valid for there to be no LC_SYMTAB, and in that case, |
+ // any symbol lookups should fail. Mark the symbol table as valid, and |
+ // LookUpExternalDefinedSymbol() will understand what it means when this is |
+ // valid but symbol_table_ is not present. |
+ symbol_table_initialized_.set_valid(); |
+ return; |
+ } |
+ |
+ // Find the __LINKEDIT segment. Technically, the symbol table can be in any |
+ // mapped segment, but by convention, it’s in the one named __LINKEDIT. |
+ mach_vm_address_t linkedit_address; |
+ mach_vm_size_t linkedit_size; |
+ const MachOImageSegmentReader* linkedit_segment = |
+ GetSegmentByName(SEG_LINKEDIT, &linkedit_address, &linkedit_size); |
+ if (!linkedit_segment) { |
+ LOG(WARNING) << "no " SEG_LINKEDIT " segment"; |
+ return; |
+ } |
+ |
+ symbol_table_.reset(new MachOImageSymbolTableReader()); |
+ if (!symbol_table_->Initialize(process_reader_, |
+ symtab_command_.get(), |
+ dysymtab_command_.get(), |
+ linkedit_segment, |
+ linkedit_address, |
+ linkedit_size, |
+ module_info_)) { |
+ symbol_table_.reset(); |
+ return; |
+ } |
+ |
+ symbol_table_initialized_.set_valid(); |
+} |
+ |
} // namespace crashpad |