Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(478)

Unified Diff: util/mac/mach_o_image_reader.cc

Issue 666483002: Create snapshot/mac and move some files from snapshot and util to there (Closed) Base URL: https://chromium.googlesource.com/crashpad/crashpad/+/master
Patch Set: Move process_reader, process_types, and mach_o_image*_reader from util/mac to snapshot/mac Created 6 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « util/mac/mach_o_image_reader.h ('k') | util/mac/mach_o_image_reader_test.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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
deleted file mode 100644
index f911ec67e465b2285efb5da892a2d91fe616c99f..0000000000000000000000000000000000000000
--- a/util/mac/mach_o_image_reader.cc
+++ /dev/null
@@ -1,671 +0,0 @@
-// Copyright 2014 The Crashpad Authors. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "util/mac/mach_o_image_reader.h"
-
-#include <mach-o/loader.h>
-#include <mach-o/nlist.h>
-#include <string.h>
-
-#include <limits>
-#include <vector>
-
-#include "base/logging.h"
-#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 {
-
-const uint32_t kInvalidSegmentIndex = std::numeric_limits<uint32_t>::max();
-
-} // namespace
-
-namespace crashpad {
-
-MachOImageReader::MachOImageReader()
- : segments_(),
- segment_map_(),
- module_info_(),
- dylinker_name_(),
- uuid_(),
- address_(0),
- size_(0),
- slide_(0),
- source_version_(0),
- symtab_command_(),
- dysymtab_command_(),
- symbol_table_(),
- id_dylib_command_(),
- process_reader_(nullptr),
- file_type_(0),
- initialized_(),
- symbol_table_initialized_() {
-}
-
-MachOImageReader::~MachOImageReader() {
-}
-
-bool MachOImageReader::Initialize(ProcessReader* process_reader,
- mach_vm_address_t address,
- const std::string& name) {
- INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
-
- process_reader_ = process_reader;
- address_ = address;
-
- module_info_ =
- base::StringPrintf(", module %s, address 0x%llx", name.c_str(), address);
-
- process_types::mach_header mach_header;
- if (!mach_header.Read(process_reader, address)) {
- LOG(WARNING) << "could not read mach_header" << module_info_;
- return false;
- }
-
- const bool is_64_bit = process_reader->Is64Bit();
- const uint32_t kExpectedMagic = is_64_bit ? MH_MAGIC_64 : MH_MAGIC;
- if (mach_header.magic != kExpectedMagic) {
- LOG(WARNING) << base::StringPrintf("unexpected mach_header::magic 0x%08x",
- mach_header.magic) << module_info_;
- return false;
- }
-
- file_type_ = mach_header.filetype;
-
- const uint32_t kExpectedSegmentCommand =
- is_64_bit ? LC_SEGMENT_64 : LC_SEGMENT;
- const uint32_t kUnexpectedSegmentCommand =
- is_64_bit ? LC_SEGMENT : LC_SEGMENT_64;
-
- const struct {
- // Which method to call when encountering a load command matching |command|.
- bool (MachOImageReader::*function)(mach_vm_address_t, const std::string&);
-
- // The minimum size that may be allotted to store the load command.
- size_t size;
-
- // The load command to match.
- uint32_t command;
-
- // True if the load command must not appear more than one time.
- bool singleton;
- } kLoadCommandReaders[] = {
- {
- &MachOImageReader::ReadSegmentCommand,
- process_types::segment_command::ExpectedSize(process_reader),
- kExpectedSegmentCommand,
- false,
- },
- {
- &MachOImageReader::ReadSymTabCommand,
- process_types::symtab_command::ExpectedSize(process_reader),
- LC_SYMTAB,
- true,
- },
- {
- &MachOImageReader::ReadDySymTabCommand,
- process_types::symtab_command::ExpectedSize(process_reader),
- LC_DYSYMTAB,
- true,
- },
- {
- &MachOImageReader::ReadIdDylibCommand,
- process_types::dylib_command::ExpectedSize(process_reader),
- LC_ID_DYLIB,
- true,
- },
- {
- &MachOImageReader::ReadDylinkerCommand,
- process_types::dylinker_command::ExpectedSize(process_reader),
- LC_LOAD_DYLINKER,
- true,
- },
- {
- &MachOImageReader::ReadDylinkerCommand,
- process_types::dylinker_command::ExpectedSize(process_reader),
- LC_ID_DYLINKER,
- true,
- },
- {
- &MachOImageReader::ReadUUIDCommand,
- process_types::uuid_command::ExpectedSize(process_reader),
- LC_UUID,
- true,
- },
- {
- &MachOImageReader::ReadSourceVersionCommand,
- process_types::source_version_command::ExpectedSize(process_reader),
- LC_SOURCE_VERSION,
- true,
- },
-
- // When reading a 64-bit process, no 32-bit segment commands should be
- // present, and vice-versa.
- {
- &MachOImageReader::ReadUnexpectedCommand,
- process_types::load_command::ExpectedSize(process_reader),
- kUnexpectedSegmentCommand,
- false,
- },
- };
-
- // This vector is parallel to the kLoadCommandReaders array, and tracks
- // whether a singleton load command matching the |command| field has been
- // found yet.
- std::vector<uint32_t> singleton_indices(arraysize(kLoadCommandReaders),
- kInvalidSegmentIndex);
-
- size_t offset = mach_header.Size();
- const mach_vm_address_t kLoadCommandAddressLimit =
- address + offset + mach_header.sizeofcmds;
-
- for (uint32_t load_command_index = 0;
- load_command_index < mach_header.ncmds;
- ++load_command_index) {
- mach_vm_address_t load_command_address = address + offset;
- std::string load_command_info = base::StringPrintf(", load command %u/%u%s",
- load_command_index,
- mach_header.ncmds,
- module_info_.c_str());
-
- process_types::load_command load_command;
-
- // Make sure that the basic load command structure doesn’t overflow the
- // space allotted for load commands.
- if (load_command_address + load_command.ExpectedSize(process_reader) >
- kLoadCommandAddressLimit) {
- LOG(WARNING) << base::StringPrintf(
- "load_command at 0x%llx exceeds sizeofcmds 0x%x",
- load_command_address,
- mach_header.sizeofcmds) << load_command_info;
- return false;
- }
-
- if (!load_command.Read(process_reader, load_command_address)) {
- LOG(WARNING) << "could not read load_command" << load_command_info;
- return false;
- }
-
- load_command_info = base::StringPrintf(", load command 0x%x %u/%u%s",
- load_command.cmd,
- load_command_index,
- mach_header.ncmds,
- module_info_.c_str());
-
- // Now that the load command’s stated size is known, make sure that it
- // doesn’t overflow the space allotted for load commands.
- if (load_command_address + load_command.cmdsize >
- kLoadCommandAddressLimit) {
- LOG(WARNING)
- << base::StringPrintf(
- "load_command at 0x%llx cmdsize 0x%x exceeds sizeofcmds 0x%x",
- load_command_address,
- load_command.cmdsize,
- mach_header.sizeofcmds) << load_command_info;
- return false;
- }
-
- for (size_t reader_index = 0;
- reader_index < arraysize(kLoadCommandReaders);
- ++reader_index) {
- if (load_command.cmd != kLoadCommandReaders[reader_index].command) {
- continue;
- }
-
- 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;
- 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;
- return false;
- }
-
- singleton_indices[reader_index] = load_command_index;
- }
-
- if (!((this)->*(kLoadCommandReaders[reader_index].function))(
- load_command_address, load_command_info)) {
- return false;
- }
-
- break;
- }
-
- offset += load_command.cmdsize;
- }
-
- // 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()) {
- LOG(WARNING) << base::StringPrintf(
- "invalid slid segment range 0x%llx + 0x%llx, "
- "segment ",
- slid_segment_address,
- slid_segment_size) << segment->Name() << module_info_;
- return false;
- }
- }
-
- if (!segment_map_.count(SEG_TEXT)) {
- // The __TEXT segment is required. Even a module with no executable code
- // will have a __TEXT segment encompassing the Mach-O header and load
- // commands. Without a __TEXT segment, |size_| will not have been computed.
- LOG(WARNING) << "no " SEG_TEXT " segment" << module_info_;
- return false;
- }
-
- if (mach_header.filetype == MH_DYLIB && !id_dylib_command_) {
- // This doesn’t render a module unusable, it’s just weird and worth noting.
- LOG(INFO) << "no LC_ID_DYLIB" << module_info_;
- }
-
- INITIALIZATION_STATE_SET_VALID(initialized_);
- return true;
-}
-
-const MachOImageSegmentReader* MachOImageReader::GetSegmentByName(
- const std::string& segment_name) const {
- INITIALIZATION_STATE_DCHECK_VALID(initialized_);
-
- const auto& iterator = segment_map_.find(segment_name);
- if (iterator == segment_map_.end()) {
- return nullptr;
- }
-
- const MachOImageSegmentReader* segment = segments_[iterator->second];
- return segment;
-}
-
-const process_types::section* MachOImageReader::GetSectionByName(
- const std::string& segment_name,
- const std::string& section_name,
- mach_vm_address_t* address) const {
- INITIALIZATION_STATE_DCHECK_VALID(initialized_);
-
- const MachOImageSegmentReader* segment = GetSegmentByName(segment_name);
- if (!segment) {
- return nullptr;
- }
-
- 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_);
-
- static_assert(NO_SECT == 0, "NO_SECT must be zero");
- if (index == NO_SECT) {
- LOG(WARNING) << "section index " << index << " out of range";
- return nullptr;
- }
-
- // Switch to a more comfortable 0-based index.
- size_t local_index = index - 1;
-
- for (const MachOImageSegmentReader* segment : segments_) {
- size_t nsects = segment->nsects();
- if (local_index < nsects) {
- const process_types::section* section =
- segment->GetSectionAtIndex(local_index, address);
-
- if (containing_segment) {
- *containing_segment = segment;
- }
-
- return section;
- }
-
- local_index -= nsects;
- }
-
- LOG(WARNING) << "section index " << index << " out of range";
- return nullptr;
-}
-
-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, &section_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));
-
- if (id_dylib_command_) {
- return id_dylib_command_->dylib_current_version;
- }
-
- // In case this was a weird dylib without an LC_ID_DYLIB command.
- return 0;
-}
-
-void MachOImageReader::UUID(crashpad::UUID* uuid) const {
- INITIALIZATION_STATE_DCHECK_VALID(initialized_);
- memcpy(uuid, &uuid_, sizeof(uuid_));
-}
-
-template <typename T>
-bool MachOImageReader::ReadLoadCommand(mach_vm_address_t load_command_address,
- const std::string& load_command_info,
- uint32_t expected_load_command_id,
- T* load_command) {
- if (!load_command->Read(process_reader_, load_command_address)) {
- LOG(WARNING) << "could not read load command" << load_command_info;
- return false;
- }
-
- DCHECK_GE(load_command->cmdsize, load_command->Size());
- DCHECK_EQ(load_command->cmd, expected_load_command_id);
- return true;
-}
-
-bool MachOImageReader::ReadSegmentCommand(
- mach_vm_address_t load_command_address,
- const std::string& load_command_info) {
- MachOImageSegmentReader* segment = new MachOImageSegmentReader();
- size_t segment_index = segments_.size();
- segments_.push_back(segment); // Takes ownership.
-
- if (!segment->Initialize(
- process_reader_, load_command_address, load_command_info)) {
- segments_.pop_back();
- return false;
- }
-
- // At this point, the segment itself is considered valid, but if one of the
- // next checks fails, it will render the module invalid. If any of the next
- // checks fail, this method should return false, but it doesn’t need to bother
- // removing the segment from segments_. The segment will be properly released
- // when the image is destroyed, and the image won’t be usable because
- // initialization won’t have completed. Most importantly, leaving the segment
- // in segments_ means that no other structures (such as perhaps segment_map_)
- // become inconsistent or require cleanup.
-
- const std::string segment_name = segment->Name();
- const auto& iterator = segment_map_.find(segment_name);
- if (iterator != segment_map_.end()) {
- LOG(WARNING) << base::StringPrintf("duplicate %s segment at %zu and %zu",
- segment_name.c_str(),
- iterator->second,
- segment_index) << load_command_info;
- return false;
- }
- segment_map_[segment_name] = segment_index;
-
- mach_vm_size_t vmsize = segment->vmsize();
-
- if (segment_name == SEG_TEXT) {
- if (vmsize == 0) {
- LOG(WARNING) << "zero-sized " SEG_TEXT " segment" << load_command_info;
- return false;
- }
-
- mach_vm_size_t fileoff = segment->fileoff();
- if (fileoff != 0) {
- LOG(WARNING) << base::StringPrintf(
- SEG_TEXT " segment has unexpected fileoff 0x%llx",
- fileoff) << load_command_info;
- return false;
- }
-
- size_ = vmsize;
-
- // The slide is computed as the difference between the __TEXT segment’s
- // preferred and actual load addresses. This is the same way that dyld
- // computes slide. See 10.9.2 dyld-239.4/src/dyldInitialization.cpp
- // slideOfMainExecutable().
- slide_ = address_ - segment->vmaddr();
- }
-
- return true;
-}
-
-bool MachOImageReader::ReadSymTabCommand(mach_vm_address_t load_command_address,
- const std::string& load_command_info) {
- symtab_command_.reset(new process_types::symtab_command());
- return ReadLoadCommand(load_command_address,
- load_command_info,
- LC_SYMTAB,
- symtab_command_.get());
-}
-
-bool MachOImageReader::ReadDySymTabCommand(
- mach_vm_address_t load_command_address,
- const std::string& load_command_info) {
- dysymtab_command_.reset(new process_types::dysymtab_command());
- return ReadLoadCommand(load_command_address,
- load_command_info,
- LC_DYSYMTAB,
- dysymtab_command_.get());
-}
-
-bool MachOImageReader::ReadIdDylibCommand(
- mach_vm_address_t load_command_address,
- const std::string& load_command_info) {
- if (file_type_ != MH_DYLIB) {
- LOG(WARNING) << base::StringPrintf(
- "LC_ID_DYLIB inappropriate in non-dylib file type 0x%x",
- file_type_) << load_command_info;
- return false;
- }
-
- DCHECK(!id_dylib_command_);
- id_dylib_command_.reset(new process_types::dylib_command());
- return ReadLoadCommand(load_command_address,
- load_command_info,
- LC_ID_DYLIB,
- id_dylib_command_.get());
-}
-
-bool MachOImageReader::ReadDylinkerCommand(
- mach_vm_address_t load_command_address,
- const std::string& load_command_info) {
- if (file_type_ != MH_EXECUTE && file_type_ != MH_DYLINKER) {
- LOG(WARNING) << base::StringPrintf(
- "LC_LOAD_DYLINKER/LC_ID_DYLINKER inappropriate in file "
- "type 0x%x",
- file_type_) << load_command_info;
- return false;
- }
-
- const uint32_t kExpectedCommand =
- file_type_ == MH_DYLINKER ? LC_ID_DYLINKER : LC_LOAD_DYLINKER;
- process_types::dylinker_command dylinker_command;
- if (!ReadLoadCommand(load_command_address,
- load_command_info,
- kExpectedCommand,
- &dylinker_command)) {
- return false;
- }
-
- if (!process_reader_->Memory()->ReadCStringSizeLimited(
- load_command_address + dylinker_command.name,
- dylinker_command.cmdsize - dylinker_command.name,
- &dylinker_name_)) {
- LOG(WARNING) << "could not read dylinker_command name" << load_command_info;
- return false;
- }
-
- return true;
-}
-
-bool MachOImageReader::ReadUUIDCommand(mach_vm_address_t load_command_address,
- const std::string& load_command_info) {
- process_types::uuid_command uuid_command;
- if (!ReadLoadCommand(
- load_command_address, load_command_info, LC_UUID, &uuid_command)) {
- return false;
- }
-
- uuid_.InitializeFromBytes(uuid_command.uuid);
- return true;
-}
-
-bool MachOImageReader::ReadSourceVersionCommand(
- mach_vm_address_t load_command_address,
- const std::string& load_command_info) {
- process_types::source_version_command source_version_command;
- if (!ReadLoadCommand(load_command_address,
- load_command_info,
- LC_SOURCE_VERSION,
- &source_version_command)) {
- return false;
- }
-
- source_version_ = source_version_command.version;
- return true;
-}
-
-bool MachOImageReader::ReadUnexpectedCommand(
- mach_vm_address_t load_command_address,
- const std::string& load_command_info) {
- LOG(WARNING) << "unexpected load command" << load_command_info;
- 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
« no previous file with comments | « util/mac/mach_o_image_reader.h ('k') | util/mac/mach_o_image_reader_test.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698