From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from simark.ca by simark.ca with LMTP id 4486NVlvw2oukjsAWB0awg (envelope-from ) for ; Mon, 05 Oct 2026 05:35:21 -0400 Authentication-Results: simark.ca; dkim=pass (2048-bit key; unprotected) header.d=ibm.com header.i=@ibm.com header.a=rsa-sha256 header.s=pp1 header.b=av7zAZ1K; dkim-atps=neutral Received: by simark.ca (Postfix, from userid 112) id C48D81E04E; Mon, 05 Oct 2026 05:35:21 -0400 (EDT) X-Spam-Checker-Version: SpamAssassin 4.0.1 (2024-03-25) on simark.ca X-Spam-Level: X-Spam-Status: No, score=-2.3 required=5.0 tests=ARC_SIGNED,ARC_VALID,BAYES_00, DKIM_SIGNED,DKIM_VALID,HTML_MESSAGE,MAILING_LIST_MULTI, RCVD_IN_DNSWL_MED,RCVD_IN_VALIDITY_CERTIFIED_BLOCKED, RCVD_IN_VALIDITY_RPBL_BLOCKED,RCVD_IN_VALIDITY_SAFE_BLOCKED autolearn=ham autolearn_force=no version=4.0.1 Received: from vm01.sourceware.org (vm01.sourceware.org [38.145.34.32]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange x25519 server-signature ECDSA (prime256v1) server-digest SHA256) (No client certificate requested) by simark.ca (Postfix) with ESMTPS id E10D61E04E for ; Mon, 05 Oct 2026 05:35:16 -0400 (EDT) Received: from vm01.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id BF3864BA23DB for ; Mon, 5 Oct 2026 09:35:15 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org BF3864BA23DB Authentication-Results: sourceware.org; dkim=pass (2048-bit key, unprotected) header.d=ibm.com header.i=@ibm.com header.a=rsa-sha256 header.s=pp1 header.b=av7zAZ1K Received: from mx0b-001b2d01.pphosted.com (mx0b-001b2d01.pphosted.com [148.163.158.5]) by sourceware.org (Postfix) with ESMTPS id 960D94BA2E39 for ; Mon, 5 Oct 2026 09:34:38 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 960D94BA2E39 Authentication-Results: sourceware.org; dmarc=none (p=none dis=none) header.from=linux.ibm.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=linux.ibm.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 960D94BA2E39 Authentication-Results: sourceware.org; arc=none smtp.remote-ip=148.163.158.5 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1791192878; cv=none; b=nb8evcSWNnCxiutw5oASO7wzyNIoKR3y3CeKlG1yCEjRPgemRASRZiX5rdRV6Lw6QZ7SJdgP26i6oEDYxfPVbgR5v8dlJxAsjcJuUc+kZtcYlweGZaWJVLL0M/k3rtYP7YIEzyUmmDLzTRokfCl2Gn1lMem1J42ai7jvxmnYaAQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1791192878; c=relaxed/simple; bh=5SrW+PU10eEfOFqcqceXGSVFH+mjZnYirIfSNx/vuNM=; h=DKIM-Signature:Message-ID:Date:MIME-Version:Subject:From:To; b=BCp1r9jgeYMsWH7j5Yv6h1ciToa13ZBLLUGJGv0c/CKAif3pxV0G9u3sojPgQHNp8HWHu95OdW6L12QvaZPS+9I+EsGD0O3takGE8NbP/YiHZv+jtXRLZ8sJ8d4EDH0QtwiQUEzGNKfaR0lqGOiQTLJGMA832sft9Y+fz1ALzqU= ARC-Authentication-Results: i=1; sourceware.org; dkim=pass (2048-bit key, unprotected) header.d=ibm.com header.i=@ibm.com header.a=rsa-sha256 header.s=pp1 header.b=av7zAZ1K DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 960D94BA2E39 Received: from pps.filterd (m0360072.ppops.net [127.0.0.1]) by mx0a-001b2d01.pphosted.com (8.18.1.11/8.18.1.11) with ESMTP id 69515VAo3290256; Mon, 5 Oct 2026 09:34:27 GMT DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=ibm.com; h=cc :content-type:date:from:in-reply-to:message-id:mime-version :references:subject:to; s=pp1; bh=HFpiNkvWxwptVksCaIe6pj2ZR5+sx4 vf2Jtr+ZbdE7A=; b=av7zAZ1KSgGn32mTDw7f69zRe33OQm8mcIkyf8K3OZLy7X wBtq3XXGIRnFdl6TMvNkQ0nmppJrMZNOFD/VaTMmSpXq/wg+WRO+7Twp3jPqqwSk hMVtIY6Xw782WWEF+KPXd8TVfMN8VvVRQHNzZpQlT73cvC/m/y22j15z6sa9N4PT 5WpQr/lPLr+iZbQNI+KaYnhybB9nq2fFT2Cei7kUpDouk7Jf2Yn9sE3H6Avdyy4o S4XkmMcSsBFmcj+45L3dFlVhwJ01mFLP1+nALVVrbFOalbO+4oVO7+Vl93rKmrTh LRYlCjhqA2C/qvMF07JA5gU36ozdAl+d08u4o6Eg== Received: from ppma22.wdc07v.mail.ibm.com (5c.69.3da9.ip4.static.sl-reverse.com [169.61.105.92]) by mx0a-001b2d01.pphosted.com (PPS) with ESMTPS id 4h2sbv0p2f-1 (version=TLSv1.3 cipher=TLS_AES_256_GCM_SHA384 bits=256 verify=NOT); Mon, 05 Oct 2026 09:34:27 +0000 (GMT) Received: from pps.filterd (ppma22.wdc07v.mail.ibm.com [127.0.0.1]) by ppma22.wdc07v.mail.ibm.com (8.18.1.11/8.18.1.11) with ESMTP id 6956hDUn2163456; Mon, 5 Oct 2026 09:34:26 GMT Received: from smtprelay01.wdc07v.mail.ibm.com ([172.16.1.68]) by ppma22.wdc07v.mail.ibm.com (PPS) with ESMTPS id 4h3cdvmwus-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Mon, 05 Oct 2026 09:34:26 +0000 (GMT) Received: from smtpav03.wdc07v.mail.ibm.com (smtpav03.wdc07v.mail.ibm.com [10.39.53.230]) by smtprelay01.wdc07v.mail.ibm.com (8.14.9/8.14.9/NCO v10.0) with ESMTP id 6959YOPD1639412 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-GCM-SHA384 bits=256 verify=OK); Mon, 5 Oct 2026 09:34:24 GMT Received: from smtpav03.wdc07v.mail.ibm.com (unknown [127.0.0.1]) by IMSVA (Postfix) with ESMTP id 213825807F; Mon, 5 Oct 2026 09:34:24 +0000 (GMT) Received: from smtpav03.wdc07v.mail.ibm.com (unknown [127.0.0.1]) by IMSVA (Postfix) with ESMTP id C31495815F; Mon, 5 Oct 2026 09:34:20 +0000 (GMT) Received: from [9.111.166.184] (unknown [9.111.166.184]) by smtpav03.wdc07v.mail.ibm.com (Postfix) with ESMTP; Mon, 5 Oct 2026 09:34:20 +0000 (GMT) Content-Type: multipart/alternative; boundary="------------4ZlxNPyeS48bU4fMrVK69GwA" Message-ID: <92b8913a-c0c6-4357-891d-851943c6dbe3@linux.ibm.com> Date: Mon, 5 Oct 2026 15:04:18 +0530 MIME-Version: 1.0 User-Agent: Mozilla Thunderbird Subject: Re: [PATCH v2] gdbserver: widen the stat fields returned by the vFile packets From: Abhay Kandpal To: gdb-patches@sourceware.org Cc: aburgess@redhat.com, eliz@gnu.org, cel@linux.ibm.com, abhay.k@ibm.com, Ulrich Weigand References: <20260904083240.3395228-1-abhay@linux.ibm.com> Content-Language: en-GB In-Reply-To: X-TM-AS-GCONF: 00 X-Proofpoint-ORIG-GUID: KUG9lF2TRVLn9pRWFKeE5vztduVbzPrN X-Authority-Analysis: v=2.4 cv=KJHPn1Fo c=1 sm=1 tr=0 ts=6ac36f23 cx=c_pps a=5BHTudwdYE3Te8bg5FgnPg==:117 a=5BHTudwdYE3Te8bg5FgnPg==:17 a=660iZSQnnn4A:10 a=VkNPw1HP01LnGYTKEx00:22 a=RnoormkPH1_aCDwRdu11:22 a=RzCfie-kr_QcCd8fBx8p:22 a=r77TgQKjGQsHNAKrUKIA:9 a=CCpqsmhAAAAA:8 a=mDV3o1hIAAAA:8 a=KA62NkaZKJYPhJKvI1YA:9 a=3ZKOabzyN94A:10 a=QEXdDO2ut3YA:10 a=VnNF1IyMAAAA:8 a=YuWNnxZPnGPgQonBewoA:9 a=CDqzwOBZVeq9ii5F:21 a=_W_S_7VecoQA:10 a=lqcHg5cX4UMA:10 a=ul9cdbp4aOFLsgKbc677:22 X-Proofpoint-GUID: KUG9lF2TRVLn9pRWFKeE5vztduVbzPrN X-Proofpoint-Spam-Details-Enc: AW1haW4tMjYxMDA1MDAzNiBTYWx0ZWRfX4wfSZKrp3mQn XxogymeksOCA6Ha5vsvE8AD3/p2XXdB/3VpOL2KZtLrk9Yu1XNLfWmSvDOFg1z36j1tgG5seZYG BeeZdD8sR8kv6hSTfllsk53ck7/W6u2Vpc9bZbL3jFpHR5a10MNoPKS3nnM+xqCUiiyJxfeM0AD 3LUoT2DVSwnu6vlfyr3ktKv+KNwSxYpSxLCwgEE/mXukKcMsLnA5Zx5qKihWJcEDH4JDY9EOILi Gsab8/KuarcQDg0l2X5Y1y8FQtnHCoh7irWHJvQ6MKEqksxDmXv3nvQljDcdePyi28kWIO/IkYd 1g150a/m6cM/KTwgAUMpsXW1vsf77QdNs/JfhzNz/Hr59HMsf4po6V48burdADtIfENylo6Acqq JVQsE9npdDJHZmo7zVnWXuP+Ybs00ZOZFiPerkdrjGPiUcQtIgVWEctj2e4+EUaMEG/i9J6Hhvu 6hVD3QPx5R6tHE+hiIg== X-Proofpoint-Spam-Info: AW1haW4tMjYxMDA1MDAzNiBTYWx0ZWRfX9UaYzg4NoA9w 8hw4NGS9dnBzzqCYhbfMpjBbA5pf/PBM7ndjPj8H+EuB2I5zJTzu0SwSaLTvckicRRP/e4xx8rb r3uViXMaNLF0GXQL5cC+nAh+194lNqg= X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.293,Aquarius:18.0.1176,Hydra:6.1.134,FMLib:17.12.100.49 definitions=2026-10-05_01,2026-10-02_02,2025-10-01_01 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 suspectscore=0 malwarescore=0 spamscore=0 lowpriorityscore=0 priorityscore=1501 phishscore=0 impostorscore=0 adultscore=0 clxscore=1015 bulkscore=0 classifier=typeunknown authscore=0 authtc= authcc= route=outbound adjust=0 reason=mlx scancount=1 engine=8.22.0-2609040000 definitions=main-2610050036 X-BeenThere: gdb-patches@sourceware.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Gdb-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: gdb-patches-bounces~public-inbox=simark.ca@sourceware.org This is a multi-part message in MIME format. --------------4ZlxNPyeS48bU4fMrVK69GwA Content-Type: text/plain; charset=UTF-8; format=flowed Content-Transfer-Encoding: 7bit Hi Andrew, Ping. Eli has given Reviewed-By on the documentation, with his comments incorporated. The remote protocol side still needs your review. Thanks, Abhay On 16/09/26 18:52, Abhay Kandpal wrote: > Hi Andrew, > Ping. Let me know if the vFile-only scope looks right to you. > Thanks, > Abhay > > > On 04/09/26 14:02, Abhay Kandpal wrote: >> The struct sent by the vFile:fstat, vFile:stat and vFile:lstat packets >> declares st_dev, st_ino, st_nlink, st_rdev and the three timestamps as >> 4-byte fields, while the corresponding struct stat members are 8 bytes >> on a 64-bit host. The values are silently truncated. >> >> This is not visible on a filesystem whose inode numbers stay below >> 2^32, but it is on a large one. On an 86 TB xfs volume, >> gdb.server/fileio-packets.exp fails because the inode reported by >> vFile:lstat does not match the one reported locally: >> >> remote = {..., 'st_ino': 2161233200, ...} >> local = {..., 'st_ino': 122420317488, ...} >> >> The field widths cannot simply be changed, as that would alter the wire >> layout and break communication between different versions of GDB and >> gdbserver. Add a new 'stat64' qSupported feature instead. When both >> sides report it, the vFile stat packets return struct fio_stat_wide, in >> which the six too-narrow fields are 8 bytes; otherwise the existing >> struct fio_stat is used, exactly as before. >> >> Internally both GDB and gdbserver now work with the wide struct, and >> the conversion happens at the wire boundary: gdbserver narrows the >> result before sending it if the feature was not negotiated, and GDB >> selects the unpacker by the length of the reply. Narrowing a value >> which does not fit now produces a warning rather than truncating >> silently. >> >> Only the vFile packets are changed. The F-packet file-i/o protocol >> uses the same struct, but writes it directly into inferior memory, >> where the target reads it as the layout documented in the manual under >> "struct stat"; that layout is left alone. >> >> While here, drop a duplicate include of gdbsupport/fileio.h from >> gdbserver/hostio.cc. >> >> Tested on powerpc64le-linux; gdb.server/fileio-packets.exp now passes on >> a filesystem with inode numbers above 2^32 and continues to pass on one >> without, with no unexpected failures in the full testsuite. >> >> gdb/ >> * NEWS: Mention the stat64 feature. >> * doc/gdb.texinfo (General Query Packets): Document the stat64 >> feature. >> (Host I/O Packets): Mention the wide reply format. >> (struct stat64): New node. >> * remote-fileio.c (remote_fileio_to_host_ulong8): New function. >> (remote_fileio_wide_to_host_stat): New function. >> * remote-fileio.h (remote_fileio_wide_to_host_stat): Declare. >> * remote.c (PACKET_stat64_feature): New enum value. >> (remote_features::stat64_feature): New method. >> (remote_protocol_features): Add stat64. >> (remote_target::remote_query_supported): Send stat64+. >> (fileio_process_fstat_and_lstat_reply): Handle both formats. >> (INIT_GDB_FILE): Add the stat64-feature packet command. >> * testsuite/gdb.server/fileio-packets.py (decode_stat_reply): >> Handle both the narrow and wide reply formats. >> >> gdbserver/ >> * hostio.cc (handle_fstat, handle_stat, handle_lstat): Send the >> wide struct when the feature is negotiated. >> * server.cc (handle_query): Handle and report stat64. >> * server.h (client_state::stat64_feature): New member. >> >> gdbsupport/ >> * fileio.cc (host_to_fileio_stat_wide): New function. >> (narrow_field, fileio_stat_wide_to_narrow): New functions. >> * fileio.h (struct fio_stat_wide): New struct. >> (bigendian_to_host): New function. >> (host_to_fileio_stat_wide, fileio_stat_wide_to_narrow): Declare. >> >> Bug:https://sourceware.org/bugzilla/show_bug.cgi?id=34567 >> Reviewed-By: Eli Zaretskii >> --- >> This patch is reg tested. >> >> gdb/NEWS | 13 +++ >> gdb/doc/gdb.texinfo | 50 ++++++++++ >> gdb/remote-fileio.c | 35 ++++++- >> gdb/remote-fileio.h | 4 + >> gdb/remote.c | 36 +++++-- >> gdb/testsuite/gdb.server/fileio-packets.py | 59 +++++++----- >> gdbserver/hostio.cc | 106 +++++++++++++++------ >> gdbserver/server.cc | 4 +- >> gdbserver/server.h | 5 + >> gdbsupport/fileio.cc | 70 ++++++++++++++ >> gdbsupport/fileio.h | 54 ++++++++++- >> 11 files changed, 370 insertions(+), 66 deletions(-) >> >> diff --git a/gdb/NEWS b/gdb/NEWS >> index 222f38e3d52..7c1a09752a9 100644 >> --- a/gdb/NEWS >> +++ b/gdb/NEWS >> @@ -3,6 +3,19 @@ >> >> *** Changes since GDB 18 >> >> +* Changed remote packets >> + >> +stat64 in qSupported >> + >> + The new stat64 feature within the qSupported packet allows GDB and >> + the stub to agree on a wider form of the stat structure returned by >> + the vFile:fstat, vFile:stat and vFile:lstat packets, in which st_dev, >> + st_ino, st_nlink, st_rdev and the three timestamps are 8 bytes rather >> + than 4. The wider form is used only when both sides report the >> + feature; otherwise the existing format is used and values which do >> + not fit are truncated as before. Note that the narrow form reads >> + timestamps as unsigned, so it overflows in 2106 rather than 2038. >> + >> *** Changes in GDB 18 >> >> * Support for the Common Trace Format (CTF) has been removed. GDB now >> diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo >> index 0030698dcee..355153212f0 100644 >> --- a/gdb/doc/gdb.texinfo >> +++ b/gdb/doc/gdb.texinfo >> @@ -46112,6 +46112,11 @@ inspected, if @samp{qIsAddressTagged} (@pxref{qIsAddressTagged}) packet >> is not supported by the stub. Access to the @file{/proc/@var{pid}/smaps} >> file is done via @samp{vFile} requests. >> >> +@item stat64 >> +The remote stub supports the wider form of the stat structure returned >> +by the @samp{vFile:fstat}, @samp{vFile:stat} and @samp{vFile:lstat} >> +packets (@pxref{struct stat64}). >> + >> @item error-message >> The remote stub supports replying with an error in a >> @samp{E.@var{errtext}} (@xref{textual error reply}) format from the >> @@ -47358,6 +47363,9 @@ On success the information is returned as a binary attachment >> and the return value is the size of this attachment in bytes. >> If an error occurs the return value is -1. The format of the >> returned binary attachment is as described in @ref{struct stat}. >> +If both @value{GDBN} and the stub report the @samp{stat64} feature in >> +the @samp{qSupported} exchange, the wider format is used instead >> +(@pxref{struct stat64}). >> >> @item vFile:stat: @var{filename} >> Get information about the file @var{filename} on the target as if from >> @@ -47365,6 +47373,9 @@ a @samp{stat} call. On success the information is returned as a binary >> attachment and the return value is the size of this attachment in >> bytes. If an error occurs the return value is -1. The format of the >> returned binary attachment is as described in @ref{struct stat}. >> +If both @value{GDBN} and the stub report the @samp{stat64} feature in >> +the @samp{qSupported} exchange, the wider format is used instead >> +(@pxref{struct stat64}). >> >> If @var{filename} is a symbolic link, then the information returned is >> about the file the link refers to, this is inline with the @samp{stat} >> @@ -47376,6 +47387,9 @@ an @samp{lstat} call. On success the information is returned as a >> binary attachment and the return value is the size of this attachment >> in bytes. If an error occurs the return value is -1. The format of >> the returned binary attachment is as described in @ref{struct stat}. >> +If both @value{GDBN} and the stub report the @samp{stat64} feature in >> +the @samp{qSupported} exchange, the wider format is used instead >> +(@pxref{struct stat64}). >> >> This packet is identical to @samp{vFile:stat}, except if >> @var{filename} is a symbolic link, then this packet returns >> @@ -48570,6 +48584,7 @@ protocol. >> * Pointer Values:: >> * Memory Transfer:: >> * struct stat:: >> +* struct stat64:: >> * struct timeval:: >> @end menu >> >> @@ -48696,6 +48711,41 @@ Note that due to size differences between the host, target, and protocol >> representations of @code{struct stat} members, these members could eventually >> get truncated on the target. >> >> +@node struct stat64 >> +@unnumberedsubsubsec struct stat64 >> +@cindex struct stat64, in file-i/o protocol >> + >> +When the @samp{stat64} feature has been negotiated, the buffer returned >> +by the @samp{vFile:fstat}, @samp{vFile:stat} and @samp{vFile:lstat} >> +packets uses the following representation instead: >> + >> +@smallexample >> +struct stat64 @{ >> + uint64_t st_dev; /* device */ >> + uint64_t st_ino; /* inode */ >> + mode_t st_mode; /* protection */ >> + uint64_t st_nlink; /* number of hard links */ >> + unsigned int st_uid; /* user ID of owner */ >> + unsigned int st_gid; /* group ID of owner */ >> + uint64_t st_rdev; /* device type (if inode device) */ >> + uint64_t st_size; /* total size, in bytes */ >> + uint64_t st_blksize; /* blocksize for filesystem I/O */ >> + uint64_t st_blocks; /* number of blocks allocated */ >> + uint64_t st_atime; /* time of last access */ >> + uint64_t st_mtime; /* time of last modification */ >> + uint64_t st_ctime; /* time of last change */ >> +@}; >> +@end smallexample >> + >> +This structure is of size 92 bytes. The fields have the same meanings >> +as in the narrow form (@pxref{struct stat}), but @code{st_dev}, >> +@code{st_ino}, @code{st_nlink}, @code{st_rdev} and the three timestamps >> +are wide enough to hold the values found on a 64-bit host without >> +truncation. >> + >> +This representation is only used by the @samp{vFile} packets; the >> +File-I/O protocol always uses the narrow form (@pxref{struct stat}). >> + >> @node struct timeval >> @unnumberedsubsubsec struct timeval >> @cindex struct timeval, in file-i/o protocol >> diff --git a/gdb/remote-fileio.c b/gdb/remote-fileio.c >> index a151161371d..76deb80d10a 100644 >> --- a/gdb/remote-fileio.c >> +++ b/gdb/remote-fileio.c >> @@ -1220,7 +1220,40 @@ remote_fileio_to_host_stat (struct fio_stat *fst, struct stat *st) >> st->st_mtime = remote_fileio_to_host_time (fst->fst_mtime); >> st->st_ctime = remote_fileio_to_host_time (fst->fst_ctime); >> } >> - >> + >> +/* Unpack an 8-byte protocol field. */ >> + >> +static ULONGEST >> +remote_fileio_to_host_ulong8 (const char *fnum) >> +{ >> + return extract_unsigned_integer ((const gdb_byte *) fnum, 8, >> + BFD_ENDIAN_BIG); >> +} >> + >> +/* See remote-fileio.h. */ >> + >> +void >> +remote_fileio_wide_to_host_stat (struct fio_stat_wide *fst, struct stat *st) >> +{ >> + memset (st, 0, sizeof (struct stat)); >> + st->st_dev = remote_fileio_to_host_ulong8 (fst->fst_dev); >> + st->st_ino = remote_fileio_to_host_ulong8 (fst->fst_ino); >> + st->st_mode = remote_fileio_to_host_mode (fst->fst_mode); >> + st->st_nlink = remote_fileio_to_host_ulong8 (fst->fst_nlink); >> + st->st_uid = remote_fileio_to_host_uint (fst->fst_uid); >> + st->st_gid = remote_fileio_to_host_uint (fst->fst_gid); >> + st->st_rdev = remote_fileio_to_host_ulong8 (fst->fst_rdev); >> + st->st_size = remote_fileio_to_host_ulong8 (fst->fst_size); >> +#ifdef HAVE_STRUCT_STAT_ST_BLKSIZE >> + st->st_blksize = remote_fileio_to_host_ulong8 (fst->fst_blksize); >> +#endif >> +#if HAVE_STRUCT_STAT_ST_BLOCKS >> + st->st_blocks = remote_fileio_to_host_ulong8 (fst->fst_blocks); >> +#endif >> + st->st_atime = remote_fileio_to_host_ulong8 (fst->fst_atime); >> + st->st_mtime = remote_fileio_to_host_ulong8 (fst->fst_mtime); >> + st->st_ctime = remote_fileio_to_host_ulong8 (fst->fst_ctime); >> +} >> >> static void >> set_system_call_allowed (const char *args, int from_tty) >> diff --git a/gdb/remote-fileio.h b/gdb/remote-fileio.h >> index d01b3df92ca..95fb042fc9d 100644 >> --- a/gdb/remote-fileio.h >> +++ b/gdb/remote-fileio.h >> @@ -44,4 +44,8 @@ extern void initialize_remote_fileio ( >> extern void remote_fileio_to_host_stat (struct fio_stat *fst, >> struct stat *st); >> >> +/* Unpack a struct fio_stat_wide. */ >> +extern void remote_fileio_wide_to_host_stat (struct fio_stat_wide *fst, >> + struct stat *st); >> + >> #endif /* GDB_REMOTE_FILEIO_H */ >> diff --git a/gdb/remote.c b/gdb/remote.c >> index 76cf6b5234f..03cb8a074b7 100644 >> --- a/gdb/remote.c >> +++ b/gdb/remote.c >> @@ -389,6 +389,8 @@ enum { >> packets and the tag violation stop replies. */ >> PACKET_memory_tagging_feature, >> >> + PACKET_stat64_feature, >> + >> /* Support for the qIsAddressTagged packet. */ >> PACKET_qIsAddressTagged, >> >> @@ -844,6 +846,9 @@ struct remote_features >> bool remote_memory_tagging_p () const >> { return packet_support (PACKET_memory_tagging_feature) == PACKET_ENABLE; } >> >> + bool stat64_feature () const >> + { return packet_support (PACKET_stat64_feature) == PACKET_ENABLE; } >> + >> /* Returns true if there is support for sending vRun inferior arguments >> as a single string. */ >> bool remote_vrun_single_arg_p () const >> @@ -6199,6 +6204,8 @@ static const struct protocol_feature remote_protocol_features[] = { >> { "no-resumed", PACKET_DISABLE, remote_supported_packet, PACKET_no_resumed }, >> { "memory-tagging", PACKET_DISABLE, remote_supported_packet, >> PACKET_memory_tagging_feature }, >> + { "stat64", PACKET_DISABLE, remote_supported_packet, >> + PACKET_stat64_feature }, >> { "error-message", PACKET_ENABLE, remote_supported_packet, >> PACKET_accept_error_message }, >> { "binary-upload", PACKET_DISABLE, remote_supported_packet, PACKET_x }, >> @@ -6309,6 +6316,10 @@ remote_target::remote_query_supported () >> != AUTO_BOOLEAN_FALSE) >> remote_query_supported_append (&q, "QThreadOptions+"); >> >> + if (m_features.packet_set_cmd_state (PACKET_stat64_feature) >> + != AUTO_BOOLEAN_FALSE) >> + remote_query_supported_append (&q, "stat64+"); >> + >> if (m_features.packet_set_cmd_state (PACKET_no_resumed) >> != AUTO_BOOLEAN_FALSE) >> remote_query_supported_append (&q, "no-resumed+"); @@ -13663,29 +13674,35 @@ remote_target::fileio_readlink (struct >> inferior *inf, const char *filename, remote syscall reply. >> EXPECTED_LEN is the length returned from the fstat or stat call, this >> the length of the returned data (in ATTACHMENT) once it has been >> decoded. The fstat/stat result (from the ATTACHMENT - data) is to be >> placed in ST. */ + data) is to be placed in ST. + The reply holds >> either a struct fio_stat or, if the stat64 feature was + negotiated, >> the wider struct fio_stat_wide; the two are distinguished + by their >> length. */ static int fileio_process_fstat_and_lstat_reply (const >> char *attachment, int attachment_len, int expected_len, struct stat >> *st) { - struct fio_stat fst; + gdb_byte buf[sizeof (struct >> fio_stat_wide)]; int read_len = remote_unescape_input ((gdb_byte *) >> attachment, attachment_len, - (gdb_byte *) &fst, sizeof (fst)); + >> buf, sizeof (buf)); if (read_len != expected_len) error (_("vFile:fstat returned %d, but %d bytes."), >> expected_len, read_len); >> >> - if (read_len != sizeof (fst)) >> - error (_("vFile:fstat returned %d bytes, but expecting %d."), >> - read_len, (int) sizeof (fst)); >> - >> - remote_fileio_to_host_stat (&fst, st); >> + if (read_len == sizeof (struct fio_stat_wide)) >> + remote_fileio_wide_to_host_stat ((struct fio_stat_wide *) buf, st); >> + else if (read_len == sizeof (struct fio_stat)) >> + remote_fileio_to_host_stat ((struct fio_stat *) buf, st); >> + else >> + error (_("vFile:fstat returned %d bytes, but expecting %d or %d."), + read_len, (int) sizeof (struct fio_stat), + (int) sizeof >> (struct fio_stat_wide)); return 0; } @@ -17039,6 +17056,9 @@ Show the >> maximum size of the address (in bits) in a memory packet."), NULL, >> add_packet_config_cmd (PACKET_memory_tagging_feature, >> "memory-tagging-feature", "memory-tagging-feature", 0); >> >> + add_packet_config_cmd (PACKET_stat64_feature, >> + "stat64-feature", "stat64-feature", 0); >> + >> add_packet_config_cmd (PACKET_qIsAddressTagged, >> "qIsAddressTagged", "memory-tagging-address-check", 0); >> >> diff --git a/gdb/testsuite/gdb.server/fileio-packets.py b/gdb/testsuite/gdb.server/fileio-packets.py >> index 54ae56fa68a..2d06edcdbf7 100644 >> --- a/gdb/testsuite/gdb.server/fileio-packets.py >> +++ b/gdb/testsuite/gdb.server/fileio-packets.py >> @@ -57,33 +57,48 @@ def decode_stat_reply(buf, byteorder="big"): >> >> buf = unescape_remote_data(buf) >> >> - if ( >> - buf[0] != ord("F") >> - or buf[1] != ord("4") >> - or buf[2] != ord("0") >> - or buf[3] != ord(";") >> - or len(buf) != 68 >> - ): >> + if buf[0:4] == b"F40;" and len(buf) == 68: >> + wide = False >> + elif buf[0:4] == b"F5c;" and len(buf) == 96: >> + wide = True >> + else: >> print(f"decode_stat_reply failed: {buf}\t(length = {len(buf)})") >> return None >> >> - # Discard the 'F40;' prefix. The rest is the 64 bytes of data to >> - # be decoded. >> + # Discard the 'FNN;' prefix. >> buf = buf[4:] >> >> - st_dev = int.from_bytes(buf[0:4], byteorder=byteorder) >> - st_ino = int.from_bytes(buf[4:8], byteorder=byteorder) >> - st_mode = int.from_bytes(buf[8:12], byteorder=byteorder) >> - st_nlink = int.from_bytes(buf[12:16], byteorder=byteorder) >> - st_uid = int.from_bytes(buf[16:20], byteorder=byteorder) >> - st_gid = int.from_bytes(buf[20:24], byteorder=byteorder) >> - st_rdev = int.from_bytes(buf[24:28], byteorder=byteorder) >> - st_size = int.from_bytes(buf[28:36], byteorder=byteorder) >> - st_blksize = int.from_bytes(buf[36:44], byteorder=byteorder) >> - st_blocks = int.from_bytes(buf[44:52], byteorder=byteorder) >> - st_atime = int.from_bytes(buf[52:56], byteorder=byteorder) >> - st_mtime = int.from_bytes(buf[56:60], byteorder=byteorder) >> - st_ctime = int.from_bytes(buf[60:64], byteorder=byteorder) >> + def field(off, size): >> + return int.from_bytes(buf[off : off + size], byteorder=byteorder) >> + >> + if wide: >> + st_dev = field(0, 8) >> + st_ino = field(8, 8) >> + st_mode = field(16, 4) >> + st_nlink = field(20, 8) >> + st_uid = field(28, 4) >> + st_gid = field(32, 4) >> + st_rdev = field(36, 8) >> + st_size = field(44, 8) >> + st_blksize = field(52, 8) >> + st_blocks = field(60, 8) >> + st_atime = field(68, 8) >> + st_mtime = field(76, 8) >> + st_ctime = field(84, 8) >> + else: >> + st_dev = field(0, 4) >> + st_ino = field(4, 4) >> + st_mode = field(8, 4) >> + st_nlink = field(12, 4) >> + st_uid = field(16, 4) >> + st_gid = field(20, 4) >> + st_rdev = field(24, 4) >> + st_size = field(28, 8) >> + st_blksize = field(36, 8) >> + st_blocks = field(44, 8) >> + st_atime = field(52, 4) >> + st_mtime = field(56, 4) >> + st_ctime = field(60, 4) >> >> return { >> "st_dev": st_dev, >> diff --git a/gdbserver/hostio.cc b/gdbserver/hostio.cc >> index b9ca27c2ff4..34def5d22da 100644 >> --- a/gdbserver/hostio.cc >> +++ b/gdbserver/hostio.cc >> @@ -26,7 +26,6 @@ >> #include >> #include >> #include >> -#include "gdbsupport/fileio.h" >> >> struct fd_list >> { >> @@ -465,7 +464,7 @@ handle_fstat (char *own_buf, int *new_packet_len) >> int fd, bytes_sent; >> char *p; >> struct stat st; >> - struct fio_stat fst; >> + client_state &cs = get_client_state (); >> >> p = own_buf + strlen ("vFile:fstat:"); >> >> @@ -483,16 +482,31 @@ handle_fstat (char *own_buf, int *new_packet_len) >> return; >> } >> >> - host_to_fileio_stat (&st, &fst); >> - >> - bytes_sent = hostio_reply_with_data (own_buf, >> - (char *) &fst, sizeof (fst), >> - new_packet_len); >> + struct fio_stat_wide wide; >> + host_to_fileio_stat_wide (&st, &wide); >> >> - /* If the response does not fit into a single packet, do not attempt >> - to return a partial response, but simply fail. */ >> - if (bytes_sent < sizeof (fst)) >> - write_enn (own_buf); >> + /* Send the wide form if GDB understands it, otherwise narrow the >> + result first. */ >> + if (cs.stat64_feature) >> + { >> + bytes_sent = hostio_reply_with_data (own_buf, (char *) &wide, >> + sizeof (wide), new_packet_len); >> + /* If the response does not fit into a single packet, do not attempt >> + to return a partial response, but simply fail. */ >> + if (bytes_sent < sizeof (wide)) >> + write_enn (own_buf); >> + } >> + else >> + { >> + struct fio_stat narrow; >> + fileio_stat_wide_to_narrow (&wide, &narrow); >> + bytes_sent = hostio_reply_with_data (own_buf, (char *) &narrow, >> + sizeof (narrow), new_packet_len); >> + /* If the response does not fit into a single packet, do not attempt >> + to return a partial response, but simply fail. */ >> + if (bytes_sent < sizeof (narrow)) >> + write_enn (own_buf); >> + } >> } >> >> static void >> @@ -501,8 +515,8 @@ handle_stat (char *own_buf, int *new_packet_len) >> int bytes_sent; >> char *p; >> struct stat st; >> - struct fio_stat fst; >> char filename[HOSTIO_PATH_MAX]; >> + client_state &cs = get_client_state (); >> >> p = own_buf + strlen ("vFile:stat:"); >> >> @@ -519,16 +533,31 @@ handle_stat (char *own_buf, int *new_packet_len) >> return; >> } >> >> - host_to_fileio_stat (&st, &fst); >> - >> - bytes_sent = hostio_reply_with_data (own_buf, >> - (char *) &fst, sizeof (fst), >> - new_packet_len); >> + struct fio_stat_wide wide; >> + host_to_fileio_stat_wide (&st, &wide); >> >> - /* If the response does not fit into a single packet, do not attempt >> - to return a partial response, but simply fail. */ >> - if (bytes_sent < sizeof (fst)) >> - write_enn (own_buf); >> + /* Send the wide form if GDB understands it, otherwise narrow the >> + result first. */ >> + if (cs.stat64_feature) >> + { >> + bytes_sent = hostio_reply_with_data (own_buf, (char *) &wide, >> + sizeof (wide), new_packet_len); >> + /* If the response does not fit into a single packet, do not attempt >> + to return a partial response, but simply fail. */ >> + if (bytes_sent < sizeof (wide)) >> + write_enn (own_buf); >> + } >> + else >> + { >> + struct fio_stat narrow; >> + fileio_stat_wide_to_narrow (&wide, &narrow); >> + bytes_sent = hostio_reply_with_data (own_buf, (char *) &narrow, >> + sizeof (narrow), new_packet_len); >> + /* If the response does not fit into a single packet, do not attempt >> + to return a partial response, but simply fail. */ >> + if (bytes_sent < sizeof (narrow)) >> + write_enn (own_buf); >> + } >> } >> >> static void >> @@ -537,8 +566,8 @@ handle_lstat (char *own_buf, int *new_packet_len) >> int ret, bytes_sent; >> char *p; >> struct stat st; >> - struct fio_stat fst; >> char filename[HOSTIO_PATH_MAX]; >> + client_state &cs = get_client_state (); >> >> p = own_buf + strlen ("vFile:lstat:"); >> >> @@ -560,16 +589,31 @@ handle_lstat (char *own_buf, int *new_packet_len) >> return; >> } >> >> - host_to_fileio_stat (&st, &fst); >> - >> - bytes_sent = hostio_reply_with_data (own_buf, >> - (char *) &fst, sizeof (fst), >> - new_packet_len); >> + struct fio_stat_wide wide; >> + host_to_fileio_stat_wide (&st, &wide); >> >> - /* If the response does not fit into a single packet, do not attempt >> - to return a partial response, but simply fail. */ >> - if (bytes_sent < sizeof (fst)) >> - write_enn (own_buf); >> + /* Send the wide form if GDB understands it, otherwise narrow the >> + result first. */ >> + if (cs.stat64_feature) >> + { >> + bytes_sent = hostio_reply_with_data (own_buf, (char *) &wide, >> + sizeof (wide), new_packet_len); >> + /* If the response does not fit into a single packet, do not attempt >> + to return a partial response, but simply fail. */ >> + if (bytes_sent < sizeof (wide)) >> + write_enn (own_buf); >> + } >> + else >> + { >> + struct fio_stat narrow; >> + fileio_stat_wide_to_narrow (&wide, &narrow); >> + bytes_sent = hostio_reply_with_data (own_buf, (char *) &narrow, >> + sizeof (narrow), new_packet_len); >> + /* If the response does not fit into a single packet, do not attempt >> + to return a partial response, but simply fail. */ >> + if (bytes_sent < sizeof (narrow)) >> + write_enn (own_buf); >> + } >> } >> >> static void >> diff --git a/gdbserver/server.cc b/gdbserver/server.cc >> index a208e24d09f..dfa24617847 100644 >> --- a/gdbserver/server.cc >> +++ b/gdbserver/server.cc >> @@ -2746,6 +2746,8 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p) >> cs.single_inferior_argument = true; >> else if (feature == "multi-wp-addr+") >> cs.multiple_wp_addr_feature = true; >> + else if (feature == "stat64+") >> + cs.stat64_feature = true; >> else >> { >> /* Move the unknown features all together. */ >> @@ -2762,7 +2764,7 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p) >> "PacketSize=%x;QPassSignals+;QProgramSignals+;" >> "QStartupWithShell+;QEnvironmentHexEncoded+;" >> "QEnvironmentReset+;QEnvironmentUnset+;" >> - "QSetWorkingDir+;binary-upload+", >> + "QSetWorkingDir+;binary-upload+;stat64+", PBUFSIZ - 1); if (target_supports_catch_syscall ()) diff --git >> a/gdbserver/server.h b/gdbserver/server.h index >> bbba3650de7..cf811f228fa 100644 --- a/gdbserver/server.h +++ >> b/gdbserver/server.h @@ -192,6 +192,11 @@ struct client_state /* If >> true, memory tagging features are supported. */ bool >> memory_tagging_feature = false; + /* True if GDB supports the wide >> form of struct stat in the vFile + fstat, stat and lstat replies. >> When false, gdbserver narrows the + result to struct fio_stat before >> sending it. */ + bool stat64_feature = false; + /* If true then >> E.errtext style errors are supported everywhere, including for the >> qRcmd and m packet. When false E.errtext errors are not supported >> with qRcmd and m packets, but are still supported diff --git >> a/gdbsupport/fileio.cc b/gdbsupport/fileio.cc index >> 6d2ffb14544..bab2c4c8efe 100644 --- a/gdbsupport/fileio.cc +++ >> b/gdbsupport/fileio.cc @@ -20,6 +20,7 @@ #include "fileio.h" >> #include >> #include >> +#include "gdbsupport/errors.h" >> >> /* See fileio.h. */ >> >> @@ -305,3 +306,72 @@ host_to_fileio_stat (struct stat *st, struct fio_stat *fst) >> host_to_fileio_time (st->st_mtime, fst->fst_mtime); >> host_to_fileio_time (st->st_ctime, fst->fst_ctime); >> } >> + >> +/* See fileio.h. */ >> + >> +void >> +host_to_fileio_stat_wide (struct stat *st, struct fio_stat_wide *fst) >> +{ >> + LONGEST blksize; >> + >> + host_to_bigendian ((LONGEST) st->st_dev, (char *) fst->fst_dev, 8); >> + host_to_bigendian ((LONGEST) st->st_ino, (char *) fst->fst_ino, 8); >> + host_to_fileio_mode (st->st_mode, fst->fst_mode); >> + host_to_bigendian ((LONGEST) st->st_nlink, (char *) fst->fst_nlink, 8); >> + host_to_fileio_uint ((long) st->st_uid, fst->fst_uid); >> + host_to_fileio_uint ((long) st->st_gid, fst->fst_gid); >> + host_to_bigendian ((LONGEST) st->st_rdev, (char *) fst->fst_rdev, 8); >> + host_to_bigendian ((LONGEST) st->st_size, (char *) fst->fst_size, 8); >> +#ifdef HAVE_STRUCT_STAT_ST_BLKSIZE >> + blksize = st->st_blksize; >> +#else >> + blksize = 512; >> +#endif >> + host_to_bigendian (blksize, (char *) fst->fst_blksize, 8); >> +#if HAVE_STRUCT_STAT_ST_BLOCKS >> + host_to_bigendian ((LONGEST) st->st_blocks, (char *) fst->fst_blocks, 8); >> +#else >> + host_to_bigendian (((LONGEST) st->st_size + blksize - 1) / blksize, >> + (char *) fst->fst_blocks, 8); >> +#endif >> + host_to_bigendian ((LONGEST) st->st_atime, (char *) fst->fst_atime, 8); >> + host_to_bigendian ((LONGEST) st->st_mtime, (char *) fst->fst_mtime, 8); >> + host_to_bigendian ((LONGEST) st->st_ctime, (char *) fst->fst_ctime, 8); >> +} >> + >> +/* Copy the 8-byte value in FROM into the 4-byte field TO, warning if >> + the value does not fit. WHAT names the field, for the warning. */ >> + >> +static void >> +narrow_field (const char *from, char *to, const char *what) >> +{ >> + ULONGEST num = bigendian_to_host (from, 8); >> + >> + if (num != (ULONGEST) (uint32_t) num) >> + warning (_("file-i/o: %s value %s does not fit in 32 bits; " >> + "the value seen by the peer will be truncated"), >> + what, pulongest (num)); >> + >> + host_to_bigendian ((LONGEST) num, to, 4); >> +} >> + >> +/* See fileio.h. */ >> + >> +void >> +fileio_stat_wide_to_narrow (const struct fio_stat_wide *wide, >> + struct fio_stat *narrow) >> +{ >> + narrow_field (wide->fst_dev, narrow->fst_dev, "st_dev"); >> + narrow_field (wide->fst_ino, narrow->fst_ino, "st_ino"); >> + memcpy (narrow->fst_mode, wide->fst_mode, 4); >> + narrow_field (wide->fst_nlink, narrow->fst_nlink, "st_nlink"); >> + memcpy (narrow->fst_uid, wide->fst_uid, 4); >> + memcpy (narrow->fst_gid, wide->fst_gid, 4); >> + narrow_field (wide->fst_rdev, narrow->fst_rdev, "st_rdev"); >> + memcpy (narrow->fst_size, wide->fst_size, 8); >> + memcpy (narrow->fst_blksize, wide->fst_blksize, 8); >> + memcpy (narrow->fst_blocks, wide->fst_blocks, 8); >> + narrow_field (wide->fst_atime, narrow->fst_atime, "st_atime"); >> + narrow_field (wide->fst_mtime, narrow->fst_mtime, "st_mtime"); >> + narrow_field (wide->fst_ctime, narrow->fst_ctime, "st_ctime"); >> +} >> diff --git a/gdbsupport/fileio.h b/gdbsupport/fileio.h >> index c2592cf8c15..fa5de54dd40 100644 >> --- a/gdbsupport/fileio.h >> +++ b/gdbsupport/fileio.h >> @@ -120,9 +120,11 @@ using fio_time_t = char[FIO_TIME_LEN]; >> using fio_long_t = char[FIO_LONG_LEN]; >> using fio_ulong_t = char[FIO_ULONG_LEN]; >> >> -/* Struct stat as used in protocol. For complete independence >> - of host/target systems, it's defined as an array with offsets >> - to the members. */ >> +/* Struct stat as used in the protocol. This is the format used by the >> + F-packet file-i/o protocol, and by the vFile stat packets when the >> + stat64 feature has not been negotiated. Several fields are too narrow >> + to hold the corresponding struct stat member on a 64-bit host; see >> + struct fio_stat_wide. */ >> >> struct fio_stat >> { >> @@ -141,6 +143,29 @@ struct fio_stat >> fio_time_t fst_ctime; >> }; >> >> +/* Struct stat as used in the wide form of the protocol. The fields >> + which cannot hold the corresponding struct stat member on a 64-bit >> + host are widened to 8 bytes. This layout is only used by the vFile >> + stat packets, and only when the stat64 feature has been negotiated >> + with the peer. */ >> + >> +struct fio_stat_wide >> +{ >> + fio_ulong_t fst_dev; >> + fio_ulong_t fst_ino; >> + fio_mode_t fst_mode; >> + fio_ulong_t fst_nlink; >> + fio_uint_t fst_uid; >> + fio_uint_t fst_gid; >> + fio_ulong_t fst_rdev; >> + fio_ulong_t fst_size; >> + fio_ulong_t fst_blksize; >> + fio_ulong_t fst_blocks; >> + fio_ulong_t fst_atime; >> + fio_ulong_t fst_mtime; >> + fio_ulong_t fst_ctime; >> +}; >> + >> struct fio_timeval >> { >> fio_time_t ftv_sec; >> @@ -179,6 +204,20 @@ host_to_bigendian (LONGEST num, char *buf, int bytes) >> buf[i] = (num >> (8 * (bytes - i - 1))) & 0xff; >> } >> >> +/* Unpack an integer of BYTES bytes from BUF, which holds it in >> + big-endian, protocol format. */ >> + >> +static inline ULONGEST >> +bigendian_to_host (const char *buf, int bytes) >> +{ >> + ULONGEST num = 0; >> + int i; >> + >> + for (i = 0; i < bytes; ++i) >> + num = (num << 8) | (unsigned char) buf[i]; >> + return num; >> +} >> + >> /* Pack a host-format integer into an fio_uint_t. */ >> >> static inline void >> @@ -199,4 +238,13 @@ host_to_fileio_time (time_t num, fio_time_t fnum) >> >> extern void host_to_fileio_stat (struct stat *st, struct fio_stat *fst); >> >> +/* Pack a host-format struct stat into a struct fio_stat_wide. */ >> +extern void host_to_fileio_stat_wide (struct stat *st, >> + struct fio_stat_wide *fst); >> + >> +/* Narrow a struct fio_stat_wide into a struct fio_stat, warning if any >> + value does not fit. */ >> +extern void fileio_stat_wide_to_narrow (const struct fio_stat_wide *wide, >> + struct fio_stat *narrow); >> + >> #endif /* GDBSUPPORT_FILEIO_H */ --------------4ZlxNPyeS48bU4fMrVK69GwA Content-Type: text/html; charset=UTF-8 Content-Transfer-Encoding: 7bit
Hi Andrew, 

