diff options
author | 2020-08-03 14:33:06 +0000 | |
---|---|---|
committer | 2020-08-03 14:33:06 +0000 | |
commit | 061da546b983eb767bad15e67af1174fb0bcf31c (patch) | |
tree | 83c78b820819d70aa40c36d90447978b300078c5 /gnu/llvm/lldb/source/Target/TargetList.cpp | |
parent | Import LLVM 10.0.0 release including clang, lld and lldb. (diff) | |
download | wireguard-openbsd-061da546b983eb767bad15e67af1174fb0bcf31c.tar.xz wireguard-openbsd-061da546b983eb767bad15e67af1174fb0bcf31c.zip |
Import LLVM 10.0.0 release including clang, lld and lldb.
ok hackroom
tested by plenty
Diffstat (limited to 'gnu/llvm/lldb/source/Target/TargetList.cpp')
-rw-r--r-- | gnu/llvm/lldb/source/Target/TargetList.cpp | 617 |
1 files changed, 617 insertions, 0 deletions
diff --git a/gnu/llvm/lldb/source/Target/TargetList.cpp b/gnu/llvm/lldb/source/Target/TargetList.cpp new file mode 100644 index 00000000000..1b4db0c2aba --- /dev/null +++ b/gnu/llvm/lldb/source/Target/TargetList.cpp @@ -0,0 +1,617 @@ +//===-- TargetList.cpp ------------------------------------------*- C++ -*-===// +// +// 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 "lldb/Target/TargetList.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Host/Host.h" +#include "lldb/Host/HostInfo.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/OptionGroupPlatform.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Target/Platform.h" +#include "lldb/Target/Process.h" +#include "lldb/Utility/Broadcaster.h" +#include "lldb/Utility/Event.h" +#include "lldb/Utility/State.h" +#include "lldb/Utility/TildeExpressionResolver.h" +#include "lldb/Utility/Timer.h" + +#include "llvm/ADT/SmallString.h" +#include "llvm/Support/FileSystem.h" + +using namespace lldb; +using namespace lldb_private; + +ConstString &TargetList::GetStaticBroadcasterClass() { + static ConstString class_name("lldb.targetList"); + return class_name; +} + +// TargetList constructor +TargetList::TargetList(Debugger &debugger) + : Broadcaster(debugger.GetBroadcasterManager(), + TargetList::GetStaticBroadcasterClass().AsCString()), + m_target_list(), m_target_list_mutex(), m_selected_target_idx(0) { + CheckInWithManager(); +} + +// Destructor +TargetList::~TargetList() { + std::lock_guard<std::recursive_mutex> guard(m_target_list_mutex); + m_target_list.clear(); +} + +Status TargetList::CreateTarget(Debugger &debugger, + llvm::StringRef user_exe_path, + llvm::StringRef triple_str, + LoadDependentFiles load_dependent_files, + const OptionGroupPlatform *platform_options, + TargetSP &target_sp) { + return CreateTargetInternal(debugger, user_exe_path, triple_str, + load_dependent_files, platform_options, target_sp, + false); +} + +Status TargetList::CreateTarget(Debugger &debugger, + llvm::StringRef user_exe_path, + const ArchSpec &specified_arch, + LoadDependentFiles load_dependent_files, + PlatformSP &platform_sp, TargetSP &target_sp) { + return CreateTargetInternal(debugger, user_exe_path, specified_arch, + load_dependent_files, platform_sp, target_sp, + false); +} + +Status TargetList::CreateTargetInternal( + Debugger &debugger, llvm::StringRef user_exe_path, + llvm::StringRef triple_str, LoadDependentFiles load_dependent_files, + const OptionGroupPlatform *platform_options, TargetSP &target_sp, + bool is_dummy_target) { + Status error; + PlatformSP platform_sp; + + // This is purposely left empty unless it is specified by triple_cstr. If not + // initialized via triple_cstr, then the currently selected platform will set + // the architecture correctly. + const ArchSpec arch(triple_str); + if (!triple_str.empty()) { + if (!arch.IsValid()) { + error.SetErrorStringWithFormat("invalid triple '%s'", + triple_str.str().c_str()); + return error; + } + } + + ArchSpec platform_arch(arch); + + bool prefer_platform_arch = false; + + CommandInterpreter &interpreter = debugger.GetCommandInterpreter(); + + // let's see if there is already an existing platform before we go creating + // another... + platform_sp = debugger.GetPlatformList().GetSelectedPlatform(); + + if (platform_options && platform_options->PlatformWasSpecified()) { + // Create a new platform if it doesn't match the selected platform + if (!platform_options->PlatformMatches(platform_sp)) { + const bool select_platform = true; + platform_sp = platform_options->CreatePlatformWithOptions( + interpreter, arch, select_platform, error, platform_arch); + if (!platform_sp) + return error; + } + } + + if (!user_exe_path.empty()) { + ModuleSpecList module_specs; + ModuleSpec module_spec; + module_spec.GetFileSpec().SetFile(user_exe_path, FileSpec::Style::native); + FileSystem::Instance().Resolve(module_spec.GetFileSpec()); + + // Resolve the executable in case we are given a path to a application + // bundle like a .app bundle on MacOSX + Host::ResolveExecutableInBundle(module_spec.GetFileSpec()); + + lldb::offset_t file_offset = 0; + lldb::offset_t file_size = 0; + const size_t num_specs = ObjectFile::GetModuleSpecifications( + module_spec.GetFileSpec(), file_offset, file_size, module_specs); + if (num_specs > 0) { + ModuleSpec matching_module_spec; + + if (num_specs == 1) { + if (module_specs.GetModuleSpecAtIndex(0, matching_module_spec)) { + if (platform_arch.IsValid()) { + if (platform_arch.IsCompatibleMatch( + matching_module_spec.GetArchitecture())) { + // If the OS or vendor weren't specified, then adopt the module's + // architecture so that the platform matching can be more + // accurate + if (!platform_arch.TripleOSWasSpecified() || + !platform_arch.TripleVendorWasSpecified()) { + prefer_platform_arch = true; + platform_arch = matching_module_spec.GetArchitecture(); + } + } else { + StreamString platform_arch_strm; + StreamString module_arch_strm; + + platform_arch.DumpTriple(platform_arch_strm.AsRawOstream()); + matching_module_spec.GetArchitecture().DumpTriple( + module_arch_strm.AsRawOstream()); + error.SetErrorStringWithFormat( + "the specified architecture '%s' is not compatible with '%s' " + "in '%s'", + platform_arch_strm.GetData(), module_arch_strm.GetData(), + module_spec.GetFileSpec().GetPath().c_str()); + return error; + } + } else { + // Only one arch and none was specified + prefer_platform_arch = true; + platform_arch = matching_module_spec.GetArchitecture(); + } + } + } else { + if (arch.IsValid()) { + module_spec.GetArchitecture() = arch; + if (module_specs.FindMatchingModuleSpec(module_spec, + matching_module_spec)) { + prefer_platform_arch = true; + platform_arch = matching_module_spec.GetArchitecture(); + } + } else { + // No architecture specified, check if there is only one platform for + // all of the architectures. + + typedef std::vector<PlatformSP> PlatformList; + PlatformList platforms; + PlatformSP host_platform_sp = Platform::GetHostPlatform(); + for (size_t i = 0; i < num_specs; ++i) { + ModuleSpec module_spec; + if (module_specs.GetModuleSpecAtIndex(i, module_spec)) { + // See if there was a selected platform and check that first + // since the user may have specified it. + if (platform_sp) { + if (platform_sp->IsCompatibleArchitecture( + module_spec.GetArchitecture(), false, nullptr)) { + platforms.push_back(platform_sp); + continue; + } + } + + // Next check the host platform it if wasn't already checked + // above + if (host_platform_sp && + (!platform_sp || + host_platform_sp->GetName() != platform_sp->GetName())) { + if (host_platform_sp->IsCompatibleArchitecture( + module_spec.GetArchitecture(), false, nullptr)) { + platforms.push_back(host_platform_sp); + continue; + } + } + + // Just find a platform that matches the architecture in the + // executable file + PlatformSP fallback_platform_sp( + Platform::GetPlatformForArchitecture( + module_spec.GetArchitecture(), nullptr)); + if (fallback_platform_sp) { + platforms.push_back(fallback_platform_sp); + } + } + } + + Platform *platform_ptr = nullptr; + bool more_than_one_platforms = false; + for (const auto &the_platform_sp : platforms) { + if (platform_ptr) { + if (platform_ptr->GetName() != the_platform_sp->GetName()) { + more_than_one_platforms = true; + platform_ptr = nullptr; + break; + } + } else { + platform_ptr = the_platform_sp.get(); + } + } + + if (platform_ptr) { + // All platforms for all modules in the executable match, so we can + // select this platform + platform_sp = platforms.front(); + } else if (!more_than_one_platforms) { + // No platforms claim to support this file + error.SetErrorString("No matching platforms found for this file, " + "specify one with the --platform option"); + return error; + } else { + // More than one platform claims to support this file, so the + // --platform option must be specified + StreamString error_strm; + std::set<Platform *> platform_set; + error_strm.Printf( + "more than one platform supports this executable ("); + for (const auto &the_platform_sp : platforms) { + if (platform_set.find(the_platform_sp.get()) == + platform_set.end()) { + if (!platform_set.empty()) + error_strm.PutCString(", "); + error_strm.PutCString(the_platform_sp->GetName().GetCString()); + platform_set.insert(the_platform_sp.get()); + } + } + error_strm.Printf( + "), use the --platform option to specify a platform"); + error.SetErrorString(error_strm.GetString()); + return error; + } + } + } + } + } + + // If we have a valid architecture, make sure the current platform is + // compatible with that architecture + if (!prefer_platform_arch && arch.IsValid()) { + if (!platform_sp->IsCompatibleArchitecture(arch, false, &platform_arch)) { + platform_sp = Platform::GetPlatformForArchitecture(arch, &platform_arch); + if (!is_dummy_target && platform_sp) + debugger.GetPlatformList().SetSelectedPlatform(platform_sp); + } + } else if (platform_arch.IsValid()) { + // if "arch" isn't valid, yet "platform_arch" is, it means we have an + // executable file with a single architecture which should be used + ArchSpec fixed_platform_arch; + if (!platform_sp->IsCompatibleArchitecture(platform_arch, false, + &fixed_platform_arch)) { + platform_sp = Platform::GetPlatformForArchitecture(platform_arch, + &fixed_platform_arch); + if (!is_dummy_target && platform_sp) + debugger.GetPlatformList().SetSelectedPlatform(platform_sp); + } + } + + if (!platform_arch.IsValid()) + platform_arch = arch; + + error = TargetList::CreateTargetInternal( + debugger, user_exe_path, platform_arch, load_dependent_files, platform_sp, + target_sp, is_dummy_target); + return error; +} + +lldb::TargetSP TargetList::GetDummyTarget(lldb_private::Debugger &debugger) { + // FIXME: Maybe the dummy target should be per-Debugger + if (!m_dummy_target_sp || !m_dummy_target_sp->IsValid()) { + ArchSpec arch(Target::GetDefaultArchitecture()); + if (!arch.IsValid()) + arch = HostInfo::GetArchitecture(); + Status err = CreateDummyTarget( + debugger, arch.GetTriple().getTriple().c_str(), m_dummy_target_sp); + } + + return m_dummy_target_sp; +} + +Status TargetList::CreateDummyTarget(Debugger &debugger, + llvm::StringRef specified_arch_name, + lldb::TargetSP &target_sp) { + PlatformSP host_platform_sp(Platform::GetHostPlatform()); + return CreateTargetInternal( + debugger, (const char *)nullptr, specified_arch_name, eLoadDependentsNo, + (const OptionGroupPlatform *)nullptr, target_sp, true); +} + +Status TargetList::CreateTargetInternal(Debugger &debugger, + llvm::StringRef user_exe_path, + const ArchSpec &specified_arch, + LoadDependentFiles load_dependent_files, + lldb::PlatformSP &platform_sp, + lldb::TargetSP &target_sp, + bool is_dummy_target) { + static Timer::Category func_cat(LLVM_PRETTY_FUNCTION); + Timer scoped_timer( + func_cat, "TargetList::CreateTarget (file = '%s', arch = '%s')", + user_exe_path.str().c_str(), specified_arch.GetArchitectureName()); + Status error; + + ArchSpec arch(specified_arch); + + if (arch.IsValid()) { + if (!platform_sp || + !platform_sp->IsCompatibleArchitecture(arch, false, nullptr)) + platform_sp = Platform::GetPlatformForArchitecture(specified_arch, &arch); + } + + if (!platform_sp) + platform_sp = debugger.GetPlatformList().GetSelectedPlatform(); + + if (!arch.IsValid()) + arch = specified_arch; + + FileSpec file(user_exe_path); + if (!FileSystem::Instance().Exists(file) && user_exe_path.startswith("~")) { + // we want to expand the tilde but we don't want to resolve any symbolic + // links so we can't use the FileSpec constructor's resolve flag + llvm::SmallString<64> unglobbed_path; + StandardTildeExpressionResolver Resolver; + Resolver.ResolveFullPath(user_exe_path, unglobbed_path); + + if (unglobbed_path.empty()) + file = FileSpec(user_exe_path); + else + file = FileSpec(unglobbed_path.c_str()); + } + + bool user_exe_path_is_bundle = false; + char resolved_bundle_exe_path[PATH_MAX]; + resolved_bundle_exe_path[0] = '\0'; + if (file) { + if (FileSystem::Instance().IsDirectory(file)) + user_exe_path_is_bundle = true; + + if (file.IsRelative() && !user_exe_path.empty()) { + llvm::SmallString<64> cwd; + if (! llvm::sys::fs::current_path(cwd)) { + FileSpec cwd_file(cwd.c_str()); + cwd_file.AppendPathComponent(file); + if (FileSystem::Instance().Exists(cwd_file)) + file = cwd_file; + } + } + + ModuleSP exe_module_sp; + if (platform_sp) { + FileSpecList executable_search_paths( + Target::GetDefaultExecutableSearchPaths()); + ModuleSpec module_spec(file, arch); + error = platform_sp->ResolveExecutable(module_spec, exe_module_sp, + executable_search_paths.GetSize() + ? &executable_search_paths + : nullptr); + } + + if (error.Success() && exe_module_sp) { + if (exe_module_sp->GetObjectFile() == nullptr) { + if (arch.IsValid()) { + error.SetErrorStringWithFormat( + "\"%s\" doesn't contain architecture %s", file.GetPath().c_str(), + arch.GetArchitectureName()); + } else { + error.SetErrorStringWithFormat("unsupported file type \"%s\"", + file.GetPath().c_str()); + } + return error; + } + target_sp.reset(new Target(debugger, arch, platform_sp, is_dummy_target)); + target_sp->SetExecutableModule(exe_module_sp, load_dependent_files); + if (user_exe_path_is_bundle) + exe_module_sp->GetFileSpec().GetPath(resolved_bundle_exe_path, + sizeof(resolved_bundle_exe_path)); + } + } else { + // No file was specified, just create an empty target with any arch if a + // valid arch was specified + target_sp.reset(new Target(debugger, arch, platform_sp, is_dummy_target)); + } + + if (target_sp) { + // Set argv0 with what the user typed, unless the user specified a + // directory. If the user specified a directory, then it is probably a + // bundle that was resolved and we need to use the resolved bundle path + if (!user_exe_path.empty()) { + // Use exactly what the user typed as the first argument when we exec or + // posix_spawn + if (user_exe_path_is_bundle && resolved_bundle_exe_path[0]) { + target_sp->SetArg0(resolved_bundle_exe_path); + } else { + // Use resolved path + target_sp->SetArg0(file.GetPath().c_str()); + } + } + if (file.GetDirectory()) { + FileSpec file_dir; + file_dir.GetDirectory() = file.GetDirectory(); + target_sp->AppendExecutableSearchPaths(file_dir); + } + + // Don't put the dummy target in the target list, it's held separately. + if (!is_dummy_target) { + std::lock_guard<std::recursive_mutex> guard(m_target_list_mutex); + m_selected_target_idx = m_target_list.size(); + m_target_list.push_back(target_sp); + // Now prime this from the dummy target: + target_sp->PrimeFromDummyTarget(debugger.GetDummyTarget()); + } else { + m_dummy_target_sp = target_sp; + } + } + + return error; +} + +bool TargetList::DeleteTarget(TargetSP &target_sp) { + std::lock_guard<std::recursive_mutex> guard(m_target_list_mutex); + collection::iterator pos, end = m_target_list.end(); + + for (pos = m_target_list.begin(); pos != end; ++pos) { + if (pos->get() == target_sp.get()) { + m_target_list.erase(pos); + return true; + } + } + return false; +} + +TargetSP TargetList::FindTargetWithExecutableAndArchitecture( + const FileSpec &exe_file_spec, const ArchSpec *exe_arch_ptr) const { + std::lock_guard<std::recursive_mutex> guard(m_target_list_mutex); + TargetSP target_sp; + collection::const_iterator pos, end = m_target_list.end(); + for (pos = m_target_list.begin(); pos != end; ++pos) { + Module *exe_module = (*pos)->GetExecutableModulePointer(); + + if (exe_module) { + if (FileSpec::Match(exe_file_spec, exe_module->GetFileSpec())) { + if (exe_arch_ptr) { + if (!exe_arch_ptr->IsCompatibleMatch(exe_module->GetArchitecture())) + continue; + } + target_sp = *pos; + break; + } + } + } + return target_sp; +} + +TargetSP TargetList::FindTargetWithProcessID(lldb::pid_t pid) const { + std::lock_guard<std::recursive_mutex> guard(m_target_list_mutex); + TargetSP target_sp; + collection::const_iterator pos, end = m_target_list.end(); + for (pos = m_target_list.begin(); pos != end; ++pos) { + Process *process = (*pos)->GetProcessSP().get(); + if (process && process->GetID() == pid) { + target_sp = *pos; + break; + } + } + return target_sp; +} + +TargetSP TargetList::FindTargetWithProcess(Process *process) const { + TargetSP target_sp; + if (process) { + std::lock_guard<std::recursive_mutex> guard(m_target_list_mutex); + collection::const_iterator pos, end = m_target_list.end(); + for (pos = m_target_list.begin(); pos != end; ++pos) { + if (process == (*pos)->GetProcessSP().get()) { + target_sp = *pos; + break; + } + } + } + return target_sp; +} + +TargetSP TargetList::GetTargetSP(Target *target) const { + TargetSP target_sp; + if (target) { + std::lock_guard<std::recursive_mutex> guard(m_target_list_mutex); + collection::const_iterator pos, end = m_target_list.end(); + for (pos = m_target_list.begin(); pos != end; ++pos) { + if (target == (*pos).get()) { + target_sp = *pos; + break; + } + } + } + return target_sp; +} + +uint32_t TargetList::SendAsyncInterrupt(lldb::pid_t pid) { + uint32_t num_async_interrupts_sent = 0; + + if (pid != LLDB_INVALID_PROCESS_ID) { + TargetSP target_sp(FindTargetWithProcessID(pid)); + if (target_sp) { + Process *process = target_sp->GetProcessSP().get(); + if (process) { + process->SendAsyncInterrupt(); + ++num_async_interrupts_sent; + } + } + } else { + // We don't have a valid pid to broadcast to, so broadcast to the target + // list's async broadcaster... + BroadcastEvent(Process::eBroadcastBitInterrupt, nullptr); + } + + return num_async_interrupts_sent; +} + +uint32_t TargetList::SignalIfRunning(lldb::pid_t pid, int signo) { + uint32_t num_signals_sent = 0; + Process *process = nullptr; + if (pid == LLDB_INVALID_PROCESS_ID) { + // Signal all processes with signal + std::lock_guard<std::recursive_mutex> guard(m_target_list_mutex); + collection::iterator pos, end = m_target_list.end(); + for (pos = m_target_list.begin(); pos != end; ++pos) { + process = (*pos)->GetProcessSP().get(); + if (process) { + if (process->IsAlive()) { + ++num_signals_sent; + process->Signal(signo); + } + } + } + } else { + // Signal a specific process with signal + TargetSP target_sp(FindTargetWithProcessID(pid)); + if (target_sp) { + process = target_sp->GetProcessSP().get(); + if (process) { + if (process->IsAlive()) { + ++num_signals_sent; + process->Signal(signo); + } + } + } + } + return num_signals_sent; +} + +int TargetList::GetNumTargets() const { + std::lock_guard<std::recursive_mutex> guard(m_target_list_mutex); + return m_target_list.size(); +} + +lldb::TargetSP TargetList::GetTargetAtIndex(uint32_t idx) const { + TargetSP target_sp; + std::lock_guard<std::recursive_mutex> guard(m_target_list_mutex); + if (idx < m_target_list.size()) + target_sp = m_target_list[idx]; + return target_sp; +} + +uint32_t TargetList::GetIndexOfTarget(lldb::TargetSP target_sp) const { + std::lock_guard<std::recursive_mutex> guard(m_target_list_mutex); + size_t num_targets = m_target_list.size(); + for (size_t idx = 0; idx < num_targets; idx++) { + if (target_sp == m_target_list[idx]) + return idx; + } + return UINT32_MAX; +} + +uint32_t TargetList::SetSelectedTarget(Target *target) { + std::lock_guard<std::recursive_mutex> guard(m_target_list_mutex); + collection::const_iterator pos, begin = m_target_list.begin(), + end = m_target_list.end(); + for (pos = begin; pos != end; ++pos) { + if (pos->get() == target) { + m_selected_target_idx = std::distance(begin, pos); + return m_selected_target_idx; + } + } + m_selected_target_idx = 0; + return m_selected_target_idx; +} + +lldb::TargetSP TargetList::GetSelectedTarget() { + std::lock_guard<std::recursive_mutex> guard(m_target_list_mutex); + if (m_selected_target_idx >= m_target_list.size()) + m_selected_target_idx = 0; + return GetTargetAtIndex(m_selected_target_idx); +} |