[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