Ping. 

Eli has given Reviewed-By on the documentation, with his comments incorporated.
The remote protocol side still needs your review.

Thanks,
Abhay

On 16/09/26 18:52, Abhay Kandpal wrote:
Hi Andrew,
Ping.  Let me know if the vFile-only scope looks right to you.
Thanks,
Abhay


On 04/09/26 14:02, Abhay Kandpal wrote:
The struct sent by the vFile:fstat, vFile:stat and vFile:lstat packets
declares st_dev, st_ino, st_nlink, st_rdev and the three timestamps as
4-byte fields, while the corresponding struct stat members are 8 bytes
on a 64-bit host.  The values are silently truncated.

This is not visible on a filesystem whose inode numbers stay below
2^32, but it is on a large one.  On an 86 TB xfs volume,
gdb.server/fileio-packets.exp fails because the inode reported by
vFile:lstat does not match the one reported locally:

  remote = {..., 'st_ino': 2161233200, ...}
  local  = {..., 'st_ino': 122420317488, ...}

The field widths cannot simply be changed, as that would alter the wire
layout and break communication between different versions of GDB and
gdbserver.  Add a new 'stat64' qSupported feature instead.  When both
sides report it, the vFile stat packets return struct fio_stat_wide, in
which the six too-narrow fields are 8 bytes; otherwise the existing
struct fio_stat is used, exactly as before.

