about summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--README.md13
-rw-r--r--fix-box64.patch46
-rw-r--r--flake.nix47
-rwxr-xr-xsrc/focaccia/cli.py1
-rw-r--r--src/focaccia/parser.py33
5 files changed, 138 insertions, 2 deletions
diff --git a/README.md b/README.md
index 0eb103b..94e6889 100644
--- a/README.md
+++ b/README.md
@@ -26,6 +26,8 @@ It will take a while to compile.
 
 `focaccia` is the main executable. Invoke `focaccia --help` to see what you can do with it.
 
+### QEMU
+
 A number of additional tools are included to simplify use when validating QEMU:
 `capture-transforms`, `convert-log`, `validate-qemu`. They enable the following workflow.
 
@@ -37,6 +39,17 @@ validate-qemu --symb-trace oracle.trace localhost 12345
 
 Using this workflow, Focaccia can determine whether a mistranslation occured in that particular QEMU run.
 
+### Box64
+
+For validating Box64, we create the oracle and test traces and compare them
+using the main executable.
+
+```bash
+capture-transforms -o oracle.trace bug.out
+BOX64_TRACE_FILE=test.trace box64 bug.out
+focaccia -o oracle.trace --symbolic -t test.trace --test-trace-type box64 --error-level error
+```
+
 ## Tools
 
 The `tools/` directory contains additional utility scripts to work with focaccia.
diff --git a/fix-box64.patch b/fix-box64.patch
new file mode 100644
index 0000000..495dd4a
--- /dev/null
+++ b/fix-box64.patch
@@ -0,0 +1,46 @@
+From 5f7a1982ac307d2bcdc9119c85847f48d193e378 Mon Sep 17 00:00:00 2001
+From: ckrinitsin <101062646+ckrinitsin@users.noreply.github.com>
+Date: Wed, 22 Oct 2025 18:20:31 +0200
+Subject: [PATCH] [TRACE] Fix flags output for better parsing (#3090)
+
+---
+ src/emu/x64emu.c | 8 ++++----
+ 1 file changed, 4 insertions(+), 4 deletions(-)
+
+diff --git a/src/emu/x64emu.c b/src/emu/x64emu.c
+index f4fa700b..dde9a496 100644
+--- a/src/emu/x64emu.c
++++ b/src/emu/x64emu.c
+@@ -442,12 +442,12 @@ const char* DumpCPURegs(x64emu_t* emu, uintptr_t ip, int is32bits)
+             if(i==_RBX) {
+                 if(emu->df) {
+ #define FLAG_CHAR(f) (ACCESS_FLAG(F_##f##F)) ? #f : "?"
+-                    sprintf(tmp, "flags=%s%s%s%s%s%s%s\n", FLAG_CHAR(O), FLAG_CHAR(D), FLAG_CHAR(S), FLAG_CHAR(Z), FLAG_CHAR(A), FLAG_CHAR(P), FLAG_CHAR(C));
++                    sprintf(tmp, " flags=%s%s%s%s%s%s%s\n", FLAG_CHAR(O), FLAG_CHAR(D), FLAG_CHAR(S), FLAG_CHAR(Z), FLAG_CHAR(A), FLAG_CHAR(P), FLAG_CHAR(C));
+                     strcat(buff, tmp);
+ #undef FLAG_CHAR
+                 } else {
+ #define FLAG_CHAR(f) (ACCESS_FLAG(F_##f##F)) ? #f : "-"
+-                    sprintf(tmp, "FLAGS=%s%s%s%s%s%s%s\n", FLAG_CHAR(O), FLAG_CHAR(D), FLAG_CHAR(S), FLAG_CHAR(Z), FLAG_CHAR(A), FLAG_CHAR(P), FLAG_CHAR(C));
++                    sprintf(tmp, " FLAGS=%s%s%s%s%s%s%s\n", FLAG_CHAR(O), FLAG_CHAR(D), FLAG_CHAR(S), FLAG_CHAR(Z), FLAG_CHAR(A), FLAG_CHAR(P), FLAG_CHAR(C));
+                     strcat(buff, tmp);
+ #undef FLAG_CHAR
+                 }
+@@ -473,12 +473,12 @@ const char* DumpCPURegs(x64emu_t* emu, uintptr_t ip, int is32bits)
+                 if(i==4) {
+                     if(emu->df) {
+ #define FLAG_CHAR(f) (ACCESS_FLAG(F_##f##F)) ? #f : "?"
+-                        sprintf(tmp, "flags=%s%s%s%s%s%s%s\n", FLAG_CHAR(O), FLAG_CHAR(D), FLAG_CHAR(S), FLAG_CHAR(Z), FLAG_CHAR(A), FLAG_CHAR(P), FLAG_CHAR(C));
++                        sprintf(tmp, " flags=%s%s%s%s%s%s%s\n", FLAG_CHAR(O), FLAG_CHAR(D), FLAG_CHAR(S), FLAG_CHAR(Z), FLAG_CHAR(A), FLAG_CHAR(P), FLAG_CHAR(C));
+                         strcat(buff, tmp);
+ #undef FLAG_CHAR
+                     } else {
+ #define FLAG_CHAR(f) (ACCESS_FLAG(F_##f##F)) ? #f : "-"
+-                        sprintf(tmp, "FLAGS=%s%s%s%s%s%s%s\n", FLAG_CHAR(O), FLAG_CHAR(D), FLAG_CHAR(S), FLAG_CHAR(Z), FLAG_CHAR(A), FLAG_CHAR(P), FLAG_CHAR(C));
++                        sprintf(tmp, " FLAGS=%s%s%s%s%s%s%s\n", FLAG_CHAR(O), FLAG_CHAR(D), FLAG_CHAR(S), FLAG_CHAR(Z), FLAG_CHAR(A), FLAG_CHAR(P), FLAG_CHAR(C));
+                         strcat(buff, tmp);
+ #undef FLAG_CHAR
+                     }
+--
+2.51.1.dirty
+
diff --git a/flake.nix b/flake.nix
index 425040b..fccabba 100644
--- a/flake.nix
+++ b/flake.nix
@@ -71,6 +71,45 @@
 			members = [ "focaccia" "miasm" ];
 		};
 
+        # Box64
+        zydis-shared-object = pkgs.zydis.overrideAttrs (oldAttrs: {
+			cmakeFlags = (oldAttrs.cmakeFlags or []) ++ [
+			  "-DZYDIS_BUILD_SHARED_LIB=ON"
+			];
+        });
+
+        box64-patched = pkgs.stdenv.mkDerivation {
+			pname = "box64";
+			version = "74d4db";
+
+        	src = pkgs.fetchFromGitHub {
+				owner = "ptitSeb";
+				repo = "box64";
+				rev = "74d4db051b4c74aaab23b19fbb51e441448faf8e";
+				sha256 = "sha256-G6tsqXsnTrs8I47YLnuivC79IFDGfbiLSm4J2Djc0kU=";
+			};
+
+			nativeBuildInputs = with pkgs; [
+				cmake
+				python
+				pkg-config
+				zydis-shared-object
+			];
+
+			cmakeFlags = [
+				"-DDYNAREC=ON"
+				"-DHAVE_TRACE=ON"
+			];
+
+			patches = [ ./fix-box64.patch ];
+			installPhase = ''
+				runHook preInstall
+				mkdir -p $out/bin
+				cp box64 $out/bin/
+				runHook postInstall
+			'';
+        };
+
 		# Another overlay layer for flake-specific overloads
 		# This might be needed because uv does not have sufficient metadata
 		# Here, uv does include metadata about build systems used by each dependency
@@ -312,12 +351,18 @@
 					packages.dev
 					musl-pkgs.gcc
 					musl-pkgs.pkg-config
+                    box64-patched
 				];
 
 				hardeningDisable = [ "pie" ];
 
 				env = uvEnv;
-				shellHook = uvShellHook;
+				shellHook = uvShellHook + ''
+                  export BOX64_TRACE=1
+                  export BOX64_DYNAREC_TRACE=1
+                  export BOX64_DYNAREC_DF=0
+                  export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:${zydis-shared-object}/lib
+                '';
 			};
 		};
 
