drgn/tests/test_object.py
Omar Sandoval d1ffd581bd libdrgn: allow reinterpreting primitive scalar values
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>
2024-01-12 15:50:23 -08:00

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)