Internally both GDB and gdbserver now work with the wide struct, and
the conversion happens at the wire boundary: gdbserver narrows the
result before sending it if the feature was not negotiated, and GDB
selects the unpacker by the length of the reply.  Narrowing a value
which does not fit now produces a warning rather than truncating
silently.

Only the vFile packets are changed.  The F-packet file-i/o protocol
uses the same struct, but writes it directly into inferior memory,
where the target reads it as the layout documented in the manual under
"struct stat"; that layout is left alone.

While here, drop a duplicate include of gdbsupport/fileio.h from
gdbserver/hostio.cc.

Tested on powerpc64le-linux; gdb.server/fileio-packets.exp now passes on
a filesystem with inode numbers above 2^32 and continues to pass on one
without, with no unexpected failures in the full testsuite.

gdb/
	* NEWS: Mention the stat64 feature.
	* doc/gdb.texinfo (General Query Packets): Document the stat64
	feature.
	(Host I/O Packets): Mention the wide reply format.
	(struct stat64): New node.
	* remote-fileio.c (remote_fileio_to_host_ulong8): New function.
	(remote_fileio_wide_to_host_stat): New function.
	* remote-fileio.h (remote_fileio_wide_to_host_stat): Declare.
	* remote.c (PACKET_stat64_feature): New enum value.
	(remote_features::stat64_feature): New method.
	(remote_protocol_features): Add stat64.
	(remote_target::remote_query_supported): Send stat64+.
	(fileio_process_fstat_and_lstat_reply): Handle both formats.
	(INIT_GDB_FILE): Add the stat64-feature packet command.
	* testsuite/gdb.server/fileio-packets.py (decode_stat_reply):
	Handle both the narrow and wide reply formats.

