From 6ccba477def79d82364bd9a0069397e82c1dcc60 Mon Sep 17 00:00:00 2001 From: Yang Liu Date: Wed, 13 May 2026 16:44:58 +0800 Subject: [PATCH] [DYNACACHE] Added auto-generated DynaCache versions (#3858) * [DYNACACHE] Added auto-generated DynaCache versions * Guarded dynacache_hashes.h with DYNAREC --- .gitignore | 1 + CMakeLists.txt | 13 ++++- src/tools/env.c | 29 +++------- tools/gen_dynacache_hashes.py | 100 ++++++++++++++++++++++++++++++++++ wine/wow64/CMakeLists.txt | 10 ++++ 5 files changed, 132 insertions(+), 21 deletions(-) create mode 100644 tools/gen_dynacache_hashes.py diff --git a/.gitignore b/.gitignore index 1721b26b5..8ea27ad89 100644 --- a/.gitignore +++ b/.gitignore @@ -58,6 +58,7 @@ build*/ .cache .gdb_history src/git_head.h +src/dynacache_hashes.h backup/ # Wrapper helper diff --git a/CMakeLists.txt b/CMakeLists.txt index 1f4b95029..4dda36b31 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -393,9 +393,19 @@ if(BOX32) ) endif() -# git_head.h is a generated file +set(DYNACACHE_HASH_HEADER "${BOX64_ROOT}/src/dynacache_hashes.h") +add_custom_target(generate_dynacache_hashes + COMMAND "${PYTHON_EXECUTABLE}" "${BOX64_ROOT}/tools/gen_dynacache_hashes.py" + --root "${BOX64_ROOT}" + --output "${DYNACACHE_HASH_HEADER}" + BYPRODUCTS "${DYNACACHE_HASH_HEADER}" + VERBATIM +) + +# generated files set_source_files_properties( "${BOX64_ROOT}/src/git_head.h" + "${BOX64_ROOT}/src/dynacache_hashes.h" PROPERTIES GENERATED TRUE HEADER_FILE_ONLY TRUE) @@ -1299,6 +1309,7 @@ add_library(interpreter OBJECT ${INTERPRETER}) add_library(mainobj OBJECT ${OS_LINUX_SRC} ${ELFLOADER_SRC} ${WRAPPEDS} ${WRAPPEDS32}) add_dependencies(mainobj generate_git_head_target) +add_dependencies(mainobj generate_dynacache_hashes) add_executable(${BOX64}) target_link_libraries(${BOX64} mainobj) diff --git a/src/tools/env.c b/src/tools/env.c index e153aef26..0f88b582e 100644 --- a/src/tools/env.c +++ b/src/tools/env.c @@ -21,6 +21,9 @@ #include "rbtree.h" #include "wine_tools.h" #include "pe_tools.h" +#ifdef DYNAREC +#include "dynacache_hashes.h" +#endif box64env_t box64env = { 0 }; @@ -916,31 +919,17 @@ done: } /* - There is 3 version to change when evoling things, depending on what is changed: - 1. FILE_VERSION for the DynaCache infrastructure - 2. DYNAREC_VERSION for dynablock_t changes and other global dynarec change - 3. ARCH_VERSION for the architecture specific changes (and there is one per arch) + There are 3 compatibility values for DynaCache: + 1. FILE_VERSION for the DynaCache infrastructure and file format + 2. DYNAREC_VERSION as the generated hash of common dynarec sources + 3. ARCH_VERSION as the generated hash of the active backend sources - An ARCH_VERSION of 0 means Unsupported and disable DynaCache. Dynacache will ignore any DynaCache file not exactly matching those 3 version. `box64 --dynacache-clean` can be used from command line to purge obsolete DyaCache files */ -#define FILE_VERSION 2 -#define HEADER_SIGN "DynaCache" -#define SET_VERSION(MAJ, MIN, REV) (((MAJ)<<24)|((MIN)<<16)|(REV)) -#ifdef ARM64 -#define ARCH_VERSION SET_VERSION(0, 0, 14) -#elif defined(RV64) -#define ARCH_VERSION SET_VERSION(0, 0, 5) -#elif defined(LA64) -#define ARCH_VERSION SET_VERSION(0, 0, 8) -#elif defined(PPC64LE) -#define ARCH_VERSION SET_VERSION(0, 0, 1) -#else -#error meh! -#endif -#define DYNAREC_VERSION SET_VERSION(0, 1, 1) +#define FILE_VERSION 2 +#define HEADER_SIGN "DynaCache" typedef struct DynaCacheHeader_s { char sign[10]; //"DynaCache\0" diff --git a/tools/gen_dynacache_hashes.py b/tools/gen_dynacache_hashes.py new file mode 100644 index 000000000..3c96ff6a4 --- /dev/null +++ b/tools/gen_dynacache_hashes.py @@ -0,0 +1,100 @@ +#!/usr/bin/env python3 + +import argparse +import hashlib +import os +from pathlib import Path +from typing import List, Set, Tuple + + +ARCH_DIRS = ("arm64", "la64", "rv64", "ppc64le") +GROUPS = ( + ("COMMON", Path("src/dynarec"), set(ARCH_DIRS)), + ("ARM64", Path("src/dynarec/arm64"), set()), + ("LOONGARCH64", Path("src/dynarec/la64"), set()), + ("RISCV64", Path("src/dynarec/rv64"), set()), + ("PPC64LE", Path("src/dynarec/ppc64le"), set()), +) + + +def iter_files(base: Path, excluded_dirs: Set[str]) -> List[Path]: + files: List[Path] = [] + for path in sorted(base.rglob("*")): + if not path.is_file(): + continue + rel_parts = path.relative_to(base).parts + if excluded_dirs and rel_parts and rel_parts[0] in excluded_dirs: + continue + files.append(path) + return files + + +def hash_files(root: Path, files: List[Path]) -> int: + digest = hashlib.sha256() + for path in files: + rel_path = path.relative_to(root).as_posix().encode("utf-8") + digest.update(rel_path) + digest.update(b"\0") + digest.update(path.read_bytes()) + digest.update(b"\0") + value = int.from_bytes(digest.digest()[:3], "big") + return value or 1 + + +def render_header(root: Path) -> str: + values: List[Tuple[str, int]] = [] + for name, rel_base, excluded_dirs in GROUPS: + base = root / rel_base + files = iter_files(base, excluded_dirs) + if not files: + raise RuntimeError(f"no files found for {name.lower()} hash in {base}") + values.append((name, hash_files(root, files))) + hashes = dict(values) + + lines = [ + "/* Auto-generated by tools/gen_dynacache_hashes.py. Do not edit. */", + "#ifndef __DYNACACHE_HASHES_H__", + "#define __DYNACACHE_HASHES_H__", + "", + f"#define DYNAREC_VERSION 0x{hashes['COMMON']:06X}u", + "", + "#if defined(ARM64)", + f"#define ARCH_VERSION 0x{hashes['ARM64']:06X}u", + "#elif defined(RV64)", + f"#define ARCH_VERSION 0x{hashes['RISCV64']:06X}u", + "#elif defined(LA64)", + f"#define ARCH_VERSION 0x{hashes['LOONGARCH64']:06X}u", + "#elif defined(PPC64LE)", + f"#define ARCH_VERSION 0x{hashes['PPC64LE']:06X}u", + "#else", + "#error Unsupported architecture", + "#endif", + "", + "#endif", + ] + return "\n".join(lines) + "\n" + + +def write_if_changed(path: Path, content: str) -> None: + path.parent.mkdir(parents=True, exist_ok=True) + if path.exists() and path.read_text(encoding="utf-8") == content: + return + tmp_path = path.with_suffix(f"{path.suffix}.{os.getpid()}.tmp") + tmp_path.write_text(content, encoding="utf-8") + tmp_path.replace(path) + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--root", required=True) + parser.add_argument("--output", required=True) + args = parser.parse_args() + + root = Path(args.root).resolve() + output = Path(args.output).resolve() + write_if_changed(output, render_header(root)) + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/wine/wow64/CMakeLists.txt b/wine/wow64/CMakeLists.txt index cb80f6c98..86e048216 100644 --- a/wine/wow64/CMakeLists.txt +++ b/wine/wow64/CMakeLists.txt @@ -119,6 +119,16 @@ include_directories( "${BOX64_ROOT}/external/musl/internal" ) +set(DYNACACHE_HASH_HEADER "${BOX64_ROOT}/src/dynacache_hashes.h") +add_custom_target(generate_dynacache_hashes + COMMAND "${PYTHON_EXECUTABLE}" "${BOX64_ROOT}/tools/gen_dynacache_hashes.py" + --root "${BOX64_ROOT}" + --output "${DYNACACHE_HASH_HEADER}" + BYPRODUCTS "${DYNACACHE_HASH_HEADER}" + VERBATIM +) +add_dependencies(wowbox64 generate_dynacache_hashes) + set(DLLTOOL aarch64-w64-mingw32-dlltool) function(import_dll name)