diff --git a/lldb/include/lldb/Interpreter/OptionValueString.h b/lldb/include/lldb/Interpreter/OptionValueString.h --- a/lldb/include/lldb/Interpreter/OptionValueString.h +++ b/lldb/include/lldb/Interpreter/OptionValueString.h @@ -109,6 +109,11 @@ bool IsCurrentValueEmpty() const { return m_current_value.empty(); } bool IsDefaultValueEmpty() const { return m_default_value.empty(); } + + void SetValidator(ValidatorCallback validator, void *baton = nullptr) { + m_validator = validator; + m_validator_baton = baton; + } protected: std::string m_current_value; diff --git a/lldb/include/lldb/Target/Platform.h b/lldb/include/lldb/Target/Platform.h --- a/lldb/include/lldb/Target/Platform.h +++ b/lldb/include/lldb/Target/Platform.h @@ -847,6 +847,8 @@ } virtual CompilerType GetSiginfoType(const llvm::Triple &triple); + + virtual Args GetExtraStartupCommands(); protected: /// Create a list of ArchSpecs with the given OS and a architectures. The diff --git a/lldb/include/lldb/Utility/StringExtractorGDBRemote.h b/lldb/include/lldb/Utility/StringExtractorGDBRemote.h --- a/lldb/include/lldb/Utility/StringExtractorGDBRemote.h +++ b/lldb/include/lldb/Utility/StringExtractorGDBRemote.h @@ -174,6 +174,7 @@ eServerPacketType_QMemTags, // write memory tags eServerPacketType_qLLDBSaveCore, + eServerPacketType_QSetIgnoredExceptions }; ServerPacketType GetServerPacketType() const; diff --git a/lldb/source/Plugins/Platform/MacOSX/PlatformDarwin.h b/lldb/source/Plugins/Platform/MacOSX/PlatformDarwin.h --- a/lldb/source/Plugins/Platform/MacOSX/PlatformDarwin.h +++ b/lldb/source/Plugins/Platform/MacOSX/PlatformDarwin.h @@ -48,6 +48,18 @@ ~PlatformDarwin() override; + static lldb::PlatformSP CreateInstance(bool force, const ArchSpec *arch); + + static void DebuggerInitialize(lldb_private::Debugger &debugger); + + static void Initialize(); + + static void Terminate(); + + static llvm::StringRef GetPluginNameStatic() { return "darwin"; } + + static llvm::StringRef GetDescriptionStatic(); + Status PutFile(const FileSpec &source, const FileSpec &destination, uint32_t uid = UINT32_MAX, uint32_t gid = UINT32_MAX) override; @@ -96,6 +108,8 @@ FileSpec LocateExecutable(const char *basename) override; Status LaunchProcess(ProcessLaunchInfo &launch_info) override; + + Args GetExtraStartupCommands() override; static std::tuple ParseVersionBuildDir(llvm::StringRef str); diff --git a/lldb/source/Plugins/Platform/MacOSX/PlatformDarwin.cpp b/lldb/source/Plugins/Platform/MacOSX/PlatformDarwin.cpp --- a/lldb/source/Plugins/Platform/MacOSX/PlatformDarwin.cpp +++ b/lldb/source/Plugins/Platform/MacOSX/PlatformDarwin.cpp @@ -19,11 +19,15 @@ #include "lldb/Core/Debugger.h" #include "lldb/Core/Module.h" #include "lldb/Core/ModuleSpec.h" +#include "lldb/Core/PluginManager.h" #include "lldb/Core/Section.h" #include "lldb/Host/Host.h" #include "lldb/Host/HostInfo.h" #include "lldb/Host/XML.h" #include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/OptionValueProperties.h" +#include "lldb/Interpreter/OptionValueString.h" +#include "lldb/Interpreter/Options.h" #include "lldb/Symbol/LocateSymbolFile.h" #include "lldb/Symbol/ObjectFile.h" #include "lldb/Symbol/SymbolFile.h" @@ -48,12 +52,137 @@ using namespace lldb; using namespace lldb_private; +static Status ExceptionMaskValidator(const char *string, void *unused) { + Status error; + llvm::StringRef str_ref(string); + llvm::SmallVector candidates; + str_ref.split(candidates, '|'); + for (auto candidate : candidates) { + if (!(candidate == "EXC_BAD_ACCESS" + || candidate == "EXC_BAD_INSTRUCTION" + || candidate == "EXC_ARITHMETIC" + || candidate == "EXC_RESOURCE" + || candidate == "EXC_GUARD")) { + error.SetErrorStringWithFormat("invalid exception type: '%s'", + candidate.str().c_str()); + return error; + } + } + return {}; +} + /// Destructor. /// /// The destructor is virtual since this class is designed to be /// inherited from by the plug-in instance. PlatformDarwin::~PlatformDarwin() = default; +// Static Variables +static uint32_t g_initialize_count = 0; + +void PlatformDarwin::Initialize() { + Platform::Initialize(); + + if (g_initialize_count++ == 0) { + PluginManager::RegisterPlugin(PlatformDarwin::GetPluginNameStatic(), + PlatformDarwin::GetDescriptionStatic(), + PlatformDarwin::CreateInstance, + PlatformDarwin::DebuggerInitialize); + } +} + +void PlatformDarwin::Terminate() { + if (g_initialize_count > 0) { + if (--g_initialize_count == 0) { + PluginManager::UnregisterPlugin(PlatformDarwin::CreateInstance); + } + } + + Platform::Terminate(); +} + +llvm::StringRef PlatformDarwin::GetDescriptionStatic() { + return "Darwin platform plug-in."; +} + +PlatformSP PlatformDarwin::CreateInstance(bool force, const ArchSpec *arch) { + // We only create subclasses of the PlatformDarwin plugin. + return PlatformSP(); +} + +#define LLDB_PROPERTIES_platformdarwin +#include "PlatformMacOSXProperties.inc" + +#define LLDB_PROPERTIES_platformdarwin +enum { +#include "PlatformMacOSXPropertiesEnum.inc" +}; + +class PlatformDarwinProperties : public Properties { +public: + static ConstString &GetSettingName() { + static ConstString g_setting_name("darwin"); + return g_setting_name; + } + + PlatformDarwinProperties() : Properties() { + m_collection_sp = std::make_shared(GetSettingName()); + m_collection_sp->Initialize(g_platformdarwin_properties); + } + + ~PlatformDarwinProperties() override = default; + + const char *GetIgnoredExceptions() const { + const uint32_t idx = ePropertyIgnoredExceptions; + const OptionValueString *option_value = + m_collection_sp->GetPropertyAtIndexAsOptionValueString( + NULL, false, idx); + assert(option_value); + return option_value->GetCurrentValue(); + } + + OptionValueString *GetIgnoredExceptionValue() { + const uint32_t idx = ePropertyIgnoredExceptions; + OptionValueString *option_value = + m_collection_sp->GetPropertyAtIndexAsOptionValueString( + NULL, false, idx); + assert(option_value); + return option_value; + } +}; + +static PlatformDarwinProperties &GetGlobalProperties() { + static PlatformDarwinProperties g_settings; + return g_settings; +} + +void PlatformDarwin::DebuggerInitialize( + lldb_private::Debugger &debugger) { + if (!PluginManager::GetSettingForPlatformPlugin( + debugger, PlatformDarwinProperties::GetSettingName())) { + const bool is_global_setting = false; + PluginManager::CreateSettingForPlatformPlugin( + debugger, GetGlobalProperties().GetValueProperties(), + ConstString("Properties for the Darwin platform plug-in."), + is_global_setting); + OptionValueString *value = GetGlobalProperties().GetIgnoredExceptionValue(); + value->SetValidator(ExceptionMaskValidator); + } +} + +Args +PlatformDarwin::GetExtraStartupCommands() { + std::string ignored_exceptions + = GetGlobalProperties().GetIgnoredExceptions(); + if (ignored_exceptions.empty()) + return {}; + Args ret_args; + std::string packet = "QSetIgnoredExceptions:"; + packet.append(ignored_exceptions); + ret_args.AppendArgument(packet); + return ret_args; +} + lldb_private::Status PlatformDarwin::PutFile(const lldb_private::FileSpec &source, const lldb_private::FileSpec &destination, uint32_t uid, diff --git a/lldb/source/Plugins/Platform/MacOSX/PlatformMacOSXProperties.td b/lldb/source/Plugins/Platform/MacOSX/PlatformMacOSXProperties.td --- a/lldb/source/Plugins/Platform/MacOSX/PlatformMacOSXProperties.td +++ b/lldb/source/Plugins/Platform/MacOSX/PlatformMacOSXProperties.td @@ -5,3 +5,12 @@ DefaultStringValue<"">, Desc<"Directories/KDKs to search for kexts in when starting a kernel debug session.">; } + +let Definition = "platformdarwin" in { + def IgnoredExceptions: Property<"ignored-exceptions", "String">, + DefaultStringValue<"">, + Desc<"List the mach exceptions to ignore, separated by '|' " + "(e.g. 'EXC_BAD_ACCESS|EXC_BAD_INSTRUCTION'). " + "lldb will instead stop on the BSD signal the exception was converted " + "into, if there is one.">; +} diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp --- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp +++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp @@ -954,12 +954,23 @@ m_gdb_comm.GetVAttachOrWaitSupported(); m_gdb_comm.EnableErrorStringInPacket(); - size_t num_cmds = GetExtraStartupCommands().GetArgumentCount(); - for (size_t idx = 0; idx < num_cmds; idx++) { - StringExtractorGDBRemote response; - m_gdb_comm.SendPacketAndWaitForResponse( - GetExtraStartupCommands().GetArgumentAtIndex(idx), response); + // First dispatch any commands from the platform: + auto handle_cmds = [&] (const Args &args) -> void { + for (const Args::ArgEntry &entry : args) { + StringExtractorGDBRemote response; + m_gdb_comm.SendPacketAndWaitForResponse( + entry.c_str(), response); + } + }; + + PlatformSP platform_sp = GetTarget().GetPlatform(); + if (platform_sp) { + handle_cmds(platform_sp->GetExtraStartupCommands()); } + + // Then dispatch any process commands: + handle_cmds(GetExtraStartupCommands()); + return error; } diff --git a/lldb/source/Target/Platform.cpp b/lldb/source/Target/Platform.cpp --- a/lldb/source/Target/Platform.cpp +++ b/lldb/source/Target/Platform.cpp @@ -1945,6 +1945,10 @@ return CompilerType(); } +Args Platform::GetExtraStartupCommands() { + return {}; +} + PlatformSP PlatformList::GetOrCreate(llvm::StringRef name) { std::lock_guard guard(m_mutex); for (const PlatformSP &platform_sp : m_platforms) { diff --git a/lldb/source/Utility/StringExtractorGDBRemote.cpp b/lldb/source/Utility/StringExtractorGDBRemote.cpp --- a/lldb/source/Utility/StringExtractorGDBRemote.cpp +++ b/lldb/source/Utility/StringExtractorGDBRemote.cpp @@ -126,6 +126,8 @@ return eServerPacketType_QSetWorkingDir; if (PACKET_STARTS_WITH("QSetLogging:")) return eServerPacketType_QSetLogging; + if (PACKET_STARTS_WITH("QSetIgnoredExceptions")) + return eServerPacketType_QSetIgnoredExceptions; if (PACKET_STARTS_WITH("QSetMaxPacketSize:")) return eServerPacketType_QSetMaxPacketSize; if (PACKET_STARTS_WITH("QSetMaxPayloadSize:")) diff --git a/lldb/test/API/macosx/ignore_exceptions/Makefile b/lldb/test/API/macosx/ignore_exceptions/Makefile new file mode 100644 --- /dev/null +++ b/lldb/test/API/macosx/ignore_exceptions/Makefile @@ -0,0 +1,4 @@ +C_SOURCES := main.c +CFLAGS_EXTRAS := -std=c99 + +include Makefile.rules diff --git a/lldb/test/API/macosx/ignore_exceptions/TestIgnoredExceptions.py b/lldb/test/API/macosx/ignore_exceptions/TestIgnoredExceptions.py new file mode 100644 --- /dev/null +++ b/lldb/test/API/macosx/ignore_exceptions/TestIgnoredExceptions.py @@ -0,0 +1,60 @@ +""" +Test that by turning off EXC_BAD_ACCESS catching, we can +debug into and out of a signal handler. +""" + +import lldb +from lldbsuite.test.decorators import * +import lldbsuite.test.lldbutil as lldbutil +from lldbsuite.test.lldbtest import * + +class TestDarwinSignalHandlers(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + NO_DEBUG_INFO_TESTCASE = True + + @skipUnlessDarwin + def test_ignored_thread(self): + """It isn't possible to convert an EXC_BAD_ACCESS to a signal when + running under the debugger, which makes debugging SIGBUS handlers + and so forth difficult. This test sends QIgnoreExceptions and that + should get us into the signal handler and out again. """ + self.build() + self.main_source_file = lldb.SBFileSpec("main.c") + self.suspended_thread_test() + + def suspended_thread_test(self): + # Make sure that we don't accept bad values: + self.match("settings set platform.plugin.darwin.ignored-exceptions EXC_BAD_AXESS", "EXC_BAD_AXESS", error=True) + # Now set ourselves to ignore some exceptions. The test depends on ignoring EXC_BAD_ACCESS, but I passed a couple + # to make sure they parse: + self.runCmd("settings set platform.plugin.darwin.ignored-exceptions EXC_BAD_ACCESS|EXC_ARITHMETIC") + (target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint(self, + "Stop here to get things going", self.main_source_file) + + sig_bkpt = target.BreakpointCreateBySourceRegex("stop here in the signal handler", + self.main_source_file) + self.assertEqual(sig_bkpt.GetNumLocations(), 1, "Found sig handler breakpoint") + return_bkpt = target.BreakpointCreateBySourceRegex("Break here to make sure we got past the signal handler", + self.main_source_file) + self.assertEqual(return_bkpt.GetNumLocations(), 1, "Found return breakpoint") + # Now continue, and we should stop with a stop reason of SIGBUS: + process.Continue() + self.assertEqual(process.state, lldb.eStateStopped, "Stopped after continue to SIGBUS") + self.assertEqual(thread.stop_reason, lldb.eStopReasonSignal) + self.assertEqual(thread.GetStopReasonDataAtIndex(0), 10, "Got a SIGBUS") + + # Now when we continue, we'll find our way into the signal handler: + threads = lldbutil.continue_to_breakpoint(process, sig_bkpt) + self.assertEqual(len(threads), 1, "Stopped at sig breakpoint") + + threads = lldbutil.continue_to_breakpoint(process, return_bkpt) + self.assertEqual(len(threads), 1, "Stopped at return breakpoint") + + # Make sure we really changed the value: + + process.Continue() + self.assertEqual(process.state, lldb.eStateExited, "Process exited") + self.assertEqual(process.exit_state, 20, "Got the right exit status") + diff --git a/lldb/test/API/macosx/ignore_exceptions/main.c b/lldb/test/API/macosx/ignore_exceptions/main.c new file mode 100644 --- /dev/null +++ b/lldb/test/API/macosx/ignore_exceptions/main.c @@ -0,0 +1,27 @@ +#include +#include +#include +#include +#include + +int g_ints[] = {10, 20, 30, 40, 50, 60}; + +void +saction_handler(int signo, siginfo_t info, void *baton) { + printf("Got into handler.\n"); + mprotect(g_ints, sizeof(g_ints), PROT_READ|PROT_WRITE); // stop here in the signal handler + g_ints[0] = 20; +} +int +main() +{ + mprotect(g_ints, 10*sizeof(int) , PROT_NONE); + struct sigaction my_action; + sigemptyset(&my_action.sa_mask); + my_action.sa_handler = (void (*)(int)) saction_handler; + my_action.sa_flags = SA_SIGINFO; + + sigaction(SIGBUS, &my_action, NULL); // Stop here to get things going. + int local_value = g_ints[1]; + return local_value; // Break here to make sure we got past the signal handler +} diff --git a/lldb/tools/debugserver/source/DNB.h b/lldb/tools/debugserver/source/DNB.h --- a/lldb/tools/debugserver/source/DNB.h +++ b/lldb/tools/debugserver/source/DNB.h @@ -51,10 +51,14 @@ nub_process_t DNBProcessGetPIDByName(const char *name); nub_process_t DNBProcessAttach(nub_process_t pid, struct timespec *timeout, - bool unmask_signals, char *err_str, + const RNBContext::IgnoredExceptions + &ignored_exceptions, + char *err_str, size_t err_len); nub_process_t DNBProcessAttachByName(const char *name, struct timespec *timeout, - bool unmask_signals, char *err_str, + const RNBContext::IgnoredExceptions + &ignored_exceptions, + char *err_str, size_t err_len); nub_process_t DNBProcessAttachWait(RNBContext *ctx, const char *wait_name, bool ignore_existing, diff --git a/lldb/tools/debugserver/source/DNB.cpp b/lldb/tools/debugserver/source/DNB.cpp --- a/lldb/tools/debugserver/source/DNB.cpp +++ b/lldb/tools/debugserver/source/DNB.cpp @@ -352,7 +352,7 @@ pid_t pid = processSP->LaunchForDebug( path, argv, envp, working_directory, stdin_path, stdout_path, stderr_path, no_stdio, ctx->LaunchFlavor(), disable_aslr, event_data, - ctx->GetUnmaskSignals(), launch_err); + ctx->GetIgnoredExceptions(), launch_err); if (err_str) { *err_str = '\0'; if (launch_err.Fail()) { @@ -412,7 +412,8 @@ } nub_process_t DNBProcessAttachByName(const char *name, struct timespec *timeout, - bool unmask_signals, char *err_str, + const RNBContext::IgnoredExceptions + &ignored_exceptions, char *err_str, size_t err_len) { if (err_str && err_len > 0) err_str[0] = '\0'; @@ -434,11 +435,13 @@ } return DNBProcessAttach(matching_proc_infos[0].kp_proc.p_pid, timeout, - unmask_signals, err_str, err_len); + ignored_exceptions, err_str, err_len); } nub_process_t DNBProcessAttach(nub_process_t attach_pid, - struct timespec *timeout, bool unmask_signals, + struct timespec *timeout, + const RNBContext::IgnoredExceptions + &ignored_exceptions, char *err_str, size_t err_len) { if (err_str && err_len > 0) err_str[0] = '\0'; @@ -487,7 +490,8 @@ DNBLogThreadedIf(LOG_PROCESS, "(DebugNub) attaching to pid %d...", attach_pid); pid = - processSP->AttachForDebug(attach_pid, unmask_signals, err_str, err_len); + processSP->AttachForDebug(attach_pid, ignored_exceptions, err_str, + err_len); if (pid != INVALID_NUB_PROCESS) { bool res = AddProcessToMap(pid, processSP); @@ -782,7 +786,8 @@ DNBLogThreadedIf(LOG_PROCESS, "Attaching to %s with pid %i...\n", waitfor_process_name, waitfor_pid); waitfor_pid = DNBProcessAttach(waitfor_pid, timeout_abstime, - ctx->GetUnmaskSignals(), err_str, err_len); + ctx->GetIgnoredExceptions(), err_str, + err_len); } bool success = waitfor_pid != INVALID_NUB_PROCESS; diff --git a/lldb/tools/debugserver/source/MacOSX/MachException.h b/lldb/tools/debugserver/source/MacOSX/MachException.h --- a/lldb/tools/debugserver/source/MacOSX/MachException.h +++ b/lldb/tools/debugserver/source/MacOSX/MachException.h @@ -127,6 +127,7 @@ uint8_t flags; // Action flags describing what to do with the exception }; static const char *Name(exception_type_t exc_type); + static exception_mask_t ExceptionMask(const char *name); }; #endif diff --git a/lldb/tools/debugserver/source/MacOSX/MachException.cpp b/lldb/tools/debugserver/source/MacOSX/MachException.cpp --- a/lldb/tools/debugserver/source/MacOSX/MachException.cpp +++ b/lldb/tools/debugserver/source/MacOSX/MachException.cpp @@ -512,3 +512,50 @@ } return NULL; } + +// Returns the exception mask for a given exception name. +// 0 is not a legit mask, so we return that in the case of an error. +exception_mask_t MachException::ExceptionMask(const char *name) { + static const char *exception_prefix = "EXC_"; + static const int prefix_len = strlen(exception_prefix); + + // All mach exceptions start with this prefix: + if (strstr(name, exception_prefix) != name) + return 0; + + name += prefix_len; + std::string name_str = name; + if (name_str == "BAD_ACCESS") + return EXC_MASK_BAD_ACCESS; + if (name_str == "BAD_INSTRUCTION") + return EXC_MASK_BAD_INSTRUCTION; + if (name_str == "ARITHMETIC") + return EXC_MASK_ARITHMETIC; + if (name_str == "EMULATION") + return EXC_MASK_EMULATION; + if (name_str == "SOFTWARE") + return EXC_MASK_SOFTWARE; + if (name_str == "BREAKPOINT") + return EXC_MASK_BREAKPOINT; + if (name_str == "SYSCALL") + return EXC_MASK_SYSCALL; + if (name_str == "MACH_SYSCALL") + return EXC_MASK_MACH_SYSCALL; + if (name_str == "RPC_ALERT") + return EXC_MASK_RPC_ALERT; +#ifdef EXC_CRASH + if (name_str == "CRASH") + return EXC_MASK_CRASH; +#endif + if (name_str == "RESOURCE") + return EXC_MASK_RESOURCE; +#ifdef EXC_GUARD + if (name_str == "GUARD") + return EXC_MASK_GUARD; +#endif +#ifdef EXC_CORPSE_NOTIFY + if (name_str == "CORPSE_NOTIFY") + return EXC_MASK_CORPSE_NOTIFY; +#endif + return 0; +} diff --git a/lldb/tools/debugserver/source/MacOSX/MachProcess.h b/lldb/tools/debugserver/source/MacOSX/MachProcess.h --- a/lldb/tools/debugserver/source/MacOSX/MachProcess.h +++ b/lldb/tools/debugserver/source/MacOSX/MachProcess.h @@ -34,6 +34,7 @@ #include "PThreadCondition.h" #include "PThreadEvent.h" #include "PThreadMutex.h" +#include "RNBContext.h" #include "ThreadInfo.h" class DNBThreadResumeActions; @@ -78,14 +79,17 @@ }; // Child process control - pid_t AttachForDebug(pid_t pid, bool unmask_signals, char *err_str, + pid_t AttachForDebug(pid_t pid, + const RNBContext::IgnoredExceptions &ignored_exceptions, + char *err_str, size_t err_len); pid_t LaunchForDebug(const char *path, char const *argv[], char const *envp[], const char *working_directory, const char *stdin_path, const char *stdout_path, const char *stderr_path, bool no_stdio, nub_launch_flavor_t launch_flavor, int disable_aslr, const char *event_data, - bool unmask_signals, DNBError &err); + const RNBContext::IgnoredExceptions &ignored_exceptions, + DNBError &err); static uint32_t GetCPUTypeForLocalProcess(pid_t pid); static pid_t ForkChildForPTraceDebugging(const char *path, char const *argv[], @@ -109,7 +113,8 @@ pid_t BoardServiceLaunchForDebug(const char *app_bundle_path, char const *argv[], char const *envp[], bool no_stdio, bool disable_aslr, - const char *event_data, bool unmask_signals, + const char *event_data, + const RNBContext::IgnoredExceptions &ignored_exceptions, DNBError &launch_err); pid_t BoardServiceForkChildForPTraceDebugging( const char *path, char const *argv[], char const *envp[], bool no_stdio, diff --git a/lldb/tools/debugserver/source/MacOSX/MachProcess.mm b/lldb/tools/debugserver/source/MacOSX/MachProcess.mm --- a/lldb/tools/debugserver/source/MacOSX/MachProcess.mm +++ b/lldb/tools/debugserver/source/MacOSX/MachProcess.mm @@ -2590,8 +2590,11 @@ return NULL; } -pid_t MachProcess::AttachForDebug(pid_t pid, bool unmask_signals, char *err_str, - size_t err_len) { +pid_t MachProcess::AttachForDebug( + pid_t pid, + const RNBContext::IgnoredExceptions &ignored_exceptions, + char *err_str, + size_t err_len) { // Clear out and clean up from any current state Clear(); if (pid != 0) { @@ -2608,7 +2611,7 @@ SetState(eStateAttaching); m_pid = pid; - if (!m_task.StartExceptionThread(unmask_signals, err)) { + if (!m_task.StartExceptionThread(ignored_exceptions, err)) { const char *err_cstr = err.AsString(); ::snprintf(err_str, err_len, "%s", err_cstr ? err_cstr : "unable to start the exception thread"); @@ -3112,7 +3115,9 @@ // working directory for inferior to this const char *stdin_path, const char *stdout_path, const char *stderr_path, bool no_stdio, nub_launch_flavor_t launch_flavor, int disable_aslr, - const char *event_data, bool unmask_signals, DNBError &launch_err) { + const char *event_data, + const RNBContext::IgnoredExceptions &ignored_exceptions, + DNBError &launch_err) { // Clear out and clean up from any current state Clear(); @@ -3138,7 +3143,7 @@ m_flags |= (eMachProcessFlagsUsingFBS | eMachProcessFlagsBoardCalculated); if (BoardServiceLaunchForDebug(app_bundle_path.c_str(), argv, envp, no_stdio, disable_aslr, event_data, - unmask_signals, launch_err) != 0) + ignored_exceptions, launch_err) != 0) return m_pid; // A successful SBLaunchForDebug() returns and assigns a // non-zero m_pid. } @@ -3152,7 +3157,7 @@ m_flags |= (eMachProcessFlagsUsingBKS | eMachProcessFlagsBoardCalculated); if (BoardServiceLaunchForDebug(app_bundle_path.c_str(), argv, envp, no_stdio, disable_aslr, event_data, - unmask_signals, launch_err) != 0) + ignored_exceptions, launch_err) != 0) return m_pid; // A successful SBLaunchForDebug() returns and assigns a // non-zero m_pid. } @@ -3164,7 +3169,7 @@ std::string app_bundle_path = GetAppBundle(path); if (!app_bundle_path.empty()) { if (SBLaunchForDebug(app_bundle_path.c_str(), argv, envp, no_stdio, - disable_aslr, unmask_signals, launch_err) != 0) + disable_aslr, ignored_exceptions, launch_err) != 0) return m_pid; // A successful SBLaunchForDebug() returns and assigns a // non-zero m_pid. } @@ -3198,7 +3203,7 @@ for (i = 0; (arg = argv[i]) != NULL; i++) m_args.push_back(arg); - m_task.StartExceptionThread(unmask_signals, launch_err); + m_task.StartExceptionThread(ignored_exceptions, launch_err); if (launch_err.Fail()) { if (launch_err.AsString() == NULL) launch_err.SetErrorString("unable to start the exception thread"); @@ -3566,7 +3571,9 @@ pid_t MachProcess::SBLaunchForDebug(const char *path, char const *argv[], char const *envp[], bool no_stdio, - bool disable_aslr, bool unmask_signals, + bool disable_aslr, + const RNBContext::IgnoredExceptions + &ignored_exceptions, DNBError &launch_err) { // Clear out and clean up from any current state Clear(); @@ -3583,7 +3590,7 @@ char const *arg; for (i = 0; (arg = argv[i]) != NULL; i++) m_args.push_back(arg); - m_task.StartExceptionThread(unmask_signals, launch_err); + m_task.StartExceptionThread(ignored_exceptions, launch_err); if (launch_err.Fail()) { if (launch_err.AsString() == NULL) @@ -3784,7 +3791,8 @@ #if defined(WITH_BKS) || defined(WITH_FBS) pid_t MachProcess::BoardServiceLaunchForDebug( const char *path, char const *argv[], char const *envp[], bool no_stdio, - bool disable_aslr, const char *event_data, bool unmask_signals, + bool disable_aslr, const char *event_data, + const RNBContext::IgnoredExceptions &ignored_exceptions, DNBError &launch_err) { DNBLogThreadedIf(LOG_PROCESS, "%s( '%s', argv)", __FUNCTION__, path); @@ -3798,7 +3806,7 @@ char const *arg; for (i = 0; (arg = argv[i]) != NULL; i++) m_args.push_back(arg); - m_task.StartExceptionThread(unmask_signals, launch_err); + m_task.StartExceptionThread(ignored_exceptions, launch_err); if (launch_err.Fail()) { if (launch_err.AsString() == NULL) diff --git a/lldb/tools/debugserver/source/MacOSX/MachTask.h b/lldb/tools/debugserver/source/MacOSX/MachTask.h --- a/lldb/tools/debugserver/source/MacOSX/MachTask.h +++ b/lldb/tools/debugserver/source/MacOSX/MachTask.h @@ -21,6 +21,7 @@ #include #include #include "DNBDefs.h" +#include "RNBContext.h" #include "MachException.h" #include "MachVMMemory.h" #include "PThreadMutex.h" @@ -67,7 +68,8 @@ kern_return_t RestoreExceptionPortInfo(); kern_return_t ShutDownExcecptionThread(); - bool StartExceptionThread(bool unmask_signals, DNBError &err); + bool StartExceptionThread( + const RNBContext::IgnoredExceptions &ignored_exceptions, DNBError &err); nub_addr_t GetDYLDAllImageInfosAddress(DNBError &err); kern_return_t BasicInfo(struct task_basic_info *info); static kern_return_t BasicInfo(task_t task, struct task_basic_info *info); diff --git a/lldb/tools/debugserver/source/MacOSX/MachTask.mm b/lldb/tools/debugserver/source/MacOSX/MachTask.mm --- a/lldb/tools/debugserver/source/MacOSX/MachTask.mm +++ b/lldb/tools/debugserver/source/MacOSX/MachTask.mm @@ -602,7 +602,9 @@ return false; } -bool MachTask::StartExceptionThread(bool unmask_signals, DNBError &err) { +bool MachTask::StartExceptionThread( + const RNBContext::IgnoredExceptions &ignored_exceptions, + DNBError &err) { DNBLogThreadedIf(LOG_EXCEPTIONS, "MachTask::%s ( )", __FUNCTION__); task_t task = TaskPortForProcessID(err); @@ -631,10 +633,9 @@ return false; } - if (unmask_signals) { - m_exc_port_info.mask = m_exc_port_info.mask & - ~(EXC_MASK_BAD_ACCESS | EXC_MASK_BAD_INSTRUCTION | - EXC_MASK_ARITHMETIC); + if (!ignored_exceptions.empty()) { + for (exception_mask_t mask : ignored_exceptions) + m_exc_port_info.mask = m_exc_port_info.mask & ~mask; } // Set the ability to get all exceptions on this port diff --git a/lldb/tools/debugserver/source/RNBContext.h b/lldb/tools/debugserver/source/RNBContext.h --- a/lldb/tools/debugserver/source/RNBContext.h +++ b/lldb/tools/debugserver/source/RNBContext.h @@ -21,6 +21,7 @@ class RNBContext { public: + using IgnoredExceptions = std::vector; enum { event_proc_state_changed = 0x001, event_proc_thread_running = 0x002, // Sticky @@ -118,10 +119,13 @@ void SetDetachOnError(bool detach) { m_detach_on_error = detach; } bool GetDetachOnError() { return m_detach_on_error; } - void SetUnmaskSignals(bool unmask_signals) { - m_unmask_signals = unmask_signals; + bool AddIgnoredException(const char *exception_name); + + void AddDefaultIgnoredExceptions(); + + const IgnoredExceptions &GetIgnoredExceptions() { + return m_ignored_exceptions; } - bool GetUnmaskSignals() { return m_unmask_signals; } protected: // Classes that inherit from RNBContext can see and modify these @@ -144,7 +148,7 @@ std::string m_working_directory; std::string m_process_event; bool m_detach_on_error = false; - bool m_unmask_signals = false; + IgnoredExceptions m_ignored_exceptions; void StartProcessStatusThread(); void StopProcessStatusThread(); diff --git a/lldb/tools/debugserver/source/RNBContext.cpp b/lldb/tools/debugserver/source/RNBContext.cpp --- a/lldb/tools/debugserver/source/RNBContext.cpp +++ b/lldb/tools/debugserver/source/RNBContext.cpp @@ -24,6 +24,7 @@ #include "DNB.h" #include "DNBLog.h" #include "RNBRemote.h" +#include "MacOSX/MachException.h" // Destructor RNBContext::~RNBContext() { SetProcessID(INVALID_NUB_PROCESS); } @@ -286,3 +287,17 @@ nub_state_t pid_state = DNBProcessGetState(m_pid); return pid_state == eStateRunning || pid_state == eStateStepping; } + +bool RNBContext::AddIgnoredException(const char *exception_name) { + exception_mask_t exc_mask = MachException::ExceptionMask(exception_name); + if (exc_mask == 0) + return false; + m_ignored_exceptions.push_back(exc_mask); + return true; +} + +void RNBContext::AddDefaultIgnoredExceptions() { + m_ignored_exceptions.push_back(EXC_MASK_BAD_ACCESS); + m_ignored_exceptions.push_back(EXC_MASK_BAD_INSTRUCTION); + m_ignored_exceptions.push_back(EXC_MASK_ARITHMETIC); +} diff --git a/lldb/tools/debugserver/source/RNBRemote.h b/lldb/tools/debugserver/source/RNBRemote.h --- a/lldb/tools/debugserver/source/RNBRemote.h +++ b/lldb/tools/debugserver/source/RNBRemote.h @@ -110,6 +110,7 @@ start_noack_mode, // 'QStartNoAckMode' prefix_reg_packets_with_tid, // 'QPrefixRegisterPacketsWithThreadID set_logging_mode, // 'QSetLogging:' + set_ignored_exceptions, // 'QSetIgnoredExceptions' set_max_packet_size, // 'QSetMaxPacketSize:' set_max_payload_size, // 'QSetMaxPayloadSize:' set_environment_variable, // 'QEnvironment:' @@ -197,6 +198,7 @@ rnb_err_t HandlePacket_QStartNoAckMode(const char *p); rnb_err_t HandlePacket_QThreadSuffixSupported(const char *p); rnb_err_t HandlePacket_QSetLogging(const char *p); + rnb_err_t HandlePacket_QSetIgnoredExceptions(const char *p); rnb_err_t HandlePacket_QSetDisableASLR(const char *p); rnb_err_t HandlePacket_QSetSTDIO(const char *p); rnb_err_t HandlePacket_QSetWorkingDir(const char *p); diff --git a/lldb/tools/debugserver/source/RNBRemote.cpp b/lldb/tools/debugserver/source/RNBRemote.cpp --- a/lldb/tools/debugserver/source/RNBRemote.cpp +++ b/lldb/tools/debugserver/source/RNBRemote.cpp @@ -394,9 +394,12 @@ "'G', 'p', and 'P') support having the thread ID appended " "to the end of the command")); t.push_back(Packet(set_logging_mode, &RNBRemote::HandlePacket_QSetLogging, - NULL, "QSetLogging:", "Check if register packets ('g', " - "'G', 'p', and 'P' support having " - "the thread ID prefix")); + NULL, "QSetLogging:", "Turn on log channels in debugserver")); + t.push_back(Packet(set_ignored_exceptions, &RNBRemote::HandlePacket_QSetIgnoredExceptions, + NULL, "QSetIgnoredExceptions:", "Set the exception types " + "debugserver won't wait for, allowing " + "them to be turned into the equivalent " + "BSD signals by the normal means.")); t.push_back(Packet( set_max_packet_size, &RNBRemote::HandlePacket_QSetMaxPacketSize, NULL, "QSetMaxPacketSize:", @@ -2210,6 +2213,37 @@ return rnb_success; } +rnb_err_t RNBRemote::HandlePacket_QSetIgnoredExceptions(const char *p) { + // We can't set the ignored exceptions if we have a running process: + if (m_ctx.HasValidProcessID()) + return SendPacket("E35"); + + p += sizeof("QSetIgnoredExceptions:") - 1; + bool success = true; + while(1) { + const char *bar = strchr(p, '|'); + if (bar == nullptr) { + success = m_ctx.AddIgnoredException(p); + break; + } else { + std::string exc_str(p, bar - p); + if (exc_str.empty()) { + success = false; + break; + } + + success = m_ctx.AddIgnoredException(exc_str.c_str()); + if (!success) + break; + p = bar + 1; + } + } + if (success) + return SendPacket("OK"); + else + return SendPacket("E36"); +} + rnb_err_t RNBRemote::HandlePacket_QThreadSuffixSupported(const char *p) { m_thread_suffix_supported = true; return SendPacket("OK"); @@ -3791,8 +3825,8 @@ "'%s'", getpid(), attach_name.c_str()); attach_pid = DNBProcessAttachByName(attach_name.c_str(), NULL, - Context().GetUnmaskSignals(), err_str, - sizeof(err_str)); + Context().GetIgnoredExceptions(), + err_str, sizeof(err_str)); } else if (strstr(p, "vAttach;") == p) { p += strlen("vAttach;"); @@ -3806,7 +3840,8 @@ DNBLog("[LaunchAttach] START %d vAttach to pid %d", getpid(), pid_attaching_to); attach_pid = DNBProcessAttach(pid_attaching_to, &attach_timeout_abstime, - false, err_str, sizeof(err_str)); + m_ctx.GetIgnoredExceptions(), + err_str, sizeof(err_str)); } } else { return HandlePacket_UNIMPLEMENTED(p); diff --git a/lldb/tools/debugserver/source/debugserver.cpp b/lldb/tools/debugserver/source/debugserver.cpp --- a/lldb/tools/debugserver/source/debugserver.cpp +++ b/lldb/tools/debugserver/source/debugserver.cpp @@ -368,7 +368,7 @@ DNBLogThreadedIf(LOG_RNB_MINIMAL, "%s Attaching to pid %i...", __FUNCTION__, attach_pid); char err_str[1024]; - pid = DNBProcessAttach(attach_pid, NULL, ctx.GetUnmaskSignals(), err_str, + pid = DNBProcessAttach(attach_pid, NULL, ctx.GetIgnoredExceptions(), err_str, sizeof(err_str)); g_pid = pid; @@ -1275,7 +1275,7 @@ break; case 'U': - ctx.SetUnmaskSignals(true); + ctx.AddDefaultIgnoredExceptions(); break; case '2': @@ -1574,7 +1574,7 @@ RNBLogSTDOUT("Attaching to process %s...\n", attach_pid_name.c_str()); nub_process_t pid = DNBProcessAttachByName( - attach_pid_name.c_str(), timeout_ptr, ctx.GetUnmaskSignals(), + attach_pid_name.c_str(), timeout_ptr, ctx.GetIgnoredExceptions(), err_str, sizeof(err_str)); g_pid = pid; if (pid == INVALID_NUB_PROCESS) {