1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
|
from arch.arch import Arch
class MemoryAccessError(Exception):
def __init__(self, addr: int, size: int, msg: str):
super().__init__(msg)
self.mem_addr = addr
self.mem_size = size
class SparseMemory:
"""Sparse memory.
Note that out-of-bound reads are possible when performed on unwritten
sections of existing pages and that there is no safeguard check for them.
"""
def __init__(self, page_size=4096):
self.page_size = page_size
self._pages: dict[int, bytes] = {}
def _to_page_addr_and_offset(self, addr: int) -> tuple[int, int]:
off = addr % self.page_size
return addr - off, off
def read(self, addr: int, size: int) -> bytes:
"""Read a number of bytes from memory.
:param addr: The offset from where to read.
:param size: The number of bytes to read, starting at at `addr`.
:return: `size` bytes of data.
:raise MemoryAccessError: If `[addr, addr + size)` is not entirely
contained in the set of stored bytes.
:raise ValueError: If `size < 0`.
"""
if size < 0:
raise ValueError(f'A negative size is not allowed!')
res = bytes()
while size > 0:
page_addr, off = self._to_page_addr_and_offset(addr)
if page_addr not in self._pages:
raise MemoryAccessError(addr, size,
f'Address {addr} is not contained in'
f' the sparse memory.')
data = self._pages[page_addr]
assert(len(data) == self.page_size)
read_size = min(size, self.page_size - off)
res += data[off:off+read_size]
size -= read_size
addr += read_size
return res
def write(self, addr: int, data: bytes):
"""Store bytes in the memory.
:param addr: The address at which to store the data.
:param data: The data to store at `addr`.
"""
offset = 0 # Current offset into `data`
while offset < len(data):
page_addr, off = self._to_page_addr_and_offset(addr)
if page_addr not in self._pages:
self._pages[page_addr] = bytes(self.page_size)
page = self._pages[page_addr]
assert(len(page) == self.page_size)
write_size = min(len(data) - offset, self.page_size - off)
new_page = page[:off] + data[offset:offset + write_size] + page[off+write_size:]
assert(len(new_page) == self.page_size)
self._pages[page_addr] = new_page
offset += write_size
addr += write_size
assert(len(data) == offset) # Exactly all data was written
class ProgramState:
"""A snapshot of the program's state."""
def __init__(self, arch: Arch):
self.arch = arch
dict_t = dict[str, int | None]
self.regs: dict_t = { reg: None for reg in arch.regnames }
self.mem = SparseMemory()
def read(self, reg: str) -> int:
"""Read a register's value.
:raise KeyError: If `reg` is not a register name.
:raise ValueError: If the register has no value.
"""
regname = self.arch.to_regname(reg)
if regname is None:
raise KeyError(f'Not a register name: {reg}')
assert(regname in self.regs)
regval = self.regs[regname]
if regval is None:
raise ValueError(f'Unable to read value of register {reg} (aka.'
f' {regname}): The register contains no value.')
return regval
def set(self, reg: str, value: int):
"""Assign a value to a register.
:raise KeyError: If `reg` is not a register name.
"""
regname = self.arch.to_regname(reg)
if regname is None:
raise KeyError(f'Not a register name: {reg}')
self.regs[regname] = value
def read_memory(self, addr: int, size: int) -> bytes:
"""Read a number of bytes from memory.
:param addr: The address from which to read data.
:param data: Number of bytes to read, starting at `addr`. Must be
at least zero.
:raise MemoryAccessError: If `[addr, addr + size)` is not entirely
contained in the set of stored bytes.
:raise ValueError: If `size < 0`.
"""
return self.mem.read(addr, size)
def write_memory(self, addr: int, data: bytes):
"""Write a number of bytes to memory.
:param addr: The address at which to store the data.
:param data: The data to store at `addr`.
"""
self.mem.write(addr, data)
def __repr__(self):
return f'Snapshot ({self.arch.archname}): ' \
+ repr({r: hex(v) for r, v in self.regs.items() if v is not None})
|