From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mail-wm1-x32d.google.com (mail-wm1-x32d.google.com [IPv6:2a00:1450:4864:20::32d]) by sourceware.org (Postfix) with ESMTPS id 06729385782A for ; Fri, 22 Oct 2021 17:10:27 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org 06729385782A Authentication-Results: sourceware.org; dmarc=none (p=none dis=none) header.from=embecosm.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=embecosm.com Received: by mail-wm1-x32d.google.com with SMTP id u8-20020a05600c440800b0030d90076dabso9246110wmn.1 for ; Fri, 22 Oct 2021 10:10:26 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=embecosm.com; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=hakFyBReXd9r/H3yOKwNGhJYYFI7b4iBl2HV3PpRgbA=; b=R006Eu05P3I0R8D+sMn1QJGKii5vSIhyWG7Rk+RwEEB81ti77f1MglFLvZCuR/snPe Ocwlh5CR7hxAqn/4iczQ4N0tw0URvNit93lDcFM6P28AwDqYGDSCej/A7gL09a7cg5Ec pgHgAIVUOIRyJMJddRcXnid2POQeejGFpWV1ZLb5GQJL9Pqls1vHLlVQX0b/iktDHNwE wzso7bXXQVFPC0LYT+Ym7AbV3XQ9fy90yJH7GnYqDzmQMxOexHUabFQU3NK+E56Ln/4T WY9yErL4ogeVUXgdOWv3NZRC6y2LN7UPN5TMML1UK4ivgn2oqnNj6o/r4cxQsbw0q3LN /eLg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=hakFyBReXd9r/H3yOKwNGhJYYFI7b4iBl2HV3PpRgbA=; b=ajJsLBvjkC4eeoRsLy5fVrOXe42Q3ucCRMF8QjqXs52RvmZeukxqtTnPd8VVywBs98 zf8tuXqRL8KicKXCNz3Tx8xkxFzO+oRCqusM8ipBNUrM5mjdT3byW24d+zy1ZXSi7pTv 0j9VcX0gfS88DvR6s4K2Ieu9ovsRHXYk4hxpsktLd7vMwTlVRKtzln7jSZAT7DMHwQ5Y t6NlozXHFMroPgquShmPecpO0JFRohqGAHQMkoIQbTk7IptBL5/Rgp6EqSiY0O5HrjAw gmjRfjYpJcqLErePo0Pxeyqqd0rkNxMk9zDB0V/XW6vmw2JVtvTB7rgPND6Jsq/LKcFS RxoQ== X-Gm-Message-State: AOAM533AmE+ThgyHpS7kfvJNKFHNZGp5lHGYRDqKnB0oqRkw9m5aEqSU MwwQFs6eoouPGeGfv2XEHcyYwPZZJQA2dw== X-Google-Smtp-Source: ABdhPJyblcA9tNoiaZ4cn2MjfDPXl3QgShKIPjKrP26UfHjNjnBY8GLiNFoK2sLqoOyoOGyxeLOVBg== X-Received: by 2002:a1c:9d14:: with SMTP id g20mr1046318wme.110.1634922625689; Fri, 22 Oct 2021 10:10:25 -0700 (PDT) Received: from localhost (host212-140-123-151.range212-140.btcentralplus.com. [212.140.123.151]) by smtp.gmail.com with ESMTPSA id s18sm6140474wrb.95.2021.10.22.10.10.25 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 22 Oct 2021 10:10:25 -0700 (PDT) From: Andrew Burgess To: gdb-patches@sourceware.org Subject: [PATCHv5 4/4] gdb: handle binary data in 'maint packet' and RemoteTargetConnection.send_packet Date: Fri, 22 Oct 2021 18:10:11 +0100 Message-Id: <1e73226e9c0b203e6021b92fb825a4b1610e5181.1634922528.git.andrew.burgess@embecosm.com> X-Mailer: git-send-email 2.25.4 In-Reply-To: References: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Spam-Status: No, score=-9.8 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, RCVD_IN_DNSWL_NONE, SCC_10_SHORT_WORD_LINES, SCC_20_SHORT_WORD_LINES, SCC_35_SHORT_WORD_LINES, SCC_5_SHORT_WORD_LINES, SPF_HELO_NONE, SPF_PASS, TXREP autolearn=ham autolearn_force=no version=3.4.4 X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on server2.sourceware.org X-BeenThere: gdb-patches@sourceware.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gdb-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Fri, 22 Oct 2021 17:10:29 -0000 Previous the 'maint packet' command, and the recently added Python API RemoteTargetConnection.send_packet, could only cope with replies that contained printable string characters. The code implementing these two features treated the reply buffer as a string, so any null-bytes would terminate the output, and if some other non-printable character was encountered, GDB would just try to print it anyway... The only way I've found to reproduce this easily is to try and examine the auxv data, which is binary in nature, here's a GDB session before my patch: (gdb) target remote :54321 ... (gdb) maint packet qXfer:auxv:read::0,1000 sending: "qXfer:auxv:read::0,1000" received: "l!" (gdb) And after my patch: (gdb) target remote :54321 ... (gdb) maint packet qXfer:auxv:read::0,1000 sending: "qXfer:auxv:read::0,1000" received: "l!\x00\x00\x00\x00\x00\x00\x00\x00\xf0\xfc\xf7\xff\x7f\x00\x00= \x10\x00\x00\x00\x00\x00\x00\x00\xff\xfb\xeb\xbf\x00\x00\x00\x00\x06\x00\x0= 0\x00\x00\x00\x00\x00\x00\x10\x00\x00\x00\x00\x00\x00\x11\x00\x00\x00\x00\x= 00\x00\x00d\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00@\x0= 0@\x00\x00\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x008\x00\x00\x00\x00\x00= \x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x0b\x00\x00\x00\x00\x00\x00\x00\x0= 7\x00\x00\x00\x00\x00\x00\x00\x00\x10\xfd\xf7\xff\x7f\x00\x00\x08\x00\x00\x= 00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x09\x00\x00\x00\x00\x00\= x00\x00P\x10@\x00\x00\x00\x00\x00\x0b\x00\x00\x00\x00\x00\x00\x00\xe8\x03\x= 00\x00\x00\x00\x00\x00\x0c\x00\x00\x00\x00\x00\x00\x00\xe8\x03\x00\x00\x00\= x00\x00\x00\x0d\x00\x00\x00\x00\x00\x00\x00\xe8\x03\x00\x00\x00\x00\x00\x00= \x0e\x00\x00\x00\x00\x00\x00\x00\xe8\x03\x00\x00\x00\x00\x00\x00\x17\x00\x0= 0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x19\x00\x00\x00\x00\x= 00\x00\x00y\xba\xff\xff\xff\x7f\x00\x00\x1a\x00\x00\x00\x00\x00\x00\x00\x00= \x00\x00\x00\x00\x00\x00\x00\x1f\x00\x00\x00\x00\x00\x00\x00\xdf\xef\xff\xf= f\xff\x7f\x00\x00\x0f\x00\x00\x00\x00\x00\x00\x00\x89\xba\xff\xff\xff\x7f\x= 00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" (gdb) The binary contents of the reply are now printed as escaped hex. Similarly, here's the same packet fetched, and printed, using this Python script: inf =3D gdb.selected_inferior() conn =3D inf.connection res =3D conn.send_packet("qXfer:auxv:read::0,1000") print ("Got: %s" % str(res)) The GDB session: (gdb) source fetch-auxv.py Got: b'l!\x00\x00\x00\x00\x00\x00\x00\x00\xf0\xfc\xf7\xff\x7f\x00\x00\x10= \x00\x00\x00\x00\x00\x00\x00\xff\xfb\xeb\xbf\x00\x00\x00\x00\x06\x00\x00\x0= 0\x00\x00\x00\x00\x00\x10\x00\x00\x00\x00\x00\x00\x11\x00\x00\x00\x00\x00\x= 00\x00d\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00@\x00@\x= 00\x00\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x008\x00\x00\x00\x00\x00\x00= \x00\x05\x00\x00\x00\x00\x00\x00\x00\x0b\x00\x00\x00\x00\x00\x00\x00\x07\x0= 0\x00\x00\x00\x00\x00\x00\x00\x10\xfd\xf7\xff\x7f\x00\x00\x08\x00\x00\x00\x= 00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\t\x00\x00\x00\x00\x00\x00\x0= 0P\x10@\x00\x00\x00\x00\x00\x0b\x00\x00\x00\x00\x00\x00\x00\xe8\x03\x00\x00= \x00\x00\x00\x00\x0c\x00\x00\x00\x00\x00\x00\x00\xe8\x03\x00\x00\x00\x00\x0= 0\x00\r\x00\x00\x00\x00\x00\x00\x00\xe8\x03\x00\x00\x00\x00\x00\x00\x0e\x00= \x00\x00\x00\x00\x00\x00\xe8\x03\x00\x00\x00\x00\x00\x00\x17\x00\x00\x00\x0= 0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x19\x00\x00\x00\x00\x00\x00\x= 00y\xba\xff\xff\xff\x7f\x00\x00\x1a\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00= \x00\x00\x00\x00\x00\x1f\x00\x00\x00\x00\x00\x00\x00\xdf\xef\xff\xff\xff\x7= f\x00\x00\x0f\x00\x00\x00\x00\x00\x00\x00\x89\xba\xff\xff\xff\x7f\x00\x00\x= 00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' The testing for this change is pretty weak right now, I just fetch the auxv data using 'maint packet' and from Python, and check that the two results match. I also check that the results, when converted to a string, contains some hex-encoded characters (e.g. \xf0, etc). --- gdb/python/py-connection.c | 21 ++++---- gdb/remote.c | 57 ++++++++++++--------- gdb/remote.h | 10 ++-- gdb/testsuite/gdb.python/py-send-packet.exp | 33 ++++++++++++ gdb/testsuite/gdb.python/py-send-packet.py | 28 ++++++++++ 5 files changed, 112 insertions(+), 37 deletions(-) diff --git a/gdb/python/py-connection.c b/gdb/python/py-connection.c index d282c225e9c..a0bea11694d 100644 --- a/gdb/python/py-connection.c +++ b/gdb/python/py-connection.c @@ -320,22 +320,25 @@ struct py_send_packet_callbacks : public send_remote_= packet_callbacks void sending (const char *args) override { /* Nothing. */ } =20 - /* When the result is returned create a Python string and assign this - into the result member variable. */ + /* When the result is returned create a Python object and assign this + into M_RESULT. If for any reason we can't create a Python object to + represent the result then M_RESULT is left untouched (it is + initialised to reference Py_None), so after this call M_RESULT will + either be an object representing the reply, or None. */ =20 - void received (const gdb::char_vector &buf) override + void received (const gdb::char_vector &buf, int bytes) override { - /* m_result is initialized to None, leave it untouched unless we got - back a useful result. */ - if (buf.data ()[0] !=3D '\0') + gdb_assert (bytes >=3D 0); + + if (bytes > 0 && buf.data ()[0] !=3D '\0') { PyObject *result; =20 #ifdef IS_PY3K - result =3D Py_BuildValue("y#", buf.data (), strlen (buf.data ())); + result =3D Py_BuildValue("y#", buf.data (), bytes); #else - result =3D PyString_FromStringAndSize (buf.data (), - strlen (buf.data ())); + //result =3D PyString_FromStringAndSize (buf.data (), bytes); + result =3D PyByteArray_FromStringAndSize (buf.data (), bytes); #endif =20 if (result !=3D nullptr) diff --git a/gdb/remote.c b/gdb/remote.c index 27af76d8dd2..a7c495a0e25 100644 --- a/gdb/remote.c +++ b/gdb/remote.c @@ -1037,8 +1037,6 @@ static int hexnumnstr (char *, ULONGEST, int); =20 static CORE_ADDR remote_address_masked (CORE_ADDR); =20 -static void print_packet (const char *); - static int stub_unpack_int (const char *buff, int fieldlength); =20 struct packet_config; @@ -9503,17 +9501,6 @@ escape_buffer (const char *buf, int n) return std::move (stb.string ()); } =20 -/* Display a null-terminated packet on stdout, for debugging, using C - string notation. */ - -static void -print_packet (const char *buf) -{ - puts_filtered ("\""); - fputstr_filtered (buf, '"', gdb_stdout); - puts_filtered ("\""); -} - int remote_target::putpkt (const char *buf) { @@ -11609,18 +11596,39 @@ struct cli_packet_command_callbacks : public send= _remote_packet_callbacks =20 void sending (const char *packet_str) override { - puts_filtered ("sending: "); - print_packet (packet_str); - puts_filtered ("\n"); + printf_filtered ("sending: \"%s\"\n", packet_str); } =20 - /* Called with BUF, the reply from the remote target. */ + /* Called with BUF, the reply from the remote target, which is BYTES in + length. Print the contents of BUF to gdb_stdout. */ =20 - void received (const gdb::char_vector &buf) override + void received (const gdb::char_vector &buf, int bytes) override { - puts_filtered ("received: "); - print_packet (buf.data ()); - puts_filtered ("\n"); + puts_filtered ("received: \""); + print_packet (buf, bytes); + puts_filtered ("\"\n"); + } + +private: + /* Print BUF, which contains BYTES bytes to gdb_stdout. Any + non-printable bytes in BUF are printed as '\x??' with '??' replaced + by the hexadecimal value of the byte. */ + + static void + print_packet (const gdb::char_vector &buf, int bytes) + { + string_file stb; + + for (int i =3D 0; i < bytes; ++i) + { + gdb_byte c =3D buf[i]; + if (isprint (c)) + fputc_unfiltered (c, &stb); + else + fprintf_unfiltered (&stb, "\\x%02x", (unsigned char) c); + } + + puts_filtered (stb.string ().c_str ()); } }; =20 @@ -11641,9 +11649,12 @@ send_remote_packet (const char *packet_str, =20 remote->putpkt (packet_str); remote_state *rs =3D remote->get_remote_state (); - remote->getpkt (&rs->buf, 0); + int bytes =3D remote->getpkt_sane (&rs->buf, 0); + + if (bytes < 0) + error (_("error while fetching packet from remote target")); =20 - callbacks->received (rs->buf); + callbacks->received (rs->buf, bytes); } =20 /* Entry point for the 'maint packet' command. */ diff --git a/gdb/remote.h b/gdb/remote.h index 22455e8f8d5..957dfcc5f5b 100644 --- a/gdb/remote.h +++ b/gdb/remote.h @@ -95,12 +95,12 @@ struct send_remote_packet_callbacks virtual void sending (const char *packet_str) =3D 0; =20 /* The RECEIVED callback is called once a reply has been received from - the remote target. The content of the reply is in BUF which can't be - modified, and which is not guaranteed to remain valid after the - RECEIVED call has returned. If you need to preserve the contents of - BUF then a copy should be taken. */ + the remote target. The content of the reply is in BUF, which is BYTES + long, which can't be modified, and which is not guaranteed to remain + valid after the RECEIVED call has returned. If you need to preserve + the contents of BUF then a copy should be taken. */ =20 - virtual void received (const gdb::char_vector &buf) =3D 0; + virtual void received (const gdb::char_vector &buf, int bytes) =3D 0; }; =20 /* Send PACKET_STR the current remote target. If PACKET_STR is nullptr, or diff --git a/gdb/testsuite/gdb.python/py-send-packet.exp b/gdb/testsuite/gd= b.python/py-send-packet.exp index dde5c270f20..63a7080e3bc 100644 --- a/gdb/testsuite/gdb.python/py-send-packet.exp +++ b/gdb/testsuite/gdb.python/py-send-packet.exp @@ -54,3 +54,36 @@ gdb_test "python run_send_packet_test()" "Send packet te= st passed" # Check the string representation of a remote target connection. gdb_test "python print(gdb.selected_inferior().connection)" \ "" + +# Check to see if there's any auxv data for this target. +gdb_test_multiple "info auxv" "" { + -re -wrap "The program has no auxiliary information now\\. " { + set skip_auxv_test true + } + -re -wrap "0\\s+AT_NULL\\s+End of vector\\s+0x0" { + set skip_auxv_test false + } +} + +if { ! $skip_auxv_test } { + # Use 'maint packet' to fetch the auxv data. + set reply_data "" + gdb_test_multiple "maint packet qXfer:auxv:read::0,1000" "" { + -re "sending: \"qXfer:auxv:read::0,1000\"\r\n" { + exp_continue + } + -re -wrap "received: \"(.*)\"" { + set reply_data $expect_out(1,string) + } + } + + # Expand the '\x' in the output, so we can pass a string through + # to Python. + set reply_data [string map {\x \\x} $reply_data] + gdb_assert { ![string equal "$reply_data" ""] } + + # Run the test, fetches the auxv data in Python and confirm it + # matches the expected results. + gdb_test "python run_auxv_send_packet_test(\"$reply_data\")" \ + "auxv send packet test passed" +} diff --git a/gdb/testsuite/gdb.python/py-send-packet.py b/gdb/testsuite/gdb= .python/py-send-packet.py index 35754164a98..226c3e9477f 100644 --- a/gdb/testsuite/gdb.python/py-send-packet.py +++ b/gdb/testsuite/gdb.python/py-send-packet.py @@ -77,5 +77,33 @@ def run_send_packet_test(): print("Send packet test passed") =20 =20 +# Convert a bytes or bytearray object to a string. This follows the +# same rules as the 'maint packet' command so that the output from the +# two sources can be compared. +def bytes_to_string(byte_array): + res =3D "" + for b in byte_array: + b =3D int(b) + if b >=3D 32 and b <=3D 126: + res =3D res + ("%c" % b) + else: + res =3D res + ("\\x%02x" % b) + return res + +# A very simple test for sending the packet that reads the auxv data. +# We convert the result to a string and expect to find some +# hex-encoded bytes in the output. This test will only work on +# targets that actually supply auxv data. +def run_auxv_send_packet_test(expected_result): + inf =3D gdb.selected_inferior() + conn =3D inf.connection + assert isinstance(conn, gdb.RemoteTargetConnection) + res =3D conn.send_packet("qXfer:auxv:read::0,1000") + string =3D bytes_to_string(res) + assert string.count("\\x") > 0 + assert string =3D=3D expected_result + print("auxv send packet test passed") + + # Just to indicate the file was sourced correctly. print("Sourcing complete.") --=20 2.25.4