mirror of
https://github.com/JakeHillion/drgn.git
synced 2024-12-22 09:13:06 +00:00
d1ffd581bd
We don't allow this because "value objects with a scalar type cannot be reinterpreted, as their memory layout in the program is not known". That doesn't really make sense: we already support reconstructing the in-memory representation with drgn_object_read_bytes(). Implement this by making drgn_object_slice() support slicing all objects, using drgn_object_read_bytes() when necessary, then make drgn_object_reinterpret() a trivial wrapper around it. Closes #378. Signed-off-by: Omar Sandoval <osandov@osandov.com>
2190 lines
76 KiB
Python
2190 lines
76 KiB
Python
# Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
# SPDX-License-Identifier: LGPL-2.1-or-later
|
|
|
|
import math
|
|
import operator
|
|
import struct
|
|
|
|
from drgn import (
|
|
FaultError,
|
|
Object,
|
|
ObjectAbsentError,
|
|
OutOfBoundsError,
|
|
Qualifiers,
|
|
TypeMember,
|
|
cast,
|
|
reinterpret,
|
|
sizeof,
|
|
)
|
|
from tests import (
|
|
MockMemorySegment,
|
|
MockProgramTestCase,
|
|
assertReprPrettyEqualsStr,
|
|
mock_program,
|
|
)
|
|
|
|
|
|
class TestInit(MockProgramTestCase):
|
|
def test_type_stays_alive(self):
|
|
obj = Object(self.prog, self.prog.int_type("int", 4, True), value=0)
|
|
self.assertIdentical(obj.type_, self.prog.int_type("int", 4, True))
|
|
type_ = obj.type_
|
|
del obj
|
|
self.assertIdentical(type_, self.prog.int_type("int", 4, True))
|
|
|
|
def test_type(self):
|
|
self.assertRaisesRegex(
|
|
TypeError, "type must be Type, str, or None", Object, self.prog, 1, value=0
|
|
)
|
|
self.assertRaisesRegex(
|
|
ValueError, "reference must have type", Object, self.prog, address=0
|
|
)
|
|
self.assertRaisesRegex(
|
|
ValueError, "absent object must have type", Object, self.prog
|
|
)
|
|
|
|
def test_address_nand_value(self):
|
|
self.assertRaisesRegex(
|
|
ValueError,
|
|
"object cannot have address and value",
|
|
Object,
|
|
self.prog,
|
|
"int",
|
|
0,
|
|
address=0,
|
|
)
|
|
self.assertRaisesRegex(
|
|
ValueError,
|
|
"object cannot have address and value",
|
|
Object,
|
|
self.prog,
|
|
"int",
|
|
value=0,
|
|
address=0,
|
|
)
|
|
|
|
def test_integer_address(self):
|
|
self.assertRaises(TypeError, Object, self.prog, "int", address="NULL")
|
|
|
|
def test_bit_field_size(self):
|
|
self.assertRaises(
|
|
TypeError, Object, self.prog, "int", address=0, bit_field_size="1"
|
|
)
|
|
self.assertRaisesRegex(
|
|
ValueError,
|
|
"bit field size cannot be zero",
|
|
Object,
|
|
self.prog,
|
|
"int",
|
|
address=0,
|
|
bit_field_size=0,
|
|
)
|
|
|
|
def test_bit_offset(self):
|
|
self.assertRaisesRegex(
|
|
ValueError,
|
|
"value cannot have bit offset",
|
|
Object,
|
|
self.prog,
|
|
"int",
|
|
value=0,
|
|
bit_offset=4,
|
|
)
|
|
self.assertRaisesRegex(
|
|
ValueError,
|
|
"value cannot have bit offset",
|
|
Object,
|
|
self.prog,
|
|
self.point_type,
|
|
value={},
|
|
bit_offset=4,
|
|
)
|
|
self.assertRaisesRegex(
|
|
ValueError,
|
|
"absent object cannot have bit offset",
|
|
Object,
|
|
self.prog,
|
|
"int",
|
|
bit_offset=4,
|
|
)
|
|
|
|
def test_integer_size(self):
|
|
self.assertRaisesRegex(
|
|
ValueError,
|
|
"unsupported integer bit size",
|
|
Object,
|
|
self.prog,
|
|
self.prog.int_type("ZERO", 0, True),
|
|
)
|
|
self.assertRaisesRegex(
|
|
ValueError,
|
|
"unsupported integer bit size",
|
|
Object,
|
|
self.prog,
|
|
self.prog.int_type("BIGGEST", 1024**3, True),
|
|
)
|
|
|
|
def test_float_size(self):
|
|
self.assertRaisesRegex(
|
|
ValueError,
|
|
"unsupported floating-point bit size",
|
|
Object,
|
|
self.prog,
|
|
self.prog.float_type("ZERO", 0),
|
|
)
|
|
self.assertRaisesRegex(
|
|
ValueError,
|
|
"unsupported floating-point bit size",
|
|
Object,
|
|
self.prog,
|
|
self.prog.float_type("BIGGEST", 32 + 1),
|
|
)
|
|
|
|
|
|
def _int_bits_cases(prog):
|
|
for signed in (True, False):
|
|
for byteorder in ("little", "big"):
|
|
for bit_size in range(1, 129):
|
|
if bit_size <= 8:
|
|
size = 1
|
|
else:
|
|
size = 1 << ((bit_size - 1).bit_length() - 3)
|
|
type = prog.int_type(
|
|
"" if signed else "u" + f"int{size}", size, signed, byteorder
|
|
)
|
|
if signed:
|
|
values = (
|
|
0xF8935CF44C45202748DE66B49BA0CBAC % (1 << (bit_size - 1)),
|
|
~0xF8935CF44C45202748DE66B49BA0CBAC % (1 << (bit_size - 1)),
|
|
-0xC256D5AAFFDC3179A6AC84E7154A215D % -(1 << (bit_size - 1)),
|
|
~-0xC256D5AAFFDC3179A6AC84E7154A215D % -(1 << (bit_size - 1)),
|
|
)
|
|
else:
|
|
values = (
|
|
0xF8935CF44C45202748DE66B49BA0CBAC % (1 << bit_size),
|
|
~0xF8935CF44C45202748DE66B49BA0CBAC % (1 << bit_size),
|
|
)
|
|
for value in values:
|
|
# value_bytes is the value converted to bytes.
|
|
if byteorder == "little":
|
|
value_bytes = (value & ((1 << bit_size) - 1)).to_bytes(
|
|
(bit_size + 7) // 8, byteorder
|
|
)
|
|
else:
|
|
value_bytes = (value << (-bit_size % 8)).to_bytes(
|
|
(bit_size + 7) // 8, byteorder, signed=signed
|
|
)
|
|
for bit_offset in range(8):
|
|
# source_bytes is a buffer containing the value at the
|
|
# given bit offset, with extra bits that should be
|
|
# ignored.
|
|
if byteorder == "little":
|
|
source_bytes = bytearray(
|
|
(value << bit_offset).to_bytes(
|
|
(bit_offset + bit_size + 7) // 8,
|
|
byteorder,
|
|
signed=signed,
|
|
)
|
|
)
|
|
source_bytes[0] |= (1 << bit_offset) - 1
|
|
if (bit_offset + bit_size) % 8 != 0:
|
|
source_bytes[-1] ^= (
|
|
0xFF << ((bit_offset + bit_size) % 8)
|
|
) & 0xFF
|
|
else:
|
|
source_bytes = bytearray(
|
|
(value << (-(bit_offset + bit_size) % 8)).to_bytes(
|
|
(bit_offset + bit_size + 7) // 8,
|
|
byteorder,
|
|
signed=signed,
|
|
)
|
|
)
|
|
source_bytes[0] ^= (0xFF00 >> bit_offset) & 0xFF
|
|
if (bit_offset + bit_size) % 8 != 0:
|
|
source_bytes[-1] |= (
|
|
1 << (-(bit_offset + bit_size) % 8)
|
|
) - 1
|
|
yield signed, byteorder, bit_size, type, bit_offset, value, value_bytes, source_bytes
|
|
|
|
|
|
class TestReference(MockProgramTestCase):
|
|
def test_basic(self):
|
|
self.add_memory_segment((1000).to_bytes(4, "little"), virt_addr=0xFFFF0000)
|
|
|
|
obj = Object(self.prog, "int", address=0xFFFF0000)
|
|
self.assertIs(obj.prog_, self.prog)
|
|
self.assertIdentical(obj.type_, self.prog.type("int"))
|
|
self.assertFalse(obj.absent_)
|
|
self.assertEqual(obj.address_, 0xFFFF0000)
|
|
self.assertEqual(obj.bit_offset_, 0)
|
|
self.assertIsNone(obj.bit_field_size_)
|
|
self.assertEqual(obj.value_(), 1000)
|
|
self.assertEqual(repr(obj), "Object(prog, 'int', address=0xffff0000)")
|
|
|
|
self.assertIdentical(obj.read_(), Object(self.prog, "int", value=1000))
|
|
|
|
obj = Object(
|
|
self.prog, self.prog.int_type("sbe32", 4, True, "big"), address=0xFFFF0000
|
|
)
|
|
self.assertEqual(obj.value_(), -402456576)
|
|
|
|
obj = Object(self.prog, "unsigned int", address=0xFFFF0000, bit_field_size=4)
|
|
self.assertEqual(obj.bit_offset_, 0)
|
|
self.assertEqual(obj.bit_field_size_, 4)
|
|
self.assertEqual(obj.value_(), 8)
|
|
self.assertEqual(
|
|
repr(obj),
|
|
"Object(prog, 'unsigned int', address=0xffff0000, bit_field_size=4)",
|
|
)
|
|
self.assertRaises(TypeError, sizeof, obj)
|
|
|
|
obj = Object(
|
|
self.prog,
|
|
"unsigned int",
|
|
address=0xFFFF0000,
|
|
bit_field_size=4,
|
|
bit_offset=4,
|
|
)
|
|
self.assertEqual(obj.bit_offset_, 4)
|
|
self.assertEqual(obj.bit_field_size_, 4)
|
|
self.assertEqual(obj.value_(), 14)
|
|
self.assertEqual(
|
|
repr(obj),
|
|
"Object(prog, 'unsigned int', address=0xffff0000, bit_offset=4, bit_field_size=4)",
|
|
)
|
|
|
|
def test_overflow(self):
|
|
Object(self.prog, "char", address=0xFFFFFFFFFFFFFFFF)
|
|
Object(
|
|
self.prog,
|
|
"char",
|
|
address=0xFFFFFFFFFFFFFFFF,
|
|
bit_field_size=1,
|
|
bit_offset=7,
|
|
)
|
|
|
|
def test_signed_big(self):
|
|
buffer = (-4).to_bytes(16, "little", signed=True)
|
|
self.add_memory_segment(buffer, virt_addr=0xFFFF0000)
|
|
obj = Object(
|
|
self.prog,
|
|
self.prog.int_type("__int128", 16, True),
|
|
address=0xFFFF0000,
|
|
)
|
|
self.assertIs(obj.prog_, self.prog)
|
|
self.assertFalse(obj.absent_)
|
|
self.assertEqual(obj.address_, 0xFFFF0000)
|
|
self.assertEqual(obj.bit_offset_, 0)
|
|
self.assertIsNone(obj.bit_field_size_)
|
|
self.assertEqual(obj.type_.size, 16)
|
|
self.assertEqual(obj.value_(), -4)
|
|
self.assertEqual(obj.to_bytes_(), buffer)
|
|
self.assertEqual(repr(obj), "Object(prog, '__int128', address=0xffff0000)")
|
|
|
|
self.assertIdentical(
|
|
obj.read_(),
|
|
Object(self.prog, self.prog.int_type("__int128", 16, True), value=-4),
|
|
)
|
|
|
|
def test_unsigned_big(self):
|
|
buffer = (1000).to_bytes(16, "little")
|
|
self.add_memory_segment(buffer, virt_addr=0xFFFF0000)
|
|
obj = Object(
|
|
self.prog,
|
|
self.prog.int_type("unsigned __int128", 16, False),
|
|
address=0xFFFF0000,
|
|
)
|
|
self.assertIs(obj.prog_, self.prog)
|
|
self.assertFalse(obj.absent_)
|
|
self.assertEqual(obj.address_, 0xFFFF0000)
|
|
self.assertEqual(obj.bit_offset_, 0)
|
|
self.assertIsNone(obj.bit_field_size_)
|
|
self.assertEqual(obj.type_.size, 16)
|
|
self.assertEqual(obj.value_(), 1000)
|
|
self.assertEqual(obj.to_bytes_(), buffer)
|
|
self.assertEqual(
|
|
repr(obj), "Object(prog, 'unsigned __int128', address=0xffff0000)"
|
|
)
|
|
|
|
self.assertIdentical(
|
|
obj.read_(),
|
|
Object(
|
|
self.prog,
|
|
self.prog.int_type("unsigned __int128", 16, False),
|
|
value=1000,
|
|
),
|
|
)
|
|
|
|
def test_int_bits(self):
|
|
buffer = bytearray(17)
|
|
self.add_memory_segment(buffer, virt_addr=0xFFFF0000)
|
|
for (
|
|
signed,
|
|
byteorder,
|
|
bit_size,
|
|
type,
|
|
bit_offset,
|
|
value,
|
|
value_bytes,
|
|
source_bytes,
|
|
) in _int_bits_cases(self.prog):
|
|
with self.subTest(
|
|
signed=signed,
|
|
byteorder=byteorder,
|
|
bit_size=bit_size,
|
|
bit_offset=bit_offset,
|
|
value=value,
|
|
):
|
|
buffer[: len(source_bytes)] = source_bytes
|
|
obj = Object(
|
|
self.prog,
|
|
type,
|
|
address=0xFFFF0000,
|
|
bit_offset=bit_offset,
|
|
bit_field_size=bit_size,
|
|
)
|
|
self.assertEqual(obj.value_(), value)
|
|
self.assertEqual(obj.to_bytes_(), value_bytes)
|
|
|
|
def test_read_float(self):
|
|
pi32 = struct.unpack("f", struct.pack("f", math.pi))[0]
|
|
for bit_size in [32, 64]:
|
|
for bit_offset in range(8):
|
|
for byteorder in ["little", "big"]:
|
|
if bit_size == 64:
|
|
fmt = "<d"
|
|
expected = math.pi
|
|
else:
|
|
fmt = "<f"
|
|
expected = pi32
|
|
tmp = int.from_bytes(struct.pack(fmt, math.pi), "little")
|
|
if byteorder == "little":
|
|
tmp <<= bit_offset
|
|
else:
|
|
tmp <<= (8 - bit_size - bit_offset) % 8
|
|
buf = tmp.to_bytes((bit_size + bit_offset + 7) // 8, byteorder)
|
|
prog = mock_program(segments=[MockMemorySegment(buf, 0)])
|
|
obj = Object(
|
|
prog,
|
|
prog.float_type(
|
|
"double" if bit_size == 64 else "float",
|
|
bit_size // 8,
|
|
byteorder,
|
|
),
|
|
address=0,
|
|
bit_offset=bit_offset,
|
|
)
|
|
self.assertEqual(obj.value_(), expected)
|
|
|
|
def test_struct(self):
|
|
self.add_memory_segment(
|
|
(
|
|
(99).to_bytes(4, "little")
|
|
+ (-1).to_bytes(4, "little", signed=True)
|
|
+ (12345).to_bytes(4, "little")
|
|
+ (0).to_bytes(4, "little")
|
|
),
|
|
virt_addr=0xFFFF0000,
|
|
)
|
|
self.types.append(self.point_type)
|
|
obj = Object(self.prog, "struct point", address=0xFFFF0000)
|
|
self.assertEqual(obj.value_(), {"x": 99, "y": -1})
|
|
self.assertEqual(sizeof(obj), 8)
|
|
|
|
type_ = self.prog.struct_type(
|
|
"foo",
|
|
16,
|
|
(
|
|
TypeMember(self.point_type, "point"),
|
|
TypeMember(
|
|
self.prog.struct_type(
|
|
None,
|
|
8,
|
|
(
|
|
TypeMember(self.prog.int_type("int", 4, True), "bar"),
|
|
TypeMember(self.prog.int_type("int", 4, True), "baz", 32),
|
|
),
|
|
),
|
|
None,
|
|
64,
|
|
),
|
|
),
|
|
)
|
|
obj = Object(self.prog, type_, address=0xFFFF0000)
|
|
self.assertEqual(
|
|
obj.value_(), {"point": {"x": 99, "y": -1}, "bar": 12345, "baz": 0}
|
|
)
|
|
|
|
def test_read_struct_bit_offset(self):
|
|
value = 12345678912345678989
|
|
for bit_size in range(1, 65):
|
|
for bit_offset in range(8):
|
|
size = (bit_size + bit_offset + 7) // 8
|
|
size_mask = (1 << (8 * size)) - 1
|
|
for byteorder in ["little", "big"]:
|
|
if byteorder == "little":
|
|
tmp = value << bit_offset
|
|
else:
|
|
tmp = value << (8 - bit_size - bit_offset) % 8
|
|
tmp &= size_mask
|
|
buf = tmp.to_bytes(size, byteorder) + b"\0"
|
|
prog = mock_program(segments=[MockMemorySegment(buf, 0)])
|
|
obj = Object(
|
|
prog,
|
|
prog.struct_type(
|
|
None,
|
|
(bit_offset + bit_size + 7) // 8,
|
|
(
|
|
TypeMember(
|
|
Object(
|
|
prog,
|
|
prog.int_type(
|
|
"unsigned long long",
|
|
8,
|
|
False,
|
|
byteorder,
|
|
),
|
|
bit_field_size=bit_size,
|
|
),
|
|
"x",
|
|
bit_offset=bit_offset,
|
|
),
|
|
),
|
|
),
|
|
address=0,
|
|
)
|
|
self.assertEqual(obj.x.value_(), value & ((1 << bit_size) - 1))
|
|
self.assertEqual(
|
|
obj.x.read_().value_(), value & ((1 << bit_size) - 1)
|
|
)
|
|
self.assertEqual(
|
|
obj.read_().x.value_(), value & ((1 << bit_size) - 1)
|
|
)
|
|
|
|
def test_array(self):
|
|
segment = bytearray()
|
|
for i in range(10):
|
|
segment.extend(i.to_bytes(4, "little"))
|
|
self.add_memory_segment(segment, virt_addr=0xFFFF0000)
|
|
obj = Object(self.prog, "int [5]", address=0xFFFF0000)
|
|
self.assertEqual(obj.value_(), [0, 1, 2, 3, 4])
|
|
self.assertEqual(sizeof(obj), 20)
|
|
|
|
obj = Object(self.prog, "int [2][5]", address=0xFFFF0000)
|
|
self.assertEqual(obj.value_(), [[0, 1, 2, 3, 4], [5, 6, 7, 8, 9]])
|
|
|
|
obj = Object(self.prog, "int [2][2][2]", address=0xFFFF0000)
|
|
self.assertEqual(obj.value_(), [[[0, 1], [2, 3]], [[4, 5], [6, 7]]])
|
|
|
|
def test_void(self):
|
|
obj = Object(self.prog, self.prog.void_type(), address=0)
|
|
self.assertIs(obj.prog_, self.prog)
|
|
self.assertIdentical(obj.type_, self.prog.void_type())
|
|
self.assertEqual(obj.address_, 0)
|
|
self.assertEqual(obj.bit_offset_, 0)
|
|
self.assertIsNone(obj.bit_field_size_)
|
|
self.assertRaisesRegex(
|
|
TypeError, "cannot read object with void type", obj.value_
|
|
)
|
|
self.assertRaisesRegex(
|
|
TypeError, "cannot read object with void type", obj.read_
|
|
)
|
|
self.assertRaises(TypeError, sizeof, obj)
|
|
|
|
def test_function(self):
|
|
obj = Object(
|
|
self.prog,
|
|
self.prog.function_type(self.prog.void_type(), (), False),
|
|
address=0,
|
|
)
|
|
self.assertIs(obj.prog_, self.prog)
|
|
self.assertIdentical(
|
|
obj.type_, self.prog.function_type(self.prog.void_type(), (), False)
|
|
)
|
|
self.assertEqual(obj.address_, 0)
|
|
self.assertEqual(obj.bit_offset_, 0)
|
|
self.assertIsNone(obj.bit_field_size_)
|
|
self.assertRaisesRegex(
|
|
TypeError, "cannot read object with function type", obj.value_
|
|
)
|
|
self.assertRaisesRegex(
|
|
TypeError, "cannot read object with function type", obj.read_
|
|
)
|
|
self.assertRaises(TypeError, sizeof, obj)
|
|
|
|
def test_incomplete(self):
|
|
# It's valid to create references with incomplete type, but not to read
|
|
# from them.
|
|
obj = Object(self.prog, self.prog.struct_type("foo"), address=0)
|
|
self.assertRaisesRegex(
|
|
TypeError, "cannot read object with incomplete structure type", obj.value_
|
|
)
|
|
self.assertRaisesRegex(
|
|
TypeError, "cannot read object with incomplete structure type", obj.read_
|
|
)
|
|
self.assertRaises(TypeError, sizeof, obj)
|
|
|
|
obj = Object(self.prog, self.prog.union_type("foo"), address=0)
|
|
self.assertRaisesRegex(
|
|
TypeError, "cannot read object with incomplete union type", obj.value_
|
|
)
|
|
self.assertRaisesRegex(
|
|
TypeError, "cannot read object with incomplete union type", obj.read_
|
|
)
|
|
|
|
obj = Object(self.prog, self.prog.enum_type("foo"), address=0)
|
|
self.assertRaisesRegex(
|
|
TypeError, "cannot read object with incomplete enumerated type", obj.value_
|
|
)
|
|
self.assertRaisesRegex(
|
|
TypeError, "cannot read object with incomplete enumerated type", obj.read_
|
|
)
|
|
|
|
obj = Object(
|
|
self.prog,
|
|
self.prog.array_type(self.prog.int_type("int", 4, True)),
|
|
address=0,
|
|
)
|
|
self.assertRaisesRegex(
|
|
TypeError, "cannot read object with incomplete array type", obj.value_
|
|
)
|
|
self.assertRaisesRegex(
|
|
TypeError, "cannot read object with incomplete array type", obj.read_
|
|
)
|
|
|
|
def test_non_scalar_bit_offset(self):
|
|
obj = Object(
|
|
self.prog,
|
|
self.prog.struct_type(
|
|
"weird", 9, (TypeMember(self.point_type, "point", bit_offset=1),)
|
|
),
|
|
address=0xFFFF0000,
|
|
)
|
|
self.assertRaisesRegex(
|
|
ValueError, "non-scalar must be byte-aligned", obj.member_, "point"
|
|
)
|
|
self.assertRaisesRegex(
|
|
ValueError,
|
|
"non-scalar must be byte-aligned",
|
|
Object,
|
|
self.prog,
|
|
self.point_type,
|
|
address=0xFFFF0000,
|
|
bit_offset=1,
|
|
)
|
|
self.assertIdentical(
|
|
Object(self.prog, self.point_type, address=0xFFFF0000, bit_offset=32),
|
|
Object(self.prog, self.point_type, address=0xFFFF0004),
|
|
)
|
|
|
|
def test_bit_field_of_big_int(self):
|
|
buffer = (1000).to_bytes(4, "little")
|
|
self.add_memory_segment(buffer, virt_addr=0xFFFF0000)
|
|
obj = Object(
|
|
self.prog,
|
|
self.prog.int_type("unsigned __int128", 16, False),
|
|
address=0xFFFF0000,
|
|
bit_field_size=32,
|
|
)
|
|
self.assertIs(obj.prog_, self.prog)
|
|
self.assertFalse(obj.absent_)
|
|
self.assertEqual(obj.address_, 0xFFFF0000)
|
|
self.assertEqual(obj.bit_offset_, 0)
|
|
self.assertEqual(obj.bit_field_size_, 32)
|
|
self.assertEqual(obj.value_(), 1000)
|
|
self.assertIdentical(
|
|
obj.read_(),
|
|
Object(
|
|
self.prog,
|
|
self.prog.int_type("unsigned __int128", 16, False),
|
|
bit_field_size=32,
|
|
value=1000,
|
|
),
|
|
)
|
|
self.assertEqual(obj.to_bytes_(), buffer)
|
|
self.assertEqual(
|
|
repr(obj),
|
|
"Object(prog, 'unsigned __int128', address=0xffff0000, bit_field_size=32)",
|
|
)
|
|
|
|
def test_non_standard_float(self):
|
|
for size in (2, 10, 16, 32):
|
|
buffer = (1000).to_bytes(size, "little")
|
|
self.add_memory_segment(buffer, virt_addr=0xFFFF0000)
|
|
obj = Object(
|
|
self.prog,
|
|
self.prog.float_type("CUSTOM_FLOAT", size),
|
|
address=0xFFFF0000,
|
|
)
|
|
self.assertIs(obj.prog_, self.prog)
|
|
self.assertFalse(obj.absent_)
|
|
self.assertEqual(obj.address_, 0xFFFF0000)
|
|
self.assertEqual(obj.bit_offset_, 0)
|
|
self.assertIsNone(obj.bit_field_size_)
|
|
self.assertEqual(obj.type_.size, size)
|
|
self.assertRaisesRegex(
|
|
NotImplementedError,
|
|
"float values which are not 32 or 64 bits are not yet supported",
|
|
obj.value_,
|
|
)
|
|
self.assertEqual(obj.to_bytes_(), buffer)
|
|
self.assertEqual(
|
|
repr(obj), "Object(prog, 'CUSTOM_FLOAT', address=0xffff0000)"
|
|
)
|
|
|
|
|
|
class TestValue(MockProgramTestCase):
|
|
def test_positional(self):
|
|
self.assertIdentical(
|
|
Object(self.prog, "int", 1), Object(self.prog, "int", value=1)
|
|
)
|
|
|
|
def test_signed(self):
|
|
obj = Object(self.prog, "int", value=-4)
|
|
self.assertIs(obj.prog_, self.prog)
|
|
self.assertIdentical(obj.type_, self.prog.type("int"))
|
|
self.assertFalse(obj.absent_)
|
|
self.assertIsNone(obj.address_)
|
|
self.assertIsNone(obj.bit_offset_)
|
|
self.assertIsNone(obj.bit_field_size_)
|
|
self.assertEqual(obj.value_(), -4)
|
|
self.assertEqual(repr(obj), "Object(prog, 'int', value=-4)")
|
|
|
|
self.assertIdentical(obj.read_(), obj)
|
|
|
|
self.assertIdentical(Object(self.prog, "int", value=2**32 - 4), obj)
|
|
self.assertIdentical(Object(self.prog, "int", value=2**64 - 4), obj)
|
|
self.assertIdentical(Object(self.prog, "int", value=2**128 - 4), obj)
|
|
self.assertIdentical(Object(self.prog, "int", value=-4.6), obj)
|
|
|
|
self.assertRaisesRegex(
|
|
TypeError,
|
|
"'int' value must be number",
|
|
Object,
|
|
self.prog,
|
|
"int",
|
|
value=b"asdf",
|
|
)
|
|
|
|
obj = Object(self.prog, "int", value=8, bit_field_size=4)
|
|
self.assertIsNone(obj.bit_offset_)
|
|
self.assertEqual(obj.bit_field_size_, 4)
|
|
self.assertEqual(obj.value_(), -8)
|
|
self.assertEqual(repr(obj), "Object(prog, 'int', value=-8, bit_field_size=4)")
|
|
|
|
def test_unsigned(self):
|
|
obj = Object(self.prog, "unsigned int", value=2**32 - 1)
|
|
self.assertIs(obj.prog_, self.prog)
|
|
self.assertIdentical(obj.type_, self.prog.type("unsigned int"))
|
|
self.assertFalse(obj.absent_)
|
|
self.assertIsNone(obj.address_)
|
|
self.assertIsNone(obj.bit_offset_)
|
|
self.assertIsNone(obj.bit_field_size_)
|
|
self.assertEqual(obj.value_(), 2**32 - 1)
|
|
self.assertEqual(repr(obj), "Object(prog, 'unsigned int', value=4294967295)")
|
|
|
|
self.assertIdentical(Object(self.prog, "unsigned int", value=-1), obj)
|
|
self.assertIdentical(Object(self.prog, "unsigned int", value=2**64 - 1), obj)
|
|
self.assertIdentical(Object(self.prog, "unsigned int", value=2**65 - 1), obj)
|
|
self.assertIdentical(
|
|
Object(self.prog, "unsigned int", value=2**32 - 1 + 0.9), obj
|
|
)
|
|
|
|
self.assertRaisesRegex(
|
|
TypeError,
|
|
"'unsigned int' value must be number",
|
|
Object,
|
|
self.prog,
|
|
"unsigned int",
|
|
value="foo",
|
|
)
|
|
|
|
obj = Object(self.prog, "unsigned int", value=24, bit_field_size=4)
|
|
self.assertIsNone(obj.bit_offset_)
|
|
self.assertEqual(obj.bit_field_size_, 4)
|
|
self.assertEqual(obj.value_(), 8)
|
|
self.assertEqual(
|
|
repr(obj), "Object(prog, 'unsigned int', value=8, bit_field_size=4)"
|
|
)
|
|
|
|
value = 12345678912345678989
|
|
for bit_size in range(1, 65):
|
|
self.assertEqual(
|
|
Object(
|
|
self.prog,
|
|
"unsigned long long",
|
|
value=value,
|
|
bit_field_size=bit_size,
|
|
).value_(),
|
|
value & ((1 << bit_size) - 1),
|
|
)
|
|
|
|
def _test_big_int_operators(self, type):
|
|
big_obj = Object(self.prog, type, 1000)
|
|
obj = Object(self.prog, "int", 0)
|
|
for op in (
|
|
operator.lt,
|
|
operator.le,
|
|
operator.eq,
|
|
operator.ge,
|
|
operator.gt,
|
|
operator.add,
|
|
operator.and_,
|
|
operator.lshift,
|
|
operator.mod,
|
|
operator.mul,
|
|
operator.or_,
|
|
operator.rshift,
|
|
operator.sub,
|
|
operator.truediv,
|
|
operator.xor,
|
|
):
|
|
self.assertRaises(NotImplementedError, op, big_obj, obj)
|
|
self.assertRaises(NotImplementedError, op, obj, big_obj)
|
|
|
|
for op in (
|
|
operator.inv,
|
|
operator.neg,
|
|
operator.pos,
|
|
):
|
|
self.assertRaises(NotImplementedError, op, big_obj)
|
|
|
|
self.assertFalse(not big_obj)
|
|
self.assertTrue(bool(big_obj))
|
|
for op in (
|
|
operator.index,
|
|
round,
|
|
math.trunc,
|
|
math.floor,
|
|
math.ceil,
|
|
):
|
|
self.assertEqual(op(big_obj), 1000)
|
|
|
|
def test_signed_big(self):
|
|
type = self.prog.int_type("__int128", 16, True)
|
|
obj = Object(self.prog, type, -4)
|
|
self.assertIs(obj.prog_, self.prog)
|
|
self.assertIdentical(obj.type_, self.prog.int_type("__int128", 16, True))
|
|
self.assertFalse(obj.absent_)
|
|
self.assertIsNone(obj.address_)
|
|
self.assertIsNone(obj.bit_offset_)
|
|
self.assertIsNone(obj.bit_field_size_)
|
|
self.assertEqual(obj.value_(), -4)
|
|
self.assertEqual(repr(obj), "Object(prog, '__int128', value=-4)")
|
|
|
|
self.assertIdentical(Object(self.prog, type, value=2**128 - 4), obj)
|
|
self.assertIdentical(Object(self.prog, type, value=-4.6), obj)
|
|
|
|
self.assertIdentical(
|
|
Object(self.prog, type, value=2**128 + 4),
|
|
Object(self.prog, type, value=4),
|
|
)
|
|
|
|
self.assertRaisesRegex(
|
|
TypeError,
|
|
"'__int128' value must be number",
|
|
Object,
|
|
self.prog,
|
|
type,
|
|
value=b"asdf",
|
|
)
|
|
|
|
self._test_big_int_operators(type)
|
|
|
|
def test_unsigned_big(self):
|
|
type = self.prog.int_type("unsigned __int128", 16, False)
|
|
obj = Object(self.prog, type, 2**128 - 1)
|
|
self.assertIs(obj.prog_, self.prog)
|
|
self.assertIdentical(
|
|
obj.type_, self.prog.int_type("unsigned __int128", 16, False)
|
|
)
|
|
self.assertFalse(obj.absent_)
|
|
self.assertIsNone(obj.address_)
|
|
self.assertIsNone(obj.bit_offset_)
|
|
self.assertIsNone(obj.bit_field_size_)
|
|
self.assertEqual(obj.value_(), 2**128 - 1)
|
|
self.assertEqual(
|
|
repr(obj),
|
|
"Object(prog, 'unsigned __int128', value=340282366920938463463374607431768211455)",
|
|
)
|
|
|
|
self.assertIdentical(Object(self.prog, type, value=-1), obj)
|
|
self.assertIdentical(Object(self.prog, type, value=2**128 - 1), obj)
|
|
self.assertIdentical(Object(self.prog, type, value=2**129 - 1), obj)
|
|
self.assertIdentical(
|
|
Object(self.prog, type, value=0.1), Object(self.prog, type, value=0)
|
|
)
|
|
|
|
self.assertRaisesRegex(
|
|
TypeError,
|
|
"'unsigned __int128' value must be number",
|
|
Object,
|
|
self.prog,
|
|
type,
|
|
value="foo",
|
|
)
|
|
|
|
self._test_big_int_operators(type)
|
|
|
|
def test_int_bits(self):
|
|
for (
|
|
signed,
|
|
byteorder,
|
|
bit_size,
|
|
type,
|
|
bit_offset,
|
|
value,
|
|
value_bytes,
|
|
source_bytes,
|
|
) in _int_bits_cases(self.prog):
|
|
with self.subTest(
|
|
signed=signed,
|
|
byteorder=byteorder,
|
|
bit_size=bit_size,
|
|
bit_offset=bit_offset,
|
|
value=value,
|
|
):
|
|
obj = Object(self.prog, type, value, bit_field_size=bit_size)
|
|
self.assertEqual(obj.value_(), value)
|
|
self.assertEqual(obj.to_bytes_(), value_bytes)
|
|
self.assertIdentical(
|
|
Object.from_bytes_(
|
|
self.prog,
|
|
obj.type_,
|
|
source_bytes,
|
|
bit_offset=bit_offset,
|
|
bit_field_size=bit_size,
|
|
),
|
|
obj,
|
|
)
|
|
|
|
def test_float(self):
|
|
obj = Object(self.prog, "double", value=3.14)
|
|
self.assertIs(obj.prog_, self.prog)
|
|
self.assertIdentical(obj.type_, self.prog.type("double"))
|
|
self.assertFalse(obj.absent_)
|
|
self.assertIsNone(obj.address_)
|
|
self.assertEqual(obj.value_(), 3.14)
|
|
self.assertEqual(repr(obj), "Object(prog, 'double', value=3.14)")
|
|
|
|
obj = Object(self.prog, "double", value=-100.0)
|
|
self.assertIdentical(Object(self.prog, "double", value=-100), obj)
|
|
|
|
self.assertRaisesRegex(
|
|
TypeError,
|
|
"'double' value must be number",
|
|
Object,
|
|
self.prog,
|
|
"double",
|
|
value={},
|
|
)
|
|
|
|
self.assertEqual(Object(self.prog, "double", value=math.e).value_(), math.e)
|
|
self.assertEqual(
|
|
Object(self.prog, "float", value=math.e).value_(),
|
|
struct.unpack("f", struct.pack("f", math.e))[0],
|
|
)
|
|
|
|
def test_enum(self):
|
|
self.assertEqual(Object(self.prog, self.color_type, value=0).value_(), 0)
|
|
|
|
def test_incomplete_struct(self):
|
|
self.assertRaisesRegex(
|
|
TypeError,
|
|
"cannot create value with incomplete structure type",
|
|
Object,
|
|
self.prog,
|
|
self.prog.struct_type("foo"),
|
|
value={},
|
|
)
|
|
|
|
def test_incomplete_union(self):
|
|
self.assertRaisesRegex(
|
|
TypeError,
|
|
"cannot create value with incomplete union type",
|
|
Object,
|
|
self.prog,
|
|
self.prog.union_type("foo"),
|
|
value={},
|
|
)
|
|
|
|
def test_incomplete_class(self):
|
|
self.assertRaisesRegex(
|
|
TypeError,
|
|
"cannot create value with incomplete class type",
|
|
Object,
|
|
self.prog,
|
|
self.prog.class_type("foo"),
|
|
value={},
|
|
)
|
|
|
|
def test_incomplete_enum(self):
|
|
self.assertRaisesRegex(
|
|
TypeError,
|
|
"cannot create value with incomplete enumerated type",
|
|
Object,
|
|
self.prog,
|
|
self.prog.enum_type("foo"),
|
|
value=0,
|
|
)
|
|
|
|
def test_incomplete_array(self):
|
|
self.assertRaisesRegex(
|
|
TypeError,
|
|
"cannot create value with incomplete array type",
|
|
Object,
|
|
self.prog,
|
|
self.prog.array_type(self.prog.int_type("int", 4, True)),
|
|
value=[],
|
|
)
|
|
|
|
def test_compound(self):
|
|
obj = Object(self.prog, self.point_type, value={"x": 100, "y": -5})
|
|
self.assertIdentical(obj.x, Object(self.prog, "int", value=100))
|
|
self.assertIdentical(obj.y, Object(self.prog, "int", value=-5))
|
|
|
|
self.assertIdentical(
|
|
Object(self.prog, self.point_type, value={}),
|
|
Object(self.prog, self.point_type, value={"x": 0, "y": 0}),
|
|
)
|
|
|
|
value = {
|
|
"a": {"x": 1, "y": 2},
|
|
"b": {"x": 3, "y": 4},
|
|
}
|
|
obj = Object(self.prog, self.line_segment_type, value=value)
|
|
self.assertIdentical(
|
|
obj.a, Object(self.prog, self.point_type, value={"x": 1, "y": 2})
|
|
)
|
|
self.assertIdentical(
|
|
obj.b, Object(self.prog, self.point_type, value={"x": 3, "y": 4})
|
|
)
|
|
self.assertEqual(obj.value_(), value)
|
|
|
|
invalid_struct = self.prog.struct_type(
|
|
"foo",
|
|
4,
|
|
(
|
|
TypeMember(self.prog.int_type("short", 2, True), "a"),
|
|
# Straddles the end of the structure.
|
|
TypeMember(self.prog.int_type("int", 4, True), "b", 16),
|
|
# Beyond the end of the structure.
|
|
TypeMember(self.prog.int_type("int", 4, True), "c", 32),
|
|
),
|
|
)
|
|
|
|
Object(self.prog, invalid_struct, value={"a": 0})
|
|
self.assertRaisesRegex(
|
|
OutOfBoundsError,
|
|
"out of bounds of value",
|
|
Object,
|
|
self.prog,
|
|
invalid_struct,
|
|
value={"a": 0, "b": 4},
|
|
)
|
|
self.assertRaisesRegex(
|
|
OutOfBoundsError,
|
|
"out of bounds of value",
|
|
Object,
|
|
self.prog,
|
|
invalid_struct,
|
|
value={"a": 0, "c": 4},
|
|
)
|
|
|
|
self.assertRaisesRegex(
|
|
TypeError,
|
|
"must be dictionary or mapping",
|
|
Object,
|
|
self.prog,
|
|
self.point_type,
|
|
value=1,
|
|
)
|
|
self.assertRaisesRegex(
|
|
TypeError,
|
|
"member key must be string",
|
|
Object,
|
|
self.prog,
|
|
self.point_type,
|
|
value={0: 0},
|
|
)
|
|
self.assertRaisesRegex(
|
|
TypeError,
|
|
"must be number",
|
|
Object,
|
|
self.prog,
|
|
self.point_type,
|
|
value={"x": []},
|
|
)
|
|
self.assertRaisesRegex(
|
|
LookupError,
|
|
"has no member 'z'",
|
|
Object,
|
|
self.prog,
|
|
self.point_type,
|
|
value={"z": 999},
|
|
)
|
|
|
|
def test_compound_offset(self):
|
|
value = {"n": 23, "x": 100, "y": -5}
|
|
obj = Object(
|
|
self.prog,
|
|
self.prog.struct_type(
|
|
None,
|
|
12,
|
|
(
|
|
TypeMember(self.prog.int_type("int", 4, True), "n"),
|
|
TypeMember(self.point_type, None, 32),
|
|
),
|
|
),
|
|
value,
|
|
)
|
|
self.assertEqual(obj.value_(), value)
|
|
self.assertIdentical(obj.x, Object(self.prog, "int", value=100))
|
|
self.assertIdentical(obj.y, Object(self.prog, "int", value=-5))
|
|
|
|
def test_compound_float(self):
|
|
for byteorder in ("little", "big"):
|
|
for type in (
|
|
self.prog.float_type("double", 8, byteorder),
|
|
self.prog.float_type("float", 4, byteorder),
|
|
):
|
|
with self.subTest(byteorder=byteorder, type=type.name):
|
|
obj = Object(
|
|
self.prog,
|
|
self.prog.struct_type(
|
|
None,
|
|
type.size * 2,
|
|
(
|
|
TypeMember(type, "a"),
|
|
TypeMember(type, "b", type.size * 8),
|
|
),
|
|
),
|
|
value={"a": 1234, "b": -3.125},
|
|
)
|
|
self.assertEqual(obj.a.value_(), 1234.0)
|
|
self.assertEqual(obj.b.value_(), -3.125)
|
|
|
|
def test_compound_bit_fields(self):
|
|
a = 0xF8935CF44C45202748DE66B49BA0CBAC
|
|
b = -0xC256D5AAFFDC3179A6AC84E7154A215D
|
|
for signed in (True, False):
|
|
if signed:
|
|
|
|
def truncate(x, bit_size):
|
|
sign = 1 << (bit_size - 1)
|
|
return (x & (sign - 1)) - (x & sign)
|
|
|
|
else:
|
|
|
|
def truncate(x, bit_size):
|
|
return x & ((1 << bit_size) - 1)
|
|
|
|
for byteorder in ("little", "big"):
|
|
for bit_size in range(1, 128):
|
|
with self.subTest(
|
|
signed=signed, byteorder=byteorder, bit_size=bit_size
|
|
):
|
|
type = self.prog.int_type(
|
|
("" if signed else "unsigned ") + "__int128", 16, signed
|
|
)
|
|
obj = Object(
|
|
self.prog,
|
|
self.prog.struct_type(
|
|
None,
|
|
type.size * 2,
|
|
(
|
|
TypeMember(
|
|
Object(
|
|
self.prog, type, bit_field_size=bit_size
|
|
),
|
|
"a",
|
|
),
|
|
TypeMember(
|
|
Object(
|
|
self.prog,
|
|
type,
|
|
bit_field_size=128 - bit_size,
|
|
),
|
|
"b",
|
|
bit_size,
|
|
),
|
|
),
|
|
),
|
|
value={"a": a, "b": b},
|
|
)
|
|
self.assertEqual(obj.a.value_(), truncate(a, bit_size))
|
|
self.assertEqual(obj.b.value_(), truncate(b, 128 - bit_size))
|
|
|
|
def test_pointer(self):
|
|
obj = Object(self.prog, "int *", value=0xFFFF0000)
|
|
self.assertFalse(obj.absent_)
|
|
self.assertIsNone(obj.address_)
|
|
self.assertEqual(obj.value_(), 0xFFFF0000)
|
|
self.assertEqual(repr(obj), "Object(prog, 'int *', value=0xffff0000)")
|
|
|
|
def test_pointer_typedef(self):
|
|
obj = Object(
|
|
self.prog,
|
|
self.prog.typedef_type("INTP", self.prog.type("int *")),
|
|
value=0xFFFF0000,
|
|
)
|
|
self.assertFalse(obj.absent_)
|
|
self.assertIsNone(obj.address_)
|
|
self.assertEqual(obj.value_(), 0xFFFF0000)
|
|
self.assertEqual(repr(obj), "Object(prog, 'INTP', value=0xffff0000)")
|
|
|
|
def test_array(self):
|
|
obj = Object(self.prog, "int [2]", value=[1, 2])
|
|
self.assertFalse(obj.absent_)
|
|
self.assertIsNone(obj.address_)
|
|
|
|
self.assertIdentical(obj[0], Object(self.prog, "int", value=1))
|
|
self.assertIdentical(obj[1], Object(self.prog, "int", value=2))
|
|
|
|
self.assertIdentical(
|
|
Object(self.prog, "int [2]", value=[]),
|
|
Object(self.prog, "int [2]", value=[0, 0]),
|
|
)
|
|
|
|
self.assertRaisesRegex(
|
|
TypeError, "must be iterable", Object, self.prog, "int [1]", value=1
|
|
)
|
|
self.assertRaisesRegex(
|
|
ValueError,
|
|
"too many items in array value",
|
|
Object,
|
|
self.prog,
|
|
"int [1]",
|
|
value=[1, 2],
|
|
)
|
|
|
|
def test_non_scalar_bit_offset(self):
|
|
obj = Object(
|
|
self.prog,
|
|
self.prog.struct_type(
|
|
"weird", 9, (TypeMember(self.point_type, "point", bit_offset=1),)
|
|
),
|
|
value={},
|
|
)
|
|
self.assertRaisesRegex(
|
|
ValueError, "non-scalar must be byte-aligned", obj.member_, "point"
|
|
)
|
|
|
|
def test_small_bit_field_of_big_int(self):
|
|
obj = Object(
|
|
self.prog,
|
|
self.prog.int_type("unsigned __int128", 16, False),
|
|
value=1000,
|
|
bit_field_size=32,
|
|
)
|
|
self.assertIsNone(obj.bit_offset_)
|
|
self.assertEqual(obj.bit_field_size_, 32)
|
|
self.assertEqual(obj.value_(), 1000)
|
|
self.assertEqual(
|
|
repr(obj),
|
|
"Object(prog, 'unsigned __int128', value=1000, bit_field_size=32)",
|
|
)
|
|
|
|
def test_non_standard_float(self):
|
|
for size in (2, 10, 16, 32):
|
|
type = self.prog.float_type("CUSTOM_FLOAT", size)
|
|
self.assertRaisesRegex(
|
|
NotImplementedError,
|
|
"float values which are not 32 or 64 bits are not yet supported",
|
|
Object,
|
|
self.prog,
|
|
type,
|
|
0,
|
|
)
|
|
self.assertRaisesRegex(
|
|
NotImplementedError,
|
|
"float values which are not 32 or 64 bits are not yet supported",
|
|
Object.from_bytes_,
|
|
self.prog,
|
|
type,
|
|
(0).to_bytes(size, "little"),
|
|
)
|
|
|
|
|
|
class TestAbsent(MockProgramTestCase):
|
|
def test_basic(self):
|
|
for obj in [
|
|
Object(self.prog, "int"),
|
|
Object(self.prog, "int", value=None, address=None),
|
|
]:
|
|
self.assertIs(obj.prog_, self.prog)
|
|
self.assertIdentical(obj.type_, self.prog.type("int"))
|
|
self.assertTrue(obj.absent_)
|
|
self.assertIsNone(obj.address_)
|
|
self.assertIsNone(obj.bit_offset_)
|
|
self.assertIsNone(obj.bit_field_size_)
|
|
self.assertRaises(ObjectAbsentError, obj.value_)
|
|
self.assertEqual(repr(obj), "Object(prog, 'int')")
|
|
|
|
self.assertRaises(ObjectAbsentError, obj.read_)
|
|
|
|
def test_bit_field(self):
|
|
obj = Object(self.prog, "int", bit_field_size=1)
|
|
self.assertIs(obj.prog_, self.prog)
|
|
self.assertIdentical(obj.type_, self.prog.type("int"))
|
|
self.assertIsNone(obj.address_)
|
|
self.assertIsNone(obj.bit_offset_)
|
|
self.assertEqual(obj.bit_field_size_, 1)
|
|
self.assertEqual(repr(obj), "Object(prog, 'int', bit_field_size=1)")
|
|
|
|
def test_operators(self):
|
|
absent = Object(self.prog, "int")
|
|
obj = Object(self.prog, "int", 1)
|
|
for op in [
|
|
operator.lt,
|
|
operator.le,
|
|
operator.eq,
|
|
operator.ge,
|
|
operator.gt,
|
|
operator.add,
|
|
operator.and_,
|
|
operator.lshift,
|
|
operator.mod,
|
|
operator.mul,
|
|
operator.or_,
|
|
operator.rshift,
|
|
operator.sub,
|
|
operator.truediv,
|
|
operator.xor,
|
|
]:
|
|
self.assertRaises(ObjectAbsentError, op, absent, obj)
|
|
self.assertRaises(ObjectAbsentError, op, obj, absent)
|
|
|
|
for op in [
|
|
operator.not_,
|
|
operator.truth,
|
|
operator.index,
|
|
operator.inv,
|
|
operator.neg,
|
|
operator.pos,
|
|
round,
|
|
math.trunc,
|
|
math.floor,
|
|
math.ceil,
|
|
]:
|
|
self.assertRaises(ObjectAbsentError, op, absent)
|
|
|
|
self.assertRaises(ObjectAbsentError, absent.address_of_)
|
|
|
|
self.assertRaises(
|
|
ObjectAbsentError,
|
|
operator.getitem,
|
|
Object(self.prog, "int [2]"),
|
|
0,
|
|
)
|
|
|
|
self.assertRaises(ObjectAbsentError, Object(self.prog, "char [16]").string_)
|
|
self.assertRaises(ObjectAbsentError, Object(self.prog, "char *").string_)
|
|
|
|
def test_big_int(self):
|
|
obj = Object(self.prog, self.prog.int_type("BIG", 16, True))
|
|
self.assertIs(obj.prog_, self.prog)
|
|
self.assertEqual(obj.type_.size, 16)
|
|
self.assertIsNone(obj.address_)
|
|
self.assertIsNone(obj.bit_offset_)
|
|
self.assertIsNone(obj.bit_field_size_)
|
|
self.assertEqual(repr(obj), "Object(prog, 'BIG')")
|
|
|
|
def test_non_standard_float(self):
|
|
for size in (2, 10, 16, 32):
|
|
obj = Object(self.prog, self.prog.float_type("CUSTOM_FLOAT", size))
|
|
self.assertIs(obj.prog_, self.prog)
|
|
self.assertEqual(obj.type_.size, size)
|
|
self.assertIsNone(obj.address_)
|
|
self.assertIsNone(obj.bit_offset_)
|
|
self.assertIsNone(obj.bit_field_size_)
|
|
self.assertEqual(repr(obj), "Object(prog, 'CUSTOM_FLOAT')")
|
|
|
|
|
|
class TestConversions(MockProgramTestCase):
|
|
def test_bool(self):
|
|
self.assertTrue(Object(self.prog, "int", value=-1))
|
|
self.assertFalse(Object(self.prog, "int", value=0))
|
|
|
|
self.assertTrue(Object(self.prog, "unsigned int", value=1))
|
|
self.assertFalse(Object(self.prog, "unsigned int", value=0))
|
|
|
|
self.assertTrue(Object(self.prog, "double", value=3.14))
|
|
self.assertFalse(Object(self.prog, "double", value=0.0))
|
|
|
|
self.assertTrue(Object(self.prog, "int *", value=0xFFFF0000))
|
|
self.assertFalse(Object(self.prog, "int *", value=0x0))
|
|
|
|
self.assertTrue(Object(self.prog, "int []", address=0))
|
|
|
|
self.assertRaisesRegex(
|
|
TypeError,
|
|
"cannot convert 'struct point' to bool",
|
|
bool,
|
|
Object(self.prog, self.point_type, address=0),
|
|
)
|
|
|
|
def test_int(self):
|
|
self.assertEqual(int(Object(self.prog, "int", value=-1)), -1)
|
|
self.assertEqual(int(Object(self.prog, "unsigned int", value=1)), 1)
|
|
self.assertEqual(int(Object(self.prog, "double", value=9.99)), 9)
|
|
self.assertEqual(int(Object(self.prog, "int *", value=0)), 0)
|
|
|
|
self.assertRaisesRegex(
|
|
TypeError,
|
|
r"cannot convert 'int \[\]' to int",
|
|
int,
|
|
Object(self.prog, "int []", address=0),
|
|
)
|
|
|
|
def test_float(self):
|
|
self.assertEqual(float(Object(self.prog, "int", value=-1)), -1.0)
|
|
self.assertEqual(float(Object(self.prog, "unsigned int", value=1)), 1.0)
|
|
self.assertEqual(float(Object(self.prog, "double", value=9.99)), 9.99)
|
|
|
|
self.assertRaisesRegex(
|
|
TypeError,
|
|
r"cannot convert 'int \*' to float",
|
|
float,
|
|
Object(self.prog, "int *", value=0xFFFF0000),
|
|
)
|
|
self.assertRaisesRegex(
|
|
TypeError,
|
|
r"cannot convert 'int \[\]' to float",
|
|
float,
|
|
Object(self.prog, "int []", address=0),
|
|
)
|
|
|
|
def test_index(self):
|
|
self.assertEqual(operator.index(Object(self.prog, "int", value=-1)), -1)
|
|
self.assertEqual(operator.index(Object(self.prog, "unsigned int", value=1)), 1)
|
|
self.assertEqual(operator.index(Object(self.prog, "int *", value=0)), 0)
|
|
|
|
self.assertRaisesRegex(
|
|
TypeError,
|
|
"'double' object cannot be interpreted as an integer",
|
|
operator.index,
|
|
Object(self.prog, "double", value=9.99),
|
|
)
|
|
self.assertRaisesRegex(
|
|
TypeError,
|
|
r"'int \[\]' object cannot be interpreted as an integer",
|
|
operator.index,
|
|
Object(self.prog, "int []", address=0),
|
|
)
|
|
|
|
def test_signed_int_value_to_bytes(self):
|
|
for byteorder in ("little", "big"):
|
|
with self.subTest(byteorder=byteorder):
|
|
self.assertEqual(
|
|
Object(
|
|
self.prog, self.prog.int_type("int", 4, True, byteorder), -100
|
|
).to_bytes_(),
|
|
(-100).to_bytes(4, byteorder, signed=True),
|
|
)
|
|
self.assertEqual(
|
|
Object(
|
|
self.prog,
|
|
self.prog.int_type("long", 8, True, byteorder),
|
|
-(2**32),
|
|
).to_bytes_(),
|
|
(-(2**32)).to_bytes(8, byteorder, signed=True),
|
|
)
|
|
|
|
def test_unsigned_int_value_to_bytes(self):
|
|
for byteorder in ("little", "big"):
|
|
with self.subTest(byteorder=byteorder):
|
|
self.assertEqual(
|
|
Object(
|
|
self.prog,
|
|
self.prog.int_type("unsigned int", 4, False, byteorder),
|
|
2**31,
|
|
).to_bytes_(),
|
|
(2**31).to_bytes(4, byteorder),
|
|
)
|
|
self.assertEqual(
|
|
Object(
|
|
self.prog,
|
|
self.prog.int_type("unsigned long", 8, False, byteorder),
|
|
2**60,
|
|
).to_bytes_(),
|
|
(2**60).to_bytes(8, byteorder),
|
|
)
|
|
|
|
def test_float64_value_to_bytes(self):
|
|
for byteorder in ("little", "big"):
|
|
with self.subTest(byteorder=byteorder):
|
|
self.assertEqual(
|
|
Object(
|
|
self.prog, self.prog.float_type("double", 8, byteorder), math.e
|
|
).to_bytes_(),
|
|
struct.pack(("<" if byteorder == "little" else ">") + "d", math.e),
|
|
)
|
|
|
|
def test_float32_value_to_bytes(self):
|
|
for byteorder in ("little", "big"):
|
|
with self.subTest(byteorder=byteorder):
|
|
self.assertEqual(
|
|
Object(
|
|
self.prog, self.prog.float_type("float", 4, byteorder), math.e
|
|
).to_bytes_(),
|
|
struct.pack(("<" if byteorder == "little" else ">") + "f", math.e),
|
|
)
|
|
|
|
def test_struct_value_to_bytes(self):
|
|
self.assertEqual(
|
|
Object(self.prog, self.point_type, {"x": 1, "y": 2}).to_bytes_(),
|
|
b"\x01\x00\x00\x00\x02\x00\x00\x00",
|
|
)
|
|
|
|
def test_int_reference_to_bytes(self):
|
|
self.add_memory_segment(b"\x78\x56\x34\x12", virt_addr=0xFFFF0000)
|
|
self.assertEqual(
|
|
Object(self.prog, "int", address=0xFFFF0000).to_bytes_(),
|
|
b"\x78\x56\x34\x12",
|
|
)
|
|
|
|
def test_int_reference_bit_offset_to_bytes(self):
|
|
self.add_memory_segment(b"\xe0Y\xd1H\x00", virt_addr=0xFFFF0000)
|
|
self.assertEqual(
|
|
Object(self.prog, "int", address=0xFFFF0000, bit_offset=2).to_bytes_(),
|
|
b"\x78\x56\x34\x12",
|
|
)
|
|
|
|
def test_int_reference_big_endian_bit_offset_to_bytes(self):
|
|
self.add_memory_segment(b"\x04\x8d\x15\x9e\x00", virt_addr=0xFFFF0000)
|
|
self.assertEqual(
|
|
Object(
|
|
self.prog,
|
|
self.prog.int_type("int", 4, True, "big"),
|
|
address=0xFFFF0000,
|
|
bit_offset=2,
|
|
).to_bytes_(),
|
|
b"\x12\x34\x56\x78",
|
|
)
|
|
|
|
def test_struct_reference_to_bytes(self):
|
|
self.add_memory_segment(
|
|
b"\x01\x00\x00\x00\x02\x00\x00\x00", virt_addr=0xFFFF0000
|
|
)
|
|
self.assertEqual(
|
|
Object(self.prog, self.point_type, address=0xFFFF0000).to_bytes_(),
|
|
b"\x01\x00\x00\x00\x02\x00\x00\x00",
|
|
)
|
|
|
|
def test_int_from_bytes(self):
|
|
for byteorder in ("little", "big"):
|
|
with self.subTest(byteorder=byteorder):
|
|
type_ = self.prog.int_type("int", 4, True, byteorder)
|
|
self.assertIdentical(
|
|
Object.from_bytes_(
|
|
self.prog, type_, (0x12345678).to_bytes(4, byteorder)
|
|
),
|
|
Object(self.prog, type_, 0x12345678),
|
|
)
|
|
|
|
def test_int_from_bytes_bit_offset(self):
|
|
self.assertIdentical(
|
|
Object.from_bytes_(self.prog, "int", b"\xe0Y\xd1H\x00", bit_offset=2),
|
|
Object(self.prog, "int", 0x12345678),
|
|
)
|
|
|
|
def test_int_from_bytes_big_endian_bit_offset(self):
|
|
self.assertIdentical(
|
|
Object.from_bytes_(
|
|
self.prog,
|
|
self.prog.int_type("int", 4, True, "big"),
|
|
b"\x04\x8d\x15\x9e\x00",
|
|
bit_offset=2,
|
|
),
|
|
Object(self.prog, self.prog.int_type("int", 4, True, "big"), 0x12345678),
|
|
)
|
|
|
|
def test_int_from_bytes_bit_field(self):
|
|
self.assertIdentical(
|
|
Object.from_bytes_(self.prog, "int", b"\xcc", bit_field_size=8),
|
|
Object(self.prog, "int", 0xCC, bit_field_size=8),
|
|
)
|
|
|
|
def test_float64_from_bytes(self):
|
|
for byteorder in ("little", "big"):
|
|
with self.subTest(byteorder=byteorder):
|
|
type_ = self.prog.float_type("double", 8, byteorder)
|
|
self.assertIdentical(
|
|
Object.from_bytes_(
|
|
self.prog,
|
|
type_,
|
|
struct.pack(
|
|
("<" if byteorder == "little" else ">") + "d", math.e
|
|
),
|
|
),
|
|
Object(self.prog, type_, math.e),
|
|
)
|
|
|
|
def test_float32_from_bytes(self):
|
|
for byteorder in ("little", "big"):
|
|
with self.subTest(byteorder=byteorder):
|
|
type_ = self.prog.float_type("float", 4, byteorder)
|
|
self.assertIdentical(
|
|
Object.from_bytes_(
|
|
self.prog,
|
|
type_,
|
|
struct.pack(
|
|
("<" if byteorder == "little" else ">") + "f", math.e
|
|
),
|
|
),
|
|
Object(self.prog, type_, math.e),
|
|
)
|
|
|
|
def test_struct_from_bytes(self):
|
|
self.assertIdentical(
|
|
Object.from_bytes_(
|
|
self.prog, self.point_type, b"\x01\x00\x00\x00\x02\x00\x00\x00"
|
|
),
|
|
Object(self.prog, self.point_type, {"x": 1, "y": 2}),
|
|
)
|
|
|
|
def test_struct_from_bytes_bit_offset(self):
|
|
self.assertIdentical(
|
|
Object.from_bytes_(
|
|
self.prog,
|
|
self.point_type,
|
|
b"\xff\x01\x00\x00\x00\x02\x00\x00\x00",
|
|
bit_offset=8,
|
|
),
|
|
Object(self.prog, self.point_type, {"x": 1, "y": 2}),
|
|
)
|
|
|
|
def test_struct_from_bytes_invalid_bit_offset(self):
|
|
self.assertRaisesRegex(
|
|
ValueError,
|
|
"non-scalar must be byte-aligned",
|
|
Object.from_bytes_,
|
|
self.prog,
|
|
self.point_type,
|
|
b"\xff\x01\x00\x00\x00\x02\x00\x00\x00",
|
|
bit_offset=2,
|
|
)
|
|
|
|
def test_from_bytes_invalid_bit_field_size(self):
|
|
self.assertRaisesRegex(
|
|
ValueError,
|
|
"bit field size cannot be zero",
|
|
Object.from_bytes_,
|
|
self.prog,
|
|
"int",
|
|
b"",
|
|
bit_field_size=0,
|
|
)
|
|
|
|
def test_from_bytes_buffer_too_small(self):
|
|
self.assertRaisesRegex(
|
|
ValueError,
|
|
"buffer is too small",
|
|
Object.from_bytes_,
|
|
self.prog,
|
|
"int",
|
|
bytes(3),
|
|
)
|
|
|
|
def test_from_bytes_incomplete_type(self):
|
|
self.assertRaisesRegex(
|
|
TypeError,
|
|
"cannot create object with void type",
|
|
Object.from_bytes_,
|
|
self.prog,
|
|
"void",
|
|
b"",
|
|
)
|
|
|
|
def test_from_bytes_bad_type(self):
|
|
self.assertRaises(TypeError, Object.from_bytes_, self.prog, None, b"")
|
|
|
|
|
|
class TestInvalidBitField(MockProgramTestCase):
|
|
def test_integer(self):
|
|
self.assertRaisesRegex(
|
|
ValueError,
|
|
"bit field size is larger than type size",
|
|
Object,
|
|
self.prog,
|
|
"int",
|
|
value=0,
|
|
bit_field_size=64,
|
|
)
|
|
self.assertRaisesRegex(
|
|
ValueError,
|
|
"bit field size is larger than type size",
|
|
Object,
|
|
self.prog,
|
|
"int",
|
|
address=0,
|
|
bit_field_size=64,
|
|
)
|
|
self.assertRaisesRegex(
|
|
ValueError,
|
|
"bit field size is larger than type size",
|
|
Object,
|
|
self.prog,
|
|
"unsigned int",
|
|
value=0,
|
|
bit_field_size=64,
|
|
)
|
|
self.assertRaisesRegex(
|
|
ValueError,
|
|
"bit field size is larger than type size",
|
|
Object,
|
|
self.prog,
|
|
"unsigned int",
|
|
address=0,
|
|
bit_field_size=64,
|
|
)
|
|
|
|
def test_float(self):
|
|
self.assertRaisesRegex(
|
|
ValueError,
|
|
"bit field must be integer",
|
|
Object,
|
|
self.prog,
|
|
"float",
|
|
value=0,
|
|
bit_field_size=16,
|
|
)
|
|
self.assertRaisesRegex(
|
|
ValueError,
|
|
"bit field must be integer",
|
|
Object,
|
|
self.prog,
|
|
"float",
|
|
address=0,
|
|
bit_field_size=16,
|
|
)
|
|
|
|
def test_reference(self):
|
|
self.assertRaisesRegex(
|
|
ValueError,
|
|
"bit field must be integer",
|
|
Object,
|
|
self.prog,
|
|
self.point_type,
|
|
address=0,
|
|
bit_field_size=4,
|
|
)
|
|
self.assertRaisesRegex(
|
|
ValueError,
|
|
"bit field must be integer",
|
|
Object,
|
|
self.prog,
|
|
self.point_type,
|
|
value={},
|
|
bit_field_size=4,
|
|
)
|
|
|
|
|
|
class TestGenericOperators(MockProgramTestCase):
|
|
def setUp(self):
|
|
super().setUp()
|
|
self.add_memory_segment(
|
|
b"".join(i.to_bytes(4, "little") for i in range(4)), virt_addr=0xFFFF0000
|
|
)
|
|
|
|
def test_len(self):
|
|
self.assertEqual(len(Object(self.prog, "int [0]", address=0)), 0)
|
|
self.assertEqual(len(Object(self.prog, "int [10]", address=0)), 10)
|
|
self.assertRaisesRegex(
|
|
TypeError, "'int' has no len()", len, Object(self.prog, "int", address=0)
|
|
)
|
|
self.assertRaisesRegex(
|
|
TypeError,
|
|
r"'int \[\]' has no len()",
|
|
len,
|
|
Object(self.prog, "int []", address=0),
|
|
)
|
|
|
|
def test_address_of(self):
|
|
obj = Object(self.prog, "int", address=0xFFFF0000)
|
|
self.assertIdentical(
|
|
obj.address_of_(), Object(self.prog, "int *", value=0xFFFF0000)
|
|
)
|
|
obj = obj.read_()
|
|
self.assertRaisesRegex(
|
|
ValueError, "cannot take address of value", obj.address_of_
|
|
)
|
|
obj = Object(self.prog, "int", address=0xFFFF0000, bit_field_size=4)
|
|
self.assertRaisesRegex(
|
|
ValueError, "cannot take address of bit field", obj.address_of_
|
|
)
|
|
obj = Object(self.prog, "int", address=0xFFFF0000, bit_offset=4)
|
|
self.assertRaisesRegex(
|
|
ValueError, "cannot take address of bit field", obj.address_of_
|
|
)
|
|
|
|
def test_subscript(self):
|
|
arr = Object(self.prog, "int [4]", address=0xFFFF0000)
|
|
incomplete_arr = Object(self.prog, "int []", address=0xFFFF0000)
|
|
ptr = Object(self.prog, "int *", value=0xFFFF0000)
|
|
for obj in [arr, incomplete_arr, ptr]:
|
|
for i in range(5):
|
|
self.assertIdentical(
|
|
obj[i], Object(self.prog, "int", address=0xFFFF0000 + 4 * i)
|
|
)
|
|
if i < 4:
|
|
self.assertIdentical(
|
|
obj[i].read_(), Object(self.prog, "int", value=i)
|
|
)
|
|
else:
|
|
self.assertRaises(FaultError, obj[i].read_)
|
|
|
|
obj = arr.read_()
|
|
for i in range(4):
|
|
self.assertIdentical(obj[i], Object(self.prog, "int", value=i))
|
|
self.assertRaisesRegex(OutOfBoundsError, "out of bounds", obj.__getitem__, 4)
|
|
obj = Object(self.prog, "int", value=0)
|
|
self.assertRaises(TypeError, obj.__getitem__, 0)
|
|
|
|
def test_cast_primitive_value(self):
|
|
obj = Object(self.prog, "long", value=2**32 + 1)
|
|
self.assertIdentical(cast("int", obj), Object(self.prog, "int", value=1))
|
|
self.assertIdentical(
|
|
cast("int", obj.read_()), Object(self.prog, "int", value=1)
|
|
)
|
|
self.assertIdentical(
|
|
cast("const int", Object(self.prog, "int", value=1)),
|
|
Object(self.prog, "const int", value=1),
|
|
)
|
|
self.assertRaisesRegex(
|
|
TypeError,
|
|
"cannot cast to 'struct point'",
|
|
cast,
|
|
self.point_type,
|
|
Object(self.prog, "int", value=1),
|
|
)
|
|
|
|
def test_cast_compound_value(self):
|
|
obj = Object(self.prog, self.point_type, address=0xFFFF0000).read_()
|
|
self.assertRaisesRegex(
|
|
TypeError,
|
|
"cannot cast to 'struct point'",
|
|
cast,
|
|
self.point_type,
|
|
obj,
|
|
)
|
|
self.assertRaisesRegex(
|
|
TypeError,
|
|
"cannot convert 'struct point' to 'enum color'",
|
|
cast,
|
|
self.color_type,
|
|
obj,
|
|
)
|
|
|
|
def test_cast_invalid(self):
|
|
obj = Object(self.prog, "int", value=1)
|
|
self.assertRaisesRegex(TypeError, "cannot cast to void type", cast, "void", obj)
|
|
|
|
def test_reinterpret_reference(self):
|
|
obj = Object(self.prog, "int", address=0xFFFF0000)
|
|
self.assertIdentical(reinterpret("int", obj), obj)
|
|
self.assertIdentical(
|
|
reinterpret(self.prog.int_type("int", 4, True, "big"), obj),
|
|
Object(
|
|
self.prog, self.prog.int_type("int", 4, True, "big"), address=0xFFFF0000
|
|
),
|
|
)
|
|
|
|
obj = Object(self.prog, "int []", address=0xFFFF0000)
|
|
self.assertIdentical(
|
|
reinterpret("int [4]", obj),
|
|
Object(self.prog, "int [4]", address=0xFFFF0000),
|
|
)
|
|
|
|
def test_reinterpret_value(self):
|
|
self.types.append(self.point_type)
|
|
self.types.append(
|
|
self.prog.struct_type(
|
|
"foo", 8, (TypeMember(self.prog.int_type("long", 8, True), "counter"),)
|
|
),
|
|
)
|
|
obj = Object(self.prog, "struct point", address=0xFFFF0008).read_()
|
|
self.assertIdentical(
|
|
reinterpret("struct foo", obj),
|
|
Object(self.prog, "struct foo", address=0xFFFF0008).read_(),
|
|
)
|
|
self.assertIdentical(reinterpret("int", obj), Object(self.prog, "int", value=2))
|
|
self.assertIdentical(
|
|
reinterpret(self.prog.int_type("int", 4, True, "big"), obj),
|
|
Object(
|
|
self.prog, self.prog.int_type("int", 4, True, "big"), value=33554432
|
|
),
|
|
)
|
|
|
|
def test_reinterpret_primitive_value_to_same_size_primitive(self):
|
|
for byteorder in ("little", "big"):
|
|
with self.subTest(byteorder=byteorder):
|
|
self.assertIdentical(
|
|
reinterpret(
|
|
self.prog.int_type("long long", 8, True, byteorder),
|
|
Object(
|
|
self.prog,
|
|
self.prog.int_type(
|
|
"unsigned long long", 8, False, byteorder
|
|
),
|
|
0xFFFFFFFFFFFFFFF3,
|
|
),
|
|
),
|
|
Object(
|
|
self.prog,
|
|
self.prog.int_type("long long", 8, True, byteorder),
|
|
-13,
|
|
),
|
|
)
|
|
|
|
def test_reinterpret_primitive_value_to_smaller_primitive(self):
|
|
with self.subTest(byteorder="little"):
|
|
self.assertIdentical(
|
|
reinterpret(
|
|
self.prog.int_type("int", 4, True),
|
|
Object(
|
|
self.prog,
|
|
self.prog.int_type("unsigned long long", 8, False),
|
|
0x000027100000029A,
|
|
),
|
|
),
|
|
Object(self.prog, self.prog.int_type("int", 4, True), 666),
|
|
)
|
|
with self.subTest(byteorder="big"):
|
|
self.assertIdentical(
|
|
reinterpret(
|
|
self.prog.int_type("int", 4, True, "big"),
|
|
Object(
|
|
self.prog,
|
|
self.prog.int_type("unsigned long long", 8, False, "big"),
|
|
0x000027100000029A,
|
|
),
|
|
),
|
|
Object(self.prog, self.prog.int_type("int", 4, True, "big"), 10000),
|
|
)
|
|
|
|
def test_reinterpret_primitive_value_to_same_size_compound(self):
|
|
with self.subTest(byteorder="little"):
|
|
self.assertIdentical(
|
|
reinterpret(
|
|
self.point_type,
|
|
Object(
|
|
self.prog,
|
|
self.prog.int_type("unsigned long long", 8, False),
|
|
0x000027100000029A,
|
|
),
|
|
),
|
|
Object(self.prog, self.point_type, {"x": 666, "y": 10000}),
|
|
)
|
|
with self.subTest(byteorder="big"):
|
|
point_type = self.prog.struct_type(
|
|
"point",
|
|
8,
|
|
(
|
|
TypeMember(self.prog.int_type("int", 4, True, "big"), "x", 0),
|
|
TypeMember(self.prog.int_type("int", 4, True, "big"), "y", 32),
|
|
),
|
|
)
|
|
self.assertIdentical(
|
|
reinterpret(
|
|
point_type,
|
|
Object(
|
|
self.prog,
|
|
self.prog.int_type("unsigned long long", 8, False, "big"),
|
|
0x000027100000029A,
|
|
),
|
|
),
|
|
Object(self.prog, point_type, {"x": 10000, "y": 666}),
|
|
)
|
|
|
|
def test_reinterpret_primitive_value_to_smaller_compound(self):
|
|
with self.subTest(byteorder="little"):
|
|
small_point_type = self.prog.struct_type(
|
|
"small_point",
|
|
4,
|
|
(
|
|
TypeMember(self.prog.int_type("short", 2, True), "x", 0),
|
|
TypeMember(self.prog.int_type("short", 2, True), "y", 16),
|
|
),
|
|
)
|
|
self.assertIdentical(
|
|
reinterpret(
|
|
small_point_type,
|
|
Object(
|
|
self.prog,
|
|
self.prog.int_type("unsigned long long", 8, False),
|
|
0x123456782710029A,
|
|
),
|
|
),
|
|
Object(self.prog, small_point_type, {"x": 666, "y": 10000}),
|
|
)
|
|
with self.subTest(byteorder="big"):
|
|
small_point_type = self.prog.struct_type(
|
|
"small_point",
|
|
4,
|
|
(
|
|
TypeMember(self.prog.int_type("short", 2, True, "big"), "x", 0),
|
|
TypeMember(self.prog.int_type("short", 2, True, "big"), "y", 16),
|
|
),
|
|
)
|
|
self.assertIdentical(
|
|
reinterpret(
|
|
small_point_type,
|
|
Object(
|
|
self.prog,
|
|
self.prog.int_type("unsigned long long", 8, False, "big"),
|
|
0x123456782710029A,
|
|
),
|
|
),
|
|
Object(self.prog, small_point_type, {"x": 0x1234, "y": 0x5678}),
|
|
)
|
|
|
|
def test_reinterpret_bit_field_value_to_same_size_primitive(self):
|
|
for byteorder in ("little", "big"):
|
|
with self.subTest(byteorder=byteorder):
|
|
self.assertIdentical(
|
|
reinterpret(
|
|
self.prog.int_type("uint24", 3, False, byteorder),
|
|
Object(
|
|
self.prog,
|
|
self.prog.int_type("unsigned int", 4, False, byteorder),
|
|
0xABCDEF,
|
|
bit_field_size=24,
|
|
),
|
|
),
|
|
Object(
|
|
self.prog,
|
|
self.prog.int_type("uint24", 3, False, byteorder),
|
|
0xABCDEF,
|
|
),
|
|
)
|
|
|
|
def test_reinterpret_bit_field_value_to_smaller_primitive(self):
|
|
with self.subTest(byteorder="little"):
|
|
self.assertIdentical(
|
|
reinterpret(
|
|
self.prog.int_type("unsigned short", 2, False),
|
|
Object(
|
|
self.prog,
|
|
self.prog.int_type("unsigned int", 4, False),
|
|
0xABCDEF,
|
|
bit_field_size=24,
|
|
),
|
|
),
|
|
Object(
|
|
self.prog,
|
|
self.prog.int_type("unsigned short", 2, False),
|
|
0xCDEF,
|
|
),
|
|
)
|
|
with self.subTest(byteorder="big"):
|
|
self.assertIdentical(
|
|
reinterpret(
|
|
self.prog.int_type("unsigned short", 2, False, "big"),
|
|
Object(
|
|
self.prog,
|
|
self.prog.int_type("unsigned int", 4, False, "big"),
|
|
0xABCDEF,
|
|
bit_field_size=24,
|
|
),
|
|
),
|
|
Object(
|
|
self.prog,
|
|
self.prog.int_type("unsigned short", 2, False, "big"),
|
|
0xABCD,
|
|
),
|
|
)
|
|
|
|
def test_member(self):
|
|
reference = Object(self.prog, self.point_type, address=0xFFFF0000)
|
|
unnamed_reference = Object(
|
|
self.prog,
|
|
self.prog.struct_type(
|
|
"point",
|
|
8,
|
|
(
|
|
TypeMember(
|
|
self.prog.struct_type(None, 8, self.point_type.members), None
|
|
),
|
|
),
|
|
),
|
|
address=0xFFFF0000,
|
|
)
|
|
ptr = Object(
|
|
self.prog, self.prog.pointer_type(self.point_type), value=0xFFFF0000
|
|
)
|
|
for obj in [reference, unnamed_reference, ptr]:
|
|
self.assertIdentical(
|
|
obj.member_("x"), Object(self.prog, "int", address=0xFFFF0000)
|
|
)
|
|
self.assertIdentical(obj.member_("x"), obj.x)
|
|
self.assertIdentical(
|
|
obj.member_("y"), Object(self.prog, "int", address=0xFFFF0004)
|
|
)
|
|
self.assertIdentical(obj.member_("y"), obj.y)
|
|
|
|
self.assertRaisesRegex(
|
|
LookupError, "'struct point' has no member 'z'", obj.member_, "z"
|
|
)
|
|
self.assertRaisesRegex(
|
|
AttributeError, "'struct point' has no member 'z'", getattr, obj, "z"
|
|
)
|
|
|
|
obj = reference.read_()
|
|
self.assertIdentical(obj.x, Object(self.prog, "int", value=0))
|
|
self.assertIdentical(obj.y, Object(self.prog, "int", value=1))
|
|
|
|
obj = Object(self.prog, "int", value=1)
|
|
self.assertRaisesRegex(
|
|
TypeError, "'int' is not a structure, union, or class", obj.member_, "x"
|
|
)
|
|
self.assertRaisesRegex(AttributeError, "no attribute", getattr, obj, "x")
|
|
|
|
def test_bit_field_member(self):
|
|
self.add_memory_segment(b"\x07\x10\x5e\x5f\x1f\0\0\0", virt_addr=0xFFFF8000)
|
|
type_ = self.prog.struct_type(
|
|
"bits",
|
|
8,
|
|
(
|
|
TypeMember(
|
|
Object(
|
|
self.prog, self.prog.int_type("int", 4, True), bit_field_size=4
|
|
),
|
|
"x",
|
|
0,
|
|
),
|
|
TypeMember(
|
|
Object(
|
|
self.prog,
|
|
self.prog.int_type("int", 4, True, qualifiers=Qualifiers.CONST),
|
|
bit_field_size=28,
|
|
),
|
|
"y",
|
|
4,
|
|
),
|
|
TypeMember(
|
|
Object(
|
|
self.prog, self.prog.int_type("int", 4, True), bit_field_size=5
|
|
),
|
|
"z",
|
|
32,
|
|
),
|
|
),
|
|
)
|
|
|
|
obj = Object(self.prog, type_, address=0xFFFF8000)
|
|
self.assertIdentical(
|
|
obj.x,
|
|
Object(
|
|
self.prog,
|
|
self.prog.int_type("int", 4, True),
|
|
address=0xFFFF8000,
|
|
bit_field_size=4,
|
|
),
|
|
)
|
|
self.assertIdentical(
|
|
obj.y,
|
|
Object(
|
|
self.prog,
|
|
self.prog.int_type("int", 4, True, qualifiers=Qualifiers.CONST),
|
|
address=0xFFFF8000,
|
|
bit_field_size=28,
|
|
bit_offset=4,
|
|
),
|
|
)
|
|
self.assertIdentical(
|
|
obj.z,
|
|
Object(
|
|
self.prog,
|
|
self.prog.int_type("int", 4, True),
|
|
address=0xFFFF8004,
|
|
bit_field_size=5,
|
|
),
|
|
)
|
|
|
|
def test_member_out_of_bounds(self):
|
|
obj = Object(
|
|
self.prog,
|
|
self.prog.struct_type("foo", 4, self.point_type.members),
|
|
address=0xFFFF0000,
|
|
).read_()
|
|
self.assertRaisesRegex(OutOfBoundsError, "out of bounds", getattr, obj, "y")
|
|
|
|
def test_string(self):
|
|
self.add_memory_segment(
|
|
b"\x00\x00\xff\xff\x00\x00\x00\x00", virt_addr=0xFFFEFFF8
|
|
)
|
|
self.add_memory_segment(b"hello\0world\0", virt_addr=0xFFFF0000)
|
|
strings = [
|
|
(Object(self.prog, "char *", address=0xFFFEFFF8), b"hello"),
|
|
(Object(self.prog, "char [2]", address=0xFFFF0000), b"he"),
|
|
(Object(self.prog, "char [8]", address=0xFFFF0000), b"hello"),
|
|
]
|
|
for obj, expected in strings:
|
|
with self.subTest(obj=obj):
|
|
self.assertEqual(obj.string_(), expected)
|
|
self.assertEqual(obj.read_().string_(), expected)
|
|
|
|
strings = [
|
|
Object(self.prog, "char []", address=0xFFFF0000),
|
|
Object(self.prog, "int []", address=0xFFFF0000),
|
|
Object(self.prog, "int [2]", address=0xFFFF0000),
|
|
Object(self.prog, "int *", value=0xFFFF0000),
|
|
]
|
|
for obj in strings:
|
|
self.assertEqual(obj.string_(), b"hello")
|
|
|
|
self.assertRaisesRegex(
|
|
TypeError,
|
|
"must be an array or pointer",
|
|
Object(self.prog, "int", value=1).string_,
|
|
)
|
|
|
|
|
|
class TestSpecialMethods(MockProgramTestCase):
|
|
def test_dir(self):
|
|
obj = Object(self.prog, "int", value=0)
|
|
self.assertEqual(dir(obj), sorted(object.__dir__(obj)))
|
|
|
|
obj = Object(self.prog, self.point_type, address=0xFFFF0000)
|
|
self.assertEqual(dir(obj), sorted(object.__dir__(obj) + ["x", "y"]))
|
|
self.assertEqual(dir(obj.address_of_()), dir(obj))
|
|
|
|
def test_round(self):
|
|
for func in [round, math.trunc, math.floor, math.ceil]:
|
|
for value in [0.0, -0.0, -0.4, 0.4, 0.5, -0.5, 0.6, -0.6, 1.0, -1.0]:
|
|
self.assertEqual(
|
|
func(Object(self.prog, "double", value=value)), func(value)
|
|
)
|
|
self.assertEqual(
|
|
func(Object(self.prog, "int", value=value)), func(int(value))
|
|
)
|
|
self.assertIdentical(
|
|
round(Object(self.prog, "int", value=1), 2),
|
|
Object(self.prog, "int", value=1),
|
|
)
|
|
self.assertIdentical(
|
|
round(Object(self.prog, "double", value=0.123), 2),
|
|
Object(self.prog, "double", value=0.12),
|
|
)
|
|
|
|
def test_iter(self):
|
|
obj = Object(self.prog, "int [4]", value=[0, 1, 2, 3])
|
|
for i, element in enumerate(obj):
|
|
self.assertIdentical(element, Object(self.prog, "int", value=i))
|
|
self.assertEqual(operator.length_hint(iter(obj)), 4)
|
|
self.assertRaisesRegex(
|
|
TypeError, "'int' is not iterable", iter, Object(self.prog, "int", value=0)
|
|
)
|
|
self.assertRaisesRegex(
|
|
TypeError,
|
|
r"'int \[\]' is not iterable",
|
|
iter,
|
|
Object(self.prog, "int []", address=0),
|
|
)
|
|
|
|
def test__repr_pretty_(self):
|
|
obj = Object(self.prog, "int", value=0)
|
|
assertReprPrettyEqualsStr(obj)
|