Skip to content

Commit 5dbec8c

Browse files
authored
[lldb] Claim to support swbreak and hwbreak packets when debugging a gdbremote (#102873)
This fixes #56125 and vadimcn/codelldb#666, as well as the downstream issue in our binary ninja debugger: Vector35/debugger#535 Basically, lldb does not claim to support the `swbreak` packet so the gdbserver would not use it. As a result, the gdbserver always sends the unmodified program counter value which, on systems like x86, causes the program counter to be off-by-one (or otherwise wrong). For reference, the lldb-server always sends the modified program counter value so it works perfectly with lldb. https://sourceware.org/gdb/current/onlinedocs/gdb.html/Stop-Reply-Packets.html#swbreak-stop-reason No new code is added to add support `swbreak`, since the way lldb works already expects the remote to have adjusted the program counter. The change just lets the gdbserver know that lldb supports it, so that it will send the adjusted program counter. To test this PR, you can use lldb to connect to a gdbserver running on e.g., Ubuntu 22.04, and see the program counter is off-by-one without the patch. With the patch, things work as expected
1 parent 4089763 commit 5dbec8c

File tree

4 files changed

+23
-6
lines changed

4 files changed

+23
-6
lines changed

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

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -352,8 +352,11 @@ void GDBRemoteCommunicationClient::GetRemoteQSupported() {
352352

353353
// build the qSupported packet
354354
std::vector<std::string> features = {"xmlRegisters=i386,arm,mips,arc",
355-
"multiprocess+", "fork-events+",
356-
"vfork-events+"};
355+
"multiprocess+",
356+
"fork-events+",
357+
"vfork-events+",
358+
"swbreak+",
359+
"hwbreak+"};
357360
StreamString packet;
358361
packet.PutCString("qSupported");
359362
for (uint32_t i = 0; i < features.size(); ++i) {

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4245,6 +4245,10 @@ std::vector<std::string> GDBRemoteCommunicationServerLLGS::HandleFeatures(
42454245
.Case("vfork-events+", Extension::vfork)
42464246
.Default({});
42474247

4248+
// We consume lldb's swbreak/hwbreak feature, but it doesn't change the
4249+
// behaviour of lldb-server. We always adjust the program counter for targets
4250+
// like x86
4251+
42484252
m_extensions_supported &= plugin_features;
42494253

42504254
// fork & vfork require multiprocess

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2354,6 +2354,9 @@ StateType ProcessGDBRemote::SetThreadStopInfo(StringExtractor &stop_packet) {
23542354
if (!key.getAsInteger(16, reg))
23552355
expedited_register_map[reg] = std::string(std::move(value));
23562356
}
2357+
// swbreak and hwbreak are also expected keys, but we don't need to
2358+
// change our behaviour for them because lldb always expects the remote
2359+
// to adjust the program counter (if relevant, e.g., for x86 targets)
23572360
}
23582361

23592362
if (stop_pid != LLDB_INVALID_PROCESS_ID && stop_pid != pid) {

lldb/test/API/functionalities/gdb_remote_client/TestStopPCs.py

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,17 @@ class TestStopPCs(GDBRemoteTestBase):
1010
def test(self):
1111
class MyResponder(MockGDBServerResponder):
1212
def haltReason(self):
13-
return "T02thread:1ff0d;threads:1ff0d,2ff0d;thread-pcs:10001bc00,10002bc00;"
13+
# lldb should treat the default halt reason, hwbreak and swbreak in the same way. Which is that it
14+
# expects the stub to have corrected the PC already, so lldb should not modify it further.
15+
return "T02thread:1ff0d;threads:1ff0d,2ff0d,3ff0d;thread-pcs:10001bc00,10002bc00,10003bc00;"
1416

1517
def threadStopInfo(self, threadnum):
1618
if threadnum == 0x1FF0D:
17-
return "T02thread:1ff0d;threads:1ff0d,2ff0d;thread-pcs:10001bc00,10002bc00;"
19+
return "T02thread:1ff0d;threads:1ff0d,2ff0d,3ff0d;thread-pcs:10001bc00,10002bc00,10003bc00;"
1820
if threadnum == 0x2FF0D:
19-
return "T00thread:2ff0d;threads:1ff0d,2ff0d;thread-pcs:10001bc00,10002bc00;"
21+
return "T00swbreak:;thread:2ff0d;threads:1ff0d,2ff0d,3ff0d;thread-pcs:10001bc00,10002bc00,10003bc00;"
22+
if threadnum == 0x3FF0D:
23+
return "T00hwbreak:;thread:3ff0d;threads:1ff0d,2ff0d,3ff0d;thread-pcs:10001bc00,10002bc00,10003bc00;"
2024

2125
def qXferRead(self, obj, annex, offset, length):
2226
if annex == "target.xml":
@@ -40,10 +44,13 @@ def qXferRead(self, obj, annex, offset, length):
4044
self.addTearDownHook(lambda: self.runCmd("log disable gdb-remote packets"))
4145
process = self.connect(target)
4246

43-
self.assertEqual(process.GetNumThreads(), 2)
47+
self.assertEqual(process.GetNumThreads(), 3)
4448
th0 = process.GetThreadAtIndex(0)
4549
th1 = process.GetThreadAtIndex(1)
50+
th2 = process.GetThreadAtIndex(2)
4651
self.assertEqual(th0.GetThreadID(), 0x1FF0D)
4752
self.assertEqual(th1.GetThreadID(), 0x2FF0D)
53+
self.assertEqual(th2.GetThreadID(), 0x3FF0D)
4854
self.assertEqual(th0.GetFrameAtIndex(0).GetPC(), 0x10001BC00)
4955
self.assertEqual(th1.GetFrameAtIndex(0).GetPC(), 0x10002BC00)
56+
self.assertEqual(th2.GetFrameAtIndex(0).GetPC(), 0x10003BC00)

0 commit comments

Comments
 (0)