crashpad/snapshot/mac/mach_o_image_symbol_table_reader.cc
Hans Wennborg 032f1aecc2 Include-what-you-use related to logging.h
Add direct includes for things provided transitively by logging.h
(or by other headers including logging.h).

This is in preparation for cleaning up unnecessary includes of
logging.h in header files (so if something depends on logging.h,
it needs include it explicitly), and for when Chromium's logging.h
no longer includes check.h, check_op.h, and notreached.h.

DEPS is also updated to roll mini_chromium to ae14a14ab4 which
includes these new header files.

Bug: chromium:1031540
Change-Id: I36f646d0a93854989dc602d0dc7139dd7a7b8621
Reviewed-on: https://chromium-review.googlesource.com/c/crashpad/crashpad/+/2250251
Commit-Queue: Hans Wennborg <hans@chromium.org>
Reviewed-by: Mark Mentovai <mark@chromium.org>
2020-06-18 13:51:20 +00:00

295 lines
12 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 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 "snapshot/mac/mach_o_image_symbol_table_reader.h"
#include <mach-o/loader.h>
#include <mach-o/nlist.h>
#include <sys/types.h>
#include <memory>
#include <utility>
#include "base/logging.h"
#include "base/strings/stringprintf.h"
#include "util/mac/checked_mach_address_range.h"
#include "util/process/process_memory_mac.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, its handled by this class, which is a “friend”
//! of MachOImageSymbolTableReader.
class MachOImageSymbolTableReaderInitializer {
public:
MachOImageSymbolTableReaderInitializer(
ProcessReaderMac* 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_->Is64Bit(),
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;
}
std::unique_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;
}
std::unique_ptr<ProcessMemoryMac::MappedMemory> string_table;
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());
bool valid_symbol = true;
if ((symbol.n_type & N_STAB) == 0 && (symbol.n_type & N_PEXT) == 0 &&
(symbol.n_type & N_EXT)) {
uint8_t symbol_type = symbol.n_type & N_TYPE;
if (symbol_type == N_ABS || symbol_type == N_SECT) {
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;
}
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;
}
}
std::string name;
if (!string_table->ReadCString(symbol.n_strx, &name)) {
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;
}
MachOImageSymbolTableReader::SymbolInformation this_symbol_info;
this_symbol_info.value = symbol.n_value;
this_symbol_info.section = symbol.n_sect;
if (!external_defined_symbols->insert(
std::make_pair(name, this_symbol_info)).second) {
LOG(WARNING) << "duplicate symbol " << name << symbol_info;
return false;
}
} else {
// External indirect symbols may be found in the portion of the symbol
// table used for external symbols as opposed to indirect symbols when
// the indirect symbols are also external. These can be produced by
// Xcode 5.1 ld64-236.3/src/ld/LinkEditClassic.hpp
// ld::tool::SymbolTableAtom<>::addGlobal(). Indirect symbols are not
// currently supported by this symbol table reader, so ignore them
// without failing or logging a message when encountering them. See
// https://groups.google.com/a/chromium.org/d/topic/crashpad-dev/k7QkLwO71Zo
valid_symbol = symbol_type == N_INDR;
}
} else {
valid_symbol = false;
}
if (!valid_symbol && dysymtab_command) {
LOG(WARNING) << "non-external symbol with type " << symbol.n_type
<< " 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 images
//! `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_->Is64Bit(), 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_;
ProcessReaderMac* 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(
ProcessReaderMac* 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 nullptr;
}
return &iterator->second;
}
} // namespace crashpad