| 1 | #!/usr/bin/env python3 |
| 2 | from pathlib import Path |
| 3 | import struct |
| 4 | import subprocess |
| 5 | import tempfile |
| 6 | |
| 7 | source = Path(__file__).resolve().parent |
| 8 | for arch in ['x86', 'x86_64', 'aarch64']: |
| 9 | subprocess.run(['python3', str(source / 'build.py'), '--arch', arch], check=True) |
| 10 | data = (source / 'build' / ('snowglobe-guest-' + arch + '.exe')).read_bytes() |
| 11 | pe = struct.unpack_from('<I', data, 60)[0] |
| 12 | assert data[pe:pe + 4] == b'PE\0\0' |
| 13 | machine, sections, _, _, _, optional_size = struct.unpack_from('<HHIIIH', data, pe + 4) |
| 14 | optional = pe + 24 |
| 15 | assert machine == {'x86': 0x14c, 'x86_64': 0x8664, 'aarch64': 0xaa64}[arch] |
| 16 | assert struct.unpack_from('<HH', data, optional + 48) == ((10, 0) if arch == 'aarch64' else (6, 1)) |
| 17 | table = optional + optional_size |
| 18 | def offset(rva): |
| 19 | for index in range(sections): |
| 20 | size, start, raw_size, raw = struct.unpack_from('<IIII', data, table + 40 * index + 8) |
| 21 | if start <= rva < start + max(size, raw_size): return raw + rva - start |
| 22 | raise AssertionError('unmapped PE address') |
| 23 | directory = optional + (96 if arch == 'x86' else 112) |
| 24 | imports = offset(struct.unpack_from('<I', data, directory + 8)[0]) |
| 25 | libraries = set() |
| 26 | while any(data[imports:imports + 20]): |
| 27 | name = offset(struct.unpack_from('<I', data, imports + 12)[0]) |
| 28 | libraries.add(data[name:data.index(0, name)].decode().lower()) |
| 29 | imports += 20 |
| 30 | assert libraries == {'kernel32.dll', 'user32.dll', 'gdi32.dll'}, libraries |
| 31 | print(arch, len(data), 'bytes; system DLLs only; minimum subsystem verified') |
| 32 | with tempfile.TemporaryDirectory() as temporary: |
| 33 | binary = Path(temporary) / 'cursor-tests' |
| 34 | subprocess.run(['cc', '-std=c99', '-Wall', '-Wextra', '-Werror', '-ffreestanding', '-fsanitize=address,undefined', '-I', str(source / 'build'), str(source / 'cursor.c'), str(source / 'tests.c'), '-o', str(binary)], check=True) |
| 35 | subprocess.run([str(binary)], check=True) |
| 36 | print('cursor alpha, inversion, dirty rectangles, and bounds passed') |