diff --git a/src/focaccia/cli.py b/src/focaccia/cli.py
index f0c6efe..88c6313 100755
--- a/src/focaccia/cli.py
+++ b/src/focaccia/cli.py
@@ -26,6 +26,7 @@ concrete_trace_parsers = {
     'focaccia': lambda f, _: parser.parse_snapshots(f),
     'qemu':     parser.parse_qemu,
     'arancini': parser.parse_arancini,
+    'box64': parser.parse_box64,
 }
 
 _MatchingAlgorithm = Callable[
diff --git a/src/focaccia/parser.py b/src/focaccia/parser.py
index 950f462..e9e5e0c 100644
--- a/src/focaccia/parser.py
+++ b/src/focaccia/parser.py
@@ -84,7 +84,7 @@ def serialize_snapshots(snapshots: Trace[ProgramState], out_stream: TextIO):
     json.dump(res, out_stream, indent=4)
 
 def _make_unknown_env() -> TraceEnvironment:
-    return TraceEnvironment('', [], [], '?')
+    return TraceEnvironment('', [], False, [], '?')
 
 def parse_qemu(stream: TextIO, arch: Arch) -> Trace[ProgramState]:
     """Parse a QEMU log from a stream.
@@ -170,3 +170,34 @@ def parse_arancini(stream: TextIO, arch: Arch) -> Trace[ProgramState]:
                 states[-1].set_register(regname, int(value, 16))
 
     return Trace(states, _make_unknown_env())
+
+def parse_box64(stream: TextIO, arch: Arch) -> Trace[ProgramState]:
+    def parse_box64_flags(state: ProgramState, flags_dump: str):
+        flags = ['O', 'D', 'S', 'Z', 'A', 'P', 'C']
+        for i, flag in enumerate(flags):
+            if flag == flags_dump[i]: # Flag is set
+                state.set_register(arch.to_regname(flag + 'F'), 1)
+            elif '-' == flags_dump[i]: # Flag is not set
+                state.set_register(arch.to_regname(flag + 'F'), 0)
+
+    trace_string = stream.read()
+
+    blocks = re.split(r'(?=\nES=)', trace_string.strip())[1:]
+    blocks = [block.strip() for block in blocks if block.strip()]
+
+    states = []
+    pattern = r'([A-Z0-9]{2,3}|flags|FLAGS)=([0-9a-fxODSZAPC?\-]+)'
+    for block in blocks:
+        states.append(ProgramState(arch))
+        matches = re.findall(pattern, block)
+
+        for regname, value in matches:
+            if regname.lower() == "flags":
+                parse_box64_flags(states[-1], value)
+                continue
+
+            regname = arch.to_regname(regname)
+            if regname is not None:
+                states[-1].set_register(regname, int(value, 16))
+
+    return Trace(states, _make_unknown_env())