https://github.com/aurore-poirier updated https://github.com/llvm/llvm-project/pull/200134
>From 08bc7e1d4b0e487db880c0ac233349e79256cedf Mon Sep 17 00:00:00 2001 From: David Spickett <[email protected]> Date: Fri, 29 May 2026 12:57:23 +0000 Subject: [PATCH 1/5] [lldb][test] Add test for qSymbol handling with an ELF program qSymbol is sent to the debug server every time libraries are loaded. It can respond to this with symbols it wants to know the value for. It's rarely used so I expect that's why we had zero test coverage for it. In this commit I'm adding a test case for ELF, derived from the bug being reported and fixed in #200134. Ideally this would run for MachO as well, as desired behaviour differs there. However, we need to fake the debug server and wrapping a real server is going to add too much complexity. The mock debug server has a list of symbols to ask for and records the results. If anything unexpected happens it'll assert with Python's built in assert as that's all I can use in this class. Unfortunately these assert failures are hard to debug, they are just presented as a crashing server for an unkown reason. This is just inherent to the mock debug server unfortunately. I have to do some validation in the mock itself, and this is the least worst way to do it. After LLDB has connected we check with the mock that it got all the answers it wanted. Right now, 2 of the symbols should have values but don't. That should be fixed by #200134. One symbol is intentionally missing to check that LLDB handles that situation properly, even when the others have been fixed. While I was doing this I realised the documentaiton for an unknown symbol response was wrong. You can cross check this with GDB's: https://sourceware.org/gdb/current/onlinedocs/gdb.html/General-Query-Packets.html#General-Query-Packets --- lldb/docs/resources/lldbgdbremote.md | 6 +- .../gdb_remote_client/TestQSymbol.py | 83 +++++++++++++++++++ .../gdb_remote_client/test_qsymbol.yaml | 33 ++++++++ 3 files changed, 119 insertions(+), 3 deletions(-) create mode 100644 lldb/test/API/functionalities/gdb_remote_client/TestQSymbol.py create mode 100644 lldb/test/API/functionalities/gdb_remote_client/test_qsymbol.yaml diff --git a/lldb/docs/resources/lldbgdbremote.md b/lldb/docs/resources/lldbgdbremote.md index 577a5ad31674f..cfdfa34ab48bb 100644 --- a/lldb/docs/resources/lldbgdbremote.md +++ b/lldb/docs/resources/lldbgdbremote.md @@ -2135,10 +2135,10 @@ symbol: read packet: qSymbol:6578616D706C65 ``` -This should be looked up by LLDB then sent back to the server. Include the name -again, with the vaue as a hex number: +This should be looked up by LLDB then sent back to the server. Include the value +as a hex number, then the name of the symbol: ``` -read packet: qSymbol:6578616D706C65:CAFEF00D +read packet: qSymbol:CAFEF00D:6578616D706C65 ``` If LLDB cannot find the value, it should respond with only the name. Note that diff --git a/lldb/test/API/functionalities/gdb_remote_client/TestQSymbol.py b/lldb/test/API/functionalities/gdb_remote_client/TestQSymbol.py new file mode 100644 index 0000000000000..d829ba7fc6409 --- /dev/null +++ b/lldb/test/API/functionalities/gdb_remote_client/TestQSymbol.py @@ -0,0 +1,83 @@ +""" +Test LLDB's handling of qSymbol sequences. +""" + +from textwrap import dedent +import lldb +from lldbsuite.test.lldbtest import * +from lldbsuite.test.decorators import * +from lldbsuite.test.gdbclientutils import * +from lldbsuite.test.lldbgdbclient import GDBRemoteTestBase +from lldbsuite.support.seven import hexlify, unhexlify + + +class MyResponder(MockGDBServerResponder): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.wanted_symbols = [ + "main", + "local_address", + "global_value", + "local_value", + "not_a_symbol", + ] + self.last_symbol_request = None + self.symbol_results = {} + + def qSymbol(self, args): + # args will be <name hex encoded>:<hex value>. + # In the initial packet both fields are empty. + if args == ":": + assert self.last_symbol_request is None + else: + # Anything else should be a response to our previous request. + value, name = args.split(":") + name = unhexlify(name) + assert name == self.last_symbol_request + + if value: + self.symbol_results[name] = int(value, 16) + + if self.wanted_symbols: + want = self.wanted_symbols.pop(0) + self.last_symbol_request = want + self.symbol_results[want] = None + + return "qSymbol:" + hexlify(want) + + # "OK" ends the qSymbol sequence. + return "OK" + + +class TestQSymbol(GDBRemoteTestBase): + @skipIfRemote + @skipIfLLVMTargetMissing("AArch64") + def test_qsymbol(self): + target = self.createTarget("test_qsymbol.yaml") + self.server.responder = MyResponder() + + if self.TraceOn(): + self.runCmd("log enable gdb-remote packets") + self.addTearDownHook(lambda: self.runCmd("log disable gdb-remote packets")) + + # LLDB will send a qSymbol shortly after connecting, which starts the sequence. + process = self.connect(target) + lldbutil.expect_state_changes( + self, self.dbg.GetListener(), process, [lldb.eStateStopped] + ) + + # LLDB should have responded in some way to all the qSymbol requests. + self.assertFalse(self.server.responder.wanted_symbols) + + expected_results = dict( + [ + ("main", 0x1000), + ("local_address", 0x1004), + # FIXME: Should return a value. + ("global_value", None), + # FIXME: Should return a value. + ("local_value", None), + ("not_a_symbol", None), + ] + ) + self.assertEqual(expected_results, self.server.responder.symbol_results) diff --git a/lldb/test/API/functionalities/gdb_remote_client/test_qsymbol.yaml b/lldb/test/API/functionalities/gdb_remote_client/test_qsymbol.yaml new file mode 100644 index 0000000000000..10a2cb99ea27f --- /dev/null +++ b/lldb/test/API/functionalities/gdb_remote_client/test_qsymbol.yaml @@ -0,0 +1,33 @@ +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC + Machine: EM_AARCH64 +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + Address: 0x1000 + Content: C0035FD6 # ret +Symbols: + - Name: main + Type: STT_FUNC + Section: .text + Value: 0x1000 + Size: 4 + Binding: STB_GLOBAL + - Name: local_address + Type: STT_NOTYPE + Section: .text + Value: 0x1004 + - Name: local_value + Type: STT_NOTYPE + Index: SHN_ABS + Value: 0xabcd + - Name: global_value + Type: STT_NOTYPE + Index: SHN_ABS + Value: 0x1234 + Binding: STB_GLOBAL +... >From c4870d493a3cf3e46d099c9cb8c686ec3ede8a99 Mon Sep 17 00:00:00 2001 From: David Spickett <[email protected]> Date: Tue, 2 Jun 2026 13:14:18 +0000 Subject: [PATCH 2/5] pass test obj in so we can use proper asserts --- .../functionalities/gdb_remote_client/TestQSymbol.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/lldb/test/API/functionalities/gdb_remote_client/TestQSymbol.py b/lldb/test/API/functionalities/gdb_remote_client/TestQSymbol.py index d829ba7fc6409..eac176b3809d7 100644 --- a/lldb/test/API/functionalities/gdb_remote_client/TestQSymbol.py +++ b/lldb/test/API/functionalities/gdb_remote_client/TestQSymbol.py @@ -12,8 +12,8 @@ class MyResponder(MockGDBServerResponder): - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) + def __init__(self, test_obj): + super().__init__() self.wanted_symbols = [ "main", "local_address", @@ -23,17 +23,18 @@ def __init__(self, *args, **kwargs): ] self.last_symbol_request = None self.symbol_results = {} + self.test_obj = test_obj def qSymbol(self, args): # args will be <name hex encoded>:<hex value>. # In the initial packet both fields are empty. if args == ":": - assert self.last_symbol_request is None + self.test_obj.assertIsNone(self.last_symbol_request) else: # Anything else should be a response to our previous request. value, name = args.split(":") name = unhexlify(name) - assert name == self.last_symbol_request + self.test_obj.assertEqual(name, self.last_symbol_request) if value: self.symbol_results[name] = int(value, 16) @@ -54,7 +55,7 @@ class TestQSymbol(GDBRemoteTestBase): @skipIfLLVMTargetMissing("AArch64") def test_qsymbol(self): target = self.createTarget("test_qsymbol.yaml") - self.server.responder = MyResponder() + self.server.responder = MyResponder(self) if self.TraceOn(): self.runCmd("log enable gdb-remote packets") >From ed39e6697c59291a750d909c234ec2eb14d65598 Mon Sep 17 00:00:00 2001 From: Aurore Poirier <[email protected]> Date: Thu, 28 May 2026 09:13:51 +0200 Subject: [PATCH 3/5] [LLDB] Serve unknown type symbols through `qSymbol` `358cf1ea302eb` introduced a divergence between GDB and LLDB where LLDB does not serve symbols of unknown type through GDB protocol command `qSymbol`. Per commit description, this is an expected behavior on MachO-based platforms, but it is not on ELF-based platforms, where LLDB should follow GDB. The changes introduced by said commit are now gated behind an architecture check. Signed-off-by: Aurore Poirier <[email protected]> --- .../GDBRemoteCommunicationClient.cpp | 82 +++++++++++-------- 1 file changed, 49 insertions(+), 33 deletions(-) diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp index b5e0b8887f039..06fcd80ee61f7 100644 --- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp +++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp @@ -15,6 +15,7 @@ #include <optional> #include <sstream> +#include "lldb/Core/Module.h" #include "lldb/Core/ModuleSpec.h" #include "lldb/Host/HostInfo.h" #include "lldb/Host/SafeMachO.h" @@ -42,6 +43,7 @@ #include "llvm/Config/llvm-config.h" // for LLVM_ENABLE_ZLIB #include "llvm/Support/ErrorExtras.h" #include "llvm/Support/JSON.h" +#include "llvm/TargetParser/Triple.h" #if HAVE_LIBCOMPRESSION #include <compression.h> @@ -4255,41 +4257,55 @@ void GDBRemoteCommunicationClient::ServeSymbolLookups( if (symbol_load_addr != LLDB_INVALID_ADDRESS) break; if (sc.symbol) { - switch (sc.symbol->GetType()) { - case eSymbolTypeInvalid: - case eSymbolTypeAbsolute: - case eSymbolTypeUndefined: - case eSymbolTypeSourceFile: - case eSymbolTypeHeaderFile: - case eSymbolTypeObjectFile: - case eSymbolTypeCommonBlock: - case eSymbolTypeBlock: - case eSymbolTypeLocal: - case eSymbolTypeParam: - case eSymbolTypeVariable: - case eSymbolTypeVariableType: - case eSymbolTypeLineEntry: - case eSymbolTypeLineHeader: - case eSymbolTypeScopeBegin: - case eSymbolTypeScopeEnd: - case eSymbolTypeAdditional: - case eSymbolTypeCompiler: - case eSymbolTypeInstrumentation: - case eSymbolTypeTrampoline: - break; - - case eSymbolTypeCode: - case eSymbolTypeResolver: - case eSymbolTypeData: - case eSymbolTypeRuntime: - case eSymbolTypeException: - case eSymbolTypeObjCClass: - case eSymbolTypeObjCMetaClass: - case eSymbolTypeObjCIVar: - case eSymbolTypeReExported: + if (sc.module_sp->GetArchitecture() + .GetTriple() + .getObjectFormat() == + llvm::Triple::ObjectFormatType::MachO) { + switch (sc.symbol->GetType()) { + case eSymbolTypeInvalid: + case eSymbolTypeAbsolute: + case eSymbolTypeUndefined: + case eSymbolTypeSourceFile: + case eSymbolTypeHeaderFile: + case eSymbolTypeObjectFile: + case eSymbolTypeCommonBlock: + case eSymbolTypeBlock: + case eSymbolTypeLocal: + case eSymbolTypeParam: + case eSymbolTypeVariable: + case eSymbolTypeVariableType: + case eSymbolTypeLineEntry: + case eSymbolTypeLineHeader: + case eSymbolTypeScopeBegin: + case eSymbolTypeScopeEnd: + case eSymbolTypeAdditional: + case eSymbolTypeCompiler: + case eSymbolTypeInstrumentation: + case eSymbolTypeTrampoline: + break; + + case eSymbolTypeCode: + case eSymbolTypeResolver: + case eSymbolTypeData: + case eSymbolTypeRuntime: + case eSymbolTypeException: + case eSymbolTypeObjCClass: + case eSymbolTypeObjCMetaClass: + case eSymbolTypeObjCIVar: + case eSymbolTypeReExported: + symbol_load_addr = + sc.symbol->GetLoadAddress(&process->GetTarget()); + break; + } + } else { + // GDB does return symbols even when they are of unknown + // type, following this behavior on non Mach-O + // architectures. symbol_load_addr = sc.symbol->GetLoadAddress(&process->GetTarget()); - break; + if (symbol_load_addr == LLDB_INVALID_ADDRESS) { + symbol_load_addr = sc.symbol->GetRawValue(); + } } } } >From 62e768f0390b3e55b9685d940338230bf482ede1 Mon Sep 17 00:00:00 2001 From: Aurore Poirier <[email protected]> Date: Mon, 22 Jun 2026 17:20:56 +0200 Subject: [PATCH 4/5] [lldb] change nesting of blocks --- .../GDBRemoteCommunicationClient.cpp | 87 +++++++++---------- 1 file changed, 43 insertions(+), 44 deletions(-) diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp index 06fcd80ee61f7..cd1a5a55f1281 100644 --- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp +++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp @@ -4257,55 +4257,54 @@ void GDBRemoteCommunicationClient::ServeSymbolLookups( if (symbol_load_addr != LLDB_INVALID_ADDRESS) break; if (sc.symbol) { - if (sc.module_sp->GetArchitecture() - .GetTriple() - .getObjectFormat() == - llvm::Triple::ObjectFormatType::MachO) { - switch (sc.symbol->GetType()) { - case eSymbolTypeInvalid: - case eSymbolTypeAbsolute: - case eSymbolTypeUndefined: - case eSymbolTypeSourceFile: - case eSymbolTypeHeaderFile: - case eSymbolTypeObjectFile: - case eSymbolTypeCommonBlock: - case eSymbolTypeBlock: - case eSymbolTypeLocal: - case eSymbolTypeParam: - case eSymbolTypeVariable: - case eSymbolTypeVariableType: - case eSymbolTypeLineEntry: - case eSymbolTypeLineHeader: - case eSymbolTypeScopeBegin: - case eSymbolTypeScopeEnd: - case eSymbolTypeAdditional: - case eSymbolTypeCompiler: - case eSymbolTypeInstrumentation: - case eSymbolTypeTrampoline: - break; - - case eSymbolTypeCode: - case eSymbolTypeResolver: - case eSymbolTypeData: - case eSymbolTypeRuntime: - case eSymbolTypeException: - case eSymbolTypeObjCClass: - case eSymbolTypeObjCMetaClass: - case eSymbolTypeObjCIVar: - case eSymbolTypeReExported: + switch (sc.symbol->GetType()) { + case eSymbolTypeInvalid: + case eSymbolTypeAbsolute: + case eSymbolTypeUndefined: + case eSymbolTypeSourceFile: + case eSymbolTypeHeaderFile: + case eSymbolTypeObjectFile: + case eSymbolTypeCommonBlock: + case eSymbolTypeBlock: + case eSymbolTypeLocal: + case eSymbolTypeParam: + case eSymbolTypeVariable: + case eSymbolTypeVariableType: + case eSymbolTypeLineEntry: + case eSymbolTypeLineHeader: + case eSymbolTypeScopeBegin: + case eSymbolTypeScopeEnd: + case eSymbolTypeAdditional: + case eSymbolTypeCompiler: + case eSymbolTypeInstrumentation: + case eSymbolTypeTrampoline: + if (sc.module_sp->GetArchitecture() + .GetTriple() + .getObjectFormat() == + llvm::Triple::ObjectFormatType::MachO) { + // GDB does return symbols even when they are of unknown + // type, following this behavior on non Mach-O + // architectures. symbol_load_addr = sc.symbol->GetLoadAddress(&process->GetTarget()); - break; + if (symbol_load_addr == LLDB_INVALID_ADDRESS) { + symbol_load_addr = sc.symbol->GetRawValue(); + } } - } else { - // GDB does return symbols even when they are of unknown - // type, following this behavior on non Mach-O - // architectures. + break; + + case eSymbolTypeCode: + case eSymbolTypeResolver: + case eSymbolTypeData: + case eSymbolTypeRuntime: + case eSymbolTypeException: + case eSymbolTypeObjCClass: + case eSymbolTypeObjCMetaClass: + case eSymbolTypeObjCIVar: + case eSymbolTypeReExported: symbol_load_addr = sc.symbol->GetLoadAddress(&process->GetTarget()); - if (symbol_load_addr == LLDB_INVALID_ADDRESS) { - symbol_load_addr = sc.symbol->GetRawValue(); - } + break; } } } >From 83266897458a348734aab1ba07c88138b9a1561c Mon Sep 17 00:00:00 2001 From: Aurore Poirier <[email protected]> Date: Mon, 22 Jun 2026 17:12:52 +0200 Subject: [PATCH 5/5] [lldb][tests] Update `qSymbol` tests accordingly --- .../API/functionalities/gdb_remote_client/TestQSymbol.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/lldb/test/API/functionalities/gdb_remote_client/TestQSymbol.py b/lldb/test/API/functionalities/gdb_remote_client/TestQSymbol.py index eac176b3809d7..3982dc6aec0a7 100644 --- a/lldb/test/API/functionalities/gdb_remote_client/TestQSymbol.py +++ b/lldb/test/API/functionalities/gdb_remote_client/TestQSymbol.py @@ -74,10 +74,8 @@ def test_qsymbol(self): [ ("main", 0x1000), ("local_address", 0x1004), - # FIXME: Should return a value. - ("global_value", None), - # FIXME: Should return a value. - ("local_value", None), + ("global_value", 0x1234), + ("local_value", 0xabcd), ("not_a_symbol", None), ] ) _______________________________________________ lldb-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits
