[Lldb-commits] [lldb] 851eb5a - [lldb/Interpreter] Let a Scripted Process report its addressable bits (#224701)
via lldb-commits
lldb-commits at lists.llvm.org
Fri Sep 18 19:50:09 PDT 2026
Author: Med Ismail Bennani
Date: 2026-09-18T19:50:03-07:00
New Revision: 851eb5a97ba67b4e8ebec39fb821c144db67a10a
URL: https://github.com/llvm/llvm-project/commit/851eb5a97ba67b4e8ebec39fb821c144db67a10a
DIFF: https://github.com/llvm/llvm-project/commit/851eb5a97ba67b4e8ebec39fb821c144db67a10a.diff
LOG: [lldb/Interpreter] Let a Scripted Process report its addressable bits (#224701)
Added:
lldb/test/API/functionalities/scripted_process/addressable_bits_scripted_process.py
Modified:
lldb/examples/python/templates/scripted_process.py
lldb/include/lldb/Interpreter/Interfaces/ScriptedProcessInterface.h
lldb/source/Plugins/Process/scripted/ScriptedFrame.cpp
lldb/source/Plugins/Process/scripted/ScriptedProcess.cpp
lldb/source/Plugins/Process/scripted/ScriptedProcess.h
lldb/source/Plugins/Process/scripted/ScriptedThread.cpp
lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedProcessPythonInterface.cpp
lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedProcessPythonInterface.h
lldb/test/API/functionalities/scripted_process/TestScriptedProcess.py
Removed:
################################################################################
diff --git a/lldb/examples/python/templates/scripted_process.py b/lldb/examples/python/templates/scripted_process.py
index 4358aa552c2ae..6c2697cb9aa95 100644
--- a/lldb/examples/python/templates/scripted_process.py
+++ b/lldb/examples/python/templates/scripted_process.py
@@ -18,6 +18,7 @@ class ScriptedProcess(metaclass=ABCMeta):
loaded_images: Optional[list[dict]] = None
threads: Optional[dict[int, "lldb.plugins.scripted_process.ScriptedThread"]] = None
metadata: Optional[dict[str, Any]] = None
+ addressable_bits: Optional[dict[str, int]] = None
target: lldb.SBTarget
args: lldb.SBStructuredData
@@ -50,6 +51,7 @@ def __init__(self, exe_ctx: lldb.SBExecutionContext, args: lldb.SBStructuredData
self.loaded_images = []
self.metadata = {}
self.capabilities = {}
+ self.addressable_bits = {}
self.pid = 42
def get_capabilities(self) -> dict[str, bool]:
@@ -62,6 +64,24 @@ def get_capabilities(self) -> dict[str, bool]:
"""
return self.capabilities
+ def get_addressable_bits(self) -> dict[str, int]:
+ """Get the number of bits this process uses for addressing.
+
+ LLDB strips the remaining bits off every code and data address, the
+ way the `LC_NOTE "addrable bits"` corefile note and the `qHostInfo`
+ `addressing_bits` key do for corefiles and live processes.
+
+ This is queried before the first stop is reported, so the threads and
+ backtraces built from that stop already have the mask applied.
+
+ Returns:
+ Dict[str:int]: A dictionary with optional "lowmem" and "highmem"
+ keys, holding the number of bits used for addressing in low and
+ high memory. "highmem" defaults to "lowmem" when it is missing.
+ The dictionary can be empty, in which case no bits are stripped.
+ """
+ return self.addressable_bits
+
def get_memory_region_containing_address(
self, addr: int
) -> Optional[lldb.SBMemoryRegionInfo]:
diff --git a/lldb/include/lldb/Interpreter/Interfaces/ScriptedProcessInterface.h b/lldb/include/lldb/Interpreter/Interfaces/ScriptedProcessInterface.h
index ea96ea9497093..4aa58308a4abc 100644
--- a/lldb/include/lldb/Interpreter/Interfaces/ScriptedProcessInterface.h
+++ b/lldb/include/lldb/Interpreter/Interfaces/ScriptedProcessInterface.h
@@ -28,6 +28,10 @@ class ScriptedProcessInterface : virtual public ScriptedInterface {
virtual StructuredData::DictionarySP GetCapabilities() { return {}; }
+ /// The number of bits this process uses for addressing, as a dictionary
+ /// with optional "lowmem" and "highmem" keys.
+ virtual StructuredData::DictionarySP GetAddressableBits() { return {}; }
+
virtual Status Attach(const ProcessAttachInfo &attach_info) {
return Status::FromErrorString("ScriptedProcess did not attach");
}
diff --git a/lldb/source/Plugins/Process/scripted/ScriptedFrame.cpp b/lldb/source/Plugins/Process/scripted/ScriptedFrame.cpp
index 9df79fadb8da2..1207aa7eef538 100644
--- a/lldb/source/Plugins/Process/scripted/ScriptedFrame.cpp
+++ b/lldb/source/Plugins/Process/scripted/ScriptedFrame.cpp
@@ -112,6 +112,7 @@ ScriptedFrame::Create(ThreadSP thread_sp,
SymbolContext sc;
Address symbol_addr;
if (pc != LLDB_INVALID_ADDRESS) {
+ pc = process_sp->FixCodeAddress(pc);
symbol_addr.SetLoadAddress(pc, &process_sp->GetTarget());
symbol_addr.CalculateSymbolContext(&sc);
}
diff --git a/lldb/source/Plugins/Process/scripted/ScriptedProcess.cpp b/lldb/source/Plugins/Process/scripted/ScriptedProcess.cpp
index e95425e87374e..38d5c407efb45 100644
--- a/lldb/source/Plugins/Process/scripted/ScriptedProcess.cpp
+++ b/lldb/source/Plugins/Process/scripted/ScriptedProcess.cpp
@@ -21,6 +21,7 @@
#include "lldb/Target/MemoryRegionInfo.h"
#include "lldb/Target/Queue.h"
#include "lldb/Target/RegisterContext.h"
+#include "lldb/Utility/AddressableBits.h"
#include "lldb/Utility/LLDBLog.h"
#include "lldb/Utility/ScriptedMetadata.h"
#include "lldb/Utility/State.h"
@@ -172,7 +173,11 @@ void ScriptedProcess::Terminate() {
Status ScriptedProcess::DoLoadCore() {
ProcessLaunchInfo launch_info = GetTarget().GetProcessLaunchInfo();
- return DoLaunch(nullptr, launch_info);
+ Status error = DoLaunch(nullptr, launch_info);
+ // Process::LoadCore() doesn't go through DidLaunch().
+ if (error.Success())
+ DidLaunchOrAttach();
+ return error;
}
Status ScriptedProcess::DoLaunch(Module *exe_module,
@@ -188,7 +193,23 @@ Status ScriptedProcess::DoLaunch(Module *exe_module,
return error;
}
-void ScriptedProcess::DidLaunch() { m_pid = GetInterface().GetProcessID(); }
+void ScriptedProcess::DidLaunch() { DidLaunchOrAttach(); }
+
+void ScriptedProcess::DidLaunchOrAttach() {
+ m_pid = GetInterface().GetProcessID();
+
+ StructuredData::DictionarySP bits_sp = GetInterface().GetAddressableBits();
+ if (!bits_sp)
+ return;
+
+ AddressableBits addressable_bits;
+ uint64_t num_bits = 0;
+ if (bits_sp->GetValueForKeyAsInteger("lowmem", num_bits))
+ addressable_bits.SetLowmemAddressableBits(num_bits);
+ if (bits_sp->GetValueForKeyAsInteger("highmem", num_bits))
+ addressable_bits.SetHighmemAddressableBits(num_bits);
+ SetAddressableBitMasks(addressable_bits);
+}
void ScriptedProcess::DidResume() {
// Update the PID again, in case the user provided a placeholder pid at launch
@@ -216,7 +237,7 @@ Status ScriptedProcess::DoAttach(const ProcessAttachInfo &attach_info) {
SetPrivateState(eStateStopped);
// NOTE: We need to set the PID before finishing to attach otherwise we will
// hit an assert when calling the attach completion handler.
- DidLaunch();
+ DidLaunchOrAttach();
return {};
}
@@ -234,6 +255,9 @@ Status ScriptedProcess::DoAttachToProcessWithName(
void ScriptedProcess::DidAttach(ArchSpec &process_arch) {
process_arch = GetArchitecture();
+ // Process::DidExec also comes through Process::CompleteAttach without
+ // resetting the address masks, so they have to be reported again here.
+ DidLaunchOrAttach();
}
Status ScriptedProcess::DoDestroy() { return Status(); }
diff --git a/lldb/source/Plugins/Process/scripted/ScriptedProcess.h b/lldb/source/Plugins/Process/scripted/ScriptedProcess.h
index c3cdf30a9d477..d2332f20f56e4 100644
--- a/lldb/source/Plugins/Process/scripted/ScriptedProcess.h
+++ b/lldb/source/Plugins/Process/scripted/ScriptedProcess.h
@@ -115,6 +115,10 @@ class ScriptedProcess : public Process {
Status DoAttach(const ProcessAttachInfo &attach_info);
+ /// Pick up the process state that has to be in place before the first stop
+ /// is reported, since the threads and stack frames are built from it.
+ void DidLaunchOrAttach();
+
private:
friend class ScriptedThread;
diff --git a/lldb/source/Plugins/Process/scripted/ScriptedThread.cpp b/lldb/source/Plugins/Process/scripted/ScriptedThread.cpp
index fcadde0b1ff64..b1f78705ef8ea 100644
--- a/lldb/source/Plugins/Process/scripted/ScriptedThread.cpp
+++ b/lldb/source/Plugins/Process/scripted/ScriptedThread.cpp
@@ -219,6 +219,7 @@ bool ScriptedThread::LoadArtificialStackFrames() {
}
Address symbol_addr;
+ pc = this->GetProcess()->FixCodeAddress(pc);
symbol_addr.SetLoadAddress(pc, &this->GetProcess()->GetTarget());
lldb::addr_t cfa = LLDB_INVALID_ADDRESS;
diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedProcessPythonInterface.cpp b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedProcessPythonInterface.cpp
index faa9aa6964866..8d5e7f4e80756 100644
--- a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedProcessPythonInterface.cpp
+++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedProcessPythonInterface.cpp
@@ -53,6 +53,19 @@ StructuredData::DictionarySP ScriptedProcessPythonInterface::GetCapabilities() {
return dict;
}
+StructuredData::DictionarySP
+ScriptedProcessPythonInterface::GetAddressableBits() {
+ Status error;
+ StructuredData::DictionarySP dict =
+ Dispatch<StructuredData::DictionarySP>("get_addressable_bits", error);
+
+ if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, dict,
+ error))
+ return {};
+
+ return dict;
+}
+
Status
ScriptedProcessPythonInterface::Attach(const ProcessAttachInfo &attach_info) {
lldb::ProcessAttachInfoSP attach_info_sp =
diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedProcessPythonInterface.h b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedProcessPythonInterface.h
index f61e5f71d0218..55b6bc135b854 100644
--- a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedProcessPythonInterface.h
+++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedProcessPythonInterface.h
@@ -37,6 +37,8 @@ class ScriptedProcessPythonInterface : public ScriptedProcessInterface,
StructuredData::DictionarySP GetCapabilities() override;
+ StructuredData::DictionarySP GetAddressableBits() override;
+
Status Attach(const ProcessAttachInfo &attach_info) override;
Status Launch() override;
diff --git a/lldb/test/API/functionalities/scripted_process/TestScriptedProcess.py b/lldb/test/API/functionalities/scripted_process/TestScriptedProcess.py
index a053475900f42..33ff43ea8bc5f 100644
--- a/lldb/test/API/functionalities/scripted_process/TestScriptedProcess.py
+++ b/lldb/test/API/functionalities/scripted_process/TestScriptedProcess.py
@@ -10,6 +10,7 @@
from lldbsuite.test import lldbutil
from lldbsuite.test import lldbtest
+import addressable_bits_scripted_process
import dummy_scripted_process
@@ -322,3 +323,47 @@ def cleanup():
"ScriptedFrame.thread should be valid after thread " "registration",
)
self.assertEqual(post_launch_frame.thread.GetThreadID(), tid)
+
+ @skipIf(archs=no_match(["arm64", "arm64e", "aarch64"]))
+ def test_scripted_process_addressable_bits(self):
+ """Test that the addressable bits a scripted process reports are in
+ effect for the very first stop, without resuming the process."""
+ self.build()
+
+ target = self.dbg.CreateTarget(self.getBuildArtifact("a.out"))
+ self.assertTrue(target, VALID_TARGET)
+
+ launch_info = lldb.SBLaunchInfo(None)
+ launch_info.SetProcessPluginName("ScriptedProcess")
+ launch_info.SetScriptedProcessClassName(
+ "addressable_bits_scripted_process.AddressableBitsScriptedProcess"
+ )
+
+ error = lldb.SBError()
+ process = target.Launch(launch_info, error)
+ self.assertSuccess(error)
+ self.assertTrue(process, PROCESS_IS_VALID)
+
+ self.assertEqual(
+ process.GetAddressMask(lldb.eAddressMaskTypeCode),
+ 0xFFFFFFFC00000000,
+ )
+ self.assertEqual(
+ process.FixAddress(addressable_bits_scripted_process.TAGGED_PC),
+ addressable_bits_scripted_process.FIXED_PC,
+ )
+
+ # The mask has to have been installed before the threads and their
+ # stack frames were built for this stop: nothing resumes the process
+ # here, so a frame that resolved its pc with the default mask would
+ # never be recomputed.
+ #
+ # Thread 0 has its frame zero built by the unwinder from the register
+ # context, thread 1 reports its frames from the script.
+ for idx in range(2):
+ frame = process.GetThreadAtIndex(idx).GetFrameAtIndex(0)
+ self.assertEqual(
+ frame.GetPC(),
+ addressable_bits_scripted_process.FIXED_PC,
+ f"thread {idx} frame zero pc",
+ )
diff --git a/lldb/test/API/functionalities/scripted_process/addressable_bits_scripted_process.py b/lldb/test/API/functionalities/scripted_process/addressable_bits_scripted_process.py
new file mode 100644
index 0000000000000..4b1f7b0ce96b4
--- /dev/null
+++ b/lldb/test/API/functionalities/scripted_process/addressable_bits_scripted_process.py
@@ -0,0 +1,86 @@
+import struct, signal
+
+from typing import Any, Dict
+
+import lldb
+from lldb.plugins.scripted_process import ScriptedProcess
+from lldb.plugins.scripted_process import ScriptedThread
+
+# A pc with junk in the bits above the addressable ones. Bit 55 is clear, so
+# the ABI strips the non-addressable bits instead of sign-extending them.
+TAGGED_PC = 0xD169800100004000
+ADDRESSABLE_BITS = 34
+FIXED_PC = 0x0000000100004000
+
+
+class AddressableBitsScriptedProcess(ScriptedProcess):
+ def __init__(self, exe_ctx: lldb.SBExecutionContext, args: lldb.SBStructuredData):
+ super().__init__(exe_ctx, args)
+ self.threads[0] = AddressableBitsScriptedThread(self, args)
+ self.threads[1] = ScriptedFramesScriptedThread(self, args)
+ self.addressable_bits = {"lowmem": ADDRESSABLE_BITS}
+
+ def read_memory_at_address(
+ self, addr: int, size: int, error: lldb.SBError
+ ) -> lldb.SBData:
+ data = lldb.SBData()
+ data.SetData(
+ error,
+ bytes(size),
+ self.target.GetByteOrder(),
+ self.target.GetAddressByteSize(),
+ )
+ return data
+
+ def get_loaded_images(self) -> list:
+ return self.loaded_images
+
+ def get_process_id(self) -> int:
+ return 42
+
+ def is_alive(self) -> bool:
+ return True
+
+ def get_scripted_thread_plugin(self) -> str:
+ return (
+ AddressableBitsScriptedThread.__module__
+ + "."
+ + AddressableBitsScriptedThread.__name__
+ )
+
+
+class AddressableBitsScriptedThread(ScriptedThread):
+ REGISTER_PC = TAGGED_PC
+
+ def __init__(self, process, args):
+ super().__init__(process, args)
+
+ def get_thread_id(self) -> int:
+ return 0x19
+
+ def get_state(self) -> int:
+ return lldb.eStateStopped
+
+ def get_stop_reason(self) -> Dict[str, Any]:
+ return {"type": lldb.eStopReasonSignal, "data": {"signal": signal.SIGINT}}
+
+ def get_register_context(self) -> str:
+ # The unwinder needs a plausible stack pointer to build frame zero.
+ regs = [0] * 33
+ regs[29] = 0x16FDFF000 # fp
+ regs[30] = self.REGISTER_PC # lr
+ regs[31] = 0x16FDFE000 # sp
+ regs[32] = self.REGISTER_PC # pc
+ return struct.pack(f"{len(regs)}Q", *regs) + struct.pack("I", 0)
+
+
+class ScriptedFramesScriptedThread(AddressableBitsScriptedThread):
+ # An address that needs no fixing, so that asserting on FIXED_PC can only
+ # pass if frame zero really came from get_stackframes() below.
+ REGISTER_PC = 0x16FDFD000
+
+ def get_thread_id(self) -> int:
+ return 0x1A
+
+ def get_stackframes(self) -> list:
+ return [{"pc": TAGGED_PC}]
More information about the lldb-commits
mailing list