daniel5151 / gdbstub

An ergonomic, featureful, and easy-to-integrate implementation of the GDB Remote Serial Protocol in Rust (with no-compromises #![no_std] support)
Other
291 stars 45 forks source link

LLDB compatibility: Send complete register definitions in target.xml #148

Open danlehmann opened 1 month ago

danlehmann commented 1 month ago

LLDB requires that the full list of registers is provided.

For example, for rv32, this string is currently returned:

fn target_description_xml() -> Option<&'static str> {
    Some(r#"<target version="1.0"><architecture>riscv:rv32</architecture></target>"#)
}

This works fine with gdb (it seems to be ignore the register list), but llvm cares. To fix it, I changed it to this:

<target version="1.0">
<architecture>riscv:rv32</architecture>
<feature name="org.gnu.gdb.riscv.cpu">
  <reg name="zero" bitsize="32" type="int" regnum="0"/>
  <reg name="ra" bitsize="32" type="code_ptr"/>
  <reg name="sp" bitsize="32" type="data_ptr"/>
  <reg name="gp" bitsize="32" type="data_ptr"/>
  <reg name="tp" bitsize="32" type="data_ptr"/>
  <reg name="t0" bitsize="32" type="int"/>
  <reg name="t1" bitsize="32" type="int"/>
  <reg name="t2" bitsize="32" type="int"/>
  <reg name="fp" bitsize="32" type="data_ptr"/>
  // remaining registers
</feature>
</architecture>

With that, lldb works out of the box; gdb is unaffected.

I think, lldb is actually within the spec to require this. See https://sourceware.org/gdb/current/onlinedocs/gdb.html/RISC_002dV-Features.html:

The ‘org.gnu.gdb.riscv.cpu’ feature is required for RISC-V targets. It should contain the registers ‘x0’ through ‘x31’, and ‘pc’. Either the architectural names (‘x0’, ‘x1’, etc) can be used, or the ABI names (‘zero’, ‘ra’, etc).

I'm working on a patch to address this.

danlehmann commented 1 month ago

To show the problem, this is the trace when connecting to the short target_description_xml in my risc-v 32 bit emulator:

 TRACE gdbstub::protocol::recv_packet > <-- +
 TRACE gdbstub::protocol::recv_packet > <-- $QStartNoAckMode#b0
 TRACE gdbstub::protocol::response_writer > --> $OK#9a
 TRACE gdbstub::protocol::recv_packet     > <-- +
 TRACE gdbstub::protocol::recv_packet     > <-- $qSupported:xmlRegisters=i386,arm,mips,arc;multiprocess+;fork-events+;vfork-events+#2e
 TRACE gdbstub::protocol::response_writer > --> $PacketSize=1000;vContSupported+;multiprocess+;QStartNoAckMode+;swbreak+;qXfer:features:read+#fd
 TRACE gdbstub::protocol::recv_packet     > <-- $QThreadSuffixSupported#e4
 INFO  gdbstub::stub::core_impl           > Unknown command: Ok("QThreadSuffixSupported")
 TRACE gdbstub::protocol::response_writer > --> $#00
 TRACE gdbstub::protocol::recv_packet     > <-- $QListThreadsInStopReply#21
 INFO  gdbstub::stub::core_impl           > Unknown command: Ok("QListThreadsInStopReply")
 TRACE gdbstub::protocol::response_writer > --> $#00
 TRACE gdbstub::protocol::recv_packet     > <-- $vCont?#49
 TRACE gdbstub::protocol::response_writer > --> $vCont;c;C;s;S#62
 TRACE gdbstub::protocol::recv_packet     > <-- $qVAttachOrWaitSupported#38
 INFO  gdbstub::stub::core_impl           > Unknown command: Ok("qVAttachOrWaitSupported")
 TRACE gdbstub::protocol::response_writer > --> $#00
 TRACE gdbstub::protocol::recv_packet     > <-- $QEnableErrorStrings#8c
 INFO  gdbstub::stub::core_impl           > Unknown command: Ok("QEnableErrorStrings")
 TRACE gdbstub::protocol::response_writer > --> $#00
 TRACE gdbstub::protocol::recv_packet     > <-- $qHostInfo#9b
 INFO  gdbstub::stub::core_impl           > Unknown command: Ok("qHostInfo")
 TRACE gdbstub::protocol::response_writer > --> $#00
 TRACE gdbstub::protocol::recv_packet     > <-- $qProcessInfo#dc
 INFO  gdbstub::stub::core_impl           > Unknown command: Ok("qProcessInfo")
 TRACE gdbstub::protocol::response_writer > --> $#00
 TRACE gdbstub::protocol::recv_packet     > <-- $qC#b4
 INFO  gdbstub::stub::core_impl           > Unknown command: Ok("qC")
 TRACE gdbstub::protocol::response_writer > --> $#00
 TRACE gdbstub::protocol::recv_packet     > <-- $qfThreadInfo#bb
 TRACE gdbstub::protocol::response_writer > --> $mp01.01#cd
 TRACE gdbstub::protocol::recv_packet     > <-- $qsThreadInfo#c8
 TRACE gdbstub::protocol::response_writer > --> $l#6c
 TRACE gdbstub::protocol::recv_packet     > <-- $?#3f
 TRACE gdbstub::protocol::response_writer > --> $T05thread:p01.01;#06
 TRACE gdbstub::protocol::recv_packet     > <-- $qProcessInfo#dc
 INFO  gdbstub::stub::core_impl           > Unknown command: Ok("qProcessInfo")
 TRACE gdbstub::protocol::response_writer > --> $#00
 TRACE gdbstub::protocol::recv_packet     > <-- $qXfer:features:read:target.xml:0,fff#7d
 TRACE gdbstub::protocol::response_writer > --> $m<target version="1.0"><architecture>riscv:rv32</architecture></target>#ab
 TRACE gdbstub::protocol::recv_packet     > <-- $qXfer:features:read:target.xml:46,fff#b7
 TRACE gdbstub::protocol::response_writer > --> $l#6c
 TRACE gdbstub::protocol::recv_packet     > <-- $qRegisterInfo0#72
 INFO  gdbstub::stub::core_impl           > Unknown command: Ok("qRegisterInfo0")
 TRACE gdbstub::protocol::response_writer > --> $#00
 TRACE gdbstub::protocol::recv_packet     > <-- $Hg1#e0
 TRACE gdbstub::protocol::response_writer > --> $OK#9a
 TRACE gdbstub::protocol::recv_packet     > <-- $p0#a0
 INFO  gdbstub::stub::core_impl           > Unknown command: Ok("p0")
 TRACE gdbstub::protocol::response_writer > --> $#00
 TRACE gdbstub::protocol::recv_packet     > <-- $qProcessInfo#dc
 INFO  gdbstub::stub::core_impl           > Unknown command: Ok("qProcessInfo")
 TRACE gdbstub::protocol::response_writer > --> $#00
 TRACE gdbstub::protocol::recv_packet     > <-- $qProcessInfo#dc
 INFO  gdbstub::stub::core_impl           > Unknown command: Ok("qProcessInfo")
 TRACE gdbstub::protocol::response_writer > --> $#00
 TRACE gdbstub::protocol::recv_packet     > <-- $qProcessInfo#dc
 INFO  gdbstub::stub::core_impl           > Unknown command: Ok("qProcessInfo")
 TRACE gdbstub::protocol::response_writer > --> $#00
 TRACE gdbstub::protocol::recv_packet     > <-- $qOffsets#4b
 INFO  gdbstub::stub::core_impl           > Unknown command: Ok("qOffsets")
 TRACE gdbstub::protocol::response_writer > --> $#00
 TRACE gdbstub::protocol::recv_packet     > <-- $qStructuredDataPlugins#02
 INFO  gdbstub::stub::core_impl           > Unknown command: Ok("qStructuredDataPlugins")
 TRACE gdbstub::protocol::response_writer > --> $#00
 TRACE gdbstub::protocol::recv_packet     > <-- $qSymbol::#5b
 INFO  gdbstub::stub::core_impl           > Unknown command: Ok("qSymbol::")
 TRACE gdbstub::protocol::response_writer > --> $#00
 TRACE gdbstub::protocol::recv_packet     > <-- $qfThreadInfo#bb
 TRACE gdbstub::protocol::response_writer > --> $mp01.01#cd
 TRACE gdbstub::protocol::recv_packet     > <-- $qsThreadInfo#c8
 TRACE gdbstub::protocol::response_writer > --> $l#6c
 TRACE gdbstub::protocol::recv_packet     > <-- $jThreadsInfo#c1
 INFO  gdbstub::stub::core_impl           > Unknown command: Ok("jThreadsInfo")
 TRACE gdbstub::protocol::response_writer > --> $#00
 TRACE gdbstub::protocol::recv_packet     > <-- $jThreadExtendedInfo:#b9
 INFO  gdbstub::stub::core_impl           > Unknown command: Ok("jThreadExtendedInfo:")
 TRACE gdbstub::protocol::response_writer > --> $#00

Registers are never queried.

danlehmann commented 1 month ago

Once I add the registers, things look as expected:

 TRACE gdbstub::protocol::recv_packet > <-- +
 TRACE gdbstub::protocol::recv_packet > <-- $QStartNoAckMode#b0
 TRACE gdbstub::protocol::response_writer > --> $OK#9a
 TRACE gdbstub::protocol::recv_packet     > <-- +
 TRACE gdbstub::protocol::recv_packet     > <-- $qSupported:xmlRegisters=i386,arm,mips,arc;multiprocess+;fork-events+;vfork-events+#2e
 TRACE gdbstub::protocol::response_writer > --> $PacketSize=1000;vContSupported+;multiprocess+;QStartNoAckMode+;swbreak+;qXfer:features:read+#fd
 TRACE gdbstub::protocol::recv_packet     > <-- $QThreadSuffixSupported#e4
 INFO  gdbstub::stub::core_impl           > Unknown command: Ok("QThreadSuffixSupported")
 TRACE gdbstub::protocol::response_writer > --> $#00
 TRACE gdbstub::protocol::recv_packet     > <-- $QListThreadsInStopReply#21
 INFO  gdbstub::stub::core_impl           > Unknown command: Ok("QListThreadsInStopReply")
 TRACE gdbstub::protocol::response_writer > --> $#00
 TRACE gdbstub::protocol::recv_packet     > <-- $vCont?#49
 TRACE gdbstub::protocol::response_writer > --> $vCont;c;C;s;S#62
 TRACE gdbstub::protocol::recv_packet     > <-- $qVAttachOrWaitSupported#38
 INFO  gdbstub::stub::core_impl           > Unknown command: Ok("qVAttachOrWaitSupported")
 TRACE gdbstub::protocol::response_writer > --> $#00
 TRACE gdbstub::protocol::recv_packet     > <-- $QEnableErrorStrings#8c
 INFO  gdbstub::stub::core_impl           > Unknown command: Ok("QEnableErrorStrings")
 TRACE gdbstub::protocol::response_writer > --> $#00
 TRACE gdbstub::protocol::recv_packet     > <-- $qHostInfo#9b
 INFO  gdbstub::stub::core_impl           > Unknown command: Ok("qHostInfo")
 TRACE gdbstub::protocol::response_writer > --> $#00
 TRACE gdbstub::protocol::recv_packet     > <-- $qProcessInfo#dc
 INFO  gdbstub::stub::core_impl           > Unknown command: Ok("qProcessInfo")
 TRACE gdbstub::protocol::response_writer > --> $#00
 TRACE gdbstub::protocol::recv_packet     > <-- $qC#b4
 INFO  gdbstub::stub::core_impl           > Unknown command: Ok("qC")
 TRACE gdbstub::protocol::response_writer > --> $#00
 TRACE gdbstub::protocol::recv_packet     > <-- $qfThreadInfo#bb
 TRACE gdbstub::protocol::response_writer > --> $mp01.01#cd
 TRACE gdbstub::protocol::recv_packet     > <-- $qsThreadInfo#c8
 TRACE gdbstub::protocol::response_writer > --> $l#6c
 TRACE gdbstub::protocol::recv_packet     > <-- $?#3f
 TRACE gdbstub::protocol::response_writer > --> $T05thread:p01.01;#06
 TRACE gdbstub::protocol::recv_packet     > <-- $qProcessInfo#dc
 INFO  gdbstub::stub::core_impl           > Unknown command: Ok("qProcessInfo")
 TRACE gdbstub::protocol::response_writer > --> $#00
 TRACE gdbstub::protocol::recv_packet     > <-- $qXfer:features:read:target.xml:0,fff#7d
 TRACE gdbstub::protocol::response_writer > --> $m<target version="1.0">
<architecture>riscv:rv32</architecture>
<feature name="org.gnu.gdb.riscv.cpu">
  <reg name="zero" bitsize="32" type="int" regnum="0"/>
  <reg name="ra" bitsize="32" type="code_ptr"/>
  <reg name="sp" bitsize="32" type="data_ptr"/>
  <reg name="gp" bitsize="32" type="data_ptr"/>
  <reg name="tp" bitsize="32" type="data_ptr"/>
  <reg name="t0" bitsize="32" type="int"/>
  <reg name="t1" bitsize="32" type="int"/>
  <reg name="t2" bitsize="32" type="int"/>
  <reg name="fp" bitsize="32" type="data_ptr"/>
  <reg name="s1" bitsize="32" type="int"/>
  <reg name="a0" bitsize="32" type="int"/>
  <reg name="a1" bitsize="32" type="int"/>
  <reg name="a2" bitsize="32" type="int"/>
  <reg name="a3" bitsize="32" type="int"/>
  <reg name="a4" bitsize="32" type="int"/>
  <reg name="a5" bitsize="32" type="int"/>
  <reg name="a6" bitsize="32" type="int"/>
  <reg name="a7" bitsize="32" type="int"/>
  <reg name="s2" bitsize="32" type="int"/>
  <reg name="s3" bitsize="32" type="int"/>
  <reg name="s4" bitsize="32" type="int"/>
  <reg name="s5" bitsize="32" type="int"/>
  <reg name="s6" bitsize="32" type="int"/>
  <reg name="s7" bitsize="32" type="int"/>
  <reg name="s8" bitsize="32" type="int"/>
  <reg name="s9" bitsize="32" type="int"/>
  <reg name="s10" bitsize="32" type="int"/>
  <reg name="s11" bitsize="32" type="int"/>
  <reg name="t3" bitsize="32" type="int"/>
  <reg name="t4" bitsize="32" type="int"/>
  <reg name="t5" bitsize="32" type="int"/>
  <reg name="t6" bitsize="32" type="int"/>
  <reg name="pc" bitsize="32" type="code_ptr"/>
</feature>
</target>#50
 TRACE gdbstub::protocol::recv_packet     > <-- $qXfer:features:read:target.xml:632,fff#e8
 TRACE gdbstub::protocol::response_writer > --> $l#6c
 TRACE gdbstub::protocol::recv_packet     > <-- $Hg1#e0
 TRACE gdbstub::protocol::response_writer > --> $OK#9a
 TRACE gdbstub::protocol::recv_packet     > <-- $p0#a0
 INFO  gdbstub::stub::core_impl           > Unknown command: Ok("p0")
 TRACE gdbstub::protocol::response_writer > --> $#00
 TRACE gdbstub::protocol::recv_packet     > <-- $qProcessInfo#dc
 INFO  gdbstub::stub::core_impl           > Unknown command: Ok("qProcessInfo")
 TRACE gdbstub::protocol::response_writer > --> $#00
 TRACE gdbstub::protocol::recv_packet     > <-- $qProcessInfo#dc
 INFO  gdbstub::stub::core_impl           > Unknown command: Ok("qProcessInfo")
 TRACE gdbstub::protocol::response_writer > --> $#00
 TRACE gdbstub::protocol::recv_packet     > <-- $qProcessInfo#dc
 INFO  gdbstub::stub::core_impl           > Unknown command: Ok("qProcessInfo")
 TRACE gdbstub::protocol::response_writer > --> $#00
 TRACE gdbstub::protocol::recv_packet     > <-- $qOffsets#4b
 INFO  gdbstub::stub::core_impl           > Unknown command: Ok("qOffsets")
 TRACE gdbstub::protocol::response_writer > --> $#00
 TRACE gdbstub::protocol::recv_packet     > <-- $qStructuredDataPlugins#02
 INFO  gdbstub::stub::core_impl           > Unknown command: Ok("qStructuredDataPlugins")
 TRACE gdbstub::protocol::response_writer > --> $#00
 TRACE gdbstub::protocol::recv_packet     > <-- $qSymbol::#5b
 INFO  gdbstub::stub::core_impl           > Unknown command: Ok("qSymbol::")
 TRACE gdbstub::protocol::response_writer > --> $#00
 TRACE gdbstub::protocol::recv_packet     > <-- $qfThreadInfo#bb
 TRACE gdbstub::protocol::response_writer > --> $mp01.01#cd
 TRACE gdbstub::protocol::recv_packet     > <-- $qsThreadInfo#c8
 TRACE gdbstub::protocol::response_writer > --> $l#6c
 TRACE gdbstub::protocol::recv_packet     > <-- $g#67
 TRACE gdbstub::protocol::response_writer > --> $000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000042#ce
 TRACE gdbstub::protocol::recv_packet     > <-- $qMemoryRegionInfo:42000000#9a
 INFO  gdbstub::stub::core_impl           > Unknown command: Ok("qMemoryRegionInfo:42000000")
 TRACE gdbstub::protocol::response_writer > --> $#00
 TRACE gdbstub::protocol::recv_packet     > <-- $jThreadsInfo#c1
 INFO  gdbstub::stub::core_impl           > Unknown command: Ok("jThreadsInfo")
 TRACE gdbstub::protocol::response_writer > --> $#00
 TRACE gdbstub::protocol::recv_packet     > <-- $jThreadExtendedInfo:#b9
 INFO  gdbstub::stub::core_impl           > Unknown command: Ok("jThreadExtendedInfo:")
 TRACE gdbstub::protocol::response_writer > --> $#00
 TRACE gdbstub::protocol::recv_packet     > <-- $x0,0#04
 INFO  gdbstub::stub::core_impl           > Unknown command: Ok("x0,0")
 TRACE gdbstub::protocol::response_writer > --> $#00
 TRACE gdbstub::protocol::recv_packet     > <-- $m42000000,200#b1
 TRACE gdbstub::protocol::response_writer > --> $1711c8fd130101001725c8fd1305852497a5c8fd938585216307b500232005001105e34db5fe1715c8fd1305a5fd9725c8fd9385252217d61d001306a69c6308b500044204c111051106e34cb5fe97d00700e78060f8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000097501300e780007d2571232e1118232c8118232a91182328211923263119232441192322511923206119232e7117232c8117232a91172328a1172326b1170013ae892324a4e63705fdfc130ac5cf37f5f0f0930a050f37c5c0c0130b050c37050303930b3530130cc4e69304c4e8130600142685814597701900e780801d814c03c5190083c5090003c6290083c6390022054d8d4206e206558e518d83c5590003c6490083c6690003c77900a205d18dc2066207d98ed58d03c6990083c6890003c7a90083c7b9002206558e4207e2075d8f598e83c6d90003c7c90083c7e90003c8f900a206d98ec20762083367f800d98e13571500298fb757555593875755#0a
daniel5151 commented 1 month ago

Reviewing the GDB RSP spec (at https://sourceware.org/gdb/current/onlinedocs/gdb.html/Target-Description-Format.html#Target-Description-Format), it does seem that gdbstub_arch is technically allowed to send over just the architecture string.

That's not to say this is strictly the best idea. If upstream GDB changes the built-in target.xml structure for an architecture, gdbstub_arch could go out-of-sync, and send registers over in the wrong order. While it does seem that GDB make those sorts of breaking changes often (if ever), its always possible they might...

In any case - I'm not at all surprised that lldb might not be a fan of getting a "partial" XML like this, as presumably, most other GDB stubs will either send over no target.xml at all, or send over a fully-featured target.xml in its entirety.

I think its quite reasonable that gdbstub_arch implementations could be updated to send over fully-featured target.xml files.

In the future, it might be nice to address this alongside #143 or #12, but I think manually adding these XML defns here is totally reasonable for now.


Given that this might be a general problem (not specific to just RISC-V), we should avoid having #149 auto-close this issue. Can you update its description accordingly?

I'll go ahead and review that RISC-V PR, and we'll get it in to fix your use case :)