// Copyright 2017 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/linux/debug_rendezvous.h" #include #include #include "base/logging.h" #include "build/build_config.h" #if BUILDFLAG(IS_ANDROID) #include #endif namespace crashpad { namespace { struct Traits32 { using Integer = int32_t; using Address = uint32_t; }; struct Traits64 { using Integer = int64_t; using Address = uint64_t; }; template struct DebugRendezvousSpecific { typename Traits::Integer r_version; typename Traits::Address r_map; typename Traits::Address r_brk; typename Traits::Integer r_state; typename Traits::Address r_ldbase; }; template struct LinkEntrySpecific { typename Traits::Address l_addr; typename Traits::Address l_name; typename Traits::Address l_ld; typename Traits::Address l_next; typename Traits::Address l_prev; }; template bool ReadLinkEntry(const ProcessMemoryRange& memory, LinuxVMAddress* address, DebugRendezvous::LinkEntry* entry_out) { LinkEntrySpecific entry; if (!memory.Read(*address, sizeof(entry), &entry)) { return false; } std::string name; if (!memory.ReadCStringSizeLimited(entry.l_name, 4096, &name)) { name.clear(); } entry_out->load_bias = entry.l_addr; entry_out->dynamic_array = entry.l_ld; entry_out->name.swap(name); *address = entry.l_next; return true; } } // namespace DebugRendezvous::LinkEntry::LinkEntry() : name(), load_bias(0), dynamic_array(0) {} DebugRendezvous::DebugRendezvous() : modules_(), executable_(), initialized_() {} DebugRendezvous::~DebugRendezvous() {} bool DebugRendezvous::Initialize(const ProcessMemoryRange& memory, LinuxVMAddress address) { INITIALIZATION_STATE_SET_INITIALIZING(initialized_); if (!(memory.Is64Bit() ? InitializeSpecific(memory, address) : InitializeSpecific(memory, address))) { return false; } INITIALIZATION_STATE_SET_VALID(initialized_); return true; } const DebugRendezvous::LinkEntry* DebugRendezvous::Executable() const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); return &executable_; } const std::vector& DebugRendezvous::Modules() const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); return modules_; } template bool DebugRendezvous::InitializeSpecific(const ProcessMemoryRange& memory, LinuxVMAddress address) { DebugRendezvousSpecific debug; if (!memory.Read(address, sizeof(debug), &debug)) { return false; } if (debug.r_version != 1) { LOG(ERROR) << "unexpected version " << debug.r_version; return false; } LinuxVMAddress link_entry_address = debug.r_map; if (!ReadLinkEntry(memory, &link_entry_address, &executable_)) { return false; } std::set visited; while (link_entry_address) { if (!visited.insert(link_entry_address).second) { LOG(ERROR) << "cycle at address 0x" << std::hex << link_entry_address; return false; } LinkEntry entry; if (!ReadLinkEntry(memory, &link_entry_address, &entry)) { return false; } modules_.push_back(entry); } #if BUILDFLAG(IS_ANDROID) // Android P (API 28) mistakenly places the vdso in the first entry in the // link map. const int android_runtime_api = android_get_device_api_level(); if (android_runtime_api == 28 && executable_.name == "[vdso]") { LinkEntry executable = modules_[0]; modules_[0] = executable_; executable_ = executable; } #endif // BUILDFLAG(IS_ANDROID) return true; } } // namespace crashpad