pyGdbToolkit 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,190 @@
1
+ # SPDX-FileCopyrightText: 2026 H2Lab Development Team
2
+ # SPDX-License-Identifier: Apache-2.0
3
+
4
+ """Typed target-memory access shared by GDB commands."""
5
+
6
+ from __future__ import annotations
7
+
8
+ from typing import Protocol, runtime_checkable
9
+
10
+ import gdb
11
+
12
+
13
+ class TargetMemory(Protocol):
14
+ """The integer-oriented target-memory operations used by providers."""
15
+
16
+ def read_bytes(self, address: int, size: int) -> bytes:
17
+ """Read an exact number of bytes from target memory."""
18
+ ...
19
+
20
+ def read_uint16(self, address: int) -> int:
21
+ """Read a little-endian unsigned 16-bit integer."""
22
+ ...
23
+
24
+ def read_uint32(self, address: int) -> int:
25
+ """Read a little-endian unsigned 32-bit integer."""
26
+ ...
27
+
28
+
29
+ @runtime_checkable
30
+ class WritableTargetMemory(TargetMemory, Protocol):
31
+ """Target-memory operations that can also write 32-bit register values."""
32
+
33
+ def write_uint32(self, address: int, value: int) -> None:
34
+ """Write a little-endian unsigned 32-bit integer."""
35
+ ...
36
+
37
+
38
+ class TargetReadError(RuntimeError):
39
+ """An error enriched with the target-memory range that could not be read."""
40
+
41
+ def __init__(self, address: int, size: int, reason: str) -> None:
42
+ """Initialize an error for one failed target-memory operation.
43
+
44
+ Parameters
45
+ ----------
46
+ address
47
+ First target address that was requested.
48
+ size
49
+ Number of bytes requested.
50
+ reason
51
+ The underlying target-access failure.
52
+ """
53
+ self.address = address
54
+ self.size = size
55
+ self.reason = reason
56
+ super().__init__(f"could not read {size} byte(s) at 0x{address:08X}: {reason}")
57
+
58
+
59
+ class TargetWriteError(RuntimeError):
60
+ """An error enriched with the target-memory range that could not be written."""
61
+
62
+ def __init__(self, address: int, size: int, reason: str) -> None:
63
+ """Initialize an error for one failed target-memory write.
64
+
65
+ Parameters
66
+ ----------
67
+ address
68
+ First target address that was requested.
69
+ size
70
+ Number of bytes requested.
71
+ reason
72
+ The underlying target-access failure.
73
+ """
74
+ self.address = address
75
+ self.size = size
76
+ self.reason = reason
77
+ super().__init__(f"could not write {size} byte(s) at 0x{address:08X}: {reason}")
78
+
79
+
80
+ class TargetMemoryReader:
81
+ """Read little-endian values from GDB's selected inferior."""
82
+
83
+ def __init__(self, inferior: gdb.Inferior | None = None) -> None:
84
+ """Create a reader for an inferior.
85
+
86
+ Parameters
87
+ ----------
88
+ inferior
89
+ An optional GDB inferior. The selected inferior is used by default.
90
+ """
91
+ if inferior is not None:
92
+ self._inferior = inferior
93
+ return
94
+ try:
95
+ self._inferior = gdb.selected_inferior()
96
+ except gdb.error as error:
97
+ raise TargetReadError(0, 0, f"could not select inferior: {error}") from error
98
+
99
+ def read_bytes(self, address: int, size: int) -> bytes:
100
+ """Read an exact byte range from the target.
101
+
102
+ Parameters
103
+ ----------
104
+ address
105
+ Target address to read.
106
+ size
107
+ Number of bytes to read.
108
+
109
+ Returns
110
+ -------
111
+ bytes
112
+ The bytes returned by the selected inferior.
113
+
114
+ Raises
115
+ ------
116
+ TargetReadError
117
+ If GDB rejects the read or returns an incomplete range.
118
+ ValueError
119
+ If the requested range is invalid.
120
+ """
121
+ if address < 0:
122
+ raise ValueError("target address must not be negative")
123
+ if size <= 0:
124
+ raise ValueError("target-memory read size must be positive")
125
+
126
+ try:
127
+ result = bytes(self._inferior.read_memory(address, size))
128
+ except (gdb.error, gdb.MemoryError) as error:
129
+ raise TargetReadError(address, size, str(error)) from error
130
+
131
+ if len(result) != size:
132
+ raise TargetReadError(address, size, f"target returned {len(result)} byte(s)")
133
+ return result
134
+
135
+ def read_uint16(self, address: int) -> int:
136
+ """Read a little-endian unsigned 16-bit value.
137
+
138
+ Parameters
139
+ ----------
140
+ address
141
+ Target address to read.
142
+
143
+ Returns
144
+ -------
145
+ int
146
+ The decoded value.
147
+ """
148
+ return int.from_bytes(self.read_bytes(address, 2), byteorder="little")
149
+
150
+ def read_uint32(self, address: int) -> int:
151
+ """Read a little-endian unsigned 32-bit value.
152
+
153
+ Parameters
154
+ ----------
155
+ address
156
+ Target address to read.
157
+
158
+ Returns
159
+ -------
160
+ int
161
+ The decoded value.
162
+ """
163
+ return int.from_bytes(self.read_bytes(address, 4), byteorder="little")
164
+
165
+ def write_uint32(self, address: int, value: int) -> None:
166
+ """Write an unsigned 32-bit value to target memory in little-endian order.
167
+
168
+ Parameters
169
+ ----------
170
+ address
171
+ Target address to write.
172
+ value
173
+ Unsigned 32-bit value to write.
174
+
175
+ Raises
176
+ ------
177
+ TargetWriteError
178
+ If GDB rejects the write.
179
+ ValueError
180
+ If the target address or value is invalid.
181
+ """
182
+ if address < 0:
183
+ raise ValueError("target address must not be negative")
184
+ if not 0 <= value <= 0xFFFFFFFF:
185
+ raise ValueError("target-memory write value must be an unsigned 32-bit value")
186
+
187
+ try:
188
+ self._inferior.write_memory(address, value.to_bytes(4, byteorder="little"))
189
+ except (gdb.error, gdb.MemoryError) as error:
190
+ raise TargetWriteError(address, 4, str(error)) from error