Issues when using LLDB with OpenOCD & the SVD2LLDB plugin

Hello,

I am trying to write a guide on how to use LLDB with Swift Embedded and the SVD2LLDB plugin, which allows you to read hardware registers with a semantic, human-friendly interface. However, we've ran into some issues.

The setup is the following:

  • We are using a Raspberry Pi Pico 2 (RP2350) (configured in ARM mode - specifically using , not RISCV) and a Raspberry Pi Debug Probe (a CMSIS-DAP-compatible SWD debugger)
  • We use Raspberry Pi's openocd port that supports the RP2350.
  • LLDB is connected to openocd via gdb-remote 3333.
  • LLDB version:
    • When trying to use the lldb from the Swift 6.3 development snapshot, we are running into an issue (described in the second part of this post).
    • Thus, for the first issue, we are using the lldb from the Swift 6.2.3 release, lldb version 17.0.0 (https://github.com/swiftlang/llvm-project.git revision 9784760565e8cae0bc0b97bad69aaf498408dc3d) Apple Swift version 6.2.3 (swift-6.2.3-RELEASE).

OpenOCD output from my current debug session, for reference, if needed:

cmd@ ~/pico/openocd (rpi-common●) % sudo src/openocd -s tcl -f interface/cmsis-dap.cfg -f target/rp2350.cfg -c "adapter speed 5000" -c 'gdb_memory_map disable'
Open On-Chip Debugger 0.12.0+dev-00002-gcd4873400 (2026-01-25-10:07)
Licensed under GNU GPL v2
For bug reports, read
	http://openocd.org/doc/doxygen/bugs.html
Info : [rp2350.cm0] Hardware thread awareness created
Info : [rp2350.cm1] Hardware thread awareness created
ocd_process_reset_inner
adapter speed: 5000 kHz
DEPRECATED! use 'gdb memory_map', not 'gdb_memory_map'
Info : Listening on port 6666 for tcl connections
Info : Listening on port 4444 for telnet connections
Info : Using CMSIS-DAPv2 interface with VID:PID=0x2e8a:0x000c, serial=E6614103E724342F
Info : CMSIS-DAP: SWD supported
Info : CMSIS-DAP: Atomic commands supported
Info : CMSIS-DAP: Test domain timer supported
Info : CMSIS-DAP: FW Version = 2.0.0
Info : CMSIS-DAP: Interface Initialised (SWD)
Info : SWCLK/TCK = 0 SWDIO/TMS = 0 TDI = 0 TDO = 0 nTRST = 0 nRESET = 0
Info : CMSIS-DAP: Interface ready
Info : clock speed 5000 kHz
Info : SWD DPIDR 0x4c013477 DPv3
Info : [rp2350.cm0] Cortex-M33 r1p0 processor detected
Info : [rp2350.cm0] target has 8 breakpoints, 4 watchpoints
Info : [rp2350.cm0] Examination succeed
Info : [rp2350.cm1] Cortex-M33 r1p0 processor detected
Info : [rp2350.cm1] target has 8 breakpoints, 4 watchpoints
Info : [rp2350.cm1] Examination succeed
Info : [rp2350.cm0] starting gdb server on 3333
Info : Listening on port 3333 for gdb connections
Info : [rp2350.cm0] external reset detected
Info : [rp2350.cm1] external reset detected
Info : accepting 'gdb' connection on tcp/3333
Info : New GDB Connection: 1, Target rp2350.cm0, state: halted
Warn : Prefer GDB command "target extended-remote :3333" instead of "target remote :3333"
Warn : DEPRECATED: This method is deprecated in favor of the hwthread pseudo RTOS
[rp2350.cm0] halted due to debug-request, current mode: Thread 
xPSR: 0x69000000 pc: 0x200007fc msp: 0x20009fd4
[rp2350.cm1] halted due to debug-request, current mode: Thread 
xPSR: 0x09000000 pc: 0x000000da msp: 0xf0000000
Warn : DEPRECATED: This method is deprecated in favor of the hwthread pseudo RTOS
Info : dropped 'gdb' connection
Info : accepting 'gdb' connection on tcp/3333
Info : New GDB Connection: 2, Target rp2350.cm0, state: halted
Warn : DEPRECATED: This method is deprecated in favor of the hwthread pseudo RTOS
Warn : DEPRECATED: This method is deprecated in favor of the hwthread pseudo RTOS
Info : dropped 'gdb' connection
Info : accepting 'gdb' connection on tcp/3333
Info : New GDB Connection: 3, Target rp2350.cm0, state: halted
Info : SWD DPIDR 0x4c013477 DPv3
Error: Failed to read memory at 0xffff0110
Info : SWD DPIDR 0x4c013477 DPv3
Error: Failed to read memory at 0xffff1010
Warn : DEPRECATED: This method is deprecated in favor of the hwthread pseudo RTOS
Info : dropped 'gdb' connection
Info : accepting 'gdb' connection on tcp/3333
Info : New GDB Connection: 4, Target rp2350.cm0, state: halted
Info : SWD DPIDR 0x4c013477 DPv3
Error: Failed to read memory at 0xffff0110
Info : SWD DPIDR 0x4c013477 DPv3
Error: Failed to read memory at 0xffff1010
Warn : DEPRECATED: This method is deprecated in favor of the hwthread pseudo RTOS
Info : SWD DPIDR 0x4c013477 DPv3
Error: Failed to read memory at 0x62707365
Warn : DEPRECATED: This method is deprecated in favor of the hwthread pseudo RTOS
Info : dropped 'gdb' connection
Info : accepting 'gdb' connection on tcp/3333
Info : New GDB Connection: 5, Target rp2350.cm0, state: halted
Warn : DEPRECATED: This method is deprecated in favor of the hwthread pseudo RTOS
Warn : DEPRECATED: This method is deprecated in favor of the hwthread pseudo RTOS
Info : dropped 'gdb' connection
Info : accepting 'gdb' connection on tcp/3333
Info : New GDB Connection: 6, Target rp2350.cm0, state: halted
Warn : DEPRECATED: This method is deprecated in favor of the hwthread pseudo RTOS

First issue - reading hardware register values with the plugin

I came across an issue when reading values from a hardware register with the plugin:

(lldb) svd read i2c1.ic_sar
RP2350:
  I2C1:
    IC_SAR: <error>
error: Failed to read some registers.

After discussing and debugging this with @rauhul, we've made some simple changes to the plugin, and tried this again with caching disabled & logs enabled.

This is the actual read command; we have commented out an error path see if the error is valid:

extension lldb.SBDebugger: SVD2LLDBDebugger {
  static let lldbEIOError: UInt32 = 5

  mutating func read(
    address: UInt64,
    bits: some FixedWidthInteger
  ) throws -> UInt64 {
    var value: UInt64 = 0
    var error = lldb.SBError()
    let bytes = bits.roundUp(toMultipleOf: 8) / 8
    precondition(bytes <= MemoryLayout.size(ofValue: value))
    var target = self.GetSelectedTarget()
    var process = target.GetProcess()
    let count = process.ReadMemory(address, &value, Int(bytes), &error)
    if count != bytes {
      // We've commented out these lines to rule out this possible error path
      // error.SetError(Self.lldbEIOError, lldb.eErrorTypePOSIX)
      // throw error
    }
    if error.IsValid() {
      throw error // a valid error is thrown
    }
    return value
  }

We have also tried to display the CString error, if available:

extension lldb.SBError: CustomStringConvertible {
  @_documentation(visibility: internal)
  public var description: String {
    if !self.IsValid() {
      "NOT AN ERROR"
    } else if let cString = lldb.GetCString(self) {
      String(cString: cString)
    } else {
      "UNKNOWN ERROR"
    }
  }
}

And, when running with log enable gdb-remote packets and settings set target.process.disable-memory-cache true, we notice that this issue seems to not have a CString description:

Process 1 stopped
* thread #1, stop reason = signal SIGINT
    frame #0: 0x200007fc Application`ledSuccess() at Application.swift:45:3
   42  	  ledSet(true)
   43  	  while true {
   44  	    nop()
-> 45  	  }
   46  	}
   47  	
   48  	private func blinkFailForever(_ code: UInt32) -> Never {
Target 0: (Application) stopped.
(lldb) svd read i2c1.ic_sar 
 <  15> send packet: $m40098008,4#6a
 <  12> read packet: $42000000#86
UNKNOWN ERROR
RP2350:
  I2C1:
    IC_SAR: <error Optional(UNKNOWN ERROR)>
error: Failed to read some registers.
(lldb) mem read --size 4 --format x --count 1 0x40098008
 <  15> send packet: $m40098008,4#6a
 <  12> read packet: $42000000#86
0x40098008: 0x00000042

If we comment out the error throwing part in the lldb.SBDebugger's extension above like this:

    if error.IsValid() {
      // throw error
    }

We can succesfully read commands:

Process 1 stopped
* thread #1, stop reason = signal SIGINT
    frame #0: 0x200007fc Application`ledSuccess() at Application.swift:45:3
   42  	  ledSet(true)
   43  	  while true {
   44  	    nop()
-> 45  	  }
   46  	}
   47  	
   48  	private func blinkFailForever(_ code: UInt32) -> Never {
Target 0: (Application) stopped.
(lldb) svd read i2c1.ic_sar
 <  15> send packet: $m40098008,4#6a
 <  12> read packet: $42000000#86
RP2350:
  I2C1:
    IC_SAR: 0x0000_0042
(lldb) mem read --size 4 --format x --count 1 0x40098008
 <  15> send packet: $m40098008,4#6a
 <  12> read packet: $42000000#86
0x40098008: 0x00000042

From what I could read online and infer, in both cases the packets sent and received from the gdb remote port seem to be fine (lldb is correctly reading reading 4 bytes with m40098008,4, and the response is 0x42, which is correct).

Second issue - lldb from 6.3 development snapshot stalls when connecting to openocd

When trying to connect to OpenOCD, the gdb-remote 3333 command stalls:

cmd@ ~ % lldb --version
lldb version 21.0.0 (https://github.com/swiftlang/llvm-project.git revision 4f7a3dbb7e97ac4a1dee94efff5b68833c07b4d0)
Apple Swift version 6.3-dev (LLVM 4f7a3dbb7e97ac4, Swift 477d32c08627ddd)
cmd@ ~ % lldb /Users/cmd/Downloads/tutorial/swift-embedded-examples/rpi-pico2-lldb/finished-tutorial/.build/armv7em-apple-none-macho/debug/Application
(lldb) target create "/Users/cmd/Downloads/tutorial/swift-embedded-examples/rpi-pico2-lldb/finished-tutorial/.build/armv7em-apple-none-macho/debug/Application"
Current executable set to '/Users/cmd/Downloads/tutorial/swift-embedded-examples/rpi-pico2-lldb/finished-tutorial/.build/armv7em-apple-none-macho/debug/Application' (armv7em).
(lldb) log enable gdb-remote packets
(lldb) settings set target.process.disable-memory-cache true
(lldb) gdb-remote 3333
 <   1> send packet: +
 history[1] tid=0x0103 <   1> send packet: +
 <  19> send packet: $QStartNoAckMode#b0
 <   1> read packet: +
 <   6> read packet: $OK#9a
 <   1> send packet: +
 < 104> send packet: $qSupported:xmlRegisters=i386,arm,mips,arc;multiprocess+;fork-events+;vfork-events+;swbreak+;hwbreak+#cd
 < 116> read packet: $PacketSize=4000;qXfer:memory-map:read-;qXfer:features:read+;qXfer:threads:read+;QStartNoAckMode+;vContSupported+#04
 <  26> send packet: $QThreadSuffixSupported#e4
 <   4> read packet: $#00
 <  27> send packet: $QListThreadsInStopReply#21
 <   4> read packet: $#00
 <  13> send packet: $qHostInfo#9b
 <   4> read packet: $#00
 <  10> send packet: $vCont?#49
 <  17> read packet: $vCont;c;C;s;S#62
 <  27> send packet: $qVAttachOrWaitSupported#38
 <   4> read packet: $#00
 <  23> send packet: $QEnableErrorStrings#8c
 <   4> read packet: $#00
 <  16> send packet: $qProcessInfo#dc
 <   4> read packet: $#00
 <   6> send packet: $qC#b4
 <  22> read packet: $QC0000000000000001#95
 <   5> send packet: $?#3f
 <   7> read packet: $S02#b5
 <  16> send packet: $qProcessInfo#dc
 <   4> read packet: $#00
 <  41> send packet: $qXfer:features:read:target.xml:0,3fff#b0
 <5525> read packet: $l<?xml version="1.0"?>
<!DOCTYPE target SYSTEM "gdb-target.dtd">
<target version="1.0">
<architecture>arm</architecture>
<feature name="org.gnu.gdb.arm.m-profile">
<reg name="r0" bitsize="32" regnum="0" save-restore="yes" type="int" group="general"/>
<reg name="r1" bitsize="32" regnum="1" save-restore="yes" type="int" group="general"/>
<reg name="r2" bitsize="32" regnum="2" save-restore="yes" type="int" group="general"/>
<reg name="r3" bitsize="32" regnum="3" save-restore="yes" type="int" group="general"/>
<reg name="r4" bitsize="32" regnum="4" save-restore="yes" type="int" group="general"/>
<reg name="r5" bitsize="32" regnum="5" save-restore="yes" type="int" group="general"/>
<reg name="r6" bitsize="32" regnum="6" save-restore="yes" type="int" group="general"/>
<reg name="r7" bitsize="32" regnum="7" save-restore="yes" type="int" group="general"/>
<reg name="r8" bitsize="32" regnum="8" save-restore="yes" type="int" group="general"/>
<reg name="r9" bitsize="32" regnum="9" save-restore="yes" type="int" group="general"/>
<reg name="r10" bitsize="32" regnum="10" save-restore="yes" type="int" group="general"/>
<reg name="r11" bitsize="32" regnum="11" save-restore="yes" type="int" group="general"/>
<reg name="r12" bitsize="32" regnum="12" save-restore="yes" type="int" group="general"/>
<reg name="sp" bitsize="32" regnum="13" save-restore="yes" type="data_ptr" group="general"/>
<reg name="lr" bitsize="32" regnum="14" save-restore="yes" type="int" group="general"/>
<reg name="pc" bitsize="32" regnum="15" save-restore="yes" type="code_ptr" group="general"/>
<reg name="xpsr" bitsize="32" regnum="16" save-restore="yes" type="int" group="general"/>
</feature>
<feature name="org.gnu.gdb.arm.m-system">
<reg name="msp" bitsize="32" regnum="17" save-restore="yes" type="data_ptr" group="system"/>
<reg name="psp" bitsize="32" regnum="18" save-restore="yes" type="data_ptr" group="system"/>
<reg name="primask" bitsize="1" regnum="20" save-restore="yes" type="int8" group="system"/>
<reg name="basepri" bitsize="8" regnum="21" save-restore="yes" type="int8" group="system"/>
<reg name="faultmask" bitsize="1" regnum="22" save-restore="yes" type="int8" group="system"/>
<reg name="control" bitsize="3" regnum="23" save-restore="yes" type="int8" group="system"/>
</feature>
<feature name="org.gnu.gdb.arm.secext">
<reg name="msp_ns" bitsize="32" regnum="24" save-restore="yes" type="data_ptr" group="stack"/>
<reg name="psp_ns" bitsize="32" regnum="25" save-restore="yes" type="data_ptr" group="stack"/>
<reg name="msp_s" bitsize="32" regnum="26" save-restore="yes" type="data_ptr" group="stack"/>
<reg name="psp_s" bitsize="32" regnum="27" save-restore="yes" type="data_ptr" group="stack"/>
<reg name="msplim_s" bitsize="32" regnum="28" save-restore="yes" type="data_ptr" group="stack"/>
<reg name="psplim_s" bitsize="32" regnum="29" save-restore="yes" type="data_ptr" group="stack"/>
<reg name="msplim_ns" bitsize="32" regnum="30" save-restore="yes" type="data_ptr" group="stack"/>
<reg name="psplim_ns" bitsize="32" regnum="31" save-restore="yes" type="data_ptr" group="stack"/>
<reg name="primask_s" bitsize="1" regnum="33" save-restore="yes" type="int8" group="system"/>
<reg name="basepri_s" bitsize="8" regnum="34" save-restore="yes" type="int8" group="system"/>
<reg name="faultmask_s" bitsize="1" regnum="35" save-restore="yes" type="int8" group="system"/>
<reg name="control_s" bitsize="3" regnum="36" save-restore="yes" type="int8" group="system"/>
<reg name="primask_ns" bitsize="1" regnum="38" save-restore="yes" type="int8" group="system"/>
<reg name="basepri_ns" bitsize="8" regnum="39" save-restore="yes" type="int8" group="system"/>
<reg name="faultmask_ns" bitsize="1" regnum="40" save-restore="yes" type="int8" group="system"/>
<reg name="control_ns" bitsize="3" regnum="41" save-restore="yes" type="int8" group="system"/>
</feature>
<feature name="org.gnu.gdb.arm.vfp">
<reg name="d0" bitsize="64" regnum="42" save-restore="yes" type="ieee_double" group="float"/>
<reg name="d1" bitsize="64" regnum="43" save-restore="yes" type="ieee_double" group="float"/>
<reg name="d2" bitsize="64" regnum="44" save-restore="yes" type="ieee_double" group="float"/>
<reg name="d3" bitsize="64" regnum="45" save-restore="yes" type="ieee_double" group="float"/>
<reg name="d4" bitsize="64" regnum="46" save-restore="yes" type="ieee_double" group="float"/>
<reg name="d5" bitsize="64" regnum="47" save-restore="yes" type="ieee_double" group="float"/>
<reg name="d6" bitsize="64" regnum="48" save-restore="yes" type="ieee_double" group="float"/>
<reg name="d7" bitsize="64" regnum="49" save-restore="yes" type="ieee_double" group="float"/>
<reg name="d8" bitsize="64" regnum="50" save-restore="yes" type="ieee_double" group="float"/>
<reg name="d9" bitsize="64" regnum="51" save-restore="yes" type="ieee_double" group="float"/>
<reg name="d10" bitsize="64" regnum="52" save-restore="yes" type="ieee_double" group="float"/>
<reg name="d11" bitsize="64" regnum="53" save-restore="yes" type="ieee_double" group="float"/>
<reg name="d12" bitsize="64" regnum="54" save-restore="yes" type="ieee_double" group="float"/>
<reg name="d13" bitsize="64" regnum="55" save-restore="yes" type="ieee_double" group="float"/>
<reg name="d14" bitsize="64" regnum="56" save-restore="yes" type="ieee_double" group="float"/>
<reg name="d15" bitsize="64" regnum="57" save-restore="yes" type="ieee_double" group="float"/>
<reg name="fpscr" bitsize="32" regnum="58" save-restore="yes" type="int" group="float"/>
</feature>
</target>
#d7
 <  16> send packet: $qfThreadInfo#bb
 <  38> read packet: $m0000000000000001,0000000000000002#9c
 <  16> send packet: $qsThreadInfo#c8
 <   5> read packet: $l#6c
 <   7> send packet: $Hg1#e0
 <   6> read packet: $OK#9a
 <   6> send packet: $p0#a0
 <  12> read packet: $00000002#82
 <  16> send packet: $qProcessInfo#dc
 <   4> read packet: $#00
 <  16> send packet: $qProcessInfo#dc
 <   4> read packet: $#00
 <  16> send packet: $qProcessInfo#dc
 <   4> read packet: $#00
 <  12> send packet: $qOffsets#4b
 <  23> read packet: $Text=0;Data=0;Bss=0#04
 <  13> send packet: $qSymbol::#5b
 <   6> read packet: $OK#9a
 <  26> send packet: $qStructuredDataPlugins#02
 <   4> read packet: $#00
 <  16> send packet: $qfThreadInfo#bb
 <  38> read packet: $m0000000000000001,0000000000000002#9c
 <  16> send packet: $qsThreadInfo#c8
 <   5> read packet: $l#6c
 <  16> send packet: $qfThreadInfo#bb
 <  38> read packet: $m0000000000000001,0000000000000002#9c
 <  16> send packet: $qsThreadInfo#c8
 <   5> read packet: $l#6c
 <   6> send packet: $pf#d6
 <  12> read packet: $fc070020#f2
 <  20> send packet: $qThreadStopInfo2#2d
 <   4> read packet: $#00
lldb.debugger.event-handler      <  16> send packet: $jThreadsInfo#c1
(lldb) lldb.debugger.event-handler      <   6> send packet: $qC#b4
lldb.debugger.event-handler      <  22> read packet: $QC0000000000000001#95
lldb.debugger.event-handler      <   6> send packet: $qC#b4
lldb.debugger.event-handler      <  22> read packet: $QC0000000000000001#95
lldb.debugger.event-handler      <   6> send packet: $qC#b4
lldb.debugger.event-handler      <  22> read packet: $QC0000000000000001#95
lldb.debugger.event-handler      <   6> send packet: $qC#b4
lldb.debugger.event-handler      <  22> read packet: $QC0000000000000001#95
lldb.debugger.event-handler      <   6> send packet: $qC#b4
lldb.debugger.event-handler      <  22> read packet: $QC0000000000000001#95
lldb.debugger.event-handler      <   6> send packet: $qC#b4
lldb.debugger.event-handler      <  22> read packet: $QC0000000000000001#95
lldb.debugger.event-handler      <   6> send packet: $qC#b4
lldb.debugger.event-handler      <  22> read packet: $QC0000000000000001#95
lldb.debugger.event-handler      <   6> send packet: $qC#b4
lldb.debugger.event-handler      <  22> read packet: $QC0000000000000001#95

// these commands just keep repeating

LLDB seems to continuously loop these thread-related commands:

lldb.debugger.event-handler      <   6> send packet: $qC#b4
lldb.debugger.event-handler      <  22> read packet: $QC0000000000000001#95

For reference, here is a complete output when using the 6.2.3 release lldb, which works:

cmd@ ~ % lldb --version   
lldb version 17.0.0 (https://github.com/swiftlang/llvm-project.git revision 9784760565e8cae0bc0b97bad69aaf498408dc3d)
Apple Swift version 6.2.3 (swift-6.2.3-RELEASE)
cmd@ ~ % lldb /Users/cmd/Downloads/tutorial/swift-embedded-examples/rpi-pico2-lldb/finished-tutorial/.build/armv7em-apple-none-macho/debug/Application
(lldb) target create "/Users/cmd/Downloads/tutorial/swift-embedded-examples/rpi-pico2-lldb/finished-tutorial/.build/armv7em-apple-none-macho/debug/Application"
Current executable set to '/Users/cmd/Downloads/tutorial/swift-embedded-examples/rpi-pico2-lldb/finished-tutorial/.build/armv7em-apple-none-macho/debug/Application' (armv7em).
(lldb) log enable gdb-remote packets
(lldb) settings set target.process.disable-memory-cache true
(lldb) gdb-remote 3333
 <   1> send packet: +
 history[1] tid=0x0103 <   1> send packet: +
 <  19> send packet: $QStartNoAckMode#b0
 <   1> read packet: +
 <   6> read packet: $OK#9a
 <   1> send packet: +
 <  86> send packet: $qSupported:xmlRegisters=i386,arm,mips,arc;multiprocess+;fork-events+;vfork-events+#2e
 < 116> read packet: $PacketSize=4000;qXfer:memory-map:read-;qXfer:features:read+;qXfer:threads:read+;QStartNoAckMode+;vContSupported+#04
 <  26> send packet: $QThreadSuffixSupported#e4
 <   4> read packet: $#00
 <  27> send packet: $QListThreadsInStopReply#21
 <   4> read packet: $#00
 <  13> send packet: $qHostInfo#9b
 <   4> read packet: $#00
 <  10> send packet: $vCont?#49
 <  17> read packet: $vCont;c;C;s;S#62
 <  27> send packet: $qVAttachOrWaitSupported#38
 <   4> read packet: $#00
 <  23> send packet: $QEnableErrorStrings#8c
 <   4> read packet: $#00
 <  16> send packet: $qProcessInfo#dc
 <   4> read packet: $#00
 <   6> send packet: $qC#b4
 <  22> read packet: $QC0000000000000001#95
 <   5> send packet: $?#3f
 <   7> read packet: $S02#b5
 <  16> send packet: $qProcessInfo#dc
 <   4> read packet: $#00
 <  41> send packet: $qXfer:features:read:target.xml:0,3fff#b0
 <5525> read packet: $l<?xml version="1.0"?>
<!DOCTYPE target SYSTEM "gdb-target.dtd">
<target version="1.0">
<architecture>arm</architecture>
<feature name="org.gnu.gdb.arm.m-profile">
<reg name="r0" bitsize="32" regnum="0" save-restore="yes" type="int" group="general"/>
<reg name="r1" bitsize="32" regnum="1" save-restore="yes" type="int" group="general"/>
<reg name="r2" bitsize="32" regnum="2" save-restore="yes" type="int" group="general"/>
<reg name="r3" bitsize="32" regnum="3" save-restore="yes" type="int" group="general"/>
<reg name="r4" bitsize="32" regnum="4" save-restore="yes" type="int" group="general"/>
<reg name="r5" bitsize="32" regnum="5" save-restore="yes" type="int" group="general"/>
<reg name="r6" bitsize="32" regnum="6" save-restore="yes" type="int" group="general"/>
<reg name="r7" bitsize="32" regnum="7" save-restore="yes" type="int" group="general"/>
<reg name="r8" bitsize="32" regnum="8" save-restore="yes" type="int" group="general"/>
<reg name="r9" bitsize="32" regnum="9" save-restore="yes" type="int" group="general"/>
<reg name="r10" bitsize="32" regnum="10" save-restore="yes" type="int" group="general"/>
<reg name="r11" bitsize="32" regnum="11" save-restore="yes" type="int" group="general"/>
<reg name="r12" bitsize="32" regnum="12" save-restore="yes" type="int" group="general"/>
<reg name="sp" bitsize="32" regnum="13" save-restore="yes" type="data_ptr" group="general"/>
<reg name="lr" bitsize="32" regnum="14" save-restore="yes" type="int" group="general"/>
<reg name="pc" bitsize="32" regnum="15" save-restore="yes" type="code_ptr" group="general"/>
<reg name="xpsr" bitsize="32" regnum="16" save-restore="yes" type="int" group="general"/>
</feature>
<feature name="org.gnu.gdb.arm.m-system">
<reg name="msp" bitsize="32" regnum="17" save-restore="yes" type="data_ptr" group="system"/>
<reg name="psp" bitsize="32" regnum="18" save-restore="yes" type="data_ptr" group="system"/>
<reg name="primask" bitsize="1" regnum="20" save-restore="yes" type="int8" group="system"/>
<reg name="basepri" bitsize="8" regnum="21" save-restore="yes" type="int8" group="system"/>
<reg name="faultmask" bitsize="1" regnum="22" save-restore="yes" type="int8" group="system"/>
<reg name="control" bitsize="3" regnum="23" save-restore="yes" type="int8" group="system"/>
</feature>
<feature name="org.gnu.gdb.arm.secext">
<reg name="msp_ns" bitsize="32" regnum="24" save-restore="yes" type="data_ptr" group="stack"/>
<reg name="psp_ns" bitsize="32" regnum="25" save-restore="yes" type="data_ptr" group="stack"/>
<reg name="msp_s" bitsize="32" regnum="26" save-restore="yes" type="data_ptr" group="stack"/>
<reg name="psp_s" bitsize="32" regnum="27" save-restore="yes" type="data_ptr" group="stack"/>
<reg name="msplim_s" bitsize="32" regnum="28" save-restore="yes" type="data_ptr" group="stack"/>
<reg name="psplim_s" bitsize="32" regnum="29" save-restore="yes" type="data_ptr" group="stack"/>
<reg name="msplim_ns" bitsize="32" regnum="30" save-restore="yes" type="data_ptr" group="stack"/>
<reg name="psplim_ns" bitsize="32" regnum="31" save-restore="yes" type="data_ptr" group="stack"/>
<reg name="primask_s" bitsize="1" regnum="33" save-restore="yes" type="int8" group="system"/>
<reg name="basepri_s" bitsize="8" regnum="34" save-restore="yes" type="int8" group="system"/>
<reg name="faultmask_s" bitsize="1" regnum="35" save-restore="yes" type="int8" group="system"/>
<reg name="control_s" bitsize="3" regnum="36" save-restore="yes" type="int8" group="system"/>
<reg name="primask_ns" bitsize="1" regnum="38" save-restore="yes" type="int8" group="system"/>
<reg name="basepri_ns" bitsize="8" regnum="39" save-restore="yes" type="int8" group="system"/>
<reg name="faultmask_ns" bitsize="1" regnum="40" save-restore="yes" type="int8" group="system"/>
<reg name="control_ns" bitsize="3" regnum="41" save-restore="yes" type="int8" group="system"/>
</feature>
<feature name="org.gnu.gdb.arm.vfp">
<reg name="d0" bitsize="64" regnum="42" save-restore="yes" type="ieee_double" group="float"/>
<reg name="d1" bitsize="64" regnum="43" save-restore="yes" type="ieee_double" group="float"/>
<reg name="d2" bitsize="64" regnum="44" save-restore="yes" type="ieee_double" group="float"/>
<reg name="d3" bitsize="64" regnum="45" save-restore="yes" type="ieee_double" group="float"/>
<reg name="d4" bitsize="64" regnum="46" save-restore="yes" type="ieee_double" group="float"/>
<reg name="d5" bitsize="64" regnum="47" save-restore="yes" type="ieee_double" group="float"/>
<reg name="d6" bitsize="64" regnum="48" save-restore="yes" type="ieee_double" group="float"/>
<reg name="d7" bitsize="64" regnum="49" save-restore="yes" type="ieee_double" group="float"/>
<reg name="d8" bitsize="64" regnum="50" save-restore="yes" type="ieee_double" group="float"/>
<reg name="d9" bitsize="64" regnum="51" save-restore="yes" type="ieee_double" group="float"/>
<reg name="d10" bitsize="64" regnum="52" save-restore="yes" type="ieee_double" group="float"/>
<reg name="d11" bitsize="64" regnum="53" save-restore="yes" type="ieee_double" group="float"/>
<reg name="d12" bitsize="64" regnum="54" save-restore="yes" type="ieee_double" group="float"/>
<reg name="d13" bitsize="64" regnum="55" save-restore="yes" type="ieee_double" group="float"/>
<reg name="d14" bitsize="64" regnum="56" save-restore="yes" type="ieee_double" group="float"/>
<reg name="d15" bitsize="64" regnum="57" save-restore="yes" type="ieee_double" group="float"/>
<reg name="fpscr" bitsize="32" regnum="58" save-restore="yes" type="int" group="float"/>
</feature>
</target>
#d7
 <  16> send packet: $qfThreadInfo#bb
 <  38> read packet: $m0000000000000001,0000000000000002#9c
 <  16> send packet: $qsThreadInfo#c8
 <   5> read packet: $l#6c
 <   7> send packet: $Hg1#e0
 <   6> read packet: $OK#9a
 <   6> send packet: $p0#a0
 <  12> read packet: $00000002#82
 <  16> send packet: $qProcessInfo#dc
 <   4> read packet: $#00
 <  16> send packet: $qProcessInfo#dc
 <   4> read packet: $#00
 <  16> send packet: $qProcessInfo#dc
 <   4> read packet: $#00
 <  12> send packet: $qOffsets#4b
 <  23> read packet: $Text=0;Data=0;Bss=0#04
 <  13> send packet: $qSymbol::#5b
 <   6> read packet: $OK#9a
 <  26> send packet: $qStructuredDataPlugins#02
 <   4> read packet: $#00
 <  16> send packet: $qfThreadInfo#bb
 <  38> read packet: $m0000000000000001,0000000000000002#9c
 <  16> send packet: $qsThreadInfo#c8
 <   5> read packet: $l#6c
 <  16> send packet: $qfThreadInfo#bb
 <  38> read packet: $m0000000000000001,0000000000000002#9c
 <  16> send packet: $qsThreadInfo#c8
 <   5> read packet: $l#6c
 <  20> send packet: $qThreadStopInfo2#2d
 <   4> read packet: $#00
 <  16> send packet: $jThreadsInfo#c1
(lldb)  <   6> send packet: $qC#b4
 <  22> read packet: $QC0000000000000001#95
 <   6> send packet: $pf#d6
 <  12> read packet: $fc070020#f2
 <   8> send packet: $x0,0#04
 <   4> read packet: $#00
 <  16> send packet: $m200007ec,12#ed
 <  40> read packet: $80b56f46012000f0010003f085f900bffde7#12
 <   6> send packet: $p7#a7
 <  12> read packet: $d49f0020#f9
 <  15> send packet: $m20009fd8,4#ca
 <  12> read packet: $470d0020#c1
 <  17> send packet: $m20000ba0,1c2#44
 < 904> read packet: $c0b501af4df804ad94b000201390002012900020adf84400fff784fe47f21060c0f200007844d0f800a01120102101f0f7fb0b90139047f2fa50c0f200007844d0f800a01b201a2102f03cf9ddf82ca00a901290daf80000416a42208847ddf82ca0daf80000816aa5208847ddf828a0daf80000806a8047ddf82ca0daf80000c06a8047ddf828a0daf80000c06a8047ddf82ca0daf80000006b804727f8120c17f8121c0c9117f8110c0d908df844108df8450010aa02320e928df840108df8410000208df8420001208df843009df9410001280bd00e980d990c9a07f81a2c07f8191c90f90100012813d005e00e9890f90100012818d01ae00e9917f81a0c40b291f90010884240f2000008bf0120099000e006e0099880f0ff3010f0010f07d137e000200990f5e701200990f2e7f8e70d9840b201280cd00c980890ffe7089840b210f15a0f40f2000008bf0120079022e06946012088617b20486102220a611d20c86046f64e70c0f20000784488604a603920086047f2a840c0f20000784446f65273c0f200037b440b2100f025f8fede00200790ffe7079810f0010f0bd0fff753fd0a9801f0e8f80b9801f0e5f814b05df804abc0bd0220fff750fdfede#c3
 <  15> send packet: $m20009fd4,4#c6
 <  12> read packet: $34a00020#ba
 <  15> send packet: $m20009fd8,4#ca
 <  12> read packet: $470d0020#c1
 <  15> send packet: $m2000a038,4#8b
 <  12> read packet: $6d0d0020#f0
 <  15> send packet: $m20000d64,a#ba
 <  24> read packet: $80b56f46fff71aff80bd#fa
 <  15> send packet: $m2000a034,4#87
 <  12> read packet: $3ca00020#e9
 <  15> send packet: $m2000a038,4#8b
 <  12> read packet: $6d0d0020#f0
 <  24> send packet: $jThreadExtendedInfo:#b9
 <   6> send packet: $qC#b4
 <  22> read packet: $QC0000000000000001#95
Process 1 stopped
* thread #1, stop reason = signal SIGINT
    frame #0: 0x200007fc Application`ledSuccess() at Application.swift:45:3
   42  	  ledSet(true)
   43  	  while true {
   44  	    nop()
-> 45  	  }
   46  	}
   47  	
   48  	private func blinkFailForever(_ code: UInt32) -> Never {
Target 0: (Application) stopped.
(lldb)
1 Like

Hey @iCMDdev,

My name is Ismail and I work on the LLDB at Apple. Thanks for bringing up this issue.

First, I’d recommend trying out the latest nightly toolchain since I fixed a bug recently that could address the register reading issue. Let me know if that’s still an issue.

Regarding the hang, can you reproduce it and run the `sample lldb` (or sample lldb-rpc-server if you’re using Xcode). Please file a feedback report using Bug Reporting - Apple Developer and attach the sample to it.

Feel free to post the feedback number here so I can take a look at it.

Thanks!

1 Like

Hello @medismailben, thanks for your reply!

I filled a feedback for the second bug, FB21947134.

For the first bug, I switched to reproducing it directly on my Mac (without extra embedded hardware / debuggers), so that I can use the latest LLDB from nightly (with external hardware, I'm encountering the second issue). I also recompiled the plugin using the latest nightly toolchain. Everything below uses main-snapshot-2026-02-06

I compiled a simple macOS Embedded Swift binary (any binary would've worked). In this example, I am trying to read a hypothetical register - which is actually just the address of the printed string (for not having to switch the "register" address every time, I disable ASLR using settings set target.disable-aslr true).

cmd@ ~/Downloads/embedded-on-macos % swiftly list       
Installed release toolchains
----------------------------
Swift 6.2.3

Installed snapshot toolchains
-----------------------------
main-snapshot-2026-02-06 (in use) (default)
6.3-snapshot-2026-01-29
6.3-snapshot-2026-01-16
main-snapshot-2026-01-09
main-snapshot-2025-11-26

Available system toolchains
---------------------------
xcode
cmd@ ~/Downloads/embedded-on-macos % cat HelloEmbedded.swift
print("Hello, Embedded Swift 😊")
cmd@ ~/Downloads/embedded-on-macos % swiftc HelloEmbedded.swift -o HelloEmbedded -enable-experimental-feature Embedded -wmo
cmd@ ~/Downloads/embedded-on-macos % lldb HelloEmbedded
(lldb) target create "HelloEmbedded"
Current executable set to '/Users/cmd/Downloads/embedded-on-macos/HelloEmbedded' (arm64).
(lldb) version
lldb version 21.0.0 (https://github.com/swiftlang/llvm-project.git revision 352c368501af15912369b83101a05bf7b354642e)
Apple Swift version 6.3-dev (LLVM 352c368501af159, Swift 83d82a5af50dc7b)
(lldb) settings set target.process.disable-memory-cache true
(lldb) settings set target.disable-aslr true
(lldb) br set -n main
Breakpoint 1: where = HelloEmbedded`main, address = 0x0000000100000460
(lldb) r
Process 22408 launched: '/Users/cmd/Downloads/embedded-on-macos/HelloEmbedded' (arm64)
Process 22408 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
    frame #0: 0x0000000100000460 HelloEmbedded`main
HelloEmbedded`main:
->  0x100000460 <+0>:  sub    sp, sp, #0x20
    0x100000464 <+4>:  stp    x29, x30, [sp, #0x10]
    0x100000468 <+8>:  add    x29, sp, #0x10
    0x10000046c <+12>: adrp   x8, 2
Target 0: (HelloEmbedded) stopped.
(lldb) disass
HelloEmbedded`main:
->  0x100000460 <+0>:  sub    sp, sp, #0x20
    0x100000464 <+4>:  stp    x29, x30, [sp, #0x10]
    0x100000468 <+8>:  add    x29, sp, #0x10
    0x10000046c <+12>: adrp   x8, 2
    0x100000470 <+16>: add    x8, x8, #0x620 ; "Hello, Embedded Swift \xf0\x9f\x98\x8a"
    0x100000474 <+20>: str    x8, [sp]
    0x100000478 <+24>: nop    
    0x10000047c <+28>: bl     0x1000004b4    ; default argument 1 of Swift.print(_: Swift.StaticString, terminator: Swift.StaticString) -> ()
    0x100000480 <+32>: mov    x3, x0
    0x100000484 <+36>: ldr    x0, [sp]
    0x100000488 <+40>: mov    x4, x1
    0x10000048c <+44>: mov    x5, x2
    0x100000490 <+48>: mov    w8, #0x1a ; =26 
    0x100000494 <+52>: mov    x1, x8
    0x100000498 <+56>: mov    w2, #0x0 ; =0 
    0x10000049c <+60>: stur   w2, [x29, #-0x4]
    0x1000004a0 <+64>: bl     0x1000004cc    ; Swift.print(_: Swift.StaticString, terminator: Swift.StaticString) -> ()
    0x1000004a4 <+68>: ldur   w0, [x29, #-0x4]
    0x1000004a8 <+72>: ldp    x29, x30, [sp, #0x10]
    0x1000004ac <+76>: add    sp, sp, #0x20
    0x1000004b0 <+80>: ret    
(lldb) br set -a 0x100000470
Breakpoint 2: where = HelloEmbedded`main + 16, address = 0x0000000100000470
(lldb) c
Process 22408 resuming
Process 22408 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 2.1
    frame #0: 0x0000000100000470 HelloEmbedded`main + 16
HelloEmbedded`main:
->  0x100000470 <+16>: add    x8, x8, #0x620
    0x100000474 <+20>: str    x8, [sp]
    0x100000478 <+24>: nop    
    0x10000047c <+28>: bl     0x1000004b4    ; default argument 1 of Swift.print(_: Swift.StaticString, terminator: Swift.StaticString) -> ()
Target 0: (HelloEmbedded) stopped.
(lldb) reg read x8
      x8 = 0x0000000100002000  HelloEmbedded`Swift.swift_isEscapingClosureAtFileLocation(object: Builtin.RawPointer, filename: Swift.UnsafePointer<Swift.Int8>, filenameLength: Swift.Int32, line: Swift.Int32, column: Swift.Int32, verificationType: Swift.UInt32) -> Swift.Bool + 120
(lldb) x/s 0x0000000100002000+0x620
0x100002620: "Hello, Embedded Swift \xf0\x9f\x98\x8a"
(lldb) svd load min.svd
Loaded SVD file: “min.svd”.
(lldb) svd info simple.reg
SIMPLE.REG:
  Description: Simple 32-bit register
  Address:     0x0000_0001_0000_2620
  Bit Width:   32
  Reset Value: 0x0000_0000
(lldb) svd read simple.reg
UNKNOWN ERROR
MINDEV:
  SIMPLE:
    REG:  <error Optional(UNKNOWN ERROR)>
error: Failed to read some registers.
(lldb) x/s 0x0000000100002620
0x100002620: "Hello, Embedded Swift \xf0\x9f\x98\x8a"
(lldb) x/x 0x0000000100002620
0x100002620: 0x6c6c6548
(lldb) svd decode simple.reg 0x6c6c6548
SIMPLE.REG: 0x6c6c_6548

Sample SVD file loaded & used by the plugin, configured to read that address:

<?xml version="1.0" encoding="UTF-8"?>
<device schemaVersion="1.3" xmlns:xs="http://www.w3.org/2001/XMLSchema-instance">
  <name>Minimal SVD</name>
  <vendor>Example</vendor>
  <description>Minimal svd with one register.</description>

  <addressUnitBits>8</addressUnitBits>
  <width>32</width>

  <peripherals>
    <peripheral>
      <name>SIMPLE</name>
      <description>Single-register peripheral</description>

      <!-- Address read -->
      <baseAddress>0x100002620</baseAddress>

      <registers>
        <register>
          <name>REG</name>
          <description>Simple 32-bit register</description>
          <addressOffset>0x0</addressOffset>
          <size>32</size>
          <access>read-write</access>
          <resetValue>0x00000000</resetValue>
        </register>
      </registers>

    </peripheral>
  </peripherals>
</device>

It seems that the issue still persists with the latest nightly toolchain.

2 Likes

After looking in more detail, I think the real issue for the second bug may be in OpenOCD, though different LLDB versions do seem to handle the issue differently.

The issue seems to be that no response (not even "unsupported command" $#00) is received whenjThreadsInfo / jThreadExtendedInfo commands are sent. Notice that in both cases, no response is received for lldb.debugger.event-handler < 16> send packet: $jThreadsInfo#c1.

If I patch OpenOCD to respond to these commands with "unsupported", everything works fine (and, in fact, faster - originally, on the LLDB that comes with Swift 6.2.3, I had to wait a bit before the connection established).

Perhaps the older LLDB has a timeout for these remote gdb server commands, and if no response is received, it goes through a fallback. And this timeout might have been removed in the newer LLDB.

1 Like

Thanks, I’ve received the report, I’ll try to find some time to look at it.

1 Like

Thanks!
Fyi, I've also submitted a patch to OpenOCD - https://review.openocd.org/c/openocd/+/9461

Hey @iCMDdev,

I’ve looked to your hang and it looks like your gdb-remote stub isn’t sending any response following the jThreadsInfo packet. As stated in the gdb-remote protocol documentation:

The remote protocol specifies a few standard replies. All commands support these, except as noted in the individual command descriptions.



empty response

An empty response (raw character sequence '$#00') means the command is not supported by the stub. This way it is possible to extend the protocol. A newer GDB can tell if a command is supported based on that response (but see also qSupported).

So lldb waits for a response and after it times out, it keeps trying again.

If you still have issues with your fix, let us know :)

1 Like

Thanks a lot! Yes, this matches what I noticed.

A patch should be applied soon to OpenOCD to fix this behavior.

Patch was merged in OpenOCD commit 22e1e1b. Thanks for the help!

1 Like