https://github.com/DavidSpickett updated https://github.com/llvm/llvm-project/pull/200411
>From 0af1fac22300cd41281d0a4b4f85eb86c6248ccd Mon Sep 17 00:00:00 2001 From: David Spickett <[email protected]> Date: Fri, 29 May 2026 12:57:23 +0000 Subject: [PATCH 1/6] [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 59a68e34e73d0..aacefecad1e82 100644 --- a/lldb/docs/resources/lldbgdbremote.md +++ b/lldb/docs/resources/lldbgdbremote.md @@ -2141,10 +2141,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..59f2b07076c10 --- /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 +... \ No newline at end of file >From 6f439be0edd1bc6ab0bbeca62e33a342ad9fdd10 Mon Sep 17 00:00:00 2001 From: David Spickett <[email protected]> Date: Fri, 29 May 2026 14:07:25 +0000 Subject: [PATCH 2/6] Add MachO variant --- .../gdb_remote_client/TestQSymbol.py | 16 +++- ...est_qsymbol.yaml => test_qsymbol_elf.yaml} | 0 .../gdb_remote_client/test_qsymbol_macho.yaml | 81 +++++++++++++++++++ 3 files changed, 93 insertions(+), 4 deletions(-) rename lldb/test/API/functionalities/gdb_remote_client/{test_qsymbol.yaml => test_qsymbol_elf.yaml} (100%) create mode 100644 lldb/test/API/functionalities/gdb_remote_client/test_qsymbol_macho.yaml diff --git a/lldb/test/API/functionalities/gdb_remote_client/TestQSymbol.py b/lldb/test/API/functionalities/gdb_remote_client/TestQSymbol.py index d829ba7fc6409..c15b103e5c43a 100644 --- a/lldb/test/API/functionalities/gdb_remote_client/TestQSymbol.py +++ b/lldb/test/API/functionalities/gdb_remote_client/TestQSymbol.py @@ -50,10 +50,8 @@ def qSymbol(self, args): class TestQSymbol(GDBRemoteTestBase): - @skipIfRemote - @skipIfLLVMTargetMissing("AArch64") - def test_qsymbol(self): - target = self.createTarget("test_qsymbol.yaml") + def do_test_qsymbol(self, program): + target = self.createTarget(program) self.server.responder = MyResponder() if self.TraceOn(): @@ -81,3 +79,13 @@ def test_qsymbol(self): ] ) self.assertEqual(expected_results, self.server.responder.symbol_results) + + @skipIfRemote + @skipIfLLVMTargetMissing("AArch64") + def test_qsymbol_elf(self): + self.do_test_qsymbol("test_qsymbol_elf.yaml") + + @skipIfRemote + @skipIfLLVMTargetMissing("AArch64") + def test_qsymbol_macho(self): + self.do_test_qsymbol("test_qsymbol_macho.yaml") diff --git a/lldb/test/API/functionalities/gdb_remote_client/test_qsymbol.yaml b/lldb/test/API/functionalities/gdb_remote_client/test_qsymbol_elf.yaml similarity index 100% rename from lldb/test/API/functionalities/gdb_remote_client/test_qsymbol.yaml rename to lldb/test/API/functionalities/gdb_remote_client/test_qsymbol_elf.yaml diff --git a/lldb/test/API/functionalities/gdb_remote_client/test_qsymbol_macho.yaml b/lldb/test/API/functionalities/gdb_remote_client/test_qsymbol_macho.yaml new file mode 100644 index 0000000000000..25cf1af2bbeb2 --- /dev/null +++ b/lldb/test/API/functionalities/gdb_remote_client/test_qsymbol_macho.yaml @@ -0,0 +1,81 @@ +--- !mach-o +FileHeader: + magic: 0xFEEDFACF # MH_MAGIC_64 + cputype: 0x0100000C # CPU_TYPE_ARM64 + cpusubtype: 0x00000000 + filetype: 0x00000002 # MH_EXECUTE + ncmds: 2 + sizeofcmds: 176 + flags: 0x00000085 # MH_NOUNDEFS | MH_DYLDLINK | MH_TWOLEVEL + reserved: 0x00000000 + +LoadCommands: + - cmd: LC_SEGMENT_64 + cmdsize: 152 + segname: __TEXT + vmaddr: 0x1000 + vmsize: 0x1000 + fileoff: 0 + filesize: 0x1000 + maxprot: 5 + initprot: 5 + nsects: 1 + flags: 0 + Sections: + - sectname: __text + segname: __TEXT + addr: 0x1000 + size: 4 + offset: 0x1000 + align: 2 + reloff: 0 + nreloc: 0 + flags: 0x80000400 # S_ATTR_PURE_INSTRUCTIONS | S_ATTR_SOME_INSTRUCTIONS + reserved1: 0 + reserved2: 0 + reserved3: 0 + content: C0035FD6 # ret + + - cmd: LC_SYMTAB + cmdsize: 24 + symoff: 0x1008 + nsyms: 4 + stroff: 0x1048 + strsize: 53 + +LinkEditData: + NameList: + # _local_address, N_SECT + - n_strx: 1 + n_type: 0x0e + n_sect: 1 + n_desc: 0 + n_value: 0x1004 + + # _local_value, N_ABS + - n_strx: 16 + n_type: 0x02 + n_sect: 0 + n_desc: 0 + n_value: 0xabcd + + # _main, N_SECT | N_EXT + - n_strx: 29 + n_type: 0x0f + n_sect: 1 + n_desc: 0 + n_value: 0x1000 + + # _global_value, N_ABS | N_EXT + - n_strx: 35 + n_type: 0x03 + n_sect: 0 + n_desc: 0 + n_value: 0x1234 + + StringTable: + - _local_address + - _local_value + - _main + - _global_value +... >From 078bd0e6109f036265eb725da7043d7072cb4063 Mon Sep 17 00:00:00 2001 From: David Spickett <[email protected]> Date: Fri, 29 May 2026 14:11:20 +0000 Subject: [PATCH 3/6] missing EOF newline --- .../API/functionalities/gdb_remote_client/test_qsymbol_elf.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lldb/test/API/functionalities/gdb_remote_client/test_qsymbol_elf.yaml b/lldb/test/API/functionalities/gdb_remote_client/test_qsymbol_elf.yaml index 59f2b07076c10..10a2cb99ea27f 100644 --- a/lldb/test/API/functionalities/gdb_remote_client/test_qsymbol_elf.yaml +++ b/lldb/test/API/functionalities/gdb_remote_client/test_qsymbol_elf.yaml @@ -30,4 +30,4 @@ Symbols: Index: SHN_ABS Value: 0x1234 Binding: STB_GLOBAL -... \ No newline at end of file +... >From a102b7ed407985b9bab9f06b97e0ad59171ef1eb Mon Sep 17 00:00:00 2001 From: David Spickett <[email protected]> Date: Mon, 1 Jun 2026 09:39:57 +0000 Subject: [PATCH 4/6] try with explicit triple --- lldb/packages/Python/lldbsuite/test/lldbgdbclient.py | 4 ++-- .../API/functionalities/gdb_remote_client/TestQSymbol.py | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lldb/packages/Python/lldbsuite/test/lldbgdbclient.py b/lldb/packages/Python/lldbsuite/test/lldbgdbclient.py index 9b2a89e934132..6a56f57964b93 100644 --- a/lldb/packages/Python/lldbsuite/test/lldbgdbclient.py +++ b/lldb/packages/Python/lldbsuite/test/lldbgdbclient.py @@ -32,7 +32,7 @@ def tearDown(self): self.server.stop() TestBase.tearDown(self) - def createTarget(self, yaml_path): + def createTarget(self, yaml_path, triple=""): """ Create a target by auto-generating the object based on the given yaml instructions. @@ -43,7 +43,7 @@ def createTarget(self, yaml_path): yaml_base, ext = os.path.splitext(yaml_path) obj_path = self.getBuildArtifact(yaml_base) self.yaml2obj(yaml_path, obj_path) - return self.dbg.CreateTarget(obj_path) + return self.dbg.CreateTargetWithFileAndTargetTriple(obj_path, triple) def connect(self, target, plugin="gdb-remote"): """ diff --git a/lldb/test/API/functionalities/gdb_remote_client/TestQSymbol.py b/lldb/test/API/functionalities/gdb_remote_client/TestQSymbol.py index c15b103e5c43a..c8f2ac69ed765 100644 --- a/lldb/test/API/functionalities/gdb_remote_client/TestQSymbol.py +++ b/lldb/test/API/functionalities/gdb_remote_client/TestQSymbol.py @@ -50,7 +50,7 @@ def qSymbol(self, args): class TestQSymbol(GDBRemoteTestBase): - def do_test_qsymbol(self, program): + def do_test_qsymbol(self, program, triple): target = self.createTarget(program) self.server.responder = MyResponder() @@ -83,9 +83,9 @@ def do_test_qsymbol(self, program): @skipIfRemote @skipIfLLVMTargetMissing("AArch64") def test_qsymbol_elf(self): - self.do_test_qsymbol("test_qsymbol_elf.yaml") + self.do_test_qsymbol("test_qsymbol_elf.yaml", "aarch64-unknown-linux-gnu") @skipIfRemote @skipIfLLVMTargetMissing("AArch64") def test_qsymbol_macho(self): - self.do_test_qsymbol("test_qsymbol_macho.yaml") + self.do_test_qsymbol("test_qsymbol_macho.yaml", "arm64-apple-macosx") >From f2ffc08e5c3b4e21441e0e0dedec367b89c400a3 Mon Sep 17 00:00:00 2001 From: David Spickett <[email protected]> Date: Mon, 1 Jun 2026 13:15:59 +0000 Subject: [PATCH 5/6] dump symtab and logs --- .../API/functionalities/gdb_remote_client/TestQSymbol.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lldb/test/API/functionalities/gdb_remote_client/TestQSymbol.py b/lldb/test/API/functionalities/gdb_remote_client/TestQSymbol.py index c8f2ac69ed765..ffd62b01d5d0c 100644 --- a/lldb/test/API/functionalities/gdb_remote_client/TestQSymbol.py +++ b/lldb/test/API/functionalities/gdb_remote_client/TestQSymbol.py @@ -51,6 +51,11 @@ def qSymbol(self, args): class TestQSymbol(GDBRemoteTestBase): def do_test_qsymbol(self, program, triple): + self.runCmd("log enable lldb process") + self.runCmd("log enable lldb target") + self.runCmd("log enable lldb object") + self.runCmd("log enable gdb-remote packets") + target = self.createTarget(program) self.server.responder = MyResponder() @@ -64,6 +69,8 @@ def do_test_qsymbol(self, program, triple): self, self.dbg.GetListener(), process, [lldb.eStateStopped] ) + self.expect("image dump symtab", substrs=["sdsddfsdf"]) + # LLDB should have responded in some way to all the qSymbol requests. self.assertFalse(self.server.responder.wanted_symbols) >From f67ff108ed2c56039a9eb1a3527b8df1d84fb962 Mon Sep 17 00:00:00 2001 From: David Spickett <[email protected]> Date: Mon, 1 Jun 2026 13:52:56 +0000 Subject: [PATCH 6/6] try setting OS/triple/etc. --- .../gdb_remote_client/TestQSymbol.py | 27 ++++++++++++++----- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/lldb/test/API/functionalities/gdb_remote_client/TestQSymbol.py b/lldb/test/API/functionalities/gdb_remote_client/TestQSymbol.py index ffd62b01d5d0c..6a6a9832cbdc4 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, triple): + super().__init__() self.wanted_symbols = [ "main", "local_address", @@ -21,6 +21,7 @@ def __init__(self, *args, **kwargs): "local_value", "not_a_symbol", ] + self.triple = triple self.last_symbol_request = None self.symbol_results = {} @@ -48,6 +49,18 @@ def qSymbol(self, args): # "OK" ends the qSymbol sequence. return "OK" + def qProcessInfo(self): + return ( + "pid:1;" + f"triple:{hexlify('arm64-apple-macosx')};" + "cputype:100000c;" + "cpusubtype:0;" + "ostype:macosx;" + "vendor:apple;" + "endian:little;" + "ptrsize:8;" + ) + class TestQSymbol(GDBRemoteTestBase): def do_test_qsymbol(self, program, triple): @@ -57,7 +70,7 @@ def do_test_qsymbol(self, program, triple): self.runCmd("log enable gdb-remote packets") target = self.createTarget(program) - self.server.responder = MyResponder() + self.server.responder = MyResponder(triple) if self.TraceOn(): self.runCmd("log enable gdb-remote packets") @@ -87,10 +100,10 @@ def do_test_qsymbol(self, program, triple): ) self.assertEqual(expected_results, self.server.responder.symbol_results) - @skipIfRemote - @skipIfLLVMTargetMissing("AArch64") - def test_qsymbol_elf(self): - self.do_test_qsymbol("test_qsymbol_elf.yaml", "aarch64-unknown-linux-gnu") + # @skipIfRemote + # @skipIfLLVMTargetMissing("AArch64") + # def test_qsymbol_elf(self): + # self.do_test_qsymbol("test_qsymbol_elf.yaml", "aarch64-unknown-linux-gnu") @skipIfRemote @skipIfLLVMTargetMissing("AArch64") _______________________________________________ lldb-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits
