Skip to content

Commit bff4673

Browse files
committed
Add a darwin platform setting to specify which exceptions debugserver
should not receive as exceptions (some will get converted to BSD signals instead). This is really the only stable way to ensure that a Mach exception gets converted to it's equivalent BSD signal. For programs that rely on BSD signal handlers, this has to happen or you can't even get the program to invoke the signal handler when under the debugger. This builds on a previous solution to this problem which required you start debugserver with the -U flag. This was not very discoverable and required lldb be the one to launch debugserver, which is not always the case. Differential Revision: https://reviews.llvm.org/D125434
1 parent ececce1 commit bff4673

File tree

25 files changed

+444
-47
lines changed

25 files changed

+444
-47
lines changed

lldb/include/lldb/Interpreter/OptionValueString.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,11 @@ class OptionValueString : public Cloneable<OptionValueString, OptionValue> {
109109
bool IsCurrentValueEmpty() const { return m_current_value.empty(); }
110110

111111
bool IsDefaultValueEmpty() const { return m_default_value.empty(); }
112+
113+
void SetValidator(ValidatorCallback validator, void *baton = nullptr) {
114+
m_validator = validator;
115+
m_validator_baton = baton;
116+
}
112117

113118
protected:
114119
std::string m_current_value;

lldb/include/lldb/Target/Platform.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -847,6 +847,8 @@ class Platform : public PluginInterface {
847847
}
848848

849849
virtual CompilerType GetSiginfoType(const llvm::Triple &triple);
850+
851+
virtual Args GetExtraStartupCommands();
850852

851853
protected:
852854
/// Create a list of ArchSpecs with the given OS and a architectures. The

lldb/include/lldb/Utility/StringExtractorGDBRemote.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,7 @@ class StringExtractorGDBRemote : public StringExtractor {
174174
eServerPacketType_QMemTags, // write memory tags
175175

176176
eServerPacketType_qLLDBSaveCore,
177+
eServerPacketType_QSetIgnoredExceptions
177178
};
178179

179180
ServerPacketType GetServerPacketType() const;

lldb/source/Plugins/Platform/MacOSX/PlatformDarwin.cpp

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,15 @@
1919
#include "lldb/Core/Debugger.h"
2020
#include "lldb/Core/Module.h"
2121
#include "lldb/Core/ModuleSpec.h"
22+
#include "lldb/Core/PluginManager.h"
2223
#include "lldb/Core/Section.h"
2324
#include "lldb/Host/Host.h"
2425
#include "lldb/Host/HostInfo.h"
2526
#include "lldb/Host/XML.h"
2627
#include "lldb/Interpreter/CommandInterpreter.h"
28+
#include "lldb/Interpreter/OptionValueProperties.h"
29+
#include "lldb/Interpreter/OptionValueString.h"
30+
#include "lldb/Interpreter/Options.h"
2731
#include "lldb/Symbol/LocateSymbolFile.h"
2832
#include "lldb/Symbol/ObjectFile.h"
2933
#include "lldb/Symbol/SymbolFile.h"
@@ -48,12 +52,137 @@
4852
using namespace lldb;
4953
using namespace lldb_private;
5054

55+
static Status ExceptionMaskValidator(const char *string, void *unused) {
56+
Status error;
57+
llvm::StringRef str_ref(string);
58+
llvm::SmallVector<llvm::StringRef> candidates;
59+
str_ref.split(candidates, '|');
60+
for (auto candidate : candidates) {
61+
if (!(candidate == "EXC_BAD_ACCESS"
62+
|| candidate == "EXC_BAD_INSTRUCTION"
63+
|| candidate == "EXC_ARITHMETIC"
64+
|| candidate == "EXC_RESOURCE"
65+
|| candidate == "EXC_GUARD")) {
66+
error.SetErrorStringWithFormat("invalid exception type: '%s'",
67+
candidate.str().c_str());
68+
return error;
69+
}
70+
}
71+
return {};
72+
}
73+
5174
/// Destructor.
5275
///
5376
/// The destructor is virtual since this class is designed to be
5477
/// inherited from by the plug-in instance.
5578
PlatformDarwin::~PlatformDarwin() = default;
5679

80+
// Static Variables
81+
static uint32_t g_initialize_count = 0;
82+
83+
void PlatformDarwin::Initialize() {
84+
Platform::Initialize();
85+
86+
if (g_initialize_count++ == 0) {
87+
PluginManager::RegisterPlugin(PlatformDarwin::GetPluginNameStatic(),
88+
PlatformDarwin::GetDescriptionStatic(),
89+
PlatformDarwin::CreateInstance,
90+
PlatformDarwin::DebuggerInitialize);
91+
}
92+
}
93+
94+
void PlatformDarwin::Terminate() {
95+
if (g_initialize_count > 0) {
96+
if (--g_initialize_count == 0) {
97+
PluginManager::UnregisterPlugin(PlatformDarwin::CreateInstance);
98+
}
99+
}
100+
101+
Platform::Terminate();
102+
}
103+
104+
llvm::StringRef PlatformDarwin::GetDescriptionStatic() {
105+
return "Darwin platform plug-in.";
106+
}
107+
108+
PlatformSP PlatformDarwin::CreateInstance(bool force, const ArchSpec *arch) {
109+
// We only create subclasses of the PlatformDarwin plugin.
110+
return PlatformSP();
111+
}
112+
113+
#define LLDB_PROPERTIES_platformdarwin
114+
#include "PlatformMacOSXProperties.inc"
115+
116+
#define LLDB_PROPERTIES_platformdarwin
117+
enum {
118+
#include "PlatformMacOSXPropertiesEnum.inc"
119+
};
120+
121+
class PlatformDarwinProperties : public Properties {
122+
public:
123+
static ConstString &GetSettingName() {
124+
static ConstString g_setting_name("darwin");
125+
return g_setting_name;
126+
}
127+
128+
PlatformDarwinProperties() : Properties() {
129+
m_collection_sp = std::make_shared<OptionValueProperties>(GetSettingName());
130+
m_collection_sp->Initialize(g_platformdarwin_properties);
131+
}
132+
133+
~PlatformDarwinProperties() override = default;
134+
135+
const char *GetIgnoredExceptions() const {
136+
const uint32_t idx = ePropertyIgnoredExceptions;
137+
const OptionValueString *option_value =
138+
m_collection_sp->GetPropertyAtIndexAsOptionValueString(
139+
NULL, false, idx);
140+
assert(option_value);
141+
return option_value->GetCurrentValue();
142+
}
143+
144+
OptionValueString *GetIgnoredExceptionValue() {
145+
const uint32_t idx = ePropertyIgnoredExceptions;
146+
OptionValueString *option_value =
147+
m_collection_sp->GetPropertyAtIndexAsOptionValueString(
148+
NULL, false, idx);
149+
assert(option_value);
150+
return option_value;
151+
}
152+
};
153+
154+
static PlatformDarwinProperties &GetGlobalProperties() {
155+
static PlatformDarwinProperties g_settings;
156+
return g_settings;
157+
}
158+
159+
void PlatformDarwin::DebuggerInitialize(
160+
lldb_private::Debugger &debugger) {
161+
if (!PluginManager::GetSettingForPlatformPlugin(
162+
debugger, PlatformDarwinProperties::GetSettingName())) {
163+
const bool is_global_setting = false;
164+
PluginManager::CreateSettingForPlatformPlugin(
165+
debugger, GetGlobalProperties().GetValueProperties(),
166+
ConstString("Properties for the Darwin platform plug-in."),
167+
is_global_setting);
168+
OptionValueString *value = GetGlobalProperties().GetIgnoredExceptionValue();
169+
value->SetValidator(ExceptionMaskValidator);
170+
}
171+
}
172+
173+
Args
174+
PlatformDarwin::GetExtraStartupCommands() {
175+
std::string ignored_exceptions
176+
= GetGlobalProperties().GetIgnoredExceptions();
177+
if (ignored_exceptions.empty())
178+
return {};
179+
Args ret_args;
180+
std::string packet = "QSetIgnoredExceptions:";
181+
packet.append(ignored_exceptions);
182+
ret_args.AppendArgument(packet);
183+
return ret_args;
184+
}
185+
57186
lldb_private::Status
58187
PlatformDarwin::PutFile(const lldb_private::FileSpec &source,
59188
const lldb_private::FileSpec &destination, uint32_t uid,

lldb/source/Plugins/Platform/MacOSX/PlatformDarwin.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,18 @@ class PlatformDarwin : public PlatformPOSIX {
4848

4949
~PlatformDarwin() override;
5050

51+
static lldb::PlatformSP CreateInstance(bool force, const ArchSpec *arch);
52+
53+
static void DebuggerInitialize(lldb_private::Debugger &debugger);
54+
55+
static void Initialize();
56+
57+
static void Terminate();
58+
59+
static llvm::StringRef GetPluginNameStatic() { return "darwin"; }
60+
61+
static llvm::StringRef GetDescriptionStatic();
62+
5163
Status PutFile(const FileSpec &source, const FileSpec &destination,
5264
uint32_t uid = UINT32_MAX, uint32_t gid = UINT32_MAX) override;
5365

@@ -96,6 +108,8 @@ class PlatformDarwin : public PlatformPOSIX {
96108
FileSpec LocateExecutable(const char *basename) override;
97109

98110
Status LaunchProcess(ProcessLaunchInfo &launch_info) override;
111+
112+
Args GetExtraStartupCommands() override;
99113

100114
static std::tuple<llvm::VersionTuple, llvm::StringRef>
101115
ParseVersionBuildDir(llvm::StringRef str);

lldb/source/Plugins/Platform/MacOSX/PlatformMacOSXProperties.td

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,12 @@ let Definition = "platformdarwinkernel" in {
55
DefaultStringValue<"">,
66
Desc<"Directories/KDKs to search for kexts in when starting a kernel debug session.">;
77
}
8+
9+
let Definition = "platformdarwin" in {
10+
def IgnoredExceptions: Property<"ignored-exceptions", "String">,
11+
DefaultStringValue<"">,
12+
Desc<"List the mach exceptions to ignore, separated by '|' "
13+
"(e.g. 'EXC_BAD_ACCESS|EXC_BAD_INSTRUCTION'). "
14+
"lldb will instead stop on the BSD signal the exception was converted "
15+
"into, if there is one.">;
16+
}

lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -954,12 +954,23 @@ Status ProcessGDBRemote::ConnectToDebugserver(llvm::StringRef connect_url) {
954954
m_gdb_comm.GetVAttachOrWaitSupported();
955955
m_gdb_comm.EnableErrorStringInPacket();
956956

957-
size_t num_cmds = GetExtraStartupCommands().GetArgumentCount();
958-
for (size_t idx = 0; idx < num_cmds; idx++) {
959-
StringExtractorGDBRemote response;
960-
m_gdb_comm.SendPacketAndWaitForResponse(
961-
GetExtraStartupCommands().GetArgumentAtIndex(idx), response);
957+
// First dispatch any commands from the platform:
958+
auto handle_cmds = [&] (const Args &args) -> void {
959+
for (const Args::ArgEntry &entry : args) {
960+
StringExtractorGDBRemote response;
961+
m_gdb_comm.SendPacketAndWaitForResponse(
962+
entry.c_str(), response);
963+
}
964+
};
965+
966+
PlatformSP platform_sp = GetTarget().GetPlatform();
967+
if (platform_sp) {
968+
handle_cmds(platform_sp->GetExtraStartupCommands());
962969
}
970+
971+
// Then dispatch any process commands:
972+
handle_cmds(GetExtraStartupCommands());
973+
963974
return error;
964975
}
965976

lldb/source/Target/Platform.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1945,6 +1945,10 @@ CompilerType Platform::GetSiginfoType(const llvm::Triple& triple) {
19451945
return CompilerType();
19461946
}
19471947

1948+
Args Platform::GetExtraStartupCommands() {
1949+
return {};
1950+
}
1951+
19481952
PlatformSP PlatformList::GetOrCreate(llvm::StringRef name) {
19491953
std::lock_guard<std::recursive_mutex> guard(m_mutex);
19501954
for (const PlatformSP &platform_sp : m_platforms) {

lldb/source/Utility/StringExtractorGDBRemote.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,8 @@ StringExtractorGDBRemote::GetServerPacketType() const {
126126
return eServerPacketType_QSetWorkingDir;
127127
if (PACKET_STARTS_WITH("QSetLogging:"))
128128
return eServerPacketType_QSetLogging;
129+
if (PACKET_STARTS_WITH("QSetIgnoredExceptions"))
130+
return eServerPacketType_QSetIgnoredExceptions;
129131
if (PACKET_STARTS_WITH("QSetMaxPacketSize:"))
130132
return eServerPacketType_QSetMaxPacketSize;
131133
if (PACKET_STARTS_WITH("QSetMaxPayloadSize:"))
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
C_SOURCES := main.c
2+
CFLAGS_EXTRAS := -std=c99
3+
4+
include Makefile.rules
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
"""
2+
Test that by turning off EXC_BAD_ACCESS catching, we can
3+
debug into and out of a signal handler.
4+
"""
5+
6+
import lldb
7+
from lldbsuite.test.decorators import *
8+
import lldbsuite.test.lldbutil as lldbutil
9+
from lldbsuite.test.lldbtest import *
10+
11+
class TestDarwinSignalHandlers(TestBase):
12+
13+
mydir = TestBase.compute_mydir(__file__)
14+
15+
NO_DEBUG_INFO_TESTCASE = True
16+
17+
@skipUnlessDarwin
18+
def test_ignored_thread(self):
19+
"""It isn't possible to convert an EXC_BAD_ACCESS to a signal when
20+
running under the debugger, which makes debugging SIGBUS handlers
21+
and so forth difficult. This test sends QIgnoreExceptions and that
22+
should get us into the signal handler and out again. """
23+
self.build()
24+
self.main_source_file = lldb.SBFileSpec("main.c")
25+
self.suspended_thread_test()
26+
27+
def suspended_thread_test(self):
28+
# Make sure that we don't accept bad values:
29+
self.match("settings set platform.plugin.darwin.ignored-exceptions EXC_BAD_AXESS", "EXC_BAD_AXESS", error=True)
30+
# Now set ourselves to ignore some exceptions. The test depends on ignoring EXC_BAD_ACCESS, but I passed a couple
31+
# to make sure they parse:
32+
self.runCmd("settings set platform.plugin.darwin.ignored-exceptions EXC_BAD_ACCESS|EXC_ARITHMETIC")
33+
(target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint(self,
34+
"Stop here to get things going", self.main_source_file)
35+
36+
sig_bkpt = target.BreakpointCreateBySourceRegex("stop here in the signal handler",
37+
self.main_source_file)
38+
self.assertEqual(sig_bkpt.GetNumLocations(), 1, "Found sig handler breakpoint")
39+
return_bkpt = target.BreakpointCreateBySourceRegex("Break here to make sure we got past the signal handler",
40+
self.main_source_file)
41+
self.assertEqual(return_bkpt.GetNumLocations(), 1, "Found return breakpoint")
42+
# Now continue, and we should stop with a stop reason of SIGBUS:
43+
process.Continue()
44+
self.assertEqual(process.state, lldb.eStateStopped, "Stopped after continue to SIGBUS")
45+
self.assertEqual(thread.stop_reason, lldb.eStopReasonSignal)
46+
self.assertEqual(thread.GetStopReasonDataAtIndex(0), 10, "Got a SIGBUS")
47+
48+
# Now when we continue, we'll find our way into the signal handler:
49+
threads = lldbutil.continue_to_breakpoint(process, sig_bkpt)
50+
self.assertEqual(len(threads), 1, "Stopped at sig breakpoint")
51+
52+
threads = lldbutil.continue_to_breakpoint(process, return_bkpt)
53+
self.assertEqual(len(threads), 1, "Stopped at return breakpoint")
54+
55+
# Make sure we really changed the value:
56+
57+
process.Continue()
58+
self.assertEqual(process.state, lldb.eStateExited, "Process exited")
59+
self.assertEqual(process.exit_state, 20, "Got the right exit status")
60+
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
#include <signal.h>
2+
#include <unistd.h>
3+
#include <stdlib.h>
4+
#include <sys/mman.h>
5+
#include <stdio.h>
6+
7+
int g_ints[] = {10, 20, 30, 40, 50, 60};
8+
9+
void
10+
saction_handler(int signo, siginfo_t info, void *baton) {
11+
printf("Got into handler.\n");
12+
mprotect(g_ints, sizeof(g_ints), PROT_READ|PROT_WRITE); // stop here in the signal handler
13+
g_ints[0] = 20;
14+
}
15+
int
16+
main()
17+
{
18+
mprotect(g_ints, 10*sizeof(int) , PROT_NONE);
19+
struct sigaction my_action;
20+
sigemptyset(&my_action.sa_mask);
21+
my_action.sa_handler = (void (*)(int)) saction_handler;
22+
my_action.sa_flags = SA_SIGINFO;
23+
24+
sigaction(SIGBUS, &my_action, NULL); // Stop here to get things going.
25+
int local_value = g_ints[1];
26+
return local_value; // Break here to make sure we got past the signal handler
27+
}

0 commit comments

Comments
 (0)