From: Tom Tromey <tromey@adacore.com>
To: gdb-patches@sourceware.org
Cc: Tom Tromey <tromey@adacore.com>
Subject: [PATCH RFC 1/6] Add py-file.c
Date: Thu, 22 Oct 2026 12:28:11 -0600 [thread overview]
Message-ID: <20261022-android-apk-file-v1-1-8444bb5f832a@adacore.com> (raw)
In-Reply-To: <20261022-android-apk-file-v1-0-8444bb5f832a@adacore.com>
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 <http://www.gnu.org/licenses/>. */
+
+#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<target_file_object>);
+
+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<target_file_object> 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
next prev parent reply other threads:[~2026-10-22 18:28 UTC|newest]
Thread overview: 7+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-22 18:28 [PATCH RFC 0/6] Handle Android APK files Tom Tromey
2026-10-22 18:28 ` Tom Tromey [this message]
2026-10-22 18:28 ` [PATCH RFC 2/6] Add py-iovec.c Tom Tromey
2026-10-22 18:28 ` [PATCH RFC 3/6] Allow missing build-id when finding an objfile Tom Tromey
2026-10-22 18:28 ` [PATCH RFC 4/6] Allow missing objfile handler to return a file Tom Tromey
2026-10-22 18:28 ` [PATCH RFC 5/6] Call ext_lang_find_objfile_from_buildid from solib_bfd_fopen Tom Tromey
2026-10-22 18:28 ` [PATCH RFC 6/6] Add gdb.handler.android module Tom Tromey
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261022-android-apk-file-v1-1-8444bb5f832a@adacore.com \
--to=tromey@adacore.com \
--cc=gdb-patches@sourceware.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox