Resubmission of https://reviews.llvm.org/D130309 with the 2 patches that fixed the linux buildbot, and new windows fixes. The FileSpec APIs allow users to modify instance variables directly by getting a non const reference to the directory and filename instance variables. This makes it impossible to control all of the times the FileSpec object is modified so we can clear cached member variables like m_resolved and with an upcoming patch caching if the file is relative or absolute. This patch modifies the APIs of FileSpec so no one can modify the directory or filename instance variables directly by adding set accessors and by removing the get accessors that are non const. Many clients were using FileSpec::GetCString(...) which returned a unique C string from a ConstString'ified version of the result of GetPath() which returned a std::string. This caused many locations to use this convenient function incorrectly and could cause many strings to be added to the constant string pool that didn't need to. Most clients were converted to using FileSpec::GetPath().c_str() when possible. Other clients were modified to use the newly renamed version of this function which returns an actualy ConstString: ConstString FileSpec::GetPathAsConstString(bool denormalize = true) const; This avoids the issue where people were getting an already uniqued "const char *" that came from a ConstString only to put the "const char *" back into a "ConstString" object. By returning the ConstString instead of a "const char *" clients can be more efficient with the result. The patch: - Removes the non const GetDirectory() and GetFilename() get accessors - Adds set accessors to replace the above functions: SetDirectory() and SetFilename(). - Adds ClearDirectory() and ClearFilename() to replace usage of the FileSpec::GetDirectory().Clear()/FileSpec::GetFilename().Clear() call sites - Fixed all incorrect usage of FileSpec::GetCString() to use FileSpec::GetPath().c_str() where appropriate, and updated other call sites that wanted a ConstString to use the newly returned ConstString appropriately and efficiently. Differential Revision: https://reviews.llvm.org/D130549
941 lines
35 KiB
C++
941 lines
35 KiB
C++
//===-- PlatformDarwinKernel.cpp ------------------------------------------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "PlatformDarwinKernel.h"
|
|
|
|
#if defined(__APPLE__) // This Plugin uses the Mac-specific
|
|
// source/Host/macosx/cfcpp utilities
|
|
|
|
#include "lldb/Breakpoint/BreakpointLocation.h"
|
|
#include "lldb/Core/Module.h"
|
|
#include "lldb/Core/ModuleList.h"
|
|
#include "lldb/Core/ModuleSpec.h"
|
|
#include "lldb/Core/PluginManager.h"
|
|
#include "lldb/Host/Host.h"
|
|
#include "lldb/Host/HostInfo.h"
|
|
#include "lldb/Interpreter/OptionValueFileSpecList.h"
|
|
#include "lldb/Interpreter/OptionValueProperties.h"
|
|
#include "lldb/Interpreter/Property.h"
|
|
#include "lldb/Symbol/ObjectFile.h"
|
|
#include "lldb/Target/Platform.h"
|
|
#include "lldb/Target/Process.h"
|
|
#include "lldb/Target/Target.h"
|
|
#include "lldb/Utility/ArchSpec.h"
|
|
#include "lldb/Utility/FileSpec.h"
|
|
#include "lldb/Utility/LLDBLog.h"
|
|
#include "lldb/Utility/Log.h"
|
|
#include "lldb/Utility/Status.h"
|
|
#include "lldb/Utility/StreamString.h"
|
|
|
|
#include "llvm/Support/FileSystem.h"
|
|
|
|
#include <CoreFoundation/CoreFoundation.h>
|
|
|
|
#include <memory>
|
|
|
|
#include "Host/macosx/cfcpp/CFCBundle.h"
|
|
|
|
using namespace lldb;
|
|
using namespace lldb_private;
|
|
|
|
// Static Variables
|
|
static uint32_t g_initialize_count = 0;
|
|
|
|
// Static Functions
|
|
void PlatformDarwinKernel::Initialize() {
|
|
PlatformDarwin::Initialize();
|
|
|
|
if (g_initialize_count++ == 0) {
|
|
PluginManager::RegisterPlugin(PlatformDarwinKernel::GetPluginNameStatic(),
|
|
PlatformDarwinKernel::GetDescriptionStatic(),
|
|
PlatformDarwinKernel::CreateInstance,
|
|
PlatformDarwinKernel::DebuggerInitialize);
|
|
}
|
|
}
|
|
|
|
void PlatformDarwinKernel::Terminate() {
|
|
if (g_initialize_count > 0) {
|
|
if (--g_initialize_count == 0) {
|
|
PluginManager::UnregisterPlugin(PlatformDarwinKernel::CreateInstance);
|
|
}
|
|
}
|
|
|
|
PlatformDarwin::Terminate();
|
|
}
|
|
|
|
PlatformSP PlatformDarwinKernel::CreateInstance(bool force,
|
|
const ArchSpec *arch) {
|
|
Log *log = GetLog(LLDBLog::Platform);
|
|
if (log) {
|
|
const char *arch_name;
|
|
if (arch && arch->GetArchitectureName())
|
|
arch_name = arch->GetArchitectureName();
|
|
else
|
|
arch_name = "<null>";
|
|
|
|
const char *triple_cstr =
|
|
arch ? arch->GetTriple().getTriple().c_str() : "<null>";
|
|
|
|
LLDB_LOGF(log, "PlatformDarwinKernel::%s(force=%s, arch={%s,%s})",
|
|
__FUNCTION__, force ? "true" : "false", arch_name, triple_cstr);
|
|
}
|
|
|
|
// This is a special plugin that we don't want to activate just based on an
|
|
// ArchSpec for normal userland debugging. It is only useful in kernel debug
|
|
// sessions and the DynamicLoaderDarwinPlugin (or a user doing 'platform
|
|
// select') will force the creation of this Platform plugin.
|
|
if (!force) {
|
|
LLDB_LOGF(log,
|
|
"PlatformDarwinKernel::%s() aborting creation of platform "
|
|
"because force == false",
|
|
__FUNCTION__);
|
|
return PlatformSP();
|
|
}
|
|
|
|
bool create = force;
|
|
LazyBool is_ios_debug_session = eLazyBoolCalculate;
|
|
|
|
if (!create && arch && arch->IsValid()) {
|
|
const llvm::Triple &triple = arch->GetTriple();
|
|
switch (triple.getVendor()) {
|
|
case llvm::Triple::Apple:
|
|
create = true;
|
|
break;
|
|
|
|
// Only accept "unknown" for vendor if the host is Apple and it "unknown"
|
|
// wasn't specified (it was just returned because it was NOT specified)
|
|
case llvm::Triple::UnknownVendor:
|
|
create = !arch->TripleVendorWasSpecified();
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (create) {
|
|
switch (triple.getOS()) {
|
|
case llvm::Triple::Darwin:
|
|
case llvm::Triple::MacOSX:
|
|
case llvm::Triple::IOS:
|
|
case llvm::Triple::WatchOS:
|
|
case llvm::Triple::TvOS:
|
|
// NEED_BRIDGEOS_TRIPLE case llvm::Triple::BridgeOS:
|
|
break;
|
|
// Only accept "vendor" for vendor if the host is Apple and it "unknown"
|
|
// wasn't specified (it was just returned because it was NOT specified)
|
|
case llvm::Triple::UnknownOS:
|
|
create = !arch->TripleOSWasSpecified();
|
|
break;
|
|
default:
|
|
create = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (arch && arch->IsValid()) {
|
|
switch (arch->GetMachine()) {
|
|
case llvm::Triple::x86:
|
|
case llvm::Triple::x86_64:
|
|
case llvm::Triple::ppc:
|
|
case llvm::Triple::ppc64:
|
|
is_ios_debug_session = eLazyBoolNo;
|
|
break;
|
|
case llvm::Triple::arm:
|
|
case llvm::Triple::aarch64:
|
|
case llvm::Triple::thumb:
|
|
is_ios_debug_session = eLazyBoolYes;
|
|
break;
|
|
default:
|
|
is_ios_debug_session = eLazyBoolCalculate;
|
|
break;
|
|
}
|
|
}
|
|
if (create) {
|
|
LLDB_LOGF(log, "PlatformDarwinKernel::%s() creating platform",
|
|
__FUNCTION__);
|
|
|
|
return PlatformSP(new PlatformDarwinKernel(is_ios_debug_session));
|
|
}
|
|
|
|
LLDB_LOGF(log, "PlatformDarwinKernel::%s() aborting creation of platform",
|
|
__FUNCTION__);
|
|
|
|
return PlatformSP();
|
|
}
|
|
|
|
llvm::StringRef PlatformDarwinKernel::GetDescriptionStatic() {
|
|
return "Darwin Kernel platform plug-in.";
|
|
}
|
|
|
|
/// Code to handle the PlatformDarwinKernel settings
|
|
|
|
#define LLDB_PROPERTIES_platformdarwinkernel
|
|
#include "PlatformMacOSXProperties.inc"
|
|
|
|
enum {
|
|
#define LLDB_PROPERTIES_platformdarwinkernel
|
|
#include "PlatformMacOSXPropertiesEnum.inc"
|
|
};
|
|
|
|
class PlatformDarwinKernelProperties : public Properties {
|
|
public:
|
|
static ConstString &GetSettingName() {
|
|
static ConstString g_setting_name("darwin-kernel");
|
|
return g_setting_name;
|
|
}
|
|
|
|
PlatformDarwinKernelProperties() : Properties() {
|
|
m_collection_sp = std::make_shared<OptionValueProperties>(GetSettingName());
|
|
m_collection_sp->Initialize(g_platformdarwinkernel_properties);
|
|
}
|
|
|
|
~PlatformDarwinKernelProperties() override = default;
|
|
|
|
FileSpecList GetKextDirectories() const {
|
|
const uint32_t idx = ePropertyKextDirectories;
|
|
const OptionValueFileSpecList *option_value =
|
|
m_collection_sp->GetPropertyAtIndexAsOptionValueFileSpecList(
|
|
NULL, false, idx);
|
|
assert(option_value);
|
|
return option_value->GetCurrentValue();
|
|
}
|
|
};
|
|
|
|
static PlatformDarwinKernelProperties &GetGlobalProperties() {
|
|
static PlatformDarwinKernelProperties g_settings;
|
|
return g_settings;
|
|
}
|
|
|
|
void PlatformDarwinKernel::DebuggerInitialize(
|
|
lldb_private::Debugger &debugger) {
|
|
if (!PluginManager::GetSettingForPlatformPlugin(
|
|
debugger, PlatformDarwinKernelProperties::GetSettingName())) {
|
|
const bool is_global_setting = true;
|
|
PluginManager::CreateSettingForPlatformPlugin(
|
|
debugger, GetGlobalProperties().GetValueProperties(),
|
|
ConstString("Properties for the PlatformDarwinKernel plug-in."),
|
|
is_global_setting);
|
|
}
|
|
}
|
|
|
|
/// Default Constructor
|
|
PlatformDarwinKernel::PlatformDarwinKernel(
|
|
lldb_private::LazyBool is_ios_debug_session)
|
|
: PlatformDarwin(false), // This is a remote platform
|
|
m_name_to_kext_path_map_with_dsyms(),
|
|
m_name_to_kext_path_map_without_dsyms(), m_search_directories(),
|
|
m_search_directories_no_recursing(), m_kernel_binaries_with_dsyms(),
|
|
m_kernel_binaries_without_dsyms(), m_kernel_dsyms_no_binaries(),
|
|
m_kernel_dsyms_yaas(), m_ios_debug_session(is_ios_debug_session)
|
|
|
|
{
|
|
CollectKextAndKernelDirectories();
|
|
SearchForKextsAndKernelsRecursively();
|
|
}
|
|
|
|
/// Destructor.
|
|
///
|
|
/// The destructor is virtual since this class is designed to be
|
|
/// inherited from by the plug-in instance.
|
|
PlatformDarwinKernel::~PlatformDarwinKernel() = default;
|
|
|
|
void PlatformDarwinKernel::GetStatus(Stream &strm) {
|
|
Platform::GetStatus(strm);
|
|
strm.Printf(" Debug session type: ");
|
|
if (m_ios_debug_session == eLazyBoolYes)
|
|
strm.Printf("iOS kernel debugging\n");
|
|
else if (m_ios_debug_session == eLazyBoolNo)
|
|
strm.Printf("Mac OS X kernel debugging\n");
|
|
else
|
|
strm.Printf("unknown kernel debugging\n");
|
|
|
|
strm.Printf("Directories searched recursively:\n");
|
|
const uint32_t num_kext_dirs = m_search_directories.size();
|
|
for (uint32_t i = 0; i < num_kext_dirs; ++i) {
|
|
strm.Printf("[%d] %s\n", i, m_search_directories[i].GetPath().c_str());
|
|
}
|
|
|
|
strm.Printf("Directories not searched recursively:\n");
|
|
const uint32_t num_kext_dirs_no_recursion =
|
|
m_search_directories_no_recursing.size();
|
|
for (uint32_t i = 0; i < num_kext_dirs_no_recursion; i++) {
|
|
strm.Printf("[%d] %s\n", i,
|
|
m_search_directories_no_recursing[i].GetPath().c_str());
|
|
}
|
|
|
|
strm.Printf(" Number of kexts with dSYMs indexed: %d\n",
|
|
(int)m_name_to_kext_path_map_with_dsyms.size());
|
|
strm.Printf(" Number of kexts without dSYMs indexed: %d\n",
|
|
(int)m_name_to_kext_path_map_without_dsyms.size());
|
|
strm.Printf(" Number of Kernel binaries with dSYMs indexed: %d\n",
|
|
(int)m_kernel_binaries_with_dsyms.size());
|
|
strm.Printf(" Number of Kernel binaries without dSYMs indexed: %d\n",
|
|
(int)m_kernel_binaries_without_dsyms.size());
|
|
strm.Printf(" Number of Kernel dSYMs with no binaries indexed: %d\n",
|
|
(int)m_kernel_dsyms_no_binaries.size());
|
|
strm.Printf(" Number of Kernel dSYM.yaa's indexed: %d\n",
|
|
(int)m_kernel_dsyms_yaas.size());
|
|
|
|
Log *log = GetLog(LLDBLog::Platform);
|
|
if (log) {
|
|
LLDB_LOGF(log, "\nkexts with dSYMs");
|
|
for (auto pos : m_name_to_kext_path_map_with_dsyms) {
|
|
LLDB_LOGF(log, "%s", pos.second.GetPath().c_str());
|
|
}
|
|
LLDB_LOGF(log, "\nkexts without dSYMs");
|
|
|
|
for (auto pos : m_name_to_kext_path_map_without_dsyms) {
|
|
LLDB_LOGF(log, "%s", pos.second.GetPath().c_str());
|
|
}
|
|
LLDB_LOGF(log, "\nkernel binaries with dSYMS");
|
|
for (auto fs : m_kernel_binaries_with_dsyms) {
|
|
LLDB_LOGF(log, "%s", fs.GetPath().c_str());
|
|
}
|
|
LLDB_LOGF(log, "\nkernel binaries without dSYMS");
|
|
for (auto fs : m_kernel_binaries_without_dsyms) {
|
|
LLDB_LOGF(log, "%s", fs.GetPath().c_str());
|
|
}
|
|
LLDB_LOGF(log, "\nkernel dSYMS with no binaries");
|
|
for (auto fs : m_kernel_dsyms_no_binaries) {
|
|
LLDB_LOGF(log, "%s", fs.GetPath().c_str());
|
|
}
|
|
LLDB_LOGF(log, "\nkernels .dSYM.yaa's");
|
|
for (auto fs : m_kernel_dsyms_yaas) {
|
|
LLDB_LOGF(log, "%s", fs.GetPath().c_str());
|
|
}
|
|
LLDB_LOGF(log, "\n");
|
|
}
|
|
}
|
|
|
|
// Populate the m_search_directories vector with directories we should search
|
|
// for kernel & kext binaries.
|
|
|
|
void PlatformDarwinKernel::CollectKextAndKernelDirectories() {
|
|
// Differentiate between "ios debug session" and "mac debug session" so we
|
|
// don't index kext bundles that won't be used in this debug session. If
|
|
// this is an ios kext debug session, looking in /System/Library/Extensions
|
|
// is a waste of stat()s, for example.
|
|
|
|
// DeveloperDirectory is something like
|
|
// "/Applications/Xcode.app/Contents/Developer"
|
|
std::string developer_dir = HostInfo::GetXcodeDeveloperDirectory().GetPath();
|
|
if (developer_dir.empty())
|
|
developer_dir = "/Applications/Xcode.app/Contents/Developer";
|
|
|
|
if (m_ios_debug_session != eLazyBoolNo) {
|
|
AddSDKSubdirsToSearchPaths(developer_dir +
|
|
"/Platforms/iPhoneOS.platform/Developer/SDKs");
|
|
AddSDKSubdirsToSearchPaths(developer_dir +
|
|
"/Platforms/AppleTVOS.platform/Developer/SDKs");
|
|
AddSDKSubdirsToSearchPaths(developer_dir +
|
|
"/Platforms/WatchOS.platform/Developer/SDKs");
|
|
AddSDKSubdirsToSearchPaths(developer_dir +
|
|
"/Platforms/BridgeOS.platform/Developer/SDKs");
|
|
}
|
|
if (m_ios_debug_session != eLazyBoolYes) {
|
|
AddSDKSubdirsToSearchPaths(developer_dir +
|
|
"/Platforms/MacOSX.platform/Developer/SDKs");
|
|
}
|
|
|
|
AddSDKSubdirsToSearchPaths("/Volumes/KernelDebugKit");
|
|
AddSDKSubdirsToSearchPaths("/AppleInternal/Developer/KDKs");
|
|
// The KDKs distributed from Apple installed on external developer systems
|
|
// may be in directories like /Library/Developer/KDKs/KDK_10.10_14A298i.kdk
|
|
AddSDKSubdirsToSearchPaths("/Library/Developer/KDKs");
|
|
|
|
if (m_ios_debug_session != eLazyBoolNo) {
|
|
}
|
|
if (m_ios_debug_session != eLazyBoolYes) {
|
|
AddRootSubdirsToSearchPaths(this, "/");
|
|
}
|
|
|
|
GetUserSpecifiedDirectoriesToSearch();
|
|
|
|
// Add simple directory /Applications/Xcode.app/Contents/Developer/../Symbols
|
|
FileSpec possible_dir(developer_dir + "/../Symbols");
|
|
FileSystem::Instance().Resolve(possible_dir);
|
|
if (FileSystem::Instance().IsDirectory(possible_dir))
|
|
m_search_directories.push_back(possible_dir);
|
|
|
|
// Add simple directory of the current working directory
|
|
FileSpec cwd(".");
|
|
FileSystem::Instance().Resolve(cwd);
|
|
m_search_directories_no_recursing.push_back(cwd);
|
|
}
|
|
|
|
void PlatformDarwinKernel::GetUserSpecifiedDirectoriesToSearch() {
|
|
FileSpecList user_dirs(GetGlobalProperties().GetKextDirectories());
|
|
std::vector<FileSpec> possible_sdk_dirs;
|
|
|
|
const uint32_t user_dirs_count = user_dirs.GetSize();
|
|
for (uint32_t i = 0; i < user_dirs_count; i++) {
|
|
FileSpec dir = user_dirs.GetFileSpecAtIndex(i);
|
|
FileSystem::Instance().Resolve(dir);
|
|
if (FileSystem::Instance().IsDirectory(dir)) {
|
|
m_search_directories.push_back(dir);
|
|
}
|
|
}
|
|
}
|
|
|
|
void PlatformDarwinKernel::AddRootSubdirsToSearchPaths(
|
|
PlatformDarwinKernel *thisp, const std::string &dir) {
|
|
const char *subdirs[] = {
|
|
"/System/Library/Extensions", "/Library/Extensions",
|
|
"/System/Library/Kernels",
|
|
"/System/Library/Extensions/KDK", // this one probably only exist in
|
|
// /AppleInternal/Developer/KDKs/*.kdk/...
|
|
nullptr};
|
|
for (int i = 0; subdirs[i] != nullptr; i++) {
|
|
FileSpec testdir(dir + subdirs[i]);
|
|
FileSystem::Instance().Resolve(testdir);
|
|
if (FileSystem::Instance().IsDirectory(testdir))
|
|
thisp->m_search_directories.push_back(testdir);
|
|
}
|
|
|
|
// Look for kernel binaries in the top level directory, without any recursion
|
|
thisp->m_search_directories_no_recursing.push_back(FileSpec(dir + "/"));
|
|
}
|
|
|
|
// Given a directory path dir, look for any subdirs named *.kdk and *.sdk
|
|
void PlatformDarwinKernel::AddSDKSubdirsToSearchPaths(const std::string &dir) {
|
|
// Look for *.kdk and *.sdk in dir
|
|
const bool find_directories = true;
|
|
const bool find_files = false;
|
|
const bool find_other = false;
|
|
FileSystem::Instance().EnumerateDirectory(
|
|
dir.c_str(), find_directories, find_files, find_other,
|
|
FindKDKandSDKDirectoriesInDirectory, this);
|
|
}
|
|
|
|
// Helper function to find *.sdk and *.kdk directories in a given directory.
|
|
FileSystem::EnumerateDirectoryResult
|
|
PlatformDarwinKernel::FindKDKandSDKDirectoriesInDirectory(
|
|
void *baton, llvm::sys::fs::file_type ft, llvm::StringRef path) {
|
|
static ConstString g_sdk_suffix = ConstString(".sdk");
|
|
static ConstString g_kdk_suffix = ConstString(".kdk");
|
|
|
|
PlatformDarwinKernel *thisp = (PlatformDarwinKernel *)baton;
|
|
FileSpec file_spec(path);
|
|
if (ft == llvm::sys::fs::file_type::directory_file &&
|
|
(file_spec.GetFileNameExtension() == g_sdk_suffix ||
|
|
file_spec.GetFileNameExtension() == g_kdk_suffix)) {
|
|
AddRootSubdirsToSearchPaths(thisp, file_spec.GetPath());
|
|
}
|
|
return FileSystem::eEnumerateDirectoryResultNext;
|
|
}
|
|
|
|
// Recursively search trough m_search_directories looking for kext and kernel
|
|
// binaries, adding files found to the appropriate lists.
|
|
void PlatformDarwinKernel::SearchForKextsAndKernelsRecursively() {
|
|
const uint32_t num_dirs = m_search_directories.size();
|
|
for (uint32_t i = 0; i < num_dirs; i++) {
|
|
const FileSpec &dir = m_search_directories[i];
|
|
const bool find_directories = true;
|
|
const bool find_files = true;
|
|
const bool find_other = true; // I think eFileTypeSymbolicLink are "other"s.
|
|
FileSystem::Instance().EnumerateDirectory(
|
|
dir.GetPath().c_str(), find_directories, find_files, find_other,
|
|
GetKernelsAndKextsInDirectoryWithRecursion, this);
|
|
}
|
|
const uint32_t num_dirs_no_recurse = m_search_directories_no_recursing.size();
|
|
for (uint32_t i = 0; i < num_dirs_no_recurse; i++) {
|
|
const FileSpec &dir = m_search_directories_no_recursing[i];
|
|
const bool find_directories = true;
|
|
const bool find_files = true;
|
|
const bool find_other = true; // I think eFileTypeSymbolicLink are "other"s.
|
|
FileSystem::Instance().EnumerateDirectory(
|
|
dir.GetPath().c_str(), find_directories, find_files, find_other,
|
|
GetKernelsAndKextsInDirectoryNoRecursion, this);
|
|
}
|
|
}
|
|
|
|
// We're only doing a filename match here. We won't try opening the file to
|
|
// see if it's really a kernel or not until we need to find a kernel of a given
|
|
// UUID. There's no cheap way to find the UUID of a file (or if it's a Mach-O
|
|
// binary at all) without creating a whole Module for the file and throwing it
|
|
// away if it's not wanted.
|
|
//
|
|
// Recurse into any subdirectories found.
|
|
|
|
FileSystem::EnumerateDirectoryResult
|
|
PlatformDarwinKernel::GetKernelsAndKextsInDirectoryWithRecursion(
|
|
void *baton, llvm::sys::fs::file_type ft, llvm::StringRef path) {
|
|
return GetKernelsAndKextsInDirectoryHelper(baton, ft, path, true);
|
|
}
|
|
|
|
FileSystem::EnumerateDirectoryResult
|
|
PlatformDarwinKernel::GetKernelsAndKextsInDirectoryNoRecursion(
|
|
void *baton, llvm::sys::fs::file_type ft, llvm::StringRef path) {
|
|
return GetKernelsAndKextsInDirectoryHelper(baton, ft, path, false);
|
|
}
|
|
|
|
FileSystem::EnumerateDirectoryResult
|
|
PlatformDarwinKernel::GetKernelsAndKextsInDirectoryHelper(
|
|
void *baton, llvm::sys::fs::file_type ft, llvm::StringRef path,
|
|
bool recurse) {
|
|
static ConstString g_kext_suffix = ConstString(".kext");
|
|
static ConstString g_dsym_suffix = ConstString(".dSYM");
|
|
static ConstString g_bundle_suffix = ConstString("Bundle");
|
|
|
|
FileSpec file_spec(path);
|
|
ConstString file_spec_extension = file_spec.GetFileNameExtension();
|
|
|
|
Log *log = GetLog(LLDBLog::Platform);
|
|
|
|
LLDB_LOGV(log, "PlatformDarwinKernel examining '{0}'", file_spec);
|
|
|
|
PlatformDarwinKernel *thisp = (PlatformDarwinKernel *)baton;
|
|
|
|
llvm::StringRef filename = file_spec.GetFilename().GetStringRef();
|
|
bool is_kernel_filename =
|
|
filename.startswith("kernel") || filename.startswith("mach");
|
|
bool is_dsym_yaa = filename.endswith(".dSYM.yaa");
|
|
|
|
if (ft == llvm::sys::fs::file_type::regular_file ||
|
|
ft == llvm::sys::fs::file_type::symlink_file) {
|
|
if (is_kernel_filename) {
|
|
if (file_spec_extension != g_dsym_suffix && !is_dsym_yaa) {
|
|
if (KernelHasdSYMSibling(file_spec)) {
|
|
LLDB_LOGF(log,
|
|
"PlatformDarwinKernel registering kernel binary '%s' with "
|
|
"dSYM sibling",
|
|
file_spec.GetPath().c_str());
|
|
thisp->m_kernel_binaries_with_dsyms.push_back(file_spec);
|
|
} else {
|
|
LLDB_LOGF(
|
|
log,
|
|
"PlatformDarwinKernel registering kernel binary '%s', no dSYM",
|
|
file_spec.GetPath().c_str());
|
|
thisp->m_kernel_binaries_without_dsyms.push_back(file_spec);
|
|
}
|
|
}
|
|
if (is_dsym_yaa) {
|
|
LLDB_LOGF(log, "PlatformDarwinKernel registering kernel .dSYM.yaa '%s'",
|
|
file_spec.GetPath().c_str());
|
|
thisp->m_kernel_dsyms_yaas.push_back(file_spec);
|
|
}
|
|
return FileSystem::eEnumerateDirectoryResultNext;
|
|
}
|
|
} else {
|
|
if (ft == llvm::sys::fs::file_type::directory_file) {
|
|
if (file_spec_extension == g_kext_suffix) {
|
|
AddKextToMap(thisp, file_spec);
|
|
// Look to see if there is a PlugIns subdir with more kexts
|
|
FileSpec contents_plugins(file_spec.GetPath() + "/Contents/PlugIns");
|
|
std::string search_here_too;
|
|
if (FileSystem::Instance().IsDirectory(contents_plugins)) {
|
|
search_here_too = contents_plugins.GetPath();
|
|
} else {
|
|
FileSpec plugins(file_spec.GetPath() + "/PlugIns");
|
|
if (FileSystem::Instance().IsDirectory(plugins)) {
|
|
search_here_too = plugins.GetPath();
|
|
}
|
|
}
|
|
|
|
if (!search_here_too.empty()) {
|
|
const bool find_directories = true;
|
|
const bool find_files = false;
|
|
const bool find_other = false;
|
|
FileSystem::Instance().EnumerateDirectory(
|
|
search_here_too.c_str(), find_directories, find_files, find_other,
|
|
recurse ? GetKernelsAndKextsInDirectoryWithRecursion
|
|
: GetKernelsAndKextsInDirectoryNoRecursion,
|
|
baton);
|
|
}
|
|
return FileSystem::eEnumerateDirectoryResultNext;
|
|
}
|
|
// Do we have a kernel dSYM with no kernel binary?
|
|
if (is_kernel_filename && file_spec_extension == g_dsym_suffix) {
|
|
if (KerneldSYMHasNoSiblingBinary(file_spec)) {
|
|
LLDB_LOGF(log,
|
|
"PlatformDarwinKernel registering kernel dSYM '%s' with "
|
|
"no binary sibling",
|
|
file_spec.GetPath().c_str());
|
|
thisp->m_kernel_dsyms_no_binaries.push_back(file_spec);
|
|
return FileSystem::eEnumerateDirectoryResultNext;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Don't recurse into dSYM/kext/bundle directories
|
|
if (recurse && file_spec_extension != g_dsym_suffix &&
|
|
file_spec_extension != g_kext_suffix &&
|
|
file_spec_extension != g_bundle_suffix) {
|
|
LLDB_LOGV(log, "PlatformDarwinKernel descending into directory '{0}'",
|
|
file_spec);
|
|
return FileSystem::eEnumerateDirectoryResultEnter;
|
|
} else {
|
|
return FileSystem::eEnumerateDirectoryResultNext;
|
|
}
|
|
}
|
|
|
|
void PlatformDarwinKernel::AddKextToMap(PlatformDarwinKernel *thisp,
|
|
const FileSpec &file_spec) {
|
|
Log *log = GetLog(LLDBLog::Platform);
|
|
CFCBundle bundle(file_spec.GetPath().c_str());
|
|
CFStringRef bundle_id(bundle.GetIdentifier());
|
|
if (bundle_id && CFGetTypeID(bundle_id) == CFStringGetTypeID()) {
|
|
char bundle_id_buf[PATH_MAX];
|
|
if (CFStringGetCString(bundle_id, bundle_id_buf, sizeof(bundle_id_buf),
|
|
kCFStringEncodingUTF8)) {
|
|
ConstString bundle_conststr(bundle_id_buf);
|
|
if (KextHasdSYMSibling(file_spec))
|
|
{
|
|
LLDB_LOGF(log,
|
|
"PlatformDarwinKernel registering kext binary '%s' with dSYM "
|
|
"sibling",
|
|
file_spec.GetPath().c_str());
|
|
thisp->m_name_to_kext_path_map_with_dsyms.insert(
|
|
std::pair<ConstString, FileSpec>(bundle_conststr, file_spec));
|
|
}
|
|
else
|
|
{
|
|
LLDB_LOGF(log,
|
|
"PlatformDarwinKernel registering kext binary '%s', no dSYM",
|
|
file_spec.GetPath().c_str());
|
|
thisp->m_name_to_kext_path_map_without_dsyms.insert(
|
|
std::pair<ConstString, FileSpec>(bundle_conststr, file_spec));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Given a FileSpec of /dir/dir/foo.kext
|
|
// Return true if any of these exist:
|
|
// /dir/dir/foo.kext.dSYM
|
|
// /dir/dir/foo.kext/Contents/MacOS/foo.dSYM
|
|
// /dir/dir/foo.kext/foo.dSYM
|
|
bool PlatformDarwinKernel::KextHasdSYMSibling(
|
|
const FileSpec &kext_bundle_filepath) {
|
|
FileSpec dsym_fspec = kext_bundle_filepath;
|
|
std::string filename = dsym_fspec.GetFilename().AsCString();
|
|
filename += ".dSYM";
|
|
dsym_fspec.SetFilename(filename);
|
|
if (FileSystem::Instance().IsDirectory(dsym_fspec)) {
|
|
return true;
|
|
}
|
|
// Should probably get the CFBundleExecutable here or call
|
|
// CFBundleCopyExecutableURL
|
|
|
|
// Look for a deep bundle foramt
|
|
ConstString executable_name =
|
|
kext_bundle_filepath.GetFileNameStrippingExtension();
|
|
std::string deep_bundle_str =
|
|
kext_bundle_filepath.GetPath() + "/Contents/MacOS/";
|
|
deep_bundle_str += executable_name.AsCString();
|
|
deep_bundle_str += ".dSYM";
|
|
dsym_fspec.SetFile(deep_bundle_str, FileSpec::Style::native);
|
|
FileSystem::Instance().Resolve(dsym_fspec);
|
|
if (FileSystem::Instance().IsDirectory(dsym_fspec)) {
|
|
return true;
|
|
}
|
|
|
|
// look for a shallow bundle format
|
|
//
|
|
std::string shallow_bundle_str = kext_bundle_filepath.GetPath() + "/";
|
|
shallow_bundle_str += executable_name.AsCString();
|
|
shallow_bundle_str += ".dSYM";
|
|
dsym_fspec.SetFile(shallow_bundle_str, FileSpec::Style::native);
|
|
FileSystem::Instance().Resolve(dsym_fspec);
|
|
return FileSystem::Instance().IsDirectory(dsym_fspec);
|
|
}
|
|
|
|
// Given a FileSpec of /dir/dir/mach.development.t7004 Return true if a dSYM
|
|
// exists next to it:
|
|
// /dir/dir/mach.development.t7004.dSYM
|
|
bool PlatformDarwinKernel::KernelHasdSYMSibling(const FileSpec &kernel_binary) {
|
|
FileSpec kernel_dsym = kernel_binary;
|
|
std::string filename = kernel_binary.GetFilename().AsCString();
|
|
filename += ".dSYM";
|
|
kernel_dsym.SetFilename(filename);
|
|
return FileSystem::Instance().IsDirectory(kernel_dsym);
|
|
}
|
|
|
|
// Given a FileSpec of /dir/dir/mach.development.t7004.dSYM
|
|
// Return true if only the dSYM exists, no binary next to it.
|
|
// /dir/dir/mach.development.t7004.dSYM
|
|
// but no
|
|
// /dir/dir/mach.development.t7004
|
|
bool PlatformDarwinKernel::KerneldSYMHasNoSiblingBinary(
|
|
const FileSpec &kernel_dsym) {
|
|
static ConstString g_dsym_suffix = ConstString(".dSYM");
|
|
std::string possible_path = kernel_dsym.GetPath();
|
|
if (kernel_dsym.GetFileNameExtension() != g_dsym_suffix)
|
|
return false;
|
|
|
|
FileSpec binary_filespec = kernel_dsym;
|
|
// Chop off the '.dSYM' extension on the filename
|
|
binary_filespec.SetFilename(binary_filespec.GetFileNameStrippingExtension());
|
|
|
|
// Is there a binary next to this this? Then return false.
|
|
if (FileSystem::Instance().Exists(binary_filespec))
|
|
return false;
|
|
|
|
// If we have at least one binary in the DWARF subdir, then
|
|
// this is a properly formed dSYM and it has no binary next
|
|
// to it.
|
|
if (GetDWARFBinaryInDSYMBundle(kernel_dsym).size() > 0)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
// TODO: This method returns a vector of FileSpec's because a
|
|
// dSYM bundle may contain multiple DWARF binaries, but it
|
|
// only implements returning the base name binary for now;
|
|
// it should iterate over every binary in the DWARF subdir
|
|
// and return them all.
|
|
std::vector<FileSpec>
|
|
PlatformDarwinKernel::GetDWARFBinaryInDSYMBundle(FileSpec dsym_bundle) {
|
|
std::vector<FileSpec> results;
|
|
static ConstString g_dsym_suffix = ConstString(".dSYM");
|
|
if (dsym_bundle.GetFileNameExtension() != g_dsym_suffix) {
|
|
return results;
|
|
}
|
|
// Drop the '.dSYM' from the filename
|
|
std::string filename =
|
|
dsym_bundle.GetFileNameStrippingExtension().GetCString();
|
|
std::string dirname = dsym_bundle.GetDirectory().GetCString();
|
|
|
|
std::string binary_filepath = dsym_bundle.GetPath();
|
|
binary_filepath += "/Contents/Resources/DWARF/";
|
|
binary_filepath += filename;
|
|
|
|
FileSpec binary_fspec(binary_filepath);
|
|
if (FileSystem::Instance().Exists(binary_fspec))
|
|
results.push_back(binary_fspec);
|
|
return results;
|
|
}
|
|
|
|
Status PlatformDarwinKernel::GetSharedModule(
|
|
const ModuleSpec &module_spec, Process *process, ModuleSP &module_sp,
|
|
const FileSpecList *module_search_paths_ptr,
|
|
llvm::SmallVectorImpl<ModuleSP> *old_modules, bool *did_create_ptr) {
|
|
Status error;
|
|
module_sp.reset();
|
|
const FileSpec &platform_file = module_spec.GetFileSpec();
|
|
|
|
// Treat the file's path as a kext bundle ID (e.g.
|
|
// "com.apple.driver.AppleIRController") and search our kext index.
|
|
std::string kext_bundle_id = platform_file.GetPath();
|
|
|
|
if (!kext_bundle_id.empty() && module_spec.GetUUID().IsValid()) {
|
|
if (kext_bundle_id == "mach_kernel") {
|
|
return GetSharedModuleKernel(module_spec, process, module_sp,
|
|
module_search_paths_ptr, old_modules,
|
|
did_create_ptr);
|
|
} else {
|
|
return GetSharedModuleKext(module_spec, process, module_sp,
|
|
module_search_paths_ptr, old_modules,
|
|
did_create_ptr);
|
|
}
|
|
} else {
|
|
// Give the generic methods, including possibly calling into DebugSymbols
|
|
// framework on macOS systems, a chance.
|
|
return PlatformDarwin::GetSharedModule(module_spec, process, module_sp,
|
|
module_search_paths_ptr, old_modules,
|
|
did_create_ptr);
|
|
}
|
|
}
|
|
|
|
Status PlatformDarwinKernel::GetSharedModuleKext(
|
|
const ModuleSpec &module_spec, Process *process, ModuleSP &module_sp,
|
|
const FileSpecList *module_search_paths_ptr,
|
|
llvm::SmallVectorImpl<ModuleSP> *old_modules, bool *did_create_ptr) {
|
|
Status error;
|
|
module_sp.reset();
|
|
const FileSpec &platform_file = module_spec.GetFileSpec();
|
|
|
|
// Treat the file's path as a kext bundle ID (e.g.
|
|
// "com.apple.driver.AppleIRController") and search our kext index.
|
|
ConstString kext_bundle(platform_file.GetPath().c_str());
|
|
// First look through the kext bundles that had a dsym next to them
|
|
if (m_name_to_kext_path_map_with_dsyms.count(kext_bundle) > 0) {
|
|
for (BundleIDToKextIterator it = m_name_to_kext_path_map_with_dsyms.begin();
|
|
it != m_name_to_kext_path_map_with_dsyms.end(); ++it) {
|
|
if (it->first == kext_bundle) {
|
|
error = ExamineKextForMatchingUUID(it->second, module_spec.GetUUID(),
|
|
module_spec.GetArchitecture(),
|
|
module_sp);
|
|
if (module_sp.get()) {
|
|
return error;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Give the generic methods, including possibly calling into DebugSymbols
|
|
// framework on macOS systems, a chance.
|
|
error = PlatformDarwin::GetSharedModule(module_spec, process, module_sp,
|
|
module_search_paths_ptr, old_modules,
|
|
did_create_ptr);
|
|
if (error.Success() && module_sp.get()) {
|
|
return error;
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|
|
Status PlatformDarwinKernel::GetSharedModuleKernel(
|
|
const ModuleSpec &module_spec, Process *process, ModuleSP &module_sp,
|
|
const FileSpecList *module_search_paths_ptr,
|
|
llvm::SmallVectorImpl<ModuleSP> *old_modules, bool *did_create_ptr) {
|
|
Status error;
|
|
module_sp.reset();
|
|
|
|
// First try all kernel binaries that have a dSYM next to them
|
|
for (auto possible_kernel : m_kernel_binaries_with_dsyms) {
|
|
if (FileSystem::Instance().Exists(possible_kernel)) {
|
|
ModuleSpec kern_spec(possible_kernel);
|
|
kern_spec.GetUUID() = module_spec.GetUUID();
|
|
module_sp.reset(new Module(kern_spec));
|
|
if (module_sp && module_sp->GetObjectFile() &&
|
|
module_sp->MatchesModuleSpec(kern_spec)) {
|
|
// module_sp is an actual kernel binary we want to add.
|
|
if (process) {
|
|
process->GetTarget().GetImages().AppendIfNeeded(module_sp);
|
|
error.Clear();
|
|
return error;
|
|
} else {
|
|
error = ModuleList::GetSharedModule(kern_spec, module_sp, nullptr,
|
|
nullptr, nullptr);
|
|
if (module_sp && module_sp->GetObjectFile() &&
|
|
module_sp->GetObjectFile()->GetType() !=
|
|
ObjectFile::Type::eTypeCoreFile) {
|
|
return error;
|
|
}
|
|
module_sp.reset();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Next try all dSYMs that have no kernel binary next to them (load
|
|
// the kernel DWARF stub as the main binary)
|
|
for (auto possible_kernel_dsym : m_kernel_dsyms_no_binaries) {
|
|
std::vector<FileSpec> objfile_names =
|
|
GetDWARFBinaryInDSYMBundle(possible_kernel_dsym);
|
|
for (FileSpec objfile : objfile_names) {
|
|
ModuleSpec kern_spec(objfile);
|
|
kern_spec.GetUUID() = module_spec.GetUUID();
|
|
kern_spec.GetSymbolFileSpec() = possible_kernel_dsym;
|
|
|
|
module_sp.reset(new Module(kern_spec));
|
|
if (module_sp && module_sp->GetObjectFile() &&
|
|
module_sp->MatchesModuleSpec(kern_spec)) {
|
|
// module_sp is an actual kernel binary we want to add.
|
|
if (process) {
|
|
process->GetTarget().GetImages().AppendIfNeeded(module_sp);
|
|
error.Clear();
|
|
return error;
|
|
} else {
|
|
error = ModuleList::GetSharedModule(kern_spec, module_sp, nullptr,
|
|
nullptr, nullptr);
|
|
if (module_sp && module_sp->GetObjectFile() &&
|
|
module_sp->GetObjectFile()->GetType() !=
|
|
ObjectFile::Type::eTypeCoreFile) {
|
|
return error;
|
|
}
|
|
module_sp.reset();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Give the generic methods, including possibly calling into DebugSymbols
|
|
// framework on macOS systems, a chance.
|
|
error = PlatformDarwin::GetSharedModule(module_spec, process, module_sp,
|
|
module_search_paths_ptr, old_modules,
|
|
did_create_ptr);
|
|
if (error.Success() && module_sp.get()) {
|
|
return error;
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|
|
std::vector<lldb_private::FileSpec>
|
|
PlatformDarwinKernel::SearchForExecutablesRecursively(const std::string &dir) {
|
|
std::vector<FileSpec> executables;
|
|
std::error_code EC;
|
|
for (llvm::sys::fs::recursive_directory_iterator it(dir.c_str(), EC),
|
|
end;
|
|
it != end && !EC; it.increment(EC)) {
|
|
auto status = it->status();
|
|
if (!status)
|
|
break;
|
|
if (llvm::sys::fs::is_regular_file(*status) &&
|
|
llvm::sys::fs::can_execute(it->path()))
|
|
executables.emplace_back(it->path());
|
|
}
|
|
return executables;
|
|
}
|
|
|
|
Status PlatformDarwinKernel::ExamineKextForMatchingUUID(
|
|
const FileSpec &kext_bundle_path, const lldb_private::UUID &uuid,
|
|
const ArchSpec &arch, ModuleSP &exe_module_sp) {
|
|
for (const auto &exe_file :
|
|
SearchForExecutablesRecursively(kext_bundle_path.GetPath())) {
|
|
if (FileSystem::Instance().Exists(exe_file)) {
|
|
ModuleSpec exe_spec(exe_file);
|
|
exe_spec.GetUUID() = uuid;
|
|
if (!uuid.IsValid()) {
|
|
exe_spec.GetArchitecture() = arch;
|
|
}
|
|
|
|
// First try to create a ModuleSP with the file / arch and see if the UUID
|
|
// matches. If that fails (this exec file doesn't have the correct uuid),
|
|
// don't call GetSharedModule (which may call in to the DebugSymbols
|
|
// framework and therefore can be slow.)
|
|
ModuleSP module_sp(new Module(exe_spec));
|
|
if (module_sp && module_sp->GetObjectFile() &&
|
|
module_sp->MatchesModuleSpec(exe_spec)) {
|
|
Status error = ModuleList::GetSharedModule(exe_spec, exe_module_sp,
|
|
NULL, NULL, NULL);
|
|
if (exe_module_sp && exe_module_sp->GetObjectFile()) {
|
|
return error;
|
|
}
|
|
}
|
|
exe_module_sp.reset();
|
|
}
|
|
}
|
|
|
|
return {};
|
|
}
|
|
|
|
std::vector<ArchSpec> PlatformDarwinKernel::GetSupportedArchitectures(
|
|
const ArchSpec &process_host_arch) {
|
|
std::vector<ArchSpec> result;
|
|
#if defined(__arm__) || defined(__arm64__) || defined(__aarch64__)
|
|
ARMGetSupportedArchitectures(result);
|
|
#else
|
|
x86GetSupportedArchitectures(result);
|
|
#endif
|
|
return result;
|
|
}
|
|
|
|
void PlatformDarwinKernel::CalculateTrapHandlerSymbolNames() {
|
|
m_trap_handlers.push_back(ConstString("trap_from_kernel"));
|
|
m_trap_handlers.push_back(ConstString("hndl_machine_check"));
|
|
m_trap_handlers.push_back(ConstString("hndl_double_fault"));
|
|
m_trap_handlers.push_back(ConstString("hndl_allintrs"));
|
|
m_trap_handlers.push_back(ConstString("hndl_alltraps"));
|
|
m_trap_handlers.push_back(ConstString("interrupt"));
|
|
m_trap_handlers.push_back(ConstString("fleh_prefabt"));
|
|
m_trap_handlers.push_back(ConstString("ExceptionVectorsBase"));
|
|
m_trap_handlers.push_back(ConstString("ExceptionVectorsTable"));
|
|
m_trap_handlers.push_back(ConstString("fleh_undef"));
|
|
m_trap_handlers.push_back(ConstString("fleh_dataabt"));
|
|
m_trap_handlers.push_back(ConstString("fleh_irq"));
|
|
m_trap_handlers.push_back(ConstString("fleh_decirq"));
|
|
m_trap_handlers.push_back(ConstString("fleh_fiq_generic"));
|
|
m_trap_handlers.push_back(ConstString("fleh_dec"));
|
|
}
|
|
|
|
#endif // __APPLE__
|