gdbserver/
	* hostio.cc (handle_fstat, handle_stat, handle_lstat): Send the
	wide struct when the feature is negotiated.
	* server.cc (handle_query): Handle and report stat64.
	* server.h (client_state::stat64_feature): New member.

gdbsupport/
	* fileio.cc (host_to_fileio_stat_wide): New function.
	(narrow_field, fileio_stat_wide_to_narrow): New functions.
	* fileio.h (struct fio_stat_wide): New struct.
	(bigendian_to_host): New function.
	(host_to_fileio_stat_wide, fileio_stat_wide_to_narrow): Declare.

Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=34567
Reviewed-By: Eli Zaretskii <eliz@gnu.org>
---
This patch is reg tested.

 gdb/NEWS                                   |  13 +++
 gdb/doc/gdb.texinfo                        |  50 ++++++++++
 gdb/remote-fileio.c                        |  35 ++++++-
 gdb/remote-fileio.h                        |   4 +
 gdb/remote.c                               |  36 +++++--
 gdb/testsuite/gdb.server/fileio-packets.py |  59 +++++++-----
 gdbserver/hostio.cc                        | 106 +++++++++++++++------
 gdbserver/server.cc                        |   4 +-
 gdbserver/server.h                         |   5 +
 gdbsupport/fileio.cc                       |  70 ++++++++++++++
 gdbsupport/fileio.h                        |  54 ++++++++++-
 11 files changed, 370 insertions(+), 66 deletions(-)

