From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from simark.ca by simark.ca with LMTP id /r0JLOFV2mrvWAwAWB0awg (envelope-from ) for ; Thu, 22 Oct 2026 14:28:49 -0400 Authentication-Results: simark.ca; dkim=pass (2048-bit key; secure) header.d=adacore.com header.i=@adacore.com header.a=rsa-sha256 header.s=google header.b=TVo7aQ5b; dkim-atps=neutral Received: by simark.ca (Postfix, from userid 112) id 9C3CE378002; Thu, 22 Oct 2026 14:28:49 -0400 (EDT) X-Spam-Checker-Version: SpamAssassin 4.0.1 (2024-03-25) on simark.ca X-Spam-Level: X-Spam-Status: No, score=-5.4 required=5.0 tests=ARC_SIGNED,ARC_VALID,BAYES_00, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,MAILING_LIST_MULTI, RCVD_IN_DNSWL_MED autolearn=ham autolearn_force=no version=4.0.1 Received: from vm01.sourceware.org (vm01.sourceware.org [IPv6:2620:52:6:3111::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 856331E007 for ; Thu, 22 Oct 2026 14:28:48 -0400 (EDT) Received: from vm01.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 1D6314BA7998 for ; Thu, 22 Oct 2026 18:28:48 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 1D6314BA7998 Authentication-Results: sourceware.org; dkim=pass (2048-bit key, secure) header.d=adacore.com header.i=@adacore.com header.a=rsa-sha256 header.s=google header.b=TVo7aQ5b Received: from mail-ot1-x32c.google.com (mail-ot1-x32c.google.com [IPv6:2607:f8b0:4864:20::32c]) by sourceware.org (Postfix) with ESMTPS id 699DC4BA5434 for ; Thu, 22 Oct 2026 18:28:21 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 699DC4BA5434 Authentication-Results: sourceware.org; dmarc=pass (p=quarantine dis=none) header.from=adacore.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=adacore.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 699DC4BA5434 Authentication-Results: sourceware.org; arc=none smtp.remote-ip=2607:f8b0:4864:20::32c ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1792693702; cv=none; b=ZIu6871SO0pVaRJb5L8nwWrh/aEq0MHl8equ9KpUsW16BLTJpssL0HhxLPvJu/c2TZGgI02ptVDSUwhvoFIRvjCQqS8bkfCedUvx/E+1igdZeQ62LVq0aNhoz23zi48uCq/7sWyU33m6iYQYM/zc9xfgsY5YSmNJTUI6y+z2LtQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1792693702; c=relaxed/simple; bh=xjSvtJmpGOfcqhbIsmALzvh1DcEexmcKEReWUj5992g=; h=DKIM-Signature:From:Date:Subject:MIME-Version:Message-Id:To; b=Ryil/zH9RiUAbiiXnL3l7Ai6jKzumb5Ge1Hka8gSQc2zSp9SJeKiiGCLjKHC/9NmfytVR2PBgxWfKJ2YivIjCAOXL6k83SNqyzBmkhU7eoXd8K3tS4rzy04BDkW02mRskACsKOsB1o5qQkcRTyECCteqBdJhvnERront26nnVw4= ARC-Authentication-Results: i=1; sourceware.org; dkim=pass (2048-bit key, secure) header.d=adacore.com header.i=@adacore.com header.a=rsa-sha256 header.s=google header.b=TVo7aQ5b DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 699DC4BA5434 Received: by mail-ot1-x32c.google.com with SMTP id 46e09a7af769-7f84a55e31aso27989a34.2 for ; Thu, 22 Oct 2026 11:28:21 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=adacore.com; s=google; t=1792693701; x=1793298501; darn=sourceware.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :content-type:mime-version:subject:date:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=83eV4Q770RnFucKOFryeC1uhjFCqEavwUB6NiKKaXO4=; b=TVo7aQ5bgfiB3zS5fcy4MKH3I+k6zVlFamshegwl85ssMdGVj3J7P7bztt8qTC0eQs tJKVZOrpGM8jFVZKHozoVlSxau/4MEasuemn626Ps5qaCD7Hh31cl7cjFdMmnzg3lhhC KoWQVYYhInDB2t3aIGI1Qw9qjIcIrwfxir4SwWA2H4lo8BbgUFODS3qEr+4v1TZwj5b1 OlNqcPt/GGVegaOaNBRr/cOmAPDf2iGo7Sn8w0xJFA9dB0FBxbtylJKDe6Sjx8dgt197 N26KHOeYH2zORZgj0LiUWGEdIvCy2tR/4P7UvYK5qDHYPsCDLLJQ3lqlvQ1yD6IruhPK 9V5w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1792693701; x=1793298501; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :content-type:mime-version:subject:date:from:x-gm-gg :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to :content-type; bh=83eV4Q770RnFucKOFryeC1uhjFCqEavwUB6NiKKaXO4=; b=n6p8jxns2MhErOBBZ17lZkk2AABXNUl84wJ1vDVqrdSeDSav3qfokyEYN+dBrGSQ2C wRiyAJ6tBOd3yNEkfCoTEQW+BQqVWRkmLFu3b9awiREsO8n46Iru3zk5gg6JWLPaB5Sf rUcDzCZwImzapGcLHm3m7YLR6ygLKCRJJmDxlyCWKNHKrZfHvlgMBFJ5Sr03SJb5kGMK LchLkwcOJj/3lt7Zet/U5hvcROOuZr1+pxPT3L6bYyyNm6rebM+ZE94EQ4rs4Hi5LQnT dcVYePtLkCddjIGSGLRFWgN6ESX+NGD8o1A9E1589hlu0cRTWQDlqRS2qpAqRDxboZIC rVcQ== X-Gm-Message-State: AFq9FYLmyN2XAtpkT60QfuWjzr3ohHJY9JCOXDisaN2FW6X0rnEH4tjW XByhpFRNGvr8NWbV7IDRh8pss9+2hh5xVzcEDLowI4uGDj/b2Lb5J9mAaqGxaH3lb39Xnl5WB/A Ymk/7hA== X-Gm-Gg: AYBFou1T/fwJiSf0Zk3Rf/+Mf2FffnQ1W/AIpiZjL/xtiZCJls+uz1/hsHoPuZKZvOr OroG/tkTfXzvCrgl7o0RqpnSkgXs35UHivQPuU+TLcg4sV2HKiLnrvnS8rqWFzMywJW5Qo8xFFg lXJQ10z6UK1laOUUB3B4ztGwGFPBjDf8WvzxOiHL7apaplWDUcJ3aAaud2nKhpkm9VyzzYR9zq2 f6/sFfh5qA70Sqn7oBbmbiXOqNlnHBTJi4MMVsKjodPCHznNQYpErKk5nvOyLJJO8G7ey/6c6kW vzOgrMEXdn3W/YPUYeWfgPyGjXbk+sF8FQDnZBBS0u9DfesQXiQnihxX8gsOi8xUW0aEFshDWj6 LkKuYungBmSP8D6P0Ujuiekl+ywMNETfLY7+gx2aBcPowYPZfdooPYjNqC2Yf3HKGbvDCPZeXGo ARtzraijzP1xu479T3NyPsgxkhGPgZFaV1fDTAbKoC3YxvVama95IbEiLHqQX84pDewWQ9pX4af Q4re4j8CsBfugD9Lhs7vUdPw6Xvfsh17diTL0UJA5ps3YGYmCPdgbl4uz6LNXatzBj032F9bCG+ aXA4G43Cs3CHQw== X-Received: by 2002:a05:6830:6d0a:b0:802:4cf7:ea46 with SMTP id 46e09a7af769-84d0860548cmr4960584a34.20.1792693700462; Thu, 22 Oct 2026 11:28:20 -0700 (PDT) Received: from [192.168.0.26] (97-122-110-129.hlrn.qwest.net. [97.122.110.129]) by smtp.gmail.com with ESMTPSA id 46e09a7af769-84f784f3c50sm419557a34.21.2026.10.22.11.28.19 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 22 Oct 2026 11:28:20 -0700 (PDT) From: Tom Tromey Date: Thu, 22 Oct 2026 12:28:11 -0600 Subject: [PATCH RFC 1/6] Add py-file.c MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: 7bit Message-Id: <20261022-android-apk-file-v1-1-8444bb5f832a@adacore.com> References: <20261022-android-apk-file-v1-0-8444bb5f832a@adacore.com> In-Reply-To: <20261022-android-apk-file-v1-0-8444bb5f832a@adacore.com> To: gdb-patches@sourceware.org Cc: Tom Tromey X-Mailer: b4 0.15.2 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 patch adds py-file.c, a Python wrapper for gdb's target file API. --- gdb/Makefile.in | 1 + gdb/python/py-file.c | 298 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 299 insertions(+) diff --git a/gdb/Makefile.in b/gdb/Makefile.in index 8cd12460000..4260c90d425 100644 --- a/gdb/Makefile.in +++ b/gdb/Makefile.in @@ -403,6 +403,7 @@ SUBDIR_PYTHON_SRCS = \ python/py-evtregistry.c \ python/py-evts.c \ python/py-exitedevent.c \ + python/py-file.c \ python/py-finishbreakpoint.c \ python/py-frame.c \ python/py-framefilter.c \ diff --git a/gdb/python/py-file.c b/gdb/python/py-file.c new file mode 100644 index 00000000000..cd178190bfc --- /dev/null +++ b/gdb/python/py-file.c @@ -0,0 +1,298 @@ +/* Python interface to target files + + Copyright (C) 2026 Free Software Foundation, Inc. + + This file is part of GDB. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#include "python-internal.h" +#include "inferior.h" +#include "target.h" + +/* These have the same values as the Python os.SEEK_* constants. + Normally this would be bad but it seems like these are fairly well + documented. */ +enum constants : int +{ + SET = 0, + CUR = 1, + END = 2, +}; + +struct target_file_object : public PyObject +{ + target_fd fd; + LONGEST offset; + LONGEST file_length; + + void close () + { + if (fd != target_fd::INVALID) + { + fileio_error ignore; + target_fileio_close (fd, &ignore); + } + } +}; + +static_assert (gdb::is_python_allocatable_v); + +extern PyTypeObject target_file_object_type; + +static void +tf_dealloc (PyObject *obj) +{ + target_file_object *tf = (target_file_object *) obj; + tf->close (); +} + +static bool +check_valid (target_file_object *tf) +{ + if (tf->fd == target_fd::INVALID) + { + PyErr_SetString (PyExc_ValueError, "file closed"); + return false; + } + return true; +} + +static PyObject * +tf_read (PyObject *self, PyObject *args) +{ + target_file_object *tf = (target_file_object *) self; + if (!check_valid (tf)) + return nullptr; + + long nbytes = -1; + if (!PyArg_ParseTuple (args, "|l", &nbytes)) + return nullptr; + + long chunk = nbytes == -1 ? 1024 : nbytes; + gdb::byte_vector result (chunk); + int n_read = 0; + try + { + while (true) + { + fileio_error err; + int this_read = target_fileio_pread (tf->fd, &result[n_read], chunk, + tf->offset, &err); + if (this_read == -1) + { + errno = fileio_error_to_host (err); + perror_with_name ("read failed"); + } + n_read += this_read; + tf->offset += this_read; + if (nbytes != -1 || this_read == 0) + break; + + result.resize (result.size () + chunk); + } + } + catch (const gdb_exception &exc) + { + return gdbpy_handle_gdb_exception (nullptr, exc); + } + + return PyBytes_FromStringAndSize ((char *) result.data (), n_read); +} + +static PyObject * +tf_seek (PyObject *self, PyObject *args) +{ + target_file_object *tf = (target_file_object *) self; + if (!check_valid (tf)) + return nullptr; + + Py_ssize_t value; + int what = SET; + if (!PyArg_ParseTuple (args, "n|i", &value, &what)) + return nullptr; + + LONGEST new_offset = tf->offset; + switch (what) + { + case SET: + new_offset = value; + break; + + case CUR: + new_offset += value; + break; + + case END: + new_offset = tf->file_length + value; + break; + + default: + PyErr_SetString (PyExc_ValueError, "invalid seek constant"); + return nullptr; + } + + /* Check the new value. Note we allow the new offset to exactly + equal the file length. */ + if (new_offset < 0 || new_offset > tf->file_length) + { + PyErr_SetString (PyExc_ValueError, "invalid seek offset"); + return nullptr; + } + + tf->offset = new_offset; + return gdb_py_object_from_ulongest (tf->offset).release (); +} + +static PyObject * +tf_seekable (PyObject *self, PyObject *args) +{ + return py_true ().release (); +} + +static PyObject * +tf_tell (PyObject *self, PyObject *args) +{ + target_file_object *tf = (target_file_object *) self; + if (!check_valid (tf)) + return nullptr; + return gdb_py_object_from_ulongest (tf->offset).release (); +} + +static PyObject * +tf_close (PyObject *self, PyObject *args) +{ + target_file_object *tf = (target_file_object *) self; + tf->close (); + return py_none ().release (); +} + +static PyObject * +tf_new (PyTypeObject *type, PyObject *args, PyObject *keywords) +{ + gdbpy_ref self + ((target_file_object *) type->tp_alloc (type, 0)); + if (self != nullptr) + self->fd = target_fd::INVALID; + return self.release (); +} + +static int +tf_init (PyObject *self, PyObject *args, PyObject *kwargs) +{ + static const char *keywords[] = { "filename", nullptr }; + const char *filename = nullptr; + + target_file_object *tf = (target_file_object *) self; + /* __init__ can be called twice, but we just reject it for this + type. */ + if (tf->fd != target_fd::INVALID) + { + PyErr_SetString (PyExc_ValueError, + "cannot call __init__ twice for gdb.TargetFile"); + return -1; + } + + if (!gdb_PyArg_ParseTupleAndKeywords (args, kwargs, "s", keywords, + &filename)) + return -1; + + try + { + fileio_error err; + tf->fd = target_fileio_open (current_inferior (), filename, + FILEIO_O_RDONLY, 0, true, &err); + if (tf->fd == target_fd::INVALID) + { + errno = fileio_error_to_host (err); + perror_with_name ("open failed"); + } + + struct stat buf; + if (target_fileio_fstat (tf->fd, &buf, &err)) + { + tf->close (); + errno = fileio_error_to_host (err); + perror_with_name ("stat failed while opening"); + } + + tf->file_length = buf.st_size; + } + catch (const gdb_exception &exc) + { + return gdbpy_handle_gdb_exception (-1, exc); + } + + return 0; +} + +static PyMethodDef target_file_object_methods[] = +{ + { "read", tf_read, METH_VARARGS, "read bytes" }, + { "seek", tf_seek, METH_VARARGS, "seek to position" }, + { "seekable", tf_seekable, METH_VARARGS, "seek to position" }, + { "tell", tf_tell, METH_NOARGS, "return current position" }, + { "close", tf_close, METH_NOARGS, "close the file" }, + { nullptr } +}; + +PyTypeObject target_file_object_type = +{ + PyVarObject_HEAD_INIT (NULL, 0) + "gdb.TargetFile", /*tp_name*/ + sizeof (target_file_object), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + tf_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT, /*tp_flags*/ + "GDB remote file object", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + target_file_object_methods, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + tf_init, /* tp_init */ + 0, /* tp_alloc */ + tf_new, /* tp_new */ +}; + +static int +gdbpy_initialize_target_file () +{ + return gdbpy_type_ready (&target_file_object_type); +} + +GDBPY_INITIALIZE_FILE (gdbpy_initialize_target_file); -- 2.56.0