Re: Incompatible implementat ion of 'x' packet in GDB vs LLDB

Luis Machado via Gdb <[email protected]>
Newsgroups gmane.comp.gdb.devel
Message-ID <[email protected]>
On 1/23/25 16:14, Andrew Burgess via Gdb wrote:
> "Aktemur, Tankut Baris via Gdb" <[email protected]> writes:
> 
>> On Thursday, January 23, 2025 8:13 AM, Robert O'Callahan wrote:
>>> On Thu, 23 Jan 2025 at 08:57, Robert O'Callahan <[email protected]>
>>> wrote:
>>>
>>>> GDB (client) 16.1 started sending the gdbserver 'x' packet. It follows the
>>>> documentation [1] and expects a leading 'b' in the response [2].
>>>> Unfortunately LLDB has supported this packet for quite a long time [3] and
>>>> does not expect a leading 'b' in the response. We added support for this
>>>> packet to rr last year and followed LLDB's format because it was the only
>>>> user of the packet at that time. So GDB 16.1 doesn't work with rr. [4]
>>>>
>>>> I realize that compatibility between GDB and LLDB flavoured gdbserver
>>>> protocols is not a priority for either team, but until now it has actually
>>>> worked in practice --- rr hasn't needed a client mode switch. We can add
>>>> one, but it will be unfortunate if GDB 16.1 and later is incompatible for
>>>> anyone who's installed the latest rr since May 2024.
>>>>
>>>> Could you make a GDB 16.1 point release that removes the 'b'? AFAIK it
>>>> serves no purpose.
>>>>
>>>
>>> It has been pointed out that if you want to return different error codes
>>> then you need the 'b'. Is that the rationale?
>>
>> Hello Rob,
>>
>> Essentially, yes.  In case of an error, the server responds with an 'E' packet.
>> To be able to distinguish an error packet from binary data, 'E' would have to be
>> added to the list of escaped characters.  Having the 'b' marker avoids that.
>>
>> Additionally, when the response is empty, per RSP, it means the packet is unsupported.
>> So, in case of a zero-length request, the 'b' marker could help us distinguish the
>> unsupported case from an actual zero-response.
>> LLDB doc says
>>
>>   To test if this packet is available, send a addr/len of 0:
>>
>>     x0,0
>>
>>   You will get an OK response if it is supported. 
>>   The reply will be the data requested in 8-bit binary data format.
>>
>> How does LLDB distinguish an "OK" response, an empty binary data, an error,
>> and an unsupported case?  These were not clear to me from the docs.
>> Is the x0,0 query special-cased?
>>
>> For the record, the 'x' packet series were discussed in
>>
>>   https://inbox.sourceware.org/gdb-patches/[email protected]/#r
>>
>> with the part specific to the 'b' marker in
>>
>>   https://inbox.sourceware.org/gdb-patches/[email protected]/
> 
> I also am not entirely sure how lldb's implementation can work in all
> cases given their description.  For now at least, I still think we made
> the right choice with our implementation, though I guess if we'd known
> we were diverging from llvm we might have picked a different letter for
> packet in order to make it completely different.
> 
> I have two thoughts for things we could do to possibly help with
> compatibility here though:
> 
>   1. Hide the 'x' packet behind a feature flag.  So instead of using the
>   empty reply to indicate 'x' packet support, we ask remote targets to
>   send 'gdb-x+' in their qSupported reply.  If a remote target sends
>   this to GDB then we will assume that they support GDB's style of 'x'
>   packet, and can make use of the packet.  Otherwise, it's 'm' packets
>   all the way.
> 
> 
>   2. The other option would be to probe for 'x' packet support as a
>   separate action.  Right now probing is done the first time we send an
>   'x' packet.  The packet is sent, and if we get back the empty packet,
>   then we know 'x' packets are supported.
> 
>   But if instead, we sent a zero length read, then by GDB's rules we
>   should get back a lone 'b'.  By lldb's rules we should get back
>   .... maybe "OK" ... or maybe the empty packet?  It's not really
>   clear.  But the important thing is that neither of those replies are
>   'b'.
> 
>   So the first time we consider sending an 'x' packet, we send
>   'xADDR,0', if we get back 'b', then we know that we have GDB style
>   support, otherwise, we disable the 'x' packet, and fall back to the
>   'm' packet.
> 
> Approach #1 is pretty straight forward, but I wanted to check what
> approach #2 would look like, so I drafted the patch below.  This patch
> is pretty rough right now, but can easily be cleaned up.
> 
> For testing this patch I hacked up gdbserver so it no longer added the
> 'b' prefix, then I tried connecting with GDB.  I see GDB send the zero
> length probe, then switch back to 'm' packets.

Thanks for the input.

> 
> Questions:
> 
>   + Do people think we should change GDB to improve compatibility?  My
>     thinking is yes, so long as the cost isn't too high.

Sounds reasonable. I think we should too, as it seems to be good timing
(just after the release of gdb 16) for us to try to improve this.

> 
>   + Do people prefer approach #1 or #2?  I'm leaning slightly more to #2
>     right now, but not massively so.  If people prefer #1 I can write
>     the patch for that instead.