diff --git a/gdb/NEWS b/gdb/NEWS
index 222f38e3d52..7c1a09752a9 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -3,6 +3,19 @@
 
 *** Changes since GDB 18
 
+* Changed remote packets
+
+stat64 in qSupported
+
+  The new stat64 feature within the qSupported packet allows GDB and
+  the stub to agree on a wider form of the stat structure returned by
+  the vFile:fstat, vFile:stat and vFile:lstat packets, in which st_dev,
+  st_ino, st_nlink, st_rdev and the three timestamps are 8 bytes rather
+  than 4.  The wider form is used only when both sides report the
+  feature; otherwise the existing format is used and values which do
+  not fit are truncated as before.  Note that the narrow form reads
+  timestamps as unsigned, so it overflows in 2106 rather than 2038.
+
 *** Changes in GDB 18
 
 * Support for the Common Trace Format (CTF) has been removed.  GDB now
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 0030698dcee..355153212f0 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -46112,6 +46112,11 @@ inspected, if @samp{qIsAddressTagged} (@pxref{qIsAddressTagged}) packet
 is not supported by the stub.  Access to the @file{/proc/@var{pid}/smaps}
 file is done via @samp{vFile} requests.
 
+@item stat64
+The remote stub supports the wider form of the stat structure returned
+by the @samp{vFile:fstat}, @samp{vFile:stat} and @samp{vFile:lstat}
+packets (@pxref{struct stat64}).
+
 @item error-message
 The remote stub supports replying with an error in a
 @samp{E.@var{errtext}} (@xref{textual error reply}) format from the
