2014-09-04 11:45:40 -04:00
|
|
|
|
// 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.
|
|
|
|
|
|
2014-10-17 13:41:45 -04:00
|
|
|
|
#include "snapshot/mac/mach_o_image_reader.h"
|
2014-09-04 11:45:40 -04:00
|
|
|
|
|
|
|
|
|
#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"
|
2015-03-11 17:07:11 -04:00
|
|
|
|
#include "client/crashpad_info.h"
|
2014-10-17 13:41:45 -04:00
|
|
|
|
#include "snapshot/mac/mach_o_image_segment_reader.h"
|
|
|
|
|
#include "snapshot/mac/mach_o_image_symbol_table_reader.h"
|
|
|
|
|
#include "snapshot/mac/process_reader.h"
|
2014-09-04 11:45:40 -04:00
|
|
|
|
#include "util/mac/checked_mach_address_range.h"
|
|
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
|
|
const uint32_t kInvalidSegmentIndex = std::numeric_limits<uint32_t>::max();
|
|
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
|
|
namespace crashpad {
|
|
|
|
|
|
|
|
|
|
MachOImageReader::MachOImageReader()
|
|
|
|
|
: segments_(),
|
|
|
|
|
segment_map_(),
|
2015-03-23 16:27:42 -04:00
|
|
|
|
module_name_(),
|
2014-09-04 11:45:40 -04:00
|
|
|
|
module_info_(),
|
|
|
|
|
dylinker_name_(),
|
|
|
|
|
uuid_(),
|
|
|
|
|
address_(0),
|
|
|
|
|
size_(0),
|
|
|
|
|
slide_(0),
|
|
|
|
|
source_version_(0),
|
|
|
|
|
symtab_command_(),
|
|
|
|
|
dysymtab_command_(),
|
2014-09-05 16:53:18 -04:00
|
|
|
|
symbol_table_(),
|
2014-09-04 11:45:40 -04:00
|
|
|
|
id_dylib_command_(),
|
2014-10-14 11:10:45 -04:00
|
|
|
|
process_reader_(nullptr),
|
2014-09-04 11:45:40 -04:00
|
|
|
|
file_type_(0),
|
2014-09-05 16:53:18 -04:00
|
|
|
|
initialized_(),
|
|
|
|
|
symbol_table_initialized_() {
|
2014-09-04 11:45:40 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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;
|
2015-03-23 16:27:42 -04:00
|
|
|
|
module_name_ = name;
|
2014-09-04 11:45:40 -04:00
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2015-03-23 16:27:42 -04:00
|
|
|
|
switch (mach_header.filetype) {
|
|
|
|
|
case MH_EXECUTE:
|
|
|
|
|
case MH_DYLIB:
|
|
|
|
|
case MH_DYLINKER:
|
|
|
|
|
case MH_BUNDLE:
|
|
|
|
|
file_type_ = mach_header.filetype;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
LOG(WARNING) << base::StringPrintf(
|
|
|
|
|
"unexpected mach_header::filetype 0x%08x",
|
|
|
|
|
mach_header.filetype) << module_info_;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2014-09-04 11:45:40 -04:00
|
|
|
|
|
|
|
|
|
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) {
|
2014-09-05 16:53:18 -04:00
|
|
|
|
LOG(WARNING) << base::StringPrintf(
|
|
|
|
|
"load command cmdsize 0x%x insufficient for 0x%zx",
|
|
|
|
|
load_command.cmdsize,
|
|
|
|
|
kLoadCommandReaders[reader_index].size)
|
|
|
|
|
<< load_command_info;
|
2014-09-04 11:45:40 -04:00
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (kLoadCommandReaders[reader_index].singleton) {
|
|
|
|
|
if (singleton_indices[reader_index] != kInvalidSegmentIndex) {
|
|
|
|
|
LOG(WARNING) << "duplicate load command at "
|
2014-09-05 16:53:18 -04:00
|
|
|
|
<< singleton_indices[reader_index] << load_command_info;
|
2014-09-04 11:45:40 -04:00
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-05 16:53:18 -04:00
|
|
|
|
// 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();
|
2014-09-04 11:45:40 -04:00
|
|
|
|
CheckedMachAddressRange slid_segment_range(
|
2014-10-17 13:41:45 -04:00
|
|
|
|
process_reader_->Is64Bit(), slid_segment_address, slid_segment_size);
|
2014-09-04 11:45:40 -04:00
|
|
|
|
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(
|
2014-09-05 16:53:18 -04:00
|
|
|
|
const std::string& segment_name) const {
|
2014-09-04 11:45:40 -04:00
|
|
|
|
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
|
|
|
|
|
|
|
|
|
const auto& iterator = segment_map_.find(segment_name);
|
|
|
|
|
if (iterator == segment_map_.end()) {
|
2014-10-14 11:10:45 -04:00
|
|
|
|
return nullptr;
|
2014-09-04 11:45:40 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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_);
|
|
|
|
|
|
2014-09-05 16:53:18 -04:00
|
|
|
|
const MachOImageSegmentReader* segment = GetSegmentByName(segment_name);
|
2014-09-04 11:45:40 -04:00
|
|
|
|
if (!segment) {
|
2014-10-14 11:10:45 -04:00
|
|
|
|
return nullptr;
|
2014-09-04 11:45:40 -04:00
|
|
|
|
}
|
|
|
|
|
|
2014-09-05 16:53:18 -04:00
|
|
|
|
return segment->GetSectionByName(section_name, address);
|
2014-09-04 11:45:40 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const process_types::section* MachOImageReader::GetSectionAtIndex(
|
|
|
|
|
size_t index,
|
2014-09-05 16:53:18 -04:00
|
|
|
|
const MachOImageSegmentReader** containing_segment,
|
2014-09-04 11:45:40 -04:00
|
|
|
|
mach_vm_address_t* address) const {
|
|
|
|
|
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
|
|
|
|
|
2014-10-01 12:29:01 -07:00
|
|
|
|
static_assert(NO_SECT == 0, "NO_SECT must be zero");
|
2014-09-04 11:45:40 -04:00
|
|
|
|
if (index == NO_SECT) {
|
|
|
|
|
LOG(WARNING) << "section index " << index << " out of range";
|
2014-10-14 11:10:45 -04:00
|
|
|
|
return nullptr;
|
2014-09-04 11:45:40 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 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 =
|
2014-09-05 16:53:18 -04:00
|
|
|
|
segment->GetSectionAtIndex(local_index, address);
|
2014-09-04 11:45:40 -04:00
|
|
|
|
|
2014-09-05 16:53:18 -04:00
|
|
|
|
if (containing_segment) {
|
|
|
|
|
*containing_segment = segment;
|
2014-09-04 11:45:40 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return section;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
local_index -= nsects;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LOG(WARNING) << "section index " << index << " out of range";
|
2014-10-14 11:10:45 -04:00
|
|
|
|
return nullptr;
|
2014-09-04 11:45:40 -04:00
|
|
|
|
}
|
|
|
|
|
|
2014-09-05 16:53:18 -04:00
|
|
|
|
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(
|
2014-10-17 13:41:45 -04:00
|
|
|
|
process_reader_->Is64Bit(), section_address, section->size);
|
2014-09-05 16:53:18 -04:00
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-04 11:45:40 -04:00
|
|
|
|
uint32_t MachOImageReader::DylibVersion() const {
|
|
|
|
|
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
2014-11-06 16:44:38 -05:00
|
|
|
|
DCHECK_EQ(FileType(), implicit_cast<uint32_t>(MH_DYLIB));
|
2014-09-04 11:45:40 -04:00
|
|
|
|
|
|
|
|
|
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_));
|
|
|
|
|
}
|
|
|
|
|
|
2015-03-11 17:07:11 -04:00
|
|
|
|
bool MachOImageReader::GetCrashpadInfo(
|
|
|
|
|
process_types::CrashpadInfo* crashpad_info) const {
|
|
|
|
|
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
|
|
|
|
|
|
|
|
|
mach_vm_address_t crashpad_info_address;
|
|
|
|
|
const process_types::section* crashpad_info_section =
|
|
|
|
|
GetSectionByName(SEG_DATA, "__crashpad_info", &crashpad_info_address);
|
|
|
|
|
if (!crashpad_info_section) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (crashpad_info_section->size <
|
|
|
|
|
crashpad_info->ExpectedSize(process_reader_)) {
|
|
|
|
|
LOG(WARNING) << "small crashpad info section size "
|
|
|
|
|
<< crashpad_info_section->size << module_info_;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!crashpad_info->Read(process_reader_, crashpad_info_address)) {
|
|
|
|
|
LOG(WARNING) << "could not read crashpad info" << module_info_;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (crashpad_info->signature != CrashpadInfo::kSignature ||
|
|
|
|
|
crashpad_info->size != crashpad_info_section->size ||
|
|
|
|
|
crashpad_info->version < 1) {
|
|
|
|
|
LOG(WARNING) << "unexpected crashpad info data" << module_info_;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-04 11:45:40 -04:00
|
|
|
|
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.
|
|
|
|
|
|
2015-03-23 16:27:42 -04:00
|
|
|
|
if (!segment->Initialize(process_reader_,
|
|
|
|
|
load_command_address,
|
|
|
|
|
load_command_info,
|
|
|
|
|
module_name_,
|
|
|
|
|
file_type_)) {
|
2014-09-04 11:45:40 -04:00
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-05 16:53:18 -04:00
|
|
|
|
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();
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-04 11:45:40 -04:00
|
|
|
|
} // namespace crashpad
|