mirror of https://github.com/pwndbg/pwndbg.git
Cross architecture sanity check tests (#2745)
* Add a test that steps through each instruction in a program in different arches to detect crashes in annotations code * lint * Add symlinks so qemu can find libraries, simplify selecting correct qemu version * lint * compile cross-arch binaries with -static * Add map of cross-arch library paths to avoid creating symlink * lint * clean up cross-arch makefile, skip the slow tests unless an environment variable is set * correct compiler versions * remove empty lines * Makefile comment * Add syscall to basic.c * Increase performance * Always run the tests * lintpull/2915/head
parent
491800e5a3
commit
232f5a1b06
@ -1,19 +1,60 @@
|
||||
.PHONY: all
|
||||
all: reference-binary.aarch64.out reference-binary.riscv64.out
|
||||
|
||||
%.aarch64.out : %.aarch64.c
|
||||
@echo "[+] Building '$@'"
|
||||
@aarch64-linux-gnu-gcc $(CFLAGS) $(EXTRA_FLAGS) -w -o $@ $< $(LDFLAGS)
|
||||
ARCHS = aarch64 arm riscv64 mips64 mips32
|
||||
|
||||
CC.aarch64 = aarch64-linux-gnu-gcc
|
||||
CC.arm = arm-linux-gnueabihf-gcc
|
||||
CC.riscv64 = riscv64-linux-gnu-gcc
|
||||
# Ubuntu riscv64 cross compiler package doesn't have "--enable-multilib" enabled to allow compiling to 32-bit
|
||||
# CC.riscv32 = riscv64-linux-gnu-gcc
|
||||
CC.mips64 = mips64-linux-gnuabi64-gcc
|
||||
CC.mips32 = mips-linux-gnu-gcc
|
||||
# CC.loongarch64 = loongarch64-linux-gnu-gcc
|
||||
|
||||
ALL_FLAGS = -g
|
||||
|
||||
CFLAGS.aarch64 = $(ALL_FLAGS)
|
||||
CFLAGS.arm = $(ALL_FLAGS)
|
||||
CFLAGS.riscv64 = -march=rv64gc -mabi=lp64d $(ALL_FLAGS)
|
||||
# CFLAGS.riscv32 = -march=rv32gc $(ALL_FLAGS)
|
||||
CFLAGS.mips64 = $(ALL_FLAGS)
|
||||
CFLAGS.mips32 = $(ALL_FLAGS)
|
||||
# CFLAGS.loongarch64 = $(ALL_FLAGS)
|
||||
|
||||
%.riscv64.out : %.riscv64.c
|
||||
@echo "[+] Building '$@'"
|
||||
@riscv64-linux-gnu-gcc -march=rv64gc -mabi=lp64d -g $(CFLAGS) $(EXTRA_FLAGS) -w -o $@ $? $(LDFLAGS)
|
||||
|
||||
AARCH64_SOURCES := $(wildcard *.aarch64.c)
|
||||
AARCH64_TARGETS := $(AARCH64_SOURCES:.aarch64.c=.aarch64.out)
|
||||
|
||||
ARM_SOURCES := $(wildcard *.arm.c)
|
||||
ARM_TARGETS := $(ARM_SOURCES:.arm.c=.arm.out)
|
||||
|
||||
RISCV64_SOURCES := $(wildcard *.riscv64.c)
|
||||
RISCV64_TARGETS := $(RISCV64_SOURCES:.riscv64.c=.riscv64.out)
|
||||
|
||||
MIPS64_SOURCES := $(wildcard *.mips64.c)
|
||||
MIPS64_TARGETS := $(MIPS64_SOURCES:.mips64.c=.mips64.out)
|
||||
|
||||
MIPS32_SOURCES := $(wildcard *.mips32.c)
|
||||
MIPS32_TARGETS := $(MIPS32_SOURCES:.mips32.c=.mips32.out)
|
||||
|
||||
|
||||
# Build basic.c for all architectures
|
||||
BASIC_C_TARGETS = $(ARCHS:%=basic.%.out)
|
||||
basic.%.out: basic.c
|
||||
@echo "[+] Building '$@'"
|
||||
$(CC.$*) $(CFLAGS.$*) -o $@ $<
|
||||
|
||||
|
||||
%.aarch64.out : %.aarch64.c
|
||||
@echo "[+] Building '$@'"
|
||||
$(CC.aarch64) $(CFLAGS.aarch64) -o $@ $<
|
||||
|
||||
%.riscv64.out : %.riscv64.c
|
||||
@echo "[+] Building '$@'"
|
||||
$(CC.riscv64) $(CFLAGS.riscv64) -o $@ $?
|
||||
|
||||
|
||||
all: $(BASIC_C_TARGETS) $(AARCH64_TARGETS) $(ARM_TARGETS) $(RISCV64_TARGETS) $(MIPS64_TARGETS) $(MIPS32_TARGETS)
|
||||
|
||||
clean:
|
||||
rm -f *.aarch64.out *.x86_64.out *.arm.out
|
||||
rm -f $(BASIC_C_TARGETS) $(AARCH64_TARGETS) $(ARM_TARGETS) $(RISCV64_TARGETS) $(MIPS64_TARGETS) $(MIPS32_TARGETS)
|
||||
|
||||
@ -0,0 +1,12 @@
|
||||
#include <unistd.h>
|
||||
#include <sys/syscall.h>
|
||||
|
||||
char* str = "hello";
|
||||
|
||||
int main(int argc, char const* argv[])
|
||||
{
|
||||
// Starting from main, this program has about ~1500 instructions until exit in most arches
|
||||
syscall(SYS_write,1,str,5);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -0,0 +1,58 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Literal
|
||||
|
||||
import gdb
|
||||
import user
|
||||
|
||||
import pwndbg.aglib.proc
|
||||
import pwndbg.commands.context
|
||||
|
||||
# The tests in this file execute for a long time - they can take 5-15 minutes to run, depending on the machine
|
||||
# They check for any crashes in the instruction enhancement code that may arise through
|
||||
# when displaying the context.
|
||||
# These are worth running after large changes in the instruction enhancement code and updates to Unicorn/Capstone.
|
||||
|
||||
NUMBER_OF_STEPS = 1500
|
||||
|
||||
|
||||
# Step through a binary, running "ctx" each time the program stops
|
||||
# This is meant to detect crashes originating from the annotations/emulation code
|
||||
def helper(
|
||||
qemu_start_binary, filename: str, qemu_arch: str, endian: Literal["big", "little"] | None = None
|
||||
):
|
||||
FILE = user.binaries.get(filename)
|
||||
|
||||
qemu_start_binary(FILE, qemu_arch, endian)
|
||||
|
||||
gdb.execute("b main")
|
||||
gdb.execute("c")
|
||||
|
||||
pwndbg.commands.context.context_disasm()
|
||||
|
||||
for i in range(NUMBER_OF_STEPS):
|
||||
if not pwndbg.aglib.proc.alive:
|
||||
break
|
||||
gdb.execute("stepi")
|
||||
pwndbg.commands.context.context_disasm()
|
||||
|
||||
|
||||
def test_basic_aarch64(qemu_start_binary):
|
||||
helper(qemu_start_binary, "basic.aarch64.out", "aarch64")
|
||||
|
||||
|
||||
def test_basic_arm(qemu_start_binary):
|
||||
helper(qemu_start_binary, "basic.arm.out", "arm")
|
||||
|
||||
|
||||
def test_basic_riscv64(qemu_start_binary):
|
||||
helper(qemu_start_binary, "basic.riscv64.out", "riscv64")
|
||||
|
||||
|
||||
def test_basic_mips64(qemu_start_binary):
|
||||
# pwnlib.context.endian defaults to "little", but these MIPS binaries are compiled to big endian.
|
||||
helper(qemu_start_binary, "basic.mips64.out", "mips64", endian="big")
|
||||
|
||||
|
||||
def test_basic_mips32(qemu_start_binary):
|
||||
helper(qemu_start_binary, "basic.mips32.out", "mips", endian="big")
|
||||
Loading…
Reference in new issue