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..09e35e59f2f612f77bd3e9eaf82cd043c08b5651 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; |
} |
@@ -255,20 +256,16 @@ bool MachOImageReader::Initialize(ProcessReader* process_reader, |
offset += load_command.cmdsize; |
} |
- // This was already checked for the unslid values while the segments were |
- // read, but now that the slide is known, check the slid values too. The |
- // individual sections don’t need to be checked because they were verified to |
- // be contained within their respective segments when the segments were read. |
- for (const MachOImageSegmentReader* segment : segments_) { |
- mach_vm_address_t slid_segment_address = segment->vmaddr(); |
- mach_vm_size_t slid_segment_size = segment->vmsize(); |
- if (segment->SegmentSlides()) { |
- slid_segment_address += slide_; |
- } else { |
- // The non-sliding __PAGEZERO segment extends instead of slides. See |
- // MachOImageSegmentReader::SegmentSlides(). |
- slid_segment_size += slide_; |
- } |
+ // Now that the slide is known, push it into the segments. |
+ for (MachOImageSegmentReader* segment : segments_) { |
+ segment->SetSlide(slide_); |
+ |
+ // This was already checked for the unslid values while the segments were |
+ // read, but now it’s possible to check the slid values too. The individual |
+ // sections don’t need to be checked because they were verified to be |
+ // contained within their respective segments when the segments were read. |
+ mach_vm_address_t slid_segment_address = segment->Address(); |
+ mach_vm_size_t slid_segment_size = segment->Size(); |
CheckedMachAddressRange slid_segment_range( |
process_reader_, slid_segment_address, slid_segment_size); |
if (!slid_segment_range.IsValid()) { |
@@ -299,9 +296,7 @@ bool MachOImageReader::Initialize(ProcessReader* process_reader, |
} |
const MachOImageSegmentReader* MachOImageReader::GetSegmentByName( |
- const std::string& segment_name, |
- mach_vm_address_t* address, |
- mach_vm_size_t* size) const { |
+ const std::string& segment_name) const { |
INITIALIZATION_STATE_DCHECK_VALID(initialized_); |
const auto& iterator = segment_map_.find(segment_name); |
@@ -310,15 +305,6 @@ const MachOImageSegmentReader* MachOImageReader::GetSegmentByName( |
} |
const MachOImageSegmentReader* segment = segments_[iterator->second]; |
- if (address) { |
- *address = segment->vmaddr() + (segment->SegmentSlides() ? slide_ : 0); |
- } |
- if (size) { |
- // The non-sliding __PAGEZERO segment extends instead of slides. See |
- // MachOImageSegmentReader::SegmentSlides(). |
- *size = segment->vmsize() + (segment->SegmentSlides() ? 0 : slide_); |
- } |
- |
return segment; |
} |
@@ -328,27 +314,17 @@ const process_types::section* MachOImageReader::GetSectionByName( |
mach_vm_address_t* address) const { |
INITIALIZATION_STATE_DCHECK_VALID(initialized_); |
- const MachOImageSegmentReader* segment = |
- GetSegmentByName(segment_name, NULL, NULL); |
+ const MachOImageSegmentReader* segment = GetSegmentByName(segment_name); |
if (!segment) { |
return NULL; |
} |
- const process_types::section* section = |
- segment->GetSectionByName(section_name); |
- if (!section) { |
- return NULL; |
- } |
- |
- if (address) { |
- *address = section->addr + (segment->SegmentSlides() ? slide_ : 0); |
- } |
- |
- return section; |
+ return segment->GetSectionByName(section_name, address); |
} |
const process_types::section* MachOImageReader::GetSectionAtIndex( |
size_t index, |
+ const MachOImageSegmentReader** containing_segment, |
mach_vm_address_t* address) const { |
INITIALIZATION_STATE_DCHECK_VALID(initialized_); |
@@ -365,10 +341,10 @@ const process_types::section* MachOImageReader::GetSectionAtIndex( |
size_t nsects = segment->nsects(); |
if (local_index < nsects) { |
const process_types::section* section = |
- segment->GetSectionAtIndex(local_index); |
+ segment->GetSectionAtIndex(local_index, address); |
- if (address) { |
- *address = section->addr + (segment->SegmentSlides() ? slide_ : 0); |
+ if (containing_segment) { |
+ *containing_segment = segment; |
} |
return section; |
@@ -381,6 +357,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 +633,39 @@ 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. |
+ const MachOImageSegmentReader* linkedit_segment = |
+ GetSegmentByName(SEG_LINKEDIT); |
+ 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, |
+ module_info_)) { |
+ symbol_table_.reset(); |
+ return; |
+ } |
+ |
+ symbol_table_initialized_.set_valid(); |
+} |
+ |
} // namespace crashpad |