From 245ac4ab827493abce190408ff8756d60c1bd2da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joel=20H=C3=B6ner?= Date: Tue, 6 Feb 2024 12:23:46 +0100 Subject: [PATCH] CI: add basic FFI struct validation --- .github/workflows/main.yml | 29 ++++++++++++++++++++++++++++- validate-structs.py | 29 +++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 1 deletion(-) create mode 100644 validate-structs.py diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index c4334c4..fc9bd5a 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -35,7 +35,34 @@ jobs: - name: Install Rust uses: dtolnay/rust-toolchain@stable - name: Build - run: cargo build ${{ matrix.extra_args }} + run: cargo build --all --examples ${{ matrix.extra_args }} + - name: Validate FFI structs + if: runner.os == 'Linux' + run: | + set -eu + + sudo apt-get install -y gdb + + executable=target/debug/examples/pattern + if [[ ! -f "${executable}" ]]; then + echo "Test executable '${executable}' not built: skipping test" + exit 0 + fi + + gdb -q "${executable}" -batch \ + -ex 'source validate-structs.py' \ + 1>script-out.txt 2>gdb-stderr.txt + + if [[ $(cat ./script-out.txt) != *"ALL STRUCTS OK"* ]]; then + echo >&2 "ERROR: struct validation failed!" + echo >&2 "================================" + cat >&2 ./gdb-stderr.txt + echo >&2 "================================" + cat >&2 ./script-out.txt + exit 1 + fi + + echo PASSED. - name: Test run: cargo test ${{ matrix.extra_args }} diff --git a/validate-structs.py b/validate-structs.py new file mode 100644 index 0000000..af7b6a6 --- /dev/null +++ b/validate-structs.py @@ -0,0 +1,29 @@ +# NOTE: this isn't a standalone script -- meant to be run within gdb! + +# gdb's rust mode doesn't parse the `<` and `>` in the types correctly +gdb.execute('set language c++') + + +checked_types = [ + ('zydis::decoder::Decoder', 'ZydisDecoder'), + ('zydis::ffi::decoder::DecodedOperand', 'ZydisDecodedOperand'), + ('zydis::ffi::decoder::AccessedFlags', 'ZydisAccessedFlags'), + ('zydis::ffi::decoder::AccessedFlags', 'ZydisAccessedFlags'), + ('zydis::ffi::decoder::AvxInfo', 'ZydisDecodedInstructionAvx'), + ('zydis::ffi::decoder::MetaInfo', 'ZydisDecodedInstructionMeta'), + ('zydis::ffi::decoder::RawInfo', 'ZydisDecodedInstructionRaw'), +] + + +def sizeof(ty: str) -> int: + return gdb.parse_and_eval(f'sizeof({ty})') + + +for bind_ty, c_ty in checked_types: + bind_ty_sz = sizeof(bind_ty) + c_ty_sz = sizeof(c_ty) + + assert bind_ty_sz == c_ty_sz, \ + f'binding type {bind_ty} is {bind_ty_sz} bytes, but expected {c_ty_sz}' + +print("ALL STRUCTS OK")