@@ -47358,6 +47363,9 @@ On success the information is returned as a binary attachment
 and the return value is the size of this attachment in bytes.
 If an error occurs the return value is -1.  The format of the
 returned binary attachment is as described in @ref{struct stat}.
+If both @value{GDBN} and the stub report the @samp{stat64} feature in
+the @samp{qSupported} exchange, the wider format is used instead
+(@pxref{struct stat64}).
 
 @item vFile:stat: @var{filename}
 Get information about the file @var{filename} on the target as if from
@@ -47365,6 +47373,9 @@ a @samp{stat} call.  On success the information is returned as a binary
 attachment and the return value is the size of this attachment in
 bytes.  If an error occurs the return value is -1.  The format of the
 returned binary attachment is as described in @ref{struct stat}.
+If both @value{GDBN} and the stub report the @samp{stat64} feature in
+the @samp{qSupported} exchange, the wider format is used instead
+(@pxref{struct stat64}).
 
 If @var{filename} is a symbolic link, then the information returned is
 about the file the link refers to, this is inline with the @samp{stat}
@@ -47376,6 +47387,9 @@ an @samp{lstat} call.  On success the information is returned as a
 binary attachment and the return value is the size of this attachment
 in bytes.  If an error occurs the return value is -1.  The format of
 the returned binary attachment is as described in @ref{struct stat}.
+If both @value{GDBN} and the stub report the @samp{stat64} feature in
+the @samp{qSupported} exchange, the wider format is used instead
+(@pxref{struct stat64}).
 
 This packet is identical to @samp{vFile:stat}, except if
 @var{filename} is a symbolic link, then this packet returns
@@ -48570,6 +48584,7 @@ protocol.
 * Pointer Values::
 * Memory Transfer::
 * struct stat::
+* struct stat64::
 * struct timeval::
 @end menu
 
@@ -48696,6 +48711,41 @@ Note that due to size differences between the host, target, and protocol
 representations of @code{struct stat} members, these members could eventually
 get truncated on the target.
 
+@node struct stat64
+@unnumberedsubsubsec struct stat64
+@cindex struct stat64, in file-i/o protocol
+
+When the @samp{stat64} feature has been negotiated, the buffer returned
+by the @samp{vFile:fstat}, @samp{vFile:stat} and @samp{vFile:lstat}
+packets uses the following representation instead:
+
+@smallexample
+struct stat64 @{
+    uint64_t      st_dev;      /* device */
+    uint64_t      st_ino;      /* inode */
+    mode_t        st_mode;     /* protection */
+    uint64_t      st_nlink;    /* number of hard links */
+    unsigned int  st_uid;      /* user ID of owner */
+    unsigned int  st_gid;      /* group ID of owner */
+    uint64_t      st_rdev;     /* device type (if inode device) */
+    uint64_t      st_size;     /* total size, in bytes */
+    uint64_t      st_blksize;  /* blocksize for filesystem I/O */
+    uint64_t      st_blocks;   /* number of blocks allocated */
+    uint64_t      st_atime;    /* time of last access */
+    uint64_t      st_mtime;    /* time of last modification */
+    uint64_t      st_ctime;    /* time of last change */
+@};
+@end smallexample
+
+This structure is of size 92 bytes.  The fields have the same meanings
+as in the narrow form (@pxref{struct stat}), but @code{st_dev},
+@code{st_ino}, @code{st_nlink}, @code{st_rdev} and the three timestamps
+are wide enough to hold the values found on a 64-bit host without
+truncation.
+
+This representation is only used by the @samp{vFile} packets; the
+File-I/O protocol always uses the narrow form (@pxref{struct stat}).
+
 @node struct timeval
 @unnumberedsubsubsec struct timeval
 @cindex struct timeval, in file-i/o protocol
diff --git a/gdb/remote-fileio.c b/gdb/remote-fileio.c
index a151161371d..76deb80d10a 100644
--- a/gdb/remote-fileio.c
+++ b/gdb/remote-fileio.c
@@ -1220,7 +1220,40 @@ remote_fileio_to_host_stat (struct fio_stat *fst, struct stat *st)
   st->st_mtime = remote_fileio_to_host_time (fst->fst_mtime);
   st->st_ctime = remote_fileio_to_host_time (fst->fst_ctime);
 }
-
+
+/* Unpack an 8-byte protocol field.  */
+
+static ULONGEST
+remote_fileio_to_host_ulong8 (const char *fnum)
+{
+  return extract_unsigned_integer ((const gdb_byte *) fnum, 8,
+				   BFD_ENDIAN_BIG);
+}
+
+/* See remote-fileio.h.  */
+
+void
+remote_fileio_wide_to_host_stat (struct fio_stat_wide *fst, struct stat *st)
+{
+  memset (st, 0, sizeof (struct stat));
+  st->st_dev = remote_fileio_to_host_ulong8 (fst->fst_dev);
+  st->st_ino = remote_fileio_to_host_ulong8 (fst->fst_ino);
+  st->st_mode = remote_fileio_to_host_mode (fst->fst_mode);
+  st->st_nlink = remote_fileio_to_host_ulong8 (fst->fst_nlink);
+  st->st_uid = remote_fileio_to_host_uint (fst->fst_uid);
+  st->st_gid = remote_fileio_to_host_uint (fst->fst_gid);
+  st->st_rdev = remote_fileio_to_host_ulong8 (fst->fst_rdev);
+  st->st_size = remote_fileio_to_host_ulong8 (fst->fst_size);
+#ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
+  st->st_blksize = remote_fileio_to_host_ulong8 (fst->fst_blksize);
+#endif
+#if HAVE_STRUCT_STAT_ST_BLOCKS
+  st->st_blocks = remote_fileio_to_host_ulong8 (fst->fst_blocks);
+#endif
+  st->st_atime = remote_fileio_to_host_ulong8 (fst->fst_atime);
+  st->st_mtime = remote_fileio_to_host_ulong8 (fst->fst_mtime);
+  st->st_ctime = remote_fileio_to_host_ulong8 (fst->fst_ctime);
+}
 
 static void
 set_system_call_allowed (const char *args, int from_tty)
