mirror of
https://github.com/JakeHillion/drgn.git
synced 2024-12-23 09:43:06 +00:00
0fad8a591a
c_parse_specifier_qualifier_list() checks whether an identifier starts
with "size_t" or "ptrdiff_t" to decide whether to return the size_t or
ptrdiff_t type. This incorrectly matches stuff like like "size_tea" and
"ptrdiff_tee". Fix this by making it an exact comparison.
Fixes: 75c3679147
("Rewrite drgn core in C")
Signed-off-by: Omar Sandoval <osandov@osandov.com>
896 lines
33 KiB
Python
896 lines
33 KiB
Python
# Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
# SPDX-License-Identifier: LGPL-2.1-or-later
|
|
import ctypes
|
|
import itertools
|
|
import os
|
|
import tempfile
|
|
import unittest.mock
|
|
|
|
from drgn import (
|
|
Architecture,
|
|
FaultError,
|
|
FindObjectFlags,
|
|
Language,
|
|
Object,
|
|
Platform,
|
|
PlatformFlags,
|
|
Program,
|
|
ProgramFlags,
|
|
Qualifiers,
|
|
TypeKind,
|
|
TypeMember,
|
|
host_platform,
|
|
)
|
|
from tests import (
|
|
DEFAULT_LANGUAGE,
|
|
MOCK_32BIT_PLATFORM,
|
|
MOCK_PLATFORM,
|
|
MockMemorySegment,
|
|
MockObject,
|
|
MockProgramTestCase,
|
|
TestCase,
|
|
mock_program,
|
|
)
|
|
from tests.elf import ET, PT
|
|
from tests.elfwriter import ElfSection, create_elf_file
|
|
|
|
|
|
def zero_memory_read(address, count, offset, physical):
|
|
return bytes(count)
|
|
|
|
|
|
class TestProgram(TestCase):
|
|
def test_set_pid(self):
|
|
# Debug the running Python interpreter itself.
|
|
prog = Program()
|
|
self.assertIsNone(prog.platform)
|
|
self.assertFalse(prog.flags & ProgramFlags.IS_LIVE)
|
|
prog.set_pid(os.getpid())
|
|
self.assertEqual(prog.platform, host_platform)
|
|
self.assertTrue(prog.flags & ProgramFlags.IS_LIVE)
|
|
self.assertRaisesRegex(
|
|
ValueError,
|
|
"program memory was already initialized",
|
|
prog.set_pid,
|
|
os.getpid(),
|
|
)
|
|
|
|
def test_pid_memory(self):
|
|
data = b"hello, world!"
|
|
buf = ctypes.create_string_buffer(data)
|
|
address = ctypes.addressof(buf)
|
|
|
|
# QEMU user-mode emulation doesn't seem to emulate /proc/$pid/mem
|
|
# correctly on a 64-bit host with a 32-bit guest; see
|
|
# https://gitlab.com/qemu-project/qemu/-/issues/698. Packit uses mock
|
|
# to cross-compile and test packages, which in turn uses QEMU user-mode
|
|
# emulation. Skip this test if /proc/$pid/mem doesn't work so that
|
|
# those builds succeed.
|
|
try:
|
|
with open("/proc/self/mem", "rb") as f:
|
|
f.seek(address)
|
|
functional_proc_pid_mem = f.read(len(data)) == data
|
|
except OSError:
|
|
functional_proc_pid_mem = False
|
|
if not functional_proc_pid_mem:
|
|
self.skipTest("/proc/$pid/mem is not functional")
|
|
|
|
prog = Program()
|
|
prog.set_pid(os.getpid())
|
|
|
|
self.assertEqual(prog.read(ctypes.addressof(buf), len(data)), data)
|
|
|
|
def test_lookup_error(self):
|
|
prog = mock_program()
|
|
self.assertRaisesRegex(
|
|
LookupError, "^could not find constant 'foo'$", prog.constant, "foo"
|
|
)
|
|
self.assertRaisesRegex(
|
|
LookupError,
|
|
"^could not find constant 'foo' in 'foo.c'$",
|
|
prog.constant,
|
|
"foo",
|
|
"foo.c",
|
|
)
|
|
self.assertRaisesRegex(
|
|
LookupError, "^could not find function 'foo'$", prog.function, "foo"
|
|
)
|
|
self.assertRaisesRegex(
|
|
LookupError,
|
|
"^could not find function 'foo' in 'foo.c'$",
|
|
prog.function,
|
|
"foo",
|
|
"foo.c",
|
|
)
|
|
self.assertRaisesRegex(LookupError, "^could not find 'foo'$", prog.type, "foo")
|
|
self.assertRaisesRegex(
|
|
LookupError, "^could not find 'foo' in 'foo.c'$", prog.type, "foo", "foo.c"
|
|
)
|
|
self.assertRaisesRegex(
|
|
LookupError, "^could not find variable 'foo'$", prog.variable, "foo"
|
|
)
|
|
self.assertRaisesRegex(
|
|
LookupError,
|
|
"^could not find variable 'foo' in 'foo.c'$",
|
|
prog.variable,
|
|
"foo",
|
|
"foo.c",
|
|
)
|
|
# prog[key] should raise KeyError instead of LookupError.
|
|
self.assertRaises(KeyError, prog.__getitem__, "foo")
|
|
# Even for non-strings.
|
|
self.assertRaises(KeyError, prog.__getitem__, 9)
|
|
|
|
def test_flags(self):
|
|
self.assertIsInstance(mock_program().flags, ProgramFlags)
|
|
|
|
def test_debug_info(self):
|
|
Program().load_debug_info([])
|
|
|
|
def test_language(self):
|
|
prog = Program()
|
|
self.assertEqual(prog.language, DEFAULT_LANGUAGE)
|
|
prog.language = Language.CPP
|
|
self.assertEqual(prog.language, Language.CPP)
|
|
prog.language = Language.C
|
|
self.assertEqual(prog.language, Language.C)
|
|
self.assertRaisesRegex(
|
|
TypeError, "language must be Language", setattr, prog, "language", "CPP"
|
|
)
|
|
|
|
|
|
class TestMemory(TestCase):
|
|
def test_simple_read(self):
|
|
data = b"hello, world"
|
|
prog = mock_program(segments=[MockMemorySegment(data, 0xFFFF0000, 0xA0)])
|
|
self.assertEqual(prog.read(0xFFFF0000, len(data)), data)
|
|
self.assertEqual(prog.read(0xA0, len(data), True), data)
|
|
|
|
def test_read_unsigned(self):
|
|
data = b"\x01\x02\x03\x04\x05\x06\x07\x08"
|
|
for word_size in [8, 4]:
|
|
for byteorder in ["little", "big"]:
|
|
flags = PlatformFlags(0)
|
|
if word_size == 8:
|
|
flags |= PlatformFlags.IS_64_BIT
|
|
if byteorder == "little":
|
|
flags |= PlatformFlags.IS_LITTLE_ENDIAN
|
|
prog = mock_program(
|
|
Platform(Architecture.UNKNOWN, flags),
|
|
segments=[MockMemorySegment(data, 0xFFFF0000, 0xA0)],
|
|
)
|
|
for size in [1, 2, 4, 8]:
|
|
read_fn = getattr(prog, f"read_u{8 * size}")
|
|
value = int.from_bytes(data[:size], byteorder)
|
|
self.assertEqual(read_fn(0xFFFF0000), value)
|
|
self.assertEqual(read_fn(0xA0, True), value)
|
|
if size == word_size:
|
|
self.assertEqual(prog.read_word(0xFFFF0000), value)
|
|
self.assertEqual(prog.read_word(0xA0, True), value)
|
|
|
|
prog = mock_program(
|
|
MOCK_32BIT_PLATFORM, segments=[MockMemorySegment(data, 0xFFFF0000, 0xA0)]
|
|
)
|
|
|
|
def test_bad_address(self):
|
|
data = b"hello, world!"
|
|
prog = mock_program(segments=[MockMemorySegment(data, 0xFFFF0000)])
|
|
self.assertRaisesRegex(
|
|
FaultError, "could not find memory segment", prog.read, 0xDEADBEEF, 4
|
|
)
|
|
self.assertRaisesRegex(
|
|
FaultError, "could not find memory segment", prog.read, 0xFFFF0000, 4, True
|
|
)
|
|
|
|
def test_segment_overflow(self):
|
|
data = b"hello, world!"
|
|
prog = mock_program(segments=[MockMemorySegment(data, 0xFFFF0000)])
|
|
self.assertRaisesRegex(
|
|
FaultError,
|
|
"could not find memory segment",
|
|
prog.read,
|
|
0xFFFF0000,
|
|
len(data) + 1,
|
|
)
|
|
|
|
def test_adjacent_segments(self):
|
|
data = b"hello, world!\0foobar"
|
|
prog = mock_program(
|
|
segments=[
|
|
MockMemorySegment(data[:4], 0xFFFF0000),
|
|
MockMemorySegment(data[4:14], 0xFFFF0004),
|
|
MockMemorySegment(data[14:], 0xFFFFF000),
|
|
]
|
|
)
|
|
self.assertEqual(prog.read(0xFFFF0000, 14), data[:14])
|
|
|
|
def test_address_overflow(self):
|
|
for bits in (64, 32):
|
|
with self.subTest(bits=bits):
|
|
prog = mock_program(
|
|
segments=[
|
|
MockMemorySegment(b"cd", 0x0),
|
|
MockMemorySegment(b"abyz", 2**bits - 2),
|
|
],
|
|
platform=MOCK_PLATFORM if bits == 64 else MOCK_32BIT_PLATFORM,
|
|
)
|
|
for start in range(3):
|
|
for size in range(4 - start):
|
|
self.assertEqual(
|
|
prog.read((2**bits - 2 + start) % 2**64, size),
|
|
b"abcd"[start : start + size],
|
|
)
|
|
|
|
def test_overlap_same_address_smaller_size(self):
|
|
# Existing segment: |_______|
|
|
# New segment: |___|
|
|
prog = Program(MOCK_PLATFORM)
|
|
segment1 = unittest.mock.Mock(side_effect=zero_memory_read)
|
|
segment2 = unittest.mock.Mock(side_effect=zero_memory_read)
|
|
prog.add_memory_segment(0xFFFF0000, 128, segment1)
|
|
prog.add_memory_segment(0xFFFF0000, 64, segment2)
|
|
prog.read(0xFFFF0000, 128)
|
|
segment1.assert_called_once_with(0xFFFF0040, 64, 64, False)
|
|
segment2.assert_called_once_with(0xFFFF0000, 64, 0, False)
|
|
|
|
def test_overlap_within_segment(self):
|
|
# Existing segment: |_______|
|
|
# New segment: |___|
|
|
prog = Program(MOCK_PLATFORM)
|
|
segment1 = unittest.mock.Mock(side_effect=zero_memory_read)
|
|
segment2 = unittest.mock.Mock(side_effect=zero_memory_read)
|
|
prog.add_memory_segment(0xFFFF0000, 128, segment1)
|
|
prog.add_memory_segment(0xFFFF0020, 64, segment2)
|
|
prog.read(0xFFFF0000, 128)
|
|
segment1.assert_has_calls(
|
|
[
|
|
unittest.mock.call(0xFFFF0000, 32, 00, False),
|
|
unittest.mock.call(0xFFFF0060, 32, 96, False),
|
|
]
|
|
)
|
|
segment2.assert_called_once_with(0xFFFF0020, 64, 0, False)
|
|
|
|
def test_overlap_same_segment(self):
|
|
# Existing segment: |_______|
|
|
# New segment: |_______|
|
|
prog = Program(MOCK_PLATFORM)
|
|
segment1 = unittest.mock.Mock(side_effect=zero_memory_read)
|
|
segment2 = unittest.mock.Mock(side_effect=zero_memory_read)
|
|
prog.add_memory_segment(0xFFFF0000, 128, segment1)
|
|
prog.add_memory_segment(0xFFFF0000, 128, segment2)
|
|
prog.read(0xFFFF0000, 128)
|
|
segment1.assert_not_called()
|
|
segment2.assert_called_once_with(0xFFFF0000, 128, 0, False)
|
|
|
|
def test_overlap_same_address_larger_size(self):
|
|
# Existing segment: |___|
|
|
# New segment: |_______|
|
|
prog = Program(MOCK_PLATFORM)
|
|
segment1 = unittest.mock.Mock(side_effect=zero_memory_read)
|
|
segment2 = unittest.mock.Mock(side_effect=zero_memory_read)
|
|
prog.add_memory_segment(0xFFFF0000, 64, segment1)
|
|
prog.add_memory_segment(0xFFFF0000, 128, segment2)
|
|
prog.read(0xFFFF0000, 128)
|
|
segment1.assert_not_called()
|
|
segment2.assert_called_once_with(0xFFFF0000, 128, 0, False)
|
|
|
|
def test_overlap_segment_tail(self):
|
|
# Existing segment: |_______|
|
|
# New segment: |_______|
|
|
prog = Program(MOCK_PLATFORM)
|
|
segment1 = unittest.mock.Mock(side_effect=zero_memory_read)
|
|
segment2 = unittest.mock.Mock(side_effect=zero_memory_read)
|
|
prog.add_memory_segment(0xFFFF0000, 128, segment1)
|
|
prog.add_memory_segment(0xFFFF0040, 128, segment2)
|
|
prog.read(0xFFFF0000, 192)
|
|
segment1.assert_called_once_with(0xFFFF0000, 64, 0, False)
|
|
segment2.assert_called_once_with(0xFFFF0040, 128, 0, False)
|
|
|
|
def test_overlap_subsume_after(self):
|
|
# Existing segments: |_|_|_|_|
|
|
# New segment: |_______|
|
|
prog = Program(MOCK_PLATFORM)
|
|
segment1 = unittest.mock.Mock(side_effect=zero_memory_read)
|
|
segment2 = unittest.mock.Mock(side_effect=zero_memory_read)
|
|
segment3 = unittest.mock.Mock(side_effect=zero_memory_read)
|
|
prog.add_memory_segment(0xFFFF0020, 32, segment1)
|
|
prog.add_memory_segment(0xFFFF0040, 32, segment1)
|
|
prog.add_memory_segment(0xFFFF0060, 32, segment1)
|
|
prog.add_memory_segment(0xFFFF0080, 64, segment2)
|
|
prog.add_memory_segment(0xFFFF0000, 128, segment3)
|
|
prog.read(0xFFFF0000, 192)
|
|
segment1.assert_not_called()
|
|
segment2.assert_called_once_with(0xFFFF0080, 64, 0, False)
|
|
segment3.assert_called_once_with(0xFFFF0000, 128, 0, False)
|
|
|
|
def test_overlap_segment_head(self):
|
|
# Existing segment: |_______|
|
|
# New segment: |_______|
|
|
prog = Program(MOCK_PLATFORM)
|
|
segment1 = unittest.mock.Mock(side_effect=zero_memory_read)
|
|
segment2 = unittest.mock.Mock(side_effect=zero_memory_read)
|
|
prog.add_memory_segment(0xFFFF0040, 128, segment1)
|
|
prog.add_memory_segment(0xFFFF0000, 128, segment2)
|
|
prog.read(0xFFFF0000, 192)
|
|
segment1.assert_called_once_with(0xFFFF0080, 64, 64, False)
|
|
segment2.assert_called_once_with(0xFFFF0000, 128, 0, False)
|
|
|
|
def test_overlap_segment_head_and_tail(self):
|
|
# Existing segment: |_______||_______|
|
|
# New segment: |_______|
|
|
prog = Program(MOCK_PLATFORM)
|
|
segment1 = unittest.mock.Mock(side_effect=zero_memory_read)
|
|
segment2 = unittest.mock.Mock(side_effect=zero_memory_read)
|
|
segment3 = unittest.mock.Mock(side_effect=zero_memory_read)
|
|
prog.add_memory_segment(0xFFFF0000, 128, segment1)
|
|
prog.add_memory_segment(0xFFFF0080, 128, segment2)
|
|
prog.add_memory_segment(0xFFFF0040, 128, segment3)
|
|
prog.read(0xFFFF0000, 256)
|
|
segment1.assert_called_once_with(0xFFFF0000, 64, 0, False)
|
|
segment2.assert_called_once_with(0xFFFF00C0, 64, 64, False)
|
|
segment3.assert_called_once_with(0xFFFF0040, 128, 0, False)
|
|
|
|
def test_overlap_subsume_at_and_after(self):
|
|
# Existing segments: |_|_|_|_|
|
|
# New segment: |_______|
|
|
prog = Program(MOCK_PLATFORM)
|
|
segment1 = unittest.mock.Mock(side_effect=zero_memory_read)
|
|
segment2 = unittest.mock.Mock(side_effect=zero_memory_read)
|
|
prog.add_memory_segment(0xFFFF0000, 32, segment1)
|
|
prog.add_memory_segment(0xFFFF0020, 32, segment1)
|
|
prog.add_memory_segment(0xFFFF0040, 32, segment1)
|
|
prog.add_memory_segment(0xFFFF0060, 32, segment1)
|
|
prog.add_memory_segment(0xFFFF0000, 128, segment2)
|
|
prog.read(0xFFFF0000, 128)
|
|
segment1.assert_not_called()
|
|
segment2.assert_called_once_with(0xFFFF0000, 128, 0, False)
|
|
|
|
def test_invalid_read_fn(self):
|
|
prog = mock_program()
|
|
|
|
self.assertRaises(TypeError, prog.add_memory_segment, 0xFFFF0000, 8, b"foo")
|
|
|
|
prog.add_memory_segment(0xFFFF0000, 8, lambda: None)
|
|
self.assertRaises(TypeError, prog.read, 0xFFFF0000, 8)
|
|
|
|
prog.add_memory_segment(
|
|
0xFFFF0000, 8, lambda address, count, offset, physical: None
|
|
)
|
|
self.assertRaises(TypeError, prog.read, 0xFFFF0000, 8)
|
|
|
|
prog.add_memory_segment(
|
|
0xFFFF0000, 8, lambda address, count, offset, physical: "asdf"
|
|
)
|
|
self.assertRaises(TypeError, prog.read, 0xFFFF0000, 8)
|
|
|
|
prog.add_memory_segment(
|
|
0xFFFF0000, 8, lambda address, count, offset, physical: b""
|
|
)
|
|
self.assertRaisesRegex(
|
|
ValueError,
|
|
r"memory read callback returned buffer of length 0 \(expected 8\)",
|
|
prog.read,
|
|
0xFFFF0000,
|
|
8,
|
|
)
|
|
|
|
|
|
class TestTypes(MockProgramTestCase):
|
|
def test_invalid_finder(self):
|
|
self.assertRaises(TypeError, self.prog.add_type_finder, "foo")
|
|
|
|
self.prog.add_type_finder(lambda kind, name, filename: "foo")
|
|
self.assertRaises(TypeError, self.prog.type, "int")
|
|
|
|
def test_finder_different_program(self):
|
|
def finder(kind, name, filename):
|
|
if kind == TypeKind.TYPEDEF and name == "foo":
|
|
prog = Program()
|
|
return prog.typedef_type("foo", prog.void_type())
|
|
else:
|
|
return None
|
|
|
|
self.prog.add_type_finder(finder)
|
|
self.assertRaisesRegex(
|
|
ValueError,
|
|
"type find callback returned type from wrong program",
|
|
self.prog.type,
|
|
"foo",
|
|
)
|
|
|
|
def test_wrong_kind(self):
|
|
self.prog.add_type_finder(lambda kind, name, filename: self.prog.void_type())
|
|
self.assertRaises(TypeError, self.prog.type, "int")
|
|
|
|
def test_not_found(self):
|
|
self.assertRaises(LookupError, self.prog.type, "struct foo")
|
|
self.prog.add_type_finder(lambda kind, name, filename: None)
|
|
self.assertRaises(LookupError, self.prog.type, "struct foo")
|
|
|
|
def test_already_type(self):
|
|
self.assertIdentical(
|
|
self.prog.type(self.prog.pointer_type(self.prog.void_type())),
|
|
self.prog.pointer_type(self.prog.void_type()),
|
|
)
|
|
|
|
def test_invalid_argument_type(self):
|
|
self.assertRaises(TypeError, self.prog.type, 1)
|
|
|
|
def test_default_primitive_types(self):
|
|
def spellings(tokens, num_optional=0):
|
|
for i in range(len(tokens) - num_optional, len(tokens) + 1):
|
|
for perm in itertools.permutations(tokens[:i]):
|
|
yield " ".join(perm)
|
|
|
|
for word_size in [8, 4]:
|
|
prog = mock_program(
|
|
MOCK_PLATFORM if word_size == 8 else MOCK_32BIT_PLATFORM
|
|
)
|
|
self.assertIdentical(prog.type("_Bool"), prog.bool_type("_Bool", 1))
|
|
self.assertIdentical(prog.type("char"), prog.int_type("char", 1, True))
|
|
for spelling in spellings(["signed", "char"]):
|
|
self.assertIdentical(
|
|
prog.type(spelling), prog.int_type("signed char", 1, True)
|
|
)
|
|
for spelling in spellings(["unsigned", "char"]):
|
|
self.assertIdentical(
|
|
prog.type(spelling), prog.int_type("unsigned char", 1, False)
|
|
)
|
|
for spelling in spellings(["short", "signed", "int"], 2):
|
|
self.assertIdentical(
|
|
prog.type(spelling), prog.int_type("short", 2, True)
|
|
)
|
|
for spelling in spellings(["short", "unsigned", "int"], 1):
|
|
self.assertIdentical(
|
|
prog.type(spelling), prog.int_type("unsigned short", 2, False)
|
|
)
|
|
for spelling in spellings(["int", "signed"], 1):
|
|
self.assertIdentical(prog.type(spelling), prog.int_type("int", 4, True))
|
|
for spelling in spellings(["unsigned", "int"]):
|
|
self.assertIdentical(
|
|
prog.type(spelling), prog.int_type("unsigned int", 4, False)
|
|
)
|
|
for spelling in spellings(["long", "signed", "int"], 2):
|
|
self.assertIdentical(
|
|
prog.type(spelling), prog.int_type("long", word_size, True)
|
|
)
|
|
for spelling in spellings(["long", "unsigned", "int"], 1):
|
|
self.assertIdentical(
|
|
prog.type(spelling),
|
|
prog.int_type("unsigned long", word_size, False),
|
|
)
|
|
for spelling in spellings(["long", "long", "signed", "int"], 2):
|
|
self.assertIdentical(
|
|
prog.type(spelling), prog.int_type("long long", 8, True)
|
|
)
|
|
for spelling in spellings(["long", "long", "unsigned", "int"], 1):
|
|
self.assertIdentical(
|
|
prog.type(spelling), prog.int_type("unsigned long long", 8, False)
|
|
)
|
|
self.assertIdentical(prog.type("float"), prog.float_type("float", 4))
|
|
self.assertIdentical(prog.type("double"), prog.float_type("double", 8))
|
|
for spelling in spellings(["long", "double"]):
|
|
self.assertIdentical(
|
|
prog.type(spelling), prog.float_type("long double", 16)
|
|
)
|
|
self.assertIdentical(
|
|
prog.type("size_t"),
|
|
prog.typedef_type(
|
|
"size_t", prog.int_type("unsigned long", word_size, False)
|
|
),
|
|
)
|
|
self.assertIdentical(
|
|
prog.type("ptrdiff_t"),
|
|
prog.typedef_type("ptrdiff_t", prog.int_type("long", word_size, True)),
|
|
)
|
|
|
|
def test_primitive_type(self):
|
|
self.types.append(self.prog.int_type("long", 4, True))
|
|
self.assertIdentical(
|
|
self.prog.type("long"), self.prog.int_type("long", 4, True)
|
|
)
|
|
|
|
def test_primitive_type_invalid(self):
|
|
# unsigned long with signed=True isn't valid, so it should be ignored.
|
|
self.types.append(self.prog.int_type("unsigned long", 4, True))
|
|
self.assertIdentical(
|
|
self.prog.type("unsigned long"),
|
|
self.prog.int_type("unsigned long", 8, False),
|
|
)
|
|
|
|
def test_size_t_and_ptrdiff_t(self):
|
|
# 64-bit architecture with 4-byte long/unsigned long.
|
|
types = []
|
|
prog = mock_program(types=types)
|
|
types.append(prog.int_type("long", 4, True))
|
|
types.append(prog.int_type("unsigned long", 4, False))
|
|
self.assertIdentical(
|
|
prog.type("size_t"),
|
|
prog.typedef_type("size_t", prog.type("unsigned long long")),
|
|
)
|
|
self.assertIdentical(
|
|
prog.type("ptrdiff_t"),
|
|
prog.typedef_type("ptrdiff_t", prog.type("long long")),
|
|
)
|
|
|
|
# 32-bit architecture with 8-byte long/unsigned long.
|
|
types = []
|
|
prog = mock_program(MOCK_32BIT_PLATFORM, types=types)
|
|
types.append(prog.int_type("long", 8, True))
|
|
types.append(prog.int_type("unsigned long", 8, False))
|
|
self.assertIdentical(
|
|
prog.type("size_t"), prog.typedef_type("size_t", prog.type("unsigned int"))
|
|
)
|
|
self.assertIdentical(
|
|
prog.type("ptrdiff_t"), prog.typedef_type("ptrdiff_t", prog.type("int"))
|
|
)
|
|
|
|
# Nonsense sizes.
|
|
types = []
|
|
prog = mock_program(types=types)
|
|
types.append(prog.int_type("int", 1, True))
|
|
types.append(prog.int_type("unsigned int", 1, False))
|
|
types.append(prog.int_type("long", 1, True))
|
|
types.append(prog.int_type("unsigned long", 1, False))
|
|
types.append(prog.int_type("long long", 2, True))
|
|
types.append(prog.int_type("unsigned long long", 2, False))
|
|
self.assertRaisesRegex(
|
|
ValueError, "no suitable integer type for size_t", prog.type, "size_t"
|
|
)
|
|
self.assertRaisesRegex(
|
|
ValueError, "no suitable integer type for ptrdiff_t", prog.type, "ptrdiff_t"
|
|
)
|
|
|
|
def test_not_size_t_or_ptrdiff_t(self):
|
|
self.types.append(
|
|
self.prog.typedef_type(
|
|
"size_tea", self.prog.int_type("unsigned char", 1, False)
|
|
)
|
|
)
|
|
self.types.append(
|
|
self.prog.typedef_type("ptrdiff_tee", self.prog.int_type("char", 1, True))
|
|
)
|
|
self.assertIdentical(
|
|
self.prog.type("size_tea"),
|
|
self.prog.typedef_type(
|
|
"size_tea", self.prog.int_type("unsigned char", 1, False)
|
|
),
|
|
)
|
|
self.assertIdentical(
|
|
self.prog.type("ptrdiff_tee"),
|
|
self.prog.typedef_type("ptrdiff_tee", self.prog.int_type("char", 1, True)),
|
|
)
|
|
|
|
def test_tagged_type(self):
|
|
self.types.append(self.point_type)
|
|
self.types.append(self.option_type)
|
|
self.types.append(self.color_type)
|
|
self.assertIdentical(self.prog.type("struct point"), self.point_type)
|
|
self.assertIdentical(self.prog.type("union option"), self.option_type)
|
|
self.assertIdentical(self.prog.type("enum color"), self.color_type)
|
|
|
|
def test_class_type(self):
|
|
struct_class = self.prog.struct_type(
|
|
"class",
|
|
8,
|
|
(TypeMember(self.prog.pointer_type(self.prog.void_type()), "ptr"),),
|
|
)
|
|
class_point = self.prog.class_type(
|
|
"Point",
|
|
8,
|
|
(
|
|
TypeMember(self.prog.int_type("int", 4, True), "x", 0),
|
|
TypeMember(self.prog.int_type("int", 4, True), "y", 32),
|
|
),
|
|
)
|
|
self.types.append(struct_class)
|
|
self.types.append(class_point)
|
|
self.prog.language = Language.C
|
|
self.assertIdentical(self.prog.type("struct class"), struct_class)
|
|
self.prog.language = Language.CPP
|
|
self.assertRaisesRegex(
|
|
SyntaxError,
|
|
"expected identifier after 'struct'",
|
|
self.prog.type,
|
|
"struct class",
|
|
)
|
|
self.assertIdentical(self.prog.type("class Point"), class_point)
|
|
|
|
def test_typedef(self):
|
|
self.types.append(self.pid_type)
|
|
self.assertIdentical(self.prog.type("pid_t"), self.pid_type)
|
|
|
|
def test_pointer(self):
|
|
self.assertIdentical(
|
|
self.prog.type("int *"),
|
|
self.prog.pointer_type(self.prog.int_type("int", 4, True)),
|
|
)
|
|
|
|
def test_pointer_to_const(self):
|
|
self.assertIdentical(
|
|
self.prog.type("const int *"),
|
|
self.prog.pointer_type(
|
|
self.prog.int_type("int", 4, True, qualifiers=Qualifiers.CONST)
|
|
),
|
|
)
|
|
|
|
def test_const_pointer(self):
|
|
self.assertIdentical(
|
|
self.prog.type("int * const"),
|
|
self.prog.pointer_type(
|
|
self.prog.int_type("int", 4, True), qualifiers=Qualifiers.CONST
|
|
),
|
|
)
|
|
|
|
def test_pointer_to_pointer(self):
|
|
self.assertIdentical(
|
|
self.prog.type("int **"),
|
|
self.prog.pointer_type(
|
|
self.prog.pointer_type(self.prog.int_type("int", 4, True))
|
|
),
|
|
)
|
|
self.assertIdentical(self.prog.type("int *((*))"), self.prog.type("int **"))
|
|
|
|
def test_pointer_to_const_pointer(self):
|
|
self.assertIdentical(
|
|
self.prog.type("int * const *"),
|
|
self.prog.pointer_type(
|
|
self.prog.pointer_type(
|
|
self.prog.int_type("int", 4, True), qualifiers=Qualifiers.CONST
|
|
)
|
|
),
|
|
)
|
|
|
|
def test_array(self):
|
|
self.assertIdentical(
|
|
self.prog.type("int [20]"),
|
|
self.prog.array_type(self.prog.int_type("int", 4, True), 20),
|
|
)
|
|
|
|
def test_array_hexadecimal(self):
|
|
self.assertIdentical(
|
|
self.prog.type("int [0x20]"),
|
|
self.prog.array_type(self.prog.int_type("int", 4, True), 32),
|
|
)
|
|
|
|
def test_array_octal(self):
|
|
self.assertIdentical(
|
|
self.prog.type("int [020]"),
|
|
self.prog.array_type(self.prog.int_type("int", 4, True), 16),
|
|
)
|
|
|
|
def test_incomplete_array(self):
|
|
self.assertIdentical(
|
|
self.prog.type("int []"),
|
|
self.prog.array_type(self.prog.int_type("int", 4, True)),
|
|
)
|
|
|
|
def test_array_two_dimensional(self):
|
|
self.assertIdentical(
|
|
self.prog.type("int [2][3]"),
|
|
self.prog.array_type(
|
|
self.prog.array_type(self.prog.int_type("int", 4, True), 3), 2
|
|
),
|
|
)
|
|
|
|
def test_array_three_dimensional(self):
|
|
self.assertIdentical(
|
|
self.prog.type("int [2][3][4]"),
|
|
self.prog.array_type(
|
|
self.prog.array_type(
|
|
self.prog.array_type(self.prog.int_type("int", 4, True), 4), 3
|
|
),
|
|
2,
|
|
),
|
|
)
|
|
|
|
def test_array_of_pointers(self):
|
|
self.assertIdentical(
|
|
self.prog.type("int *[2][3]"),
|
|
self.prog.array_type(
|
|
self.prog.array_type(
|
|
self.prog.pointer_type(self.prog.int_type("int", 4, True)), 3
|
|
),
|
|
2,
|
|
),
|
|
)
|
|
|
|
def test_pointer_to_array(self):
|
|
self.assertIdentical(
|
|
self.prog.type("int (*)[2]"),
|
|
self.prog.pointer_type(
|
|
self.prog.array_type(self.prog.int_type("int", 4, True), 2)
|
|
),
|
|
)
|
|
|
|
def test_pointer_to_two_dimensional_array(self):
|
|
self.assertIdentical(
|
|
self.prog.type("int (*)[2][3]"),
|
|
self.prog.pointer_type(
|
|
self.prog.array_type(
|
|
self.prog.array_type(self.prog.int_type("int", 4, True), 3), 2
|
|
)
|
|
),
|
|
)
|
|
|
|
def test_pointer_to_pointer_to_array(self):
|
|
self.assertIdentical(
|
|
self.prog.type("int (**)[2]"),
|
|
self.prog.pointer_type(
|
|
self.prog.pointer_type(
|
|
self.prog.array_type(self.prog.int_type("int", 4, True), 2)
|
|
)
|
|
),
|
|
)
|
|
|
|
def test_pointer_to_array_of_pointers(self):
|
|
self.assertIdentical(
|
|
self.prog.type("int *(*)[2]"),
|
|
self.prog.pointer_type(
|
|
self.prog.array_type(
|
|
self.prog.pointer_type(self.prog.int_type("int", 4, True)), 2
|
|
)
|
|
),
|
|
)
|
|
self.assertIdentical(
|
|
self.prog.type("int *((*)[2])"), self.prog.type("int *(*)[2]")
|
|
)
|
|
|
|
def test_array_of_pointers_to_array(self):
|
|
self.assertIdentical(
|
|
self.prog.type("int (*[2])[3]"),
|
|
self.prog.array_type(
|
|
self.prog.pointer_type(
|
|
self.prog.array_type(self.prog.int_type("int", 4, True), 3)
|
|
),
|
|
2,
|
|
),
|
|
)
|
|
|
|
|
|
class TestObjects(MockProgramTestCase):
|
|
def test_invalid_finder(self):
|
|
self.assertRaises(TypeError, self.prog.add_object_finder, "foo")
|
|
|
|
self.prog.add_object_finder(lambda prog, name, flags, filename: "foo")
|
|
self.assertRaises(TypeError, self.prog.object, "foo")
|
|
|
|
def test_not_found(self):
|
|
self.assertRaises(LookupError, self.prog.object, "foo")
|
|
self.prog.add_object_finder(lambda prog, name, flags, filename: None)
|
|
self.assertRaises(LookupError, self.prog.object, "foo")
|
|
self.assertFalse("foo" in self.prog)
|
|
|
|
def test_constant(self):
|
|
self.objects.append(
|
|
MockObject("PAGE_SIZE", self.prog.int_type("int", 4, True), value=4096)
|
|
)
|
|
self.assertIdentical(
|
|
self.prog["PAGE_SIZE"],
|
|
Object(self.prog, self.prog.int_type("int", 4, True), value=4096),
|
|
)
|
|
self.assertIdentical(
|
|
self.prog.object("PAGE_SIZE", FindObjectFlags.CONSTANT),
|
|
self.prog["PAGE_SIZE"],
|
|
)
|
|
self.assertTrue("PAGE_SIZE" in self.prog)
|
|
|
|
def test_function(self):
|
|
self.objects.append(
|
|
MockObject(
|
|
"func",
|
|
self.prog.function_type(self.prog.void_type(), (), False),
|
|
address=0xFFFF0000,
|
|
)
|
|
)
|
|
self.assertIdentical(
|
|
self.prog["func"],
|
|
Object(
|
|
self.prog,
|
|
self.prog.function_type(self.prog.void_type(), (), False),
|
|
address=0xFFFF0000,
|
|
),
|
|
)
|
|
self.assertIdentical(
|
|
self.prog.object("func", FindObjectFlags.FUNCTION), self.prog["func"]
|
|
)
|
|
self.assertTrue("func" in self.prog)
|
|
|
|
def test_variable(self):
|
|
self.objects.append(
|
|
MockObject(
|
|
"counter", self.prog.int_type("int", 4, True), address=0xFFFF0000
|
|
)
|
|
)
|
|
self.assertIdentical(
|
|
self.prog["counter"],
|
|
Object(self.prog, self.prog.int_type("int", 4, True), address=0xFFFF0000),
|
|
)
|
|
self.assertIdentical(
|
|
self.prog.object("counter", FindObjectFlags.VARIABLE), self.prog["counter"]
|
|
)
|
|
self.assertTrue("counter" in self.prog)
|
|
|
|
|
|
class TestCoreDump(TestCase):
|
|
def test_not_core_dump(self):
|
|
prog = Program()
|
|
self.assertRaisesRegex(
|
|
ValueError, "not an ELF core file", prog.set_core_dump, "/dev/null"
|
|
)
|
|
with tempfile.NamedTemporaryFile() as f:
|
|
f.write(create_elf_file(ET.EXEC, []))
|
|
f.flush()
|
|
self.assertRaisesRegex(
|
|
ValueError, "not an ELF core file", prog.set_core_dump, f.name
|
|
)
|
|
|
|
def test_twice(self):
|
|
prog = Program()
|
|
with tempfile.NamedTemporaryFile() as f:
|
|
f.write(create_elf_file(ET.CORE, []))
|
|
f.flush()
|
|
prog.set_core_dump(f.name)
|
|
self.assertRaisesRegex(
|
|
ValueError,
|
|
"program memory was already initialized",
|
|
prog.set_core_dump,
|
|
f.name,
|
|
)
|
|
|
|
def test_simple(self):
|
|
data = b"hello, world"
|
|
prog = Program()
|
|
with tempfile.NamedTemporaryFile() as f:
|
|
f.write(
|
|
create_elf_file(
|
|
ET.CORE, [ElfSection(p_type=PT.LOAD, vaddr=0xFFFF0000, data=data)]
|
|
)
|
|
)
|
|
f.flush()
|
|
prog.set_core_dump(f.name)
|
|
self.assertEqual(prog.read(0xFFFF0000, len(data)), data)
|
|
self.assertRaises(FaultError, prog.read, 0x0, len(data), physical=True)
|
|
|
|
def test_physical(self):
|
|
data = b"hello, world"
|
|
prog = Program()
|
|
with tempfile.NamedTemporaryFile() as f:
|
|
f.write(
|
|
create_elf_file(
|
|
ET.CORE,
|
|
[
|
|
ElfSection(
|
|
p_type=PT.LOAD, vaddr=0xFFFF0000, paddr=0xA0, data=data
|
|
),
|
|
],
|
|
)
|
|
)
|
|
f.flush()
|
|
prog.set_core_dump(f.name)
|
|
self.assertEqual(prog.read(0xFFFF0000, len(data)), data)
|
|
self.assertEqual(prog.read(0xA0, len(data), physical=True), data)
|
|
|
|
def test_unsaved(self):
|
|
data = b"hello, world"
|
|
prog = Program()
|
|
with tempfile.NamedTemporaryFile() as f:
|
|
f.write(
|
|
create_elf_file(
|
|
ET.CORE,
|
|
[
|
|
ElfSection(
|
|
p_type=PT.LOAD,
|
|
vaddr=0xFFFF0000,
|
|
data=data,
|
|
memsz=len(data) + 4,
|
|
),
|
|
],
|
|
)
|
|
)
|
|
f.flush()
|
|
prog.set_core_dump(f.name)
|
|
with self.assertRaisesRegex(FaultError, "memory not saved in core dump") as cm:
|
|
prog.read(0xFFFF0000, len(data) + 4)
|
|
self.assertEqual(cm.exception.address, 0xFFFF000C)
|