Skip to content

Commit b5d8fc5

Browse files
authored
[lldb] Fix setting CanJIT if memory cannot be allocated (llvm#176099)
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 ```
1 parent deed378 commit b5d8fc5

4 files changed

Lines changed: 73 additions & 5 deletions

File tree

lldb/packages/Python/lldbsuite/test/gdbclientutils.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,13 @@ def _respond_impl(self, packet) -> Union[Response, List[Response]]:
261261
return self.qRegisterInfo(regnum)
262262
if packet == "k":
263263
return self.k()
264+
if packet[0:2] == "_M":
265+
size_str, permissions = packet[2:].split(",")
266+
size = int(size_str, 16)
267+
return self._M(size, permissions)
268+
if packet[0:2] == "_m":
269+
addr = int(packet[2:], 16)
270+
return self._m(addr)
264271

265272
return self.other(packet)
266273

@@ -409,6 +416,12 @@ def qRegisterInfo(self, num) -> str:
409416
def k(self):
410417
return ["W01", self.RESPONSE_DISCONNECT]
411418

419+
def _M(self, size, permissions):
420+
return ""
421+
422+
def _m(self, addr):
423+
return ""
424+
412425
"""
413426
Raised when we receive a packet for which there is no default action.
414427
Override the responder class to implement behavior suitable for the test at

lldb/packages/Python/lldbsuite/test/lldbgdbclient.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ def tearDown(self):
3232
self.server.stop()
3333
TestBase.tearDown(self)
3434

35-
def createTarget(self, yaml_path):
35+
def createTarget(self, yaml_path, triple=""):
3636
"""
3737
Create a target by auto-generating the object based on the given yaml
3838
instructions.
@@ -43,7 +43,7 @@ def createTarget(self, yaml_path):
4343
yaml_base, ext = os.path.splitext(yaml_path)
4444
obj_path = self.getBuildArtifact(yaml_base)
4545
self.yaml2obj(yaml_path, obj_path)
46-
return self.dbg.CreateTarget(obj_path)
46+
return self.dbg.CreateTargetWithFileAndTargetTriple(obj_path, triple)
4747

4848
def connect(self, target, plugin="gdb-remote"):
4949
"""

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3170,9 +3170,9 @@ lldb::addr_t ProcessGDBRemote::DoAllocateMemory(size_t size,
31703170

31713171
if (m_gdb_comm.SupportsAllocDeallocMemory() != eLazyBoolNo) {
31723172
allocated_addr = m_gdb_comm.AllocateMemory(size, permissions);
3173-
if (allocated_addr != LLDB_INVALID_ADDRESS ||
3174-
m_gdb_comm.SupportsAllocDeallocMemory() == eLazyBoolYes)
3175-
return allocated_addr;
3173+
assert((allocated_addr == LLDB_INVALID_ADDRESS ||
3174+
m_gdb_comm.SupportsAllocDeallocMemory() == eLazyBoolYes) &&
3175+
"Memory can only be allocated if the support is enabled");
31763176
}
31773177

31783178
if (m_gdb_comm.SupportsAllocDeallocMemory() == eLazyBoolNo) {
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import lldb
2+
from lldbsuite.test.lldbtest import *
3+
from lldbsuite.test.decorators import *
4+
from lldbsuite.test.gdbclientutils import *
5+
from lldbsuite.test.lldbgdbclient import GDBRemoteTestBase
6+
7+
8+
class MyResponder(MockGDBServerResponder):
9+
def _M(self, size, permissions) -> str:
10+
return "E04"
11+
12+
def readRegister(self, regnum):
13+
return "E01"
14+
15+
def readRegisters(self):
16+
return "".join(
17+
[
18+
# x0
19+
"2000000000000000",
20+
# x1..x30, sp, pc
21+
32 * "0000000000000000",
22+
# cpsr
23+
"00000000",
24+
]
25+
)
26+
27+
28+
class TestExprNoAlloc(GDBRemoteTestBase):
29+
@skipIfRemote
30+
@skipIfLLVMTargetMissing("AArch64")
31+
def test(self):
32+
"""
33+
We should be able to evaluate an expression that requires no allocations,
34+
even if the server responds to '_M' with an error. 'CanJIT' should be set
35+
to 'eCanJITNo' for this response; otherwise, 'IRMemoryMap' would attempt
36+
to allocate memory in the inferior process and fail.
37+
"""
38+
39+
self.server.responder = MyResponder()
40+
# Note: DynamicLoaderStatic disables JIT by calling 'm_process->SetCanJIT(false)'
41+
# in LoadAllImagesAtFileAddresses(). Specifying a triple with "-linux" enables
42+
# DynamicLoaderPOSIXDYLD to be used instead.
43+
self.target = self.createTarget("basic_eh_frame-aarch64.yaml", "aarch64-linux")
44+
process = self.connect(self.target)
45+
lldbutil.expect_state_changes(
46+
self, self.dbg.GetListener(), process, [lldb.eStateStopped]
47+
)
48+
49+
self.expect_expr("$x0", result_type="unsigned long", result_value="32")
50+
res = self.target.EvaluateExpression("(int)foo()")
51+
self.assertFalse(res.GetError().Success())
52+
self.assertIn(
53+
"Can't evaluate the expression without a running target",
54+
res.GetError().GetCString(),
55+
)

0 commit comments

Comments
 (0)