diff --git a/gdb/remote-fileio.h b/gdb/remote-fileio.h
index d01b3df92ca..95fb042fc9d 100644
--- a/gdb/remote-fileio.h
+++ b/gdb/remote-fileio.h
@@ -44,4 +44,8 @@ extern void initialize_remote_fileio (
 extern void remote_fileio_to_host_stat (struct fio_stat *fst,
 					struct stat *st);
 
+/* Unpack a struct fio_stat_wide.  */
+extern void remote_fileio_wide_to_host_stat (struct fio_stat_wide *fst,
+					     struct stat *st);
+
 #endif /* GDB_REMOTE_FILEIO_H */
diff --git a/gdb/remote.c b/gdb/remote.c
index 76cf6b5234f..03cb8a074b7 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -389,6 +389,8 @@ enum {
      packets and the tag violation stop replies.  */
   PACKET_memory_tagging_feature,
 
+  PACKET_stat64_feature,
+
   /* Support for the qIsAddressTagged packet.  */
   PACKET_qIsAddressTagged,
 
@@ -844,6 +846,9 @@ struct remote_features
   bool remote_memory_tagging_p () const
   { return packet_support (PACKET_memory_tagging_feature) == PACKET_ENABLE; }
 
+  bool stat64_feature () const
+  { return packet_support (PACKET_stat64_feature) == PACKET_ENABLE; }
+
   /* Returns true if there is support for sending vRun inferior arguments
      as a single string.  */
   bool remote_vrun_single_arg_p () const
@@ -6199,6 +6204,8 @@ static const struct protocol_feature remote_protocol_features[] = {
   { "no-resumed", PACKET_DISABLE, remote_supported_packet, PACKET_no_resumed },
   { "memory-tagging", PACKET_DISABLE, remote_supported_packet,
     PACKET_memory_tagging_feature },
+  { "stat64", PACKET_DISABLE, remote_supported_packet,
+    PACKET_stat64_feature },
   { "error-message", PACKET_ENABLE, remote_supported_packet,
     PACKET_accept_error_message },
   { "binary-upload", PACKET_DISABLE, remote_supported_packet, PACKET_x },
@@ -6309,6 +6316,10 @@ remote_target::remote_query_supported ()
 	  != AUTO_BOOLEAN_FALSE)
 	remote_query_supported_append (&q, "QThreadOptions+");
 
+      if (m_features.packet_set_cmd_state (PACKET_stat64_feature)
+	  != AUTO_BOOLEAN_FALSE)
+	remote_query_supported_append (&q, "stat64+");
+
       if (m_features.packet_set_cmd_state (PACKET_no_resumed)
 	  != AUTO_BOOLEAN_FALSE)
 	remote_query_supported_append (&q, "no-resumed+");
@@ -13663,29 +13674,35 @@ remote_target::fileio_readlink (struct inferior *inf, const char *filename,
    remote syscall reply.  EXPECTED_LEN is the length returned from the
    fstat or stat call, this the length of the returned data (in ATTACHMENT)
    once it has been decoded.  The fstat/stat result (from the ATTACHMENT
-   data) is to be placed in ST.  */
+   data) is to be placed in ST.
 
+   The reply holds either a struct fio_stat or, if the stat64 feature was
+   negotiated, the wider struct fio_stat_wide; the two are distinguished
+   by their length.  */
 static int
 fileio_process_fstat_and_lstat_reply (const char *attachment,
 				      int attachment_len,
 				      int expected_len,
 				      struct stat *st)
 {
-  struct fio_stat fst;
+  gdb_byte buf[sizeof (struct fio_stat_wide)];
 
   int read_len
     = remote_unescape_input ((gdb_byte *) attachment, attachment_len,
-			     (gdb_byte *) &fst, sizeof (fst));
+			     buf, sizeof (buf));
 
   if (read_len != expected_len)
     error (_("vFile:fstat returned %d, but %d bytes."),
 	   expected_len, read_len);
 
-  if (read_len != sizeof (fst))
-    error (_("vFile:fstat returned %d bytes, but expecting %d."),
-	   read_len, (int) sizeof (fst));
-
-  remote_fileio_to_host_stat (&fst, st);
+  if (read_len == sizeof (struct fio_stat_wide))
+    remote_fileio_wide_to_host_stat ((struct fio_stat_wide *) buf, st);
+  else if (read_len == sizeof (struct fio_stat))
+    remote_fileio_to_host_stat ((struct fio_stat *) buf, st);
+  else
+    error (_("vFile:fstat returned %d bytes, but expecting %d or %d."),
+	   read_len, (int) sizeof (struct fio_stat),
+	   (int) sizeof (struct fio_stat_wide));
 
   return 0;
 }
@@ -17039,6 +17056,9 @@ Show the maximum size of the address (in bits) in a memory packet."), NULL,
   add_packet_config_cmd (PACKET_memory_tagging_feature,
 			 "memory-tagging-feature", "memory-tagging-feature", 0);
 
+  add_packet_config_cmd (PACKET_stat64_feature,
+			 "stat64-feature", "stat64-feature", 0);
+
   add_packet_config_cmd (PACKET_qIsAddressTagged,
 			 "qIsAddressTagged", "memory-tagging-address-check", 0);
 
diff --git a/gdb/testsuite/gdb.server/fileio-packets.py b/gdb/testsuite/gdb.server/fileio-packets.py
index 54ae56fa68a..2d06edcdbf7 100644
--- a/gdb/testsuite/gdb.server/fileio-packets.py
+++ b/gdb/testsuite/gdb.server/fileio-packets.py
@@ -57,33 +57,48 @@ def decode_stat_reply(buf, byteorder="big"):
 
     buf = unescape_remote_data(buf)
 
-    if (
-        buf[0] != ord("F")
-        or buf[1] != ord("4")
-        or buf[2] != ord("0")
-        or buf[3] != ord(";")
-        or len(buf) != 68
-    ):
+    if buf[0:4] == b"F40;" and len(buf) == 68:
+        wide = False
+    elif buf[0:4] == b"F5c;" and len(buf) == 96:
+        wide = True
+    else:
         print(f"decode_stat_reply failed: {buf}\t(length = {len(buf)})")
         return None
 
-    # Discard the 'F40;' prefix.  The rest is the 64 bytes of data to
-    # be decoded.
+    # Discard the 'FNN;' prefix.
     buf = buf[4:]
 
-    st_dev = int.from_bytes(buf[0:4], byteorder=byteorder)
-    st_ino = int.from_bytes(buf[4:8], byteorder=byteorder)
-    st_mode = int.from_bytes(buf[8:12], byteorder=byteorder)
-    st_nlink = int.from_bytes(buf[12:16], byteorder=byteorder)
-    st_uid = int.from_bytes(buf[16:20], byteorder=byteorder)
-    st_gid = int.from_bytes(buf[20:24], byteorder=byteorder)
-    st_rdev = int.from_bytes(buf[24:28], byteorder=byteorder)
-    st_size = int.from_bytes(buf[28:36], byteorder=byteorder)
-    st_blksize = int.from_bytes(buf[36:44], byteorder=byteorder)
-    st_blocks = int.from_bytes(buf[44:52], byteorder=byteorder)
-    st_atime = int.from_bytes(buf[52:56], byteorder=byteorder)
-    st_mtime = int.from_bytes(buf[56:60], byteorder=byteorder)
-    st_ctime = int.from_bytes(buf[60:64], byteorder=byteorder)
+    def field(off, size):
+        return int.from_bytes(buf[off : off + size], byteorder=byteorder)
+
+    if wide:
+        st_dev     = field(0, 8)
+        st_ino     = field(8, 8)
+        st_mode    = field(16, 4)
+        st_nlink   = field(20, 8)
+        st_uid     = field(28, 4)
+        st_gid     = field(32, 4)
+        st_rdev    = field(36, 8)
+        st_size    = field(44, 8)
+        st_blksize = field(52, 8)
+        st_blocks  = field(60, 8)
+        st_atime   = field(68, 8)
+        st_mtime   = field(76, 8)
+        st_ctime   = field(84, 8)
+    else:
+        st_dev     = field(0, 4)
+        st_ino     = field(4, 4)
+        st_mode    = field(8, 4)
+        st_nlink   = field(12, 4)
+        st_uid     = field(16, 4)
+        st_gid     = field(20, 4)
+        st_rdev    = field(24, 4)
+        st_size    = field(28, 8)
+        st_blksize = field(36, 8)
+        st_blocks  = field(44, 8)
+        st_atime   = field(52, 4)
+        st_mtime   = field(56, 4)
+        st_ctime   = field(60, 4)
 
     return {
         "st_dev": st_dev,
diff --git a/gdbserver/hostio.cc b/gdbserver/hostio.cc
index b9ca27c2ff4..34def5d22da 100644
--- a/gdbserver/hostio.cc
+++ b/gdbserver/hostio.cc
@@ -26,7 +26,6 @@
 #include <unistd.h>
 #include <sys/types.h>
 #include <sys/stat.h>
-#include "gdbsupport/fileio.h"
 
 struct fd_list
 {
@@ -465,7 +464,7 @@ handle_fstat (char *own_buf, int *new_packet_len)
   int fd, bytes_sent;
   char *p;
   struct stat st;
-  struct fio_stat fst;
+  client_state &cs = get_client_state ();
 
   p = own_buf + strlen ("vFile:fstat:");
 
@@ -483,16 +482,31 @@ handle_fstat (char *own_buf, int *new_packet_len)
       return;
     }
 
-  host_to_fileio_stat (&st, &fst);
-
-  bytes_sent = hostio_reply_with_data (own_buf,
-				       (char *) &fst, sizeof (fst),
-				       new_packet_len);
+  struct fio_stat_wide wide;
+  host_to_fileio_stat_wide (&st, &wide);
 
-  /* If the response does not fit into a single packet, do not attempt
-     to return a partial response, but simply fail.  */
-  if (bytes_sent < sizeof (fst))
-    write_enn (own_buf);
+  /* Send the wide form if GDB understands it, otherwise narrow the
+     result first.  */
+  if (cs.stat64_feature)
+    {
+      bytes_sent = hostio_reply_with_data (own_buf, (char *) &wide,
+					   sizeof (wide), new_packet_len);
+      /* If the response does not fit into a single packet, do not attempt
+	 to return a partial response, but simply fail.  */
+      if (bytes_sent < sizeof (wide))
+	write_enn (own_buf);
+    }
+  else
+    {
+      struct fio_stat narrow;
+      fileio_stat_wide_to_narrow (&wide, &narrow);
+      bytes_sent = hostio_reply_with_data (own_buf, (char *) &narrow,
+					   sizeof (narrow), new_packet_len);
+      /* If the response does not fit into a single packet, do not attempt
+	 to return a partial response, but simply fail.  */
+      if (bytes_sent < sizeof (narrow))
+	write_enn (own_buf);
+    }
 }
 
 static void
@@ -501,8 +515,8 @@ handle_stat (char *own_buf, int *new_packet_len)
   int bytes_sent;
   char *p;
   struct stat st;
-  struct fio_stat fst;
   char filename[HOSTIO_PATH_MAX];
+  client_state &cs = get_client_state ();
 
   p = own_buf + strlen ("vFile:stat:");
 
@@ -519,16 +533,31 @@ handle_stat (char *own_buf, int *new_packet_len)
       return;
     }
 
-  host_to_fileio_stat (&st, &fst);
-
-  bytes_sent = hostio_reply_with_data (own_buf,
-				       (char *) &fst, sizeof (fst),
-				       new_packet_len);
+  struct fio_stat_wide wide;
+  host_to_fileio_stat_wide (&st, &wide);
 
-  /* If the response does not fit into a single packet, do not attempt
-     to return a partial response, but simply fail.  */
-  if (bytes_sent < sizeof (fst))
-    write_enn (own_buf);
+  /* Send the wide form if GDB understands it, otherwise narrow the
+     result first.  */
+  if (cs.stat64_feature)
+    {
+      bytes_sent = hostio_reply_with_data (own_buf, (char *) &wide,
+					   sizeof (wide), new_packet_len);
+      /* If the response does not fit into a single packet, do not attempt
+	 to return a partial response, but simply fail.  */
+      if (bytes_sent < sizeof (wide))
+	write_enn (own_buf);
+    }
+  else
+    {
+      struct fio_stat narrow;
+      fileio_stat_wide_to_narrow (&wide, &narrow);
+      bytes_sent = hostio_reply_with_data (own_buf, (char *) &narrow,
+					   sizeof (narrow), new_packet_len);
+      /* If the response does not fit into a single packet, do not attempt
+	 to return a partial response, but simply fail.  */
+      if (bytes_sent < sizeof (narrow))
+	write_enn (own_buf);
+    }
 }
 
 static void
@@ -537,8 +566,8 @@ handle_lstat (char *own_buf, int *new_packet_len)
   int ret, bytes_sent;
   char *p;
   struct stat st;
-  struct fio_stat fst;
   char filename[HOSTIO_PATH_MAX];
+  client_state &cs = get_client_state ();
 
   p = own_buf + strlen ("vFile:lstat:");
 
@@ -560,16 +589,31 @@ handle_lstat (char *own_buf, int *new_packet_len)
       return;
     }
 
-  host_to_fileio_stat (&st, &fst);
-
-  bytes_sent = hostio_reply_with_data (own_buf,
-				       (char *) &fst, sizeof (fst),
-				       new_packet_len);
+  struct fio_stat_wide wide;
+  host_to_fileio_stat_wide (&st, &wide);
 
