https://github.com/igorkudrin updated https://github.com/llvm/llvm-project/pull/176099
>From 69a7a99ac530de807877954a3dbcf0c636fc443e Mon Sep 17 00:00:00 2001 From: Igor Kudrin <[email protected]> Date: Wed, 14 Jan 2026 22:19:01 -0800 Subject: [PATCH 1/7] [lldb] Fix setting CanJIT if memory cannot be allocated When a server is unable to allocate memory for the `_M` packet, it may respond with an error code. In this case, `GDBRemoteCommunicationClient::AllocateMemory()` sets `m_supports_alloc_dealloc_memory` to `eLazyBoolYes`; `eLazyBoolNo` is only used if the server cannot handle the packet at all. Before this patch, `ProcessGDBRemote::DoAllocateMemory()` checked this flag and returned `LLDB_INVALID_ADDRESS` without setting an error, which caused `Process::CanJIT()` to set `m_can_jit = eCanJITYes`, resulting in `IRMemoryMap::FindSpace()` attempting to allocate memory in the inferior process and failing. With the patch, `ProcessGDBRemote::DoAllocateMemory()` returns an error and `m_can_jit` is set to `eCanJITNo`. Example debug session: ``` (lldb) platform connect... (lldb) file test (lldb) br set... (lldb) run Process 100 launched:... Process 100 stopped * thread #1,... (lldb) expr $x0 error: Couldn't allocate space for materialized struct: Couldn't malloc: address space is full error: errored out in virtual lldb_private::LLVMUserExpression::DoExecute, couldn't PrepareToExecuteJITExpression ``` --- .../Python/lldbsuite/test/lldbgdbclient.py | 7 ++- .../Process/gdb-remote/ProcessGDBRemote.cpp | 6 +-- .../gdb_remote_client/TestExprNoAlloc.py | 44 +++++++++++++++++++ 3 files changed, 52 insertions(+), 5 deletions(-) create mode 100644 lldb/test/API/functionalities/gdb_remote_client/TestExprNoAlloc.py diff --git a/lldb/packages/Python/lldbsuite/test/lldbgdbclient.py b/lldb/packages/Python/lldbsuite/test/lldbgdbclient.py index 9b2a89e934132..6c68254d42628 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,10 @@ 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) + if triple: + return self.dbg.CreateTargetWithFileAndTargetTriple(obj_path, triple) + else: + return self.dbg.CreateTarget(obj_path) def connect(self, target, plugin="gdb-remote"): """ diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp index 80a8f441da12e..445ce820030d3 100644 --- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp +++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp @@ -3170,9 +3170,9 @@ lldb::addr_t ProcessGDBRemote::DoAllocateMemory(size_t size, if (m_gdb_comm.SupportsAllocDeallocMemory() != eLazyBoolNo) { allocated_addr = m_gdb_comm.AllocateMemory(size, permissions); - if (allocated_addr != LLDB_INVALID_ADDRESS || - m_gdb_comm.SupportsAllocDeallocMemory() == eLazyBoolYes) - return allocated_addr; + assert((allocated_addr == LLDB_INVALID_ADDRESS || + m_gdb_comm.SupportsAllocDeallocMemory() == eLazyBoolYes) && + "Memory can only be allocated if the support is enabled"); } if (m_gdb_comm.SupportsAllocDeallocMemory() == eLazyBoolNo) { diff --git a/lldb/test/API/functionalities/gdb_remote_client/TestExprNoAlloc.py b/lldb/test/API/functionalities/gdb_remote_client/TestExprNoAlloc.py new file mode 100644 index 0000000000000..3a77ceed32b57 --- /dev/null +++ b/lldb/test/API/functionalities/gdb_remote_client/TestExprNoAlloc.py @@ -0,0 +1,44 @@ +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 + + +class MyResponder(MockGDBServerResponder): + def other(self, packet) -> str: + if packet.startswith("_M"): + return "E04" + else: + return super().other(packet) + + def readRegister(self, regnum): + return "E01" + + def readRegisters(self): + return "20000000000000002000000000000000f0c154bfffff00005daa985a8fea0b48f0b954bfffff0000ad13cce570150b48380000000000000070456abfffff0000a700000000000000000000000000000001010101010101010000000000000000f0c154bfffff00000f2700000000000008e355bfffff0000080e55bfffff0000281041000000000010de61bfffff00005c05000000000000f0c154bfffff000090fcffffffff00008efcffffffff00008ffcffffffff00000000000000000000001000000000000090fcffffffff000000d06cbfffff0000f0c154bfffff00000100000000000000d0b954bfffff0000e407400000000000d0b954bfffff0000e40740000000000000100000" + + +class TestExprNoAlloc(GDBRemoteTestBase): + @skipIfRemote + @skipIfLLVMTargetMissing("AArch64") + def test(self): + """ + Test that a simple expression can be evaluated when the server supports the '_M' + packet, but memory cannot be allocated, and it returns an error code response. + In this case, 'CanJIT' used to be set to 'eCanJITYes', so 'IRMemoryMap' tried to + allocated memory in the inferior process and failed. + """ + + self.server.responder = MyResponder() + # Note: DynamicLoaderStatic disables JIT by calling 'm_process->SetCanJIT(false)' + # in LoadAllImagesAtFileAddresses(). Specifying a triple with "-linux" enables + # DynamicLoaderPOSIXDYLD to be used instead. + self.target = self.createTarget("basic_eh_frame-aarch64.yaml", "aarch64-linux") + process = self.connect(self.target) + lldbutil.expect_state_changes( + self, self.dbg.GetListener(), process, [lldb.eStateStopped] + ) + + self.expect_expr("$x1", result_type="unsigned long", result_value="32") >From 40b49a3f02aaecf98eed3e2a9b10712833920a6c Mon Sep 17 00:00:00 2001 From: Igor Kudrin <[email protected]> Date: Thu, 15 Jan 2026 13:56:08 -0800 Subject: [PATCH 2/7] fixup! simplify GDBRemoteTestBase.createTarget() --- lldb/packages/Python/lldbsuite/test/lldbgdbclient.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/lldb/packages/Python/lldbsuite/test/lldbgdbclient.py b/lldb/packages/Python/lldbsuite/test/lldbgdbclient.py index 6c68254d42628..6a56f57964b93 100644 --- a/lldb/packages/Python/lldbsuite/test/lldbgdbclient.py +++ b/lldb/packages/Python/lldbsuite/test/lldbgdbclient.py @@ -43,10 +43,7 @@ def createTarget(self, yaml_path, triple=""): yaml_base, ext = os.path.splitext(yaml_path) obj_path = self.getBuildArtifact(yaml_base) self.yaml2obj(yaml_path, obj_path) - if triple: - return self.dbg.CreateTargetWithFileAndTargetTriple(obj_path, triple) - else: - return self.dbg.CreateTarget(obj_path) + return self.dbg.CreateTargetWithFileAndTargetTriple(obj_path, triple) def connect(self, target, plugin="gdb-remote"): """ >From 7b1f907208ad192804d569894653685fb0949d4d Mon Sep 17 00:00:00 2001 From: Igor Kudrin <[email protected]> Date: Thu, 15 Jan 2026 21:55:30 -0800 Subject: [PATCH 3/7] fixup! remove redundant import --- .../API/functionalities/gdb_remote_client/TestExprNoAlloc.py | 1 - 1 file changed, 1 deletion(-) diff --git a/lldb/test/API/functionalities/gdb_remote_client/TestExprNoAlloc.py b/lldb/test/API/functionalities/gdb_remote_client/TestExprNoAlloc.py index 3a77ceed32b57..ead7fdea2799c 100644 --- a/lldb/test/API/functionalities/gdb_remote_client/TestExprNoAlloc.py +++ b/lldb/test/API/functionalities/gdb_remote_client/TestExprNoAlloc.py @@ -1,4 +1,3 @@ -from textwrap import dedent import lldb from lldbsuite.test.lldbtest import * from lldbsuite.test.decorators import * >From ca23b8634c7cf87baafae05738202a21f1121476 Mon Sep 17 00:00:00 2001 From: Igor Kudrin <[email protected]> Date: Thu, 15 Jan 2026 21:59:10 -0800 Subject: [PATCH 4/7] fixup! add handlers for '_M' and '_m' to MockGDBServerResponder --- .../Python/lldbsuite/test/gdbclientutils.py | 13 +++++++++++++ .../gdb_remote_client/TestExprNoAlloc.py | 7 ++----- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/lldb/packages/Python/lldbsuite/test/gdbclientutils.py b/lldb/packages/Python/lldbsuite/test/gdbclientutils.py index 4c40299f3256d..ed8ea7c981c2c 100644 --- a/lldb/packages/Python/lldbsuite/test/gdbclientutils.py +++ b/lldb/packages/Python/lldbsuite/test/gdbclientutils.py @@ -261,6 +261,13 @@ def _respond_impl(self, packet) -> Union[Response, List[Response]]: return self.qRegisterInfo(regnum) if packet == "k": return self.k() + if packet[0:2] == "_M": + size_str, permissions = packet[2:].split(",") + size = int(size_str, 16) + return self._M(size, permissions) + if packet[0:2] == "_m": + addr = int(packet[2:], 16) + return self._m(addr) return self.other(packet) @@ -409,6 +416,12 @@ def qRegisterInfo(self, num) -> str: def k(self): return ["W01", self.RESPONSE_DISCONNECT] + def _M(self, size, permissions): + return "" + + def _m(self, addr): + return "" + """ Raised when we receive a packet for which there is no default action. Override the responder class to implement behavior suitable for the test at diff --git a/lldb/test/API/functionalities/gdb_remote_client/TestExprNoAlloc.py b/lldb/test/API/functionalities/gdb_remote_client/TestExprNoAlloc.py index ead7fdea2799c..aaeb96291411d 100644 --- a/lldb/test/API/functionalities/gdb_remote_client/TestExprNoAlloc.py +++ b/lldb/test/API/functionalities/gdb_remote_client/TestExprNoAlloc.py @@ -6,11 +6,8 @@ class MyResponder(MockGDBServerResponder): - def other(self, packet) -> str: - if packet.startswith("_M"): - return "E04" - else: - return super().other(packet) + def _M(self, size, permissions) -> str: + return "E04" def readRegister(self, regnum): return "E01" >From d2a6c9676e4a9b8032296d628576629b308db3db Mon Sep 17 00:00:00 2001 From: Igor Kudrin <[email protected]> Date: Thu, 15 Jan 2026 22:02:56 -0800 Subject: [PATCH 5/7] fixup! simplify the response string in 'readRegisters()' --- .../gdb_remote_client/TestExprNoAlloc.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/lldb/test/API/functionalities/gdb_remote_client/TestExprNoAlloc.py b/lldb/test/API/functionalities/gdb_remote_client/TestExprNoAlloc.py index aaeb96291411d..254c70ccbb971 100644 --- a/lldb/test/API/functionalities/gdb_remote_client/TestExprNoAlloc.py +++ b/lldb/test/API/functionalities/gdb_remote_client/TestExprNoAlloc.py @@ -13,7 +13,16 @@ def readRegister(self, regnum): return "E01" def readRegisters(self): - return "20000000000000002000000000000000f0c154bfffff00005daa985a8fea0b48f0b954bfffff0000ad13cce570150b48380000000000000070456abfffff0000a700000000000000000000000000000001010101010101010000000000000000f0c154bfffff00000f2700000000000008e355bfffff0000080e55bfffff0000281041000000000010de61bfffff00005c05000000000000f0c154bfffff000090fcffffffff00008efcffffffff00008ffcffffffff00000000000000000000001000000000000090fcffffffff000000d06cbfffff0000f0c154bfffff00000100000000000000d0b954bfffff0000e407400000000000d0b954bfffff0000e40740000000000000100000" + return "".join( + [ + # x0 + "2000000000000000", + # x1..x30, sp, pc + 32 * "0000000000000000", + # cpsr + "00000000", + ] + ) class TestExprNoAlloc(GDBRemoteTestBase): @@ -37,4 +46,4 @@ def test(self): self, self.dbg.GetListener(), process, [lldb.eStateStopped] ) - self.expect_expr("$x1", result_type="unsigned long", result_value="32") + self.expect_expr("$x0", result_type="unsigned long", result_value="32") >From d072b389e96a16aee22459b3c0a0c833b00e18a3 Mon Sep 17 00:00:00 2001 From: Igor Kudrin <[email protected]> Date: Thu, 15 Jan 2026 22:03:53 -0800 Subject: [PATCH 6/7] fixup! update the test description --- .../functionalities/gdb_remote_client/TestExprNoAlloc.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lldb/test/API/functionalities/gdb_remote_client/TestExprNoAlloc.py b/lldb/test/API/functionalities/gdb_remote_client/TestExprNoAlloc.py index 254c70ccbb971..caabebb380952 100644 --- a/lldb/test/API/functionalities/gdb_remote_client/TestExprNoAlloc.py +++ b/lldb/test/API/functionalities/gdb_remote_client/TestExprNoAlloc.py @@ -30,10 +30,10 @@ class TestExprNoAlloc(GDBRemoteTestBase): @skipIfLLVMTargetMissing("AArch64") def test(self): """ - Test that a simple expression can be evaluated when the server supports the '_M' - packet, but memory cannot be allocated, and it returns an error code response. - In this case, 'CanJIT' used to be set to 'eCanJITYes', so 'IRMemoryMap' tried to - allocated memory in the inferior process and failed. + We should be able to evaluate an expression that requires no allocations, + even if the server responds to '_M' with an error. 'CanJIT' should be set + to 'eCanJITNo' for this response; otherwise, 'IRMemoryMap' would attempt + to allocate memory in the inferior process and fail. """ self.server.responder = MyResponder() >From 925c8999f32b6bdfe855ced12e02c1f5ff97f62b Mon Sep 17 00:00:00 2001 From: Igor Kudrin <[email protected]> Date: Thu, 15 Jan 2026 22:04:34 -0800 Subject: [PATCH 7/7] fixup! add an expression which fails without JIT --- .../functionalities/gdb_remote_client/TestExprNoAlloc.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lldb/test/API/functionalities/gdb_remote_client/TestExprNoAlloc.py b/lldb/test/API/functionalities/gdb_remote_client/TestExprNoAlloc.py index caabebb380952..4fcd7f833c68c 100644 --- a/lldb/test/API/functionalities/gdb_remote_client/TestExprNoAlloc.py +++ b/lldb/test/API/functionalities/gdb_remote_client/TestExprNoAlloc.py @@ -47,3 +47,9 @@ def test(self): ) self.expect_expr("$x0", result_type="unsigned long", result_value="32") + res = self.target.EvaluateExpression("(int)foo()") + self.assertFalse(res.GetError().Success()) + self.assertIn( + "Can't evaluate the expression without a running target", + res.GetError().GetCString(), + ) _______________________________________________ lldb-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits
