You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
pwndbg/tests/qemu-tests/conftest.py

194 lines
5.2 KiB
Python

"""
This file should consist of global test fixtures.
"""
from __future__ import annotations
import os
import subprocess
import sys
import typing
from typing import Dict
from typing import Literal
from typing import Tuple
import gdb
import pytest
from pwndbg.lib import tempfile
_start_binary_called = False
QEMU_PORT = os.environ.get("QEMU_PORT")
ZIGPATH = os.environ.get("ZIGPATH")
COMPILATION_TARGETS_TYPE = Literal[
"aarch64",
"arm",
"riscv32",
"riscv64",
"loongarch64",
"powerpc32",
"powerpc64",
"mips32",
"mipsel32",
"mips64",
"s390x",
"sparc",
]
COMPILATION_TARGETS: list[COMPILATION_TARGETS_TYPE] = list(
typing.get_args(COMPILATION_TARGETS_TYPE)
)
# Tuple contains (Zig target,extra_cli_args,qemu_suffix),
COMPILE_AND_RUN_INFO: Dict[COMPILATION_TARGETS_TYPE, Tuple[str, Tuple[str, ...], str]] = {
"aarch64": ("aarch64-freestanding", (), "aarch64"),
# TODO: when updating to newer version of Zig, this -mcpu option can be removed
"arm": ("arm-freestanding", ("-mcpu=cortex_a7",), "arm"),
"riscv32": ("riscv32-freestanding", (), "riscv32"),
"riscv64": ("riscv64-freestanding", (), "riscv64"),
"mips32": ("mips-freestanding", (), "mips"),
"mipsel32": ("mipsel-freestanding", (), "mipsel"),
"mips64": ("mips64-freestanding", (), "mips64"),
"loongarch64": ("loongarch64-freestanding", (), "loongarch64"),
"s390x": ("s390x-freestanding", (), "s390x"),
"sparc": ("sparc64-freestanding", (), "sparc64"),
"powerpc32": ("powerpc-freestanding", (), "ppc"),
"powerpc64": ("powerpc64-freestanding", (), "ppc64"),
}
@pytest.fixture
def qemu_assembly_run():
"""
Returns function that launches given binary with 'starti' command
The `path` is returned from `make_elf_from_assembly` (provided by pwntools)
"""
if ZIGPATH is None:
raise Exception("ZIGPATH not defined")
PATH_TO_ZIG = os.path.join(ZIGPATH, "zig")
qemu: subprocess.Popen = None
if QEMU_PORT is None:
print("'QEMU_PORT' environment variable not set")
sys.exit(1)
def _start_binary(asm: str, arch: COMPILATION_TARGETS_TYPE):
nonlocal qemu
if arch not in COMPILATION_TARGETS or arch not in COMPILE_AND_RUN_INFO:
raise Exception(f"Unknown compilation target: {arch}")
zig_target, extra_cli_args, qemu_suffix = COMPILE_AND_RUN_INFO[arch]
# Place assembly and compiled binary in a temporary folder
# named /tmp/pwndbg-*
tmpdir = tempfile.tempdir()
asm_file = os.path.join(tmpdir, "input.S")
with open(asm_file, "w") as f:
f.write(asm)
compiled_file = os.path.join(tmpdir, "out.elf")
# Build the binary with Zig
compile_process = subprocess.run(
[
PATH_TO_ZIG,
"cc",
*extra_cli_args,
f"--target={zig_target}",
asm_file,
"-o",
compiled_file,
],
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
universal_newlines=True,
)
if compile_process.returncode != 0:
raise Exception("Compilation error", compile_process.stdout, compile_process.stderr)
qemu = subprocess.Popen(
[
f"qemu-{qemu_suffix}",
"-g",
f"{QEMU_PORT}",
f"{compiled_file}",
]
)
os.environ["PWNDBG_IN_TEST"] = "1"
os.environ["COLUMNS"] = "80"
gdb.execute("set exception-verbose on")
gdb.execute("set context-reserve-lines never")
gdb.execute("set width 80")
gdb.execute(f"target remote :{QEMU_PORT}")
global _start_binary_called
# if _start_binary_called:
# raise Exception('Starting more than one binary is not supported in pwndbg tests.')
_start_binary_called = True
yield _start_binary
qemu.kill()
@pytest.fixture
def qemu_start_binary():
"""
Returns function that launches given binary with 'starti' command
Argument `path` is the path to the binary
"""
qemu: subprocess.Popen = None
if QEMU_PORT is None:
print("'QEMU_PORT' environment variable not set")
sys.exit(1)
def _start_binary(path: str, arch: COMPILATION_TARGETS_TYPE):
nonlocal qemu
if arch not in COMPILATION_TARGETS or arch not in COMPILE_AND_RUN_INFO:
raise Exception(f"Unknown compilation target: {arch}")
_, _, qemu_suffix = COMPILE_AND_RUN_INFO[arch]
qemu = subprocess.Popen(
[
f"qemu-{qemu_suffix}",
"-g",
f"{QEMU_PORT}",
f"{path}",
]
)
os.environ["PWNDBG_IN_TEST"] = "1"
os.environ["COLUMNS"] = "80"
gdb.execute("set exception-verbose on")
gdb.execute("set context-reserve-lines never")
gdb.execute("set width 80")
gdb.execute(f"target remote :{QEMU_PORT}")
global _start_binary_called
# if _start_binary_called:
# raise Exception('Starting more than one binary is not supported in pwndbg tests.')
_start_binary_called = True
yield _start_binary
qemu.kill()