-  /* If the response does not fit into a single packet, do not attempt
-     to return a partial response, but simply fail.  */
-  if (bytes_sent < sizeof (fst))
-    write_enn (own_buf);
+  /* Send the wide form if GDB understands it, otherwise narrow the
+     result first.  */
+  if (cs.stat64_feature)
+    {
+      bytes_sent = hostio_reply_with_data (own_buf, (char *) &wide,
+					   sizeof (wide), new_packet_len);
+      /* If the response does not fit into a single packet, do not attempt
+	 to return a partial response, but simply fail.  */
+      if (bytes_sent < sizeof (wide))
+	write_enn (own_buf);
+    }
+  else
+    {
+      struct fio_stat narrow;
+      fileio_stat_wide_to_narrow (&wide, &narrow);
+      bytes_sent = hostio_reply_with_data (own_buf, (char *) &narrow,
+					   sizeof (narrow), new_packet_len);
+      /* If the response does not fit into a single packet, do not attempt
+	 to return a partial response, but simply fail.  */
+      if (bytes_sent < sizeof (narrow))
+	write_enn (own_buf);
+    }
 }
 
 static void
diff --git a/gdbserver/server.cc b/gdbserver/server.cc
index a208e24d09f..dfa24617847 100644
--- a/gdbserver/server.cc
+++ b/gdbserver/server.cc
@@ -2746,6 +2746,8 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
 		cs.single_inferior_argument = true;
 	      else if (feature == "multi-wp-addr+")
 		cs.multiple_wp_addr_feature = true;
+	      else if (feature == "stat64+")
+		cs.stat64_feature = true;
 	      else
 		{
 		  /* Move the unknown features all together.  */
@@ -2762,7 +2764,7 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
 	       "PacketSize=%x;QPassSignals+;QProgramSignals+;"
 	       "QStartupWithShell+;QEnvironmentHexEncoded+;"
 	       "QEnvironmentReset+;QEnvironmentUnset+;"
-	       "QSetWorkingDir+;binary-upload+",
+	       "QSetWorkingDir+;binary-upload+;stat64+",
 	       PBUFSIZ - 1);
 
       if (target_supports_catch_syscall ())
diff --git a/gdbserver/server.h b/gdbserver/server.h
index bbba3650de7..cf811f228fa 100644
--- a/gdbserver/server.h
+++ b/gdbserver/server.h
@@ -192,6 +192,11 @@ struct client_state
   /* If true, memory tagging features are supported.  */
   bool memory_tagging_feature = false;
 
+  /* True if GDB supports the wide form of struct stat in the vFile
+     fstat, stat and lstat replies.  When false, gdbserver narrows the
+     result to struct fio_stat before sending it.  */
+  bool stat64_feature = false;
+
   /* If true then E.errtext style errors are supported everywhere,
      including for the qRcmd and m packet.  When false E.errtext errors
      are not supported with qRcmd and m packets, but are still supported
diff --git a/gdbsupport/fileio.cc b/gdbsupport/fileio.cc
index 6d2ffb14544..bab2c4c8efe 100644
--- a/gdbsupport/fileio.cc
+++ b/gdbsupport/fileio.cc
@@ -20,6 +20,7 @@
 #include "fileio.h"
 #include <sys/stat.h>
 #include <fcntl.h>
+#include "gdbsupport/errors.h"
 
 /* See fileio.h.  */
 
@@ -305,3 +306,72 @@ host_to_fileio_stat (struct stat *st, struct fio_stat *fst)
   host_to_fileio_time (st->st_mtime, fst->fst_mtime);
   host_to_fileio_time (st->st_ctime, fst->fst_ctime);
 }
+
+/* See fileio.h.  */
+
+void
+host_to_fileio_stat_wide (struct stat *st, struct fio_stat_wide *fst)
+{
+  LONGEST blksize;
+
+  host_to_bigendian ((LONGEST) st->st_dev, (char *) fst->fst_dev, 8);
+  host_to_bigendian ((LONGEST) st->st_ino, (char *) fst->fst_ino, 8);
+  host_to_fileio_mode (st->st_mode, fst->fst_mode);
+  host_to_bigendian ((LONGEST) st->st_nlink, (char *) fst->fst_nlink, 8);
+  host_to_fileio_uint ((long) st->st_uid, fst->fst_uid);
+  host_to_fileio_uint ((long) st->st_gid, fst->fst_gid);
+  host_to_bigendian ((LONGEST) st->st_rdev, (char *) fst->fst_rdev, 8);
+  host_to_bigendian ((LONGEST) st->st_size, (char *) fst->fst_size, 8);
+#ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
+  blksize = st->st_blksize;
+#else
+  blksize = 512;
+#endif
+  host_to_bigendian (blksize, (char *) fst->fst_blksize, 8);
+#if HAVE_STRUCT_STAT_ST_BLOCKS
+  host_to_bigendian ((LONGEST) st->st_blocks, (char *) fst->fst_blocks, 8);
+#else
+  host_to_bigendian (((LONGEST) st->st_size + blksize - 1) / blksize,
+		     (char *) fst->fst_blocks, 8);
+#endif
+  host_to_bigendian ((LONGEST) st->st_atime, (char *) fst->fst_atime, 8);
+  host_to_bigendian ((LONGEST) st->st_mtime, (char *) fst->fst_mtime, 8);
+  host_to_bigendian ((LONGEST) st->st_ctime, (char *) fst->fst_ctime, 8);
+}
+
+/* Copy the 8-byte value in FROM into the 4-byte field TO, warning if
+   the value does not fit.  WHAT names the field, for the warning.  */
+
+static void
+narrow_field (const char *from, char *to, const char *what)
+{
+  ULONGEST num = bigendian_to_host (from, 8);
+
+  if (num != (ULONGEST) (uint32_t) num)
+    warning (_("file-i/o: %s value %s does not fit in 32 bits; "
+	       "the value seen by the peer will be truncated"),
+	     what, pulongest (num));
+
+  host_to_bigendian ((LONGEST) num, to, 4);
+}
+
+/* See fileio.h.  */
+
+void
+fileio_stat_wide_to_narrow (const struct fio_stat_wide *wide,
+			    struct fio_stat *narrow)
+{
+  narrow_field (wide->fst_dev, narrow->fst_dev, "st_dev");
+  narrow_field (wide->fst_ino, narrow->fst_ino, "st_ino");
+  memcpy (narrow->fst_mode, wide->fst_mode, 4);
+  narrow_field (wide->fst_nlink, narrow->fst_nlink, "st_nlink");
+  memcpy (narrow->fst_uid, wide->fst_uid, 4);
+  memcpy (narrow->fst_gid, wide->fst_gid, 4);
+  narrow_field (wide->fst_rdev, narrow->fst_rdev, "st_rdev");
+  memcpy (narrow->fst_size, wide->fst_size, 8);
+  memcpy (narrow->fst_blksize, wide->fst_blksize, 8);
+  memcpy (narrow->fst_blocks, wide->fst_blocks, 8);
+  narrow_field (wide->fst_atime, narrow->fst_atime, "st_atime");
+  narrow_field (wide->fst_mtime, narrow->fst_mtime, "st_mtime");
+  narrow_field (wide->fst_ctime, narrow->fst_ctime, "st_ctime");
+}
diff --git a/gdbsupport/fileio.h b/gdbsupport/fileio.h
index c2592cf8c15..fa5de54dd40 100644
--- a/gdbsupport/fileio.h
+++ b/gdbsupport/fileio.h
@@ -120,9 +120,11 @@ using fio_time_t = char[FIO_TIME_LEN];
 using fio_long_t = char[FIO_LONG_LEN];
 using fio_ulong_t = char[FIO_ULONG_LEN];
 
-/* Struct stat as used in protocol.  For complete independence
-   of host/target systems, it's defined as an array with offsets
-   to the members. */
+/* Struct stat as used in the protocol.  This is the format used by the
+   F-packet file-i/o protocol, and by the vFile stat packets when the
+   stat64 feature has not been negotiated.  Several fields are too narrow
+   to hold the corresponding struct stat member on a 64-bit host; see
+   struct fio_stat_wide.  */
 
 struct fio_stat
 {
@@ -141,6 +143,29 @@ struct fio_stat
   fio_time_t  fst_ctime;
 };
 
+/* Struct stat as used in the wide form of the protocol.  The fields
+   which cannot hold the corresponding struct stat member on a 64-bit
+   host are widened to 8 bytes.  This layout is only used by the vFile
+   stat packets, and only when the stat64 feature has been negotiated
+   with the peer.  */
+
+struct fio_stat_wide
+{
+  fio_ulong_t fst_dev;
+  fio_ulong_t fst_ino;
+  fio_mode_t  fst_mode;
+  fio_ulong_t fst_nlink;
+  fio_uint_t  fst_uid;
+  fio_uint_t  fst_gid;
+  fio_ulong_t fst_rdev;
+  fio_ulong_t fst_size;
+  fio_ulong_t fst_blksize;
+  fio_ulong_t fst_blocks;
+  fio_ulong_t fst_atime;
+  fio_ulong_t fst_mtime;
+  fio_ulong_t fst_ctime;
+};
+
 struct fio_timeval
 {
   fio_time_t  ftv_sec;
@@ -179,6 +204,20 @@ host_to_bigendian (LONGEST num, char *buf, int bytes)
     buf[i] = (num >> (8 * (bytes - i - 1))) & 0xff;
 }
 
+/* Unpack an integer of BYTES bytes from BUF, which holds it in
+   big-endian, protocol format.  */
+
+static inline ULONGEST
+bigendian_to_host (const char *buf, int bytes)
+{
+  ULONGEST num = 0;
+  int i;
+
+  for (i = 0; i < bytes; ++i)
+    num = (num << 8) | (unsigned char) buf[i];
+  return num;
+}
+
 /* Pack a host-format integer into an fio_uint_t.  */
 
 static inline void
@@ -199,4 +238,13 @@ host_to_fileio_time (time_t num, fio_time_t fnum)
 
 extern void host_to_fileio_stat (struct stat *st, struct fio_stat *fst);
 
+/* Pack a host-format struct stat into a struct fio_stat_wide.  */
+extern void host_to_fileio_stat_wide (struct stat *st,
+				      struct fio_stat_wide *fst);
+
+/* Narrow a struct fio_stat_wide into a struct fio_stat, warning if any
+   value does not fit.  */
+extern void fileio_stat_wide_to_narrow (const struct fio_stat_wide *wide,
+					struct fio_stat *narrow);
+
 #endif /* GDBSUPPORT_FILEIO_H */
--------------4ZlxNPyeS48bU4fMrVK69GwA--