Approach #2 seems to be a bit better from your description, as it checks
things from gdb's side as opposed to chaging the server side. At least
that's my personal take on it.

> 
> Thanks,
> Andrew
> 
> ---
> 
> diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
> index b65124d807f..e92227d3803 100644
> --- a/gdb/doc/gdb.texinfo
> +++ b/gdb/doc/gdb.texinfo
> @@ -43531,6 +43531,10 @@ Packets
>  use byte accesses, or not.  For this reason, this packet may not be
>  suitable for accessing memory-mapped I/O devices.
>  
> +If @var{length} is zero then the reply should contain the leading
> +@samp{b} prefix and not data.  @value{GDBN} sends a zero length
> +@samp{x} packet to probe for support.
> +
>  Reply:
>  @table @samp
>  @item b @var{XX@dots{}}
> diff --git a/gdb/remote.c b/gdb/remote.c
> index 64622dbfcdf..a332fcd6f95 100644
> --- a/gdb/remote.c
> +++ b/gdb/remote.c
> @@ -9683,17 +9683,55 @@ remote_target::remote_read_bytes_1 (CORE_ADDR memaddr, gdb_byte *myaddr,
>    memaddr = remote_address_masked (memaddr);
>  
>    /* Construct "m/x"<memaddr>","<len>".  */
> -  auto send_request = [this, rs, memaddr, todo_units] (char format) -> void
> +  auto send_request = [this, rs, memaddr] (char format, int len) -> void
>      {
>        char *buffer = rs->buf.data ();
>        *buffer++ = format;
>        buffer += hexnumstr (buffer, (ULONGEST) memaddr);
>        *buffer++ = ',';
> -      buffer += hexnumstr (buffer, (ULONGEST) todo_units);
> +      buffer += hexnumstr (buffer, (ULONGEST) len);
>        *buffer = '\0';
>        putpkt (rs->buf);
>      };
>  
> +  /* Unfortunately, lldb and GDB disagree on the reply format for the 'x'
> +     packet.  GDB expects a 'b' as the first byte of a successful packet,
> +     while lldb does not.  There are good reasons why GDB uses the 'b'
> +     prefix; it's not possible to distinguish between an empty
> +     not-supported reply and a failed read empty reply, unless there is a
> +     prefix.
> +
> +     As lldb "got there first", and there are clients in the wild that
> +     support the lldb style reply, we probe with a zero length read.  If
> +     this returns a single 'b' then we know that the 'x' packet is
> +     supported.
> +
> +     If we get back an error packet (starts with 'E') then we cannot know
> +     if a successful read would start with a 'b' or not.  We'll probe again
> +     next time.  */
> +  if (m_features.packet_support (PACKET_x) == PACKET_SUPPORT_UNKNOWN)
> +    {
> +      send_request ('x', 0);
> +      int packet_len = getpkt (&rs->buf);
> +
> +      /* Catastrophic error fetching packet.  */
> +      if (packet_len < 0)
> +	return TARGET_XFER_E_IO;
> +
> +      /* If we get back a lone 'b' with no data then the remote replied as
> +	 expected.  Anything else and we just disable the 'x' packet.  */
> +      if (rs->buf[0] == 'b' && rs->buf[1] == '\0')
> +	{
> +	  remote_debug_printf ("binary memory read is supported by target");
> +	  m_features.m_protocol_packets[PACKET_x].support = PACKET_ENABLE;
> +	}
> +      else
> +	{
> +	  remote_debug_printf ("binary memory read NOT supported by target");
> +	  m_features.m_protocol_packets[PACKET_x].support = PACKET_DISABLE;
> +	}
> +    }
> +
>    /* Determine which packet format to use.  The target's support for
>       'x' may be unknown.  We just try.  If it doesn't work, we try
>       again using 'm'.  */
> @@ -9703,32 +9741,11 @@ remote_target::remote_read_bytes_1 (CORE_ADDR memaddr, gdb_byte *myaddr,
>    else
>      packet_format = 'x';
>  
> -  send_request (packet_format);
> +  send_request (packet_format, todo_units);
>    int packet_len = getpkt (&rs->buf);
>    if (packet_len < 0)
>      return TARGET_XFER_E_IO;
>  
> -  if (m_features.packet_support (PACKET_x) == PACKET_SUPPORT_UNKNOWN)
> -    {
> -      if (rs->buf[0] == '\0')
> -	{
> -	  remote_debug_printf ("binary uploading NOT supported by target");
> -	  m_features.m_protocol_packets[PACKET_x].support = PACKET_DISABLE;
> -
> -	  /* Try again using 'm'.  */
> -	  packet_format = 'm';
> -	  send_request (packet_format);
> -	  packet_len = getpkt (&rs->buf);
> -	  if (packet_len < 0)
> -	    return TARGET_XFER_E_IO;
> -	}
> -      else
> -	{
> -	  remote_debug_printf ("binary uploading supported by target");
> -	  m_features.m_protocol_packets[PACKET_x].support = PACKET_ENABLE;
> -	}
> -    }
> -
>    packet_result result = packet_check_result (rs->buf);
>    if (result.status () == PACKET_ERROR)
>      return TARGET_XFER_E_IO;
>
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.