2014-09-05 16:53:18 -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.
|
|
|
|
|
|
|
|
|
|
#include "util/mac/mach_o_image_symbol_table_reader.h"
|
|
|
|
|
|
|
|
|
|
#include <mach-o/loader.h>
|
|
|
|
|
#include <mach-o/nlist.h>
|
|
|
|
|
|
|
|
|
|
#include "base/memory/scoped_ptr.h"
|
|
|
|
|
#include "base/strings/stringprintf.h"
|
|
|
|
|
#include "util/mac/checked_mach_address_range.h"
|
|
|
|
|
#include "util/mach/task_memory.h"
|
|
|
|
|
|
|
|
|
|
namespace crashpad {
|
|
|
|
|
|
|
|
|
|
namespace internal {
|
|
|
|
|
|
|
|
|
|
//! \brief The internal implementation for MachOImageSymbolTableReader.
|
|
|
|
|
//!
|
|
|
|
|
//! Initialization is broken into more than one function that needs to share
|
|
|
|
|
//! data, so member variables are used. However, much of this data is irrelevant
|
|
|
|
|
//! after initialization is completed, so rather than doing it in
|
|
|
|
|
//! MachOImageSymbolTableReader, it’s handled by this class, which is a “friend”
|
|
|
|
|
//! of MachOImageSymbolTableReader.
|
|
|
|
|
class MachOImageSymbolTableReaderInitializer {
|
|
|
|
|
public:
|
|
|
|
|
MachOImageSymbolTableReaderInitializer(
|
|
|
|
|
ProcessReader* process_reader,
|
|
|
|
|
const MachOImageSegmentReader* linkedit_segment,
|
|
|
|
|
const std::string& module_info)
|
|
|
|
|
: module_info_(module_info),
|
|
|
|
|
linkedit_range_(),
|
|
|
|
|
process_reader_(process_reader),
|
|
|
|
|
linkedit_segment_(linkedit_segment) {
|
|
|
|
|
linkedit_range_.SetRange(
|
|
|
|
|
process_reader_, linkedit_segment->Address(), linkedit_segment->Size());
|
|
|
|
|
DCHECK(linkedit_range_.IsValid());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
~MachOImageSymbolTableReaderInitializer() {}
|
|
|
|
|
|
|
|
|
|
//! \brief Reads the symbol table from another process.
|
|
|
|
|
//!
|
|
|
|
|
//! \sa MachOImageSymbolTableReader::Initialize()
|
|
|
|
|
bool Initialize(const process_types::symtab_command* symtab_command,
|
|
|
|
|
const process_types::dysymtab_command* dysymtab_command,
|
|
|
|
|
MachOImageSymbolTableReader::SymbolInformationMap*
|
|
|
|
|
external_defined_symbols) {
|
|
|
|
|
mach_vm_address_t symtab_address =
|
|
|
|
|
AddressForLinkEditComponent(symtab_command->symoff);
|
|
|
|
|
uint32_t symbol_count = symtab_command->nsyms;
|
|
|
|
|
size_t nlist_size = process_types::nlist::ExpectedSize(process_reader_);
|
|
|
|
|
mach_vm_size_t symtab_size = symbol_count * nlist_size;
|
|
|
|
|
if (!IsInLinkEditSegment(symtab_address, symtab_size, "symtab")) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If a dysymtab is present, use it to filter the symtab for just the
|
|
|
|
|
// portion used for extdefsym. If no dysymtab is present, the entire symtab
|
|
|
|
|
// will need to be consulted.
|
|
|
|
|
uint32_t skip_count = 0;
|
|
|
|
|
if (dysymtab_command) {
|
|
|
|
|
if (dysymtab_command->iextdefsym >= symtab_command->nsyms ||
|
|
|
|
|
dysymtab_command->iextdefsym + dysymtab_command->nextdefsym >
|
|
|
|
|
symtab_command->nsyms) {
|
|
|
|
|
LOG(WARNING) << base::StringPrintf(
|
|
|
|
|
"dysymtab extdefsym %u + %u > symtab nsyms %u",
|
|
|
|
|
dysymtab_command->iextdefsym,
|
|
|
|
|
dysymtab_command->nextdefsym,
|
|
|
|
|
symtab_command->nsyms) << module_info_;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
skip_count = dysymtab_command->iextdefsym;
|
|
|
|
|
mach_vm_size_t skip_size = skip_count * nlist_size;
|
|
|
|
|
symtab_address += skip_size;
|
|
|
|
|
symtab_size -= skip_size;
|
|
|
|
|
symbol_count = dysymtab_command->nextdefsym;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
mach_vm_address_t strtab_address =
|
|
|
|
|
AddressForLinkEditComponent(symtab_command->stroff);
|
|
|
|
|
mach_vm_size_t strtab_size = symtab_command->strsize;
|
|
|
|
|
if (!IsInLinkEditSegment(strtab_address, strtab_size, "strtab")) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
scoped_ptr<process_types::nlist[]> symbols(
|
|
|
|
|
new process_types::nlist[symtab_command->nsyms]);
|
|
|
|
|
if (!process_types::nlist::ReadArrayInto(
|
|
|
|
|
process_reader_, symtab_address, symbol_count, &symbols[0])) {
|
|
|
|
|
LOG(WARNING) << "could not read symbol table" << module_info_;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-11 15:10:12 -04:00
|
|
|
|
scoped_ptr<TaskMemory::MappedMemory> string_table;
|
2014-09-05 16:53:18 -04:00
|
|
|
|
for (size_t symbol_index = 0; symbol_index < symbol_count; ++symbol_index) {
|
|
|
|
|
const process_types::nlist& symbol = symbols[symbol_index];
|
|
|
|
|
std::string symbol_info = base::StringPrintf(", symbol index %zu%s",
|
|
|
|
|
skip_count + symbol_index,
|
|
|
|
|
module_info_.c_str());
|
|
|
|
|
uint8_t symbol_type = symbol.n_type & N_TYPE;
|
|
|
|
|
if ((symbol.n_type & N_STAB) == 0 && (symbol.n_type & N_PEXT) == 0 &&
|
|
|
|
|
(symbol_type == N_ABS || symbol_type == N_SECT) &&
|
|
|
|
|
(symbol.n_type & N_EXT)) {
|
|
|
|
|
if (symbol.n_strx >= strtab_size) {
|
|
|
|
|
LOG(WARNING) << base::StringPrintf(
|
|
|
|
|
"string at 0x%x out of bounds (0x%llx)",
|
|
|
|
|
symbol.n_strx,
|
|
|
|
|
strtab_size) << symbol_info;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-11 15:10:12 -04:00
|
|
|
|
if (!string_table) {
|
|
|
|
|
string_table = process_reader_->Memory()->ReadMapped(
|
|
|
|
|
strtab_address, strtab_size);
|
|
|
|
|
if (!string_table) {
|
|
|
|
|
LOG(WARNING) << "could not read string table" << module_info_;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-05 16:53:18 -04:00
|
|
|
|
std::string name;
|
2014-09-11 15:10:12 -04:00
|
|
|
|
if (!string_table->ReadCString(symbol.n_strx, &name)) {
|
2014-09-05 16:53:18 -04:00
|
|
|
|
LOG(WARNING) << "could not read string" << symbol_info;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (symbol_type == N_ABS && symbol.n_sect != NO_SECT) {
|
|
|
|
|
LOG(WARNING) << base::StringPrintf("N_ABS symbol %s in section %u",
|
|
|
|
|
name.c_str(),
|
|
|
|
|
symbol.n_sect) << symbol_info;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (symbol_type == N_SECT && symbol.n_sect == NO_SECT) {
|
|
|
|
|
LOG(WARNING) << base::StringPrintf(
|
|
|
|
|
"N_SECT symbol %s in section NO_SECT",
|
|
|
|
|
name.c_str()) << symbol_info;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (external_defined_symbols->count(name)) {
|
|
|
|
|
LOG(WARNING) << "duplicate symbol " << name << symbol_info;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
MachOImageSymbolTableReader::SymbolInformation symbol_info;
|
|
|
|
|
symbol_info.value = symbol.n_value;
|
|
|
|
|
symbol_info.section = symbol.n_sect;
|
|
|
|
|
(*external_defined_symbols)[name] = symbol_info;
|
|
|
|
|
} else if (dysymtab_command) {
|
|
|
|
|
LOG(WARNING) << "non-external symbol in extdefsym" << symbol_info;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
//! \brief Computes the address for data in the `__LINKEDIT` segment
|
|
|
|
|
//! identified by its file offset in a Mach-O image.
|
|
|
|
|
//!
|
|
|
|
|
//! \param[in] fileoff The file offset relative to the beginning of an image’s
|
|
|
|
|
//! `mach_header` or `mach_header_64` of the data in the `__LINKEDIT`
|
|
|
|
|
//! segment.
|
|
|
|
|
//!
|
|
|
|
|
//! \return The address, in the remote process’ address space, of the
|
|
|
|
|
//! requested data.
|
|
|
|
|
mach_vm_address_t AddressForLinkEditComponent(uint32_t fileoff) const {
|
|
|
|
|
return linkedit_range_.Base() + fileoff - linkedit_segment_->fileoff();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//! \brief Determines whether an address range is located within the
|
|
|
|
|
//! `__LINKEDIT` segment.
|
|
|
|
|
//!
|
|
|
|
|
//! \param[in] address The base address of the range to check.
|
|
|
|
|
//! \param[in] size The size of the range to check.
|
|
|
|
|
//! \param[in] tag A string that identifies the range being checked. This is
|
|
|
|
|
//! used only for logging.
|
|
|
|
|
//!
|
|
|
|
|
//! \return `true` if the range identified by \a address + \a size lies
|
|
|
|
|
//! entirely within the `__LINKEDIT` segment. `false` if that range is
|
|
|
|
|
//! invalid, or if that range is not contained by the `__LINKEDIT`
|
|
|
|
|
//! segment, with an appropriate message logged.
|
|
|
|
|
bool IsInLinkEditSegment(mach_vm_address_t address,
|
|
|
|
|
mach_vm_size_t size,
|
|
|
|
|
const char* tag) const {
|
|
|
|
|
CheckedMachAddressRange subrange(process_reader_, address, size);
|
|
|
|
|
if (!subrange.IsValid()) {
|
|
|
|
|
LOG(WARNING) << base::StringPrintf("invalid %s range (0x%llx + 0x%llx)",
|
|
|
|
|
tag,
|
|
|
|
|
address,
|
|
|
|
|
size) << module_info_;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!linkedit_range_.ContainsRange(subrange)) {
|
|
|
|
|
LOG(WARNING) << base::StringPrintf(
|
|
|
|
|
"%s at 0x%llx + 0x%llx outside of " SEG_LINKEDIT
|
|
|
|
|
" segment at 0x%llx + 0x%llx",
|
|
|
|
|
tag,
|
|
|
|
|
address,
|
|
|
|
|
size,
|
|
|
|
|
linkedit_range_.Base(),
|
|
|
|
|
linkedit_range_.Size()) << module_info_;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::string module_info_;
|
|
|
|
|
CheckedMachAddressRange linkedit_range_;
|
|
|
|
|
ProcessReader* process_reader_; // weak
|
|
|
|
|
const MachOImageSegmentReader* linkedit_segment_; // weak
|
|
|
|
|
|
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(MachOImageSymbolTableReaderInitializer);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
} // namespace internal
|
|
|
|
|
|
|
|
|
|
MachOImageSymbolTableReader::MachOImageSymbolTableReader()
|
|
|
|
|
: external_defined_symbols_(), initialized_() {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
MachOImageSymbolTableReader::~MachOImageSymbolTableReader() {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool MachOImageSymbolTableReader::Initialize(
|
|
|
|
|
ProcessReader* process_reader,
|
|
|
|
|
const process_types::symtab_command* symtab_command,
|
|
|
|
|
const process_types::dysymtab_command* dysymtab_command,
|
|
|
|
|
const MachOImageSegmentReader* linkedit_segment,
|
|
|
|
|
const std::string& module_info) {
|
|
|
|
|
INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
|
|
|
|
|
|
|
|
|
|
internal::MachOImageSymbolTableReaderInitializer initializer(process_reader,
|
|
|
|
|
linkedit_segment,
|
|
|
|
|
module_info);
|
|
|
|
|
if (!initializer.Initialize(
|
|
|
|
|
symtab_command, dysymtab_command, &external_defined_symbols_)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
INITIALIZATION_STATE_SET_VALID(initialized_);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const MachOImageSymbolTableReader::SymbolInformation*
|
|
|
|
|
MachOImageSymbolTableReader::LookUpExternalDefinedSymbol(
|
|
|
|
|
const std::string& name) const {
|
|
|
|
|
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
|
|
|
|
|
|
|
|
|
const auto& iterator = external_defined_symbols_.find(name);
|
|
|
|
|
if (iterator == external_defined_symbols_.end()) {
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
return &iterator->second;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace crashpad
|