From 7d3b1dfbfbce7208aab1cc7aab7a2ceb0665bcec Mon Sep 17 00:00:00 2001 From: Yang Liu Date: Fri, 15 May 2026 17:48:51 +0800 Subject: [PATCH] [DYNACACHE] Created dynacache.c (#3865) * [DYNACACHE] Created dynacache.c This is pure code moving * Update * Fixed a typo --- CMakeLists.txt | 1 + docs/USAGE_CN.md | 8 +- docs/gen/usage_cn.json | 8 + src/core.c | 1 + src/include/custommem.h | 5 +- src/include/dynacache.h | 34 ++ src/include/dynacache_compress.h | 4 +- src/include/env.h | 20 - src/tools/dynacache.c | 808 ++++++++++++++++++++++++++++++ src/tools/env.c | 809 +------------------------------ src/tools/env_private.h | 18 + wine/wow64/CMakeLists.txt | 1 + 12 files changed, 884 insertions(+), 833 deletions(-) create mode 100644 src/include/dynacache.h create mode 100644 src/tools/dynacache.c create mode 100644 src/tools/env_private.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 3c1339976..146786f87 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -471,6 +471,7 @@ set(ELFLOADER_SRC "${BOX64_ROOT}/src/tools/pathcoll.c" "${BOX64_ROOT}/src/tools/rbtree.c" "${BOX64_ROOT}/src/tools/env.c" + "${BOX64_ROOT}/src/tools/dynacache.c" "${BOX64_ROOT}/src/tools/wine_tools.c" "${BOX64_ROOT}/src/tools/pe_tools.c" "${BOX64_ROOT}/src/wrapped/generated/wrapper.c" diff --git a/docs/USAGE_CN.md b/docs/USAGE_CN.md index 4a20b3a17..5b038c5d1 100644 --- a/docs/USAGE_CN.md +++ b/docs/USAGE_CN.md @@ -252,11 +252,11 @@ DynaCache 文件夹的最大大小(MiB)。超过限制时,会先删除无 ### BOX64_DYNACACHE_COMPRESS -Compress or not DynaCache files on disk +设置是否压缩磁盘上的 DynaCache 文件。 - * 0: Do not compress dynacache files - * 1: Compress dynacache files using Zlib with fastest compression. [默认值] - * 2: Compress dynacache files using ZLib with default compression. + * 0: 不压缩 DynaCache 文件。 + * 1: 使用 Zlib 最快压缩级别压缩 DynaCache 文件。 [默认值] + * 2: 使用 Zlib 默认压缩级别压缩 DynaCache 文件。 ### BOX64_DYNACACHE_MIN diff --git a/docs/gen/usage_cn.json b/docs/gen/usage_cn.json index ed6f88ab7..34b431a19 100644 --- a/docs/gen/usage_cn.json +++ b/docs/gen/usage_cn.json @@ -375,6 +375,14 @@ "2048": "默认值为 2048 MiB。" } }, + "BOX64_DYNACACHE_COMPRESS": { + "description": "设置是否压缩磁盘上的 DynaCache 文件。", + "options": { + "0": "不压缩 DynaCache 文件。", + "1": "使用 Zlib 最快压缩级别压缩 DynaCache 文件。", + "2": "使用 Zlib 默认压缩级别压缩 DynaCache 文件。" + } + }, "BOX64_DYNACACHE_MIN": { "description": "DynaCache 写入磁盘的最小大小(KB)。默认大小为 350KB。", "options": { diff --git a/src/core.c b/src/core.c index 2b5ed88b5..d4aca7a2f 100644 --- a/src/core.c +++ b/src/core.c @@ -46,6 +46,7 @@ #include "library.h" #include "core.h" #include "env.h" +#include "dynacache.h" #include "cleanup.h" #include "freq.h" #include "hostext.h" diff --git a/src/include/custommem.h b/src/include/custommem.h index 0459dc586..c43c8a581 100644 --- a/src/include/custommem.h +++ b/src/include/custommem.h @@ -30,17 +30,18 @@ size_t customGetUsableSize(void* p); #endif #ifdef DYNAREC +#include "dynacache.h" typedef struct dynablock_s dynablock_t; typedef struct mmaplist_s mmaplist_t; -typedef struct DynaCacheBlock_s DynaCacheBlock_t; -typedef struct CompressedDynaCacheBlock_s CompressedDynaCacheBlock_t; // custom protection flag to mark Page that are Write protected for Dynarec purpose uintptr_t AllocDynarecMap(uintptr_t x64_addr, size_t size, int is_new); void FreeDynarecMap(uintptr_t addr); mmaplist_t* NewMmaplist(); void DelMmaplist(mmaplist_t* list); int MmaplistHasNew(mmaplist_t* list, int clear); +int MmaplistIsDirty(mmaplist_t* list); int MmaplistNBlocks(mmaplist_t* list); +size_t MmaplistTotalAlloc(mmaplist_t* list); void MmaplistFillBlocks(mmaplist_t* list, CompressedDynaCacheBlock_t* blocks); void MmaplistAddNBlocks(mmaplist_t* list, int nblocks); int MmaplistAddBlock(mmaplist_t* list, int fd, off_t offset, void* orig, size_t size, intptr_t delta_map, uintptr_t mapping_start); diff --git a/src/include/dynacache.h b/src/include/dynacache.h new file mode 100644 index 000000000..96beb89e1 --- /dev/null +++ b/src/include/dynacache.h @@ -0,0 +1,34 @@ +#ifndef __DYNACACHE_H__ +#define __DYNACACHE_H__ + +#include +#include + +typedef struct mapping_s mapping_t; + +#ifdef DYNAREC +typedef struct blocklist_s blocklist_t; + +typedef struct DynaCacheBlock_s { + blocklist_t* block; + size_t size; + size_t free_size; +} DynaCacheBlock_t; + +#define COMP_NONE 0 +#define COMP_Z 1 +#define COMP_Z_MAX 2 +typedef struct CompressedDynaCacheBlock_s { + DynaCacheBlock_t block; + size_t compsize; + uint8_t type; +} CompressedDynaCacheBlock_t; + +void SerializeMmaplist(mapping_t* mapping); +void MmapDynaCache(mapping_t* mapping); +#endif + +void DynaCacheList(const char* filter); +void DynaCacheClean(); + +#endif // __DYNACACHE_H__ diff --git a/src/include/dynacache_compress.h b/src/include/dynacache_compress.h index 7974082d0..0c9c2e3d2 100644 --- a/src/include/dynacache_compress.h +++ b/src/include/dynacache_compress.h @@ -3,7 +3,9 @@ #include +#include "dynacache.h" + void* dc_compress(void* src, size_t src_size, int type, size_t* dest_size); int dc_uncompress(void* src, size_t src_size, int type, void* dest, size_t dest_size); // return 0 is OK -#endif //__DYNACACHE_COMPRESS__H__ \ No newline at end of file +#endif //__DYNACACHE_COMPRESS__H__ diff --git a/src/include/env.h b/src/include/env.h index 666516d6f..ab9614d90 100644 --- a/src/include/env.h +++ b/src/include/env.h @@ -225,24 +225,6 @@ typedef struct box64env_s { } box64env_t; typedef struct mmaplist_s mmaplist_t; -#ifdef DYNAREC -typedef struct blocklist_s blocklist_t; - -typedef struct DynaCacheBlock_s { - blocklist_t* block; - size_t size; - size_t free_size; -} DynaCacheBlock_t; - -#define COMP_NONE 0 -#define COMP_Z 1 -#define COMP_Z_MAX 2 -typedef struct CompressedDynaCacheBlock_s { - DynaCacheBlock_t block; - size_t compsize; - uint8_t type; -} CompressedDynaCacheBlock_t; -#endif void InitializeEnvFiles(); int ApplyEnvFileEntry(const char* name); @@ -260,8 +242,6 @@ size_t SizeFileMapped(uintptr_t addr); mmaplist_t* GetMmaplistByAddr(uintptr_t addr); int IsAddrNeedReloc(uintptr_t addr); void SerializeAllMapping(); -void DynaCacheList(const char* name); -void DynaCacheClean(); int IsAddrMappingLoadAndClean(uintptr_t addr); #endif // __ENV_H diff --git a/src/tools/dynacache.c b/src/tools/dynacache.c new file mode 100644 index 000000000..7fa648f75 --- /dev/null +++ b/src/tools/dynacache.c @@ -0,0 +1,808 @@ +#include +#include +#include +#include +#include +#include +#if defined(DYNAREC) && !defined(WIN32) +#include +#include +#include +#include +#include +#include +#endif + +#include "os.h" +#include "env.h" +#include "custommem.h" +#include "khash.h" +#include "debug.h" +#include "fileutils.h" +#include "dynacache.h" +#include "dynacache_compress.h" +#include "env_private.h" +#ifdef DYNAREC +#include "dynacache_hashes.h" +#endif + +#ifdef _WIN32 +#define PATHSEP "\\" +#define HOME "USERPROFILE" +#else +#define PATHSEP "/" +#define HOME "HOME" +#endif + +#ifdef DYNAREC +int nUnalignedRange(uintptr_t start, size_t size); +void getUnalignedRange(uintptr_t start, size_t size, uintptr_t addrs[]); +void add_unaligned_address(uintptr_t addr); + +const char* GetDynacacheFolder(mapping_t* mapping) +{ + static char folder[4096] = { 0 }; + if(mapping && mapping->env && mapping->env->is_dynacache_folder_overridden && mapping->env->dynacache_folder) { + if (FileExist(mapping->env->dynacache_folder, 0) || MakeDir(mapping->env->dynacache_folder)) { + strcpy(folder, mapping->env->dynacache_folder); + goto done; + } + } else if (box64env.dynacache_folder) { + if (FileExist(box64env.dynacache_folder, 0) || MakeDir(box64env.dynacache_folder)) { + strcpy(folder, box64env.dynacache_folder); + goto done; + } + } + + if(GetEnv("XDG_CACHE_HOME")) + strcpy(folder, GetEnv("XDG_CACHE_HOME")); + else if(GetEnv(HOME)) { + strcpy(folder, GetEnv(HOME)); + strcat(folder, PATHSEP); + strcat(folder, ".cache"); + if (!FileExist(folder, 0) && !MakeDir(folder)) + return NULL; + + } else + return NULL; + strcat(folder, PATHSEP); + strcat(folder, "box64"); + if (!FileExist(folder, 0) && !MakeDir(folder)) + return NULL; + +done: + if (folder[strlen(folder) - 1] != PATHSEP[0]) { + strcat(folder, PATHSEP); + } + return folder; +} + +/* + 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 + + 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 3 +#define HEADER_SIGN "DynaCache" + +typedef struct DynaCacheHeader_s { + char sign[10]; //"DynaCache\0" + uint64_t file_version:16; + uint64_t dynarec_version:24; + uint64_t arch_version:24; + uint64_t cpuext; + uint64_t dynarec_settings; + size_t pagesize; + size_t codesize; + uintptr_t map_addr; + size_t map_len; + size_t file_length; + uint32_t filename_length; + uint32_t nblocks; + uint32_t nLockAddresses; + uint32_t nUnalignedAddresses; + char filename[]; +} DynaCacheHeader_t; + +#define DYNAREC_SETTINGS() \ + DS_GO(BOX64_DYNAREC_ALIGNED_ATOMICS, dynarec_aligned_atomics, 1) \ + DS_GO(BOX64_DYNAREC_BIGBLOCK, dynarec_bigblock, 2) \ + DS_GO(BOX64_DYNAREC_CALLRET, dynarec_callret, 2) \ + DS_GO(BOX64_DYNAREC_DF, dynarec_df, 1) \ + DS_GO(BOX64_DYNAREC_DIRTY, dynarec_dirty, 2) \ + DS_GO(BOX64_DYNAREC_DIV0, dynarec_div0, 1) \ + DS_GO(BOX64_DYNAREC_FASTNAN, dynarec_fastnan, 1) \ + DS_GO(BOX64_DYNAREC_FASTROUND, dynarec_fastround, 2) \ + DS_GO(BOX64_DYNAREC_FORWARD, dynarec_forward, 10) \ + DS_GO(BOX64_DYNAREC_NATIVEFLAGS, dynarec_nativeflags, 1) \ + DS_GO(BOX64_DYNAREC_SAFEFLAGS, dynarec_safeflags, 2) \ + DS_GO(BOX64_DYNAREC_STRONGMEM, dynarec_strongmem, 2) \ + DS_GO(BOX64_DYNAREC_VOLATILE_METADATA, dynarec_volatile_metadata, 1)\ + DS_GO(BOX64_DYNAREC_WEAKBARRIER, dynarec_weakbarrier, 2) \ + DS_GO(BOX64_DYNAREC_X87DOUBLE, dynarec_x87double, 2) \ + DS_GO(BOX64_DYNAREC_NOARCH, dynarec_noarch, 2) \ + DS_GO(BOX64_AES, aes, 1) \ + DS_GO(BOX64_PCLMULQDQ, pclmulqdq, 1) \ + DS_GO(BOX64_SHAEXT, shaext, 1) \ + DS_GO(BOX64_SSE42, sse42, 1) \ + DS_GO(BOX64_AVX, avx, 2) \ + DS_GO(BOX64_X87_NO80BITS, x87_no80bits, 1) \ + DS_GO(BOX64_RDTSC_1GHZ, rdtsc_1ghz, 1) \ + DS_GO(BOX64_SSE_FLUSHTO0, sse_flushto0, 1) \ + +#define DS_GO(A, B, C) uint64_t B:C; +typedef union dynarec_settings_s { + struct { + DYNAREC_SETTINGS() + }; + uint64_t x; +} dynarec_settings_t; +#undef DS_GO +uint64_t GetDynSetting(mapping_t* mapping) +{ + dynarec_settings_t settings = {0}; + #define DS_GO(A, B, C) settings.B = (mapping->env && mapping->env->is_##B##_overridden)?mapping->env->B:box64env.B; + DYNAREC_SETTINGS() + #undef DS_GO + return settings.x; +} +void PrintDynfSettings(int level, uint64_t s) +{ + dynarec_settings_t settings = {0}; + settings.x = s; + #define DS_GO(A, B, C) if(settings.B) printf_log_prefix(0, level, "\t\t" #A "=%d\n", settings.B); + DYNAREC_SETTINGS() + #undef DS_GO +} +#undef DYNAREC_SETTINGS + +char* MmaplistName(const char* filename, uint64_t dynarec_settings, const char* fullname) +{ + // names are FOLDER/filename-YYYYY-XXXXX.box64 + // Where XXXXX is the hash of the full name + // and YYYY is the Dynarec optim (in hex) + static char mapname[4096]; + snprintf(mapname, 4095-6, "%s-%llx-%u", filename, dynarec_settings, __ac_X31_hash_string(fullname)); + strcat(mapname, ".box64"); + return mapname; +} + +char* GetMmaplistName(mapping_t* mapping) +{ + return MmaplistName(mapping->filename+1, GetDynSetting(mapping), mapping->fullname); +} + +const char* NicePrintSize(size_t sz) +{ + static char buf[256]; + const char* units[] = {"", "kb", "Mb", "Gb"}; + int idx = 0; + size_t ratio = 0; + while(idx>ratio)<50) { + snprintf(buf, 255, "%zd.%zd%s", sz>>ratio, (sz>>(ratio-1))%10, units[idx]); + } else { + snprintf(buf, 255, "%zd%s", sz>>ratio, units[idx]); + } + return buf; +} + +#ifndef WIN32 +typedef struct DynaCacheFileEntry_s { + char* filename; + uint64_t size; + time_t mtime; + int valid; +} DynaCacheFileEntry_t; + +int ReadDynaCache(const char* folder, const char* name, mapping_t* mapping, int verbose); + +#ifndef O_CLOEXEC +#define O_CLOEXEC 0 +#endif + +#define DYNACACHE_TMP_STALE_SECONDS 60 + +static void SyncDynaCacheFolder(const char* folder) +{ +#ifdef O_DIRECTORY + int fd = open(folder, O_RDONLY|O_DIRECTORY|O_CLOEXEC); +#else + int fd = open(folder, O_RDONLY|O_CLOEXEC); +#endif + if(fd>=0) { + fsync(fd); + close(fd); + } +} + +static int DynaCacheOpenTempFile(const char* mapname, char* tmpname, size_t tmpname_size) +{ + for(int i=0; i<100; ++i) { + snprintf(tmpname, tmpname_size, "%s.tmp.%ld.%d", mapname, (long)getpid(), i); + int fd = open(tmpname, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, 0644); + if(fd>=0) + return fd; + if(errno!=EEXIST) + return -1; + } + errno = EEXIST; + return -1; +} + +static int RemoveStaleDynaCacheTempFile(const char* folder, const char* name) +{ + if(!strstr(name, ".box64.tmp.")) + return 0; + + char filename[strlen(folder)+strlen(name)+1]; + strcpy(filename, folder); + strcat(filename, name); + + struct stat st = {0}; + if(lstat(filename, &st) || !S_ISREG(st.st_mode) || st.st_size<0) + return 0; + + time_t now = time(NULL); + if(now==(time_t)-1 || st.st_mtime > now-DYNACACHE_TMP_STALE_SECONDS) + return 0; + + size_t size = st.st_size; + if(unlink(filename)) + return 0; + + dynarec_log(LOG_INFO, "DynaCache removed stale temporary cache file %s (%s)\n", filename, NicePrintSize(size)); + return 1; +} + +static int DynaCacheFileEntryCmp(const void* a, const void* b) +{ + const DynaCacheFileEntry_t* fa = a; + const DynaCacheFileEntry_t* fb = b; + if(fa->mtime < fb->mtime) + return -1; + if(fa->mtime > fb->mtime) + return 1; + return strcmp(fa->filename, fb->filename); +} + +static void PruneDynaCacheFolder(const char* folder, uint64_t max_size) +{ + if(!max_size) + return; + DIR* dir = opendir(folder); + if(!dir) return; + + DynaCacheFileEntry_t* files = NULL; + size_t count = 0; + size_t capacity = 0; + uint64_t total = 0; + + // collect all dynacache files in the folder + struct dirent* d = NULL; + while((d = readdir(dir))) { + size_t l = strlen(d->d_name); + if(RemoveStaleDynaCacheTempFile(folder, d->d_name)) + continue; + if(l<=6 || strcmp(d->d_name+l-6, ".box64")) continue; + + char* filename = box_malloc(strlen(folder)+l+1); + strcpy(filename, folder); + strcat(filename, d->d_name); + + struct stat st = {0}; + if(lstat(filename, &st) || !S_ISREG(st.st_mode) || st.st_size<0) { + box_free(filename); + continue; + } + + if(count == capacity) { + size_t new_capacity = capacity ? capacity*2 : 16; + DynaCacheFileEntry_t* new_files = box_realloc(files, new_capacity*sizeof(*files)); + files = new_files; + capacity = new_capacity; + } + + files[count].filename = filename; + files[count].size = st.st_size; + files[count].mtime = st.st_mtime; + files[count].valid = -1; + total += files[count].size; + ++count; + } + closedir(dir); + + // start pruning, invalid files first, then older ones until we are under 80% of set max size + if(total > max_size && count) { + uint64_t target_size = (max_size/5) * 4; + size_t folder_len = strlen(folder); + for(size_t i=0; itarget_size; ++i) { + files[i].valid = (ReadDynaCache(folder, files[i].filename+folder_len, NULL, 0)==0); + if(files[i].valid) continue; + uint64_t size = files[i].size; + if(!unlink(files[i].filename)) { + dynarec_log(LOG_INFO, "DynaCache removed invalid cache file %s (%s)\n", files[i].filename, NicePrintSize(size)); + files[i].size = 0; + total = (size>total)?0:total-size; + } + } + qsort(files, count, sizeof(*files), DynaCacheFileEntryCmp); + for(size_t i=0; itarget_size; ++i) { + if(files[i].valid!=1 || !files[i].size) + continue; + uint64_t size = files[i].size; + if(!unlink(files[i].filename)) { + dynarec_log(LOG_INFO, "DynaCache removed old cache file %s (%s)\n", files[i].filename, NicePrintSize(size)); + total = (size>total)?0:total-size; + } + } + } + + for(size_t i=0; ienv && mapping->env->is_dynacache_overridden && (mapping->env->dynacache!=1)) + return; + if((!mapping->env || !mapping->env->is_dynacache_overridden) && box64env.dynacache!=1) + return; + // don't do serialize for program that needs purge=1 + if(mapping->env && mapping->env->is_dynarec_purge_overridden && mapping->env->dynarec_purge) + return; + if((!mapping->env || !mapping->env->is_dynarec_purge_overridden) && box64env.dynarec_purge) + return; + // don't do serialize for program that needs dirty=1 + if(mapping->env && mapping->env->is_dynarec_dirty_overridden && mapping->env->dynarec_dirty) + return; + if((!mapping->env || !mapping->env->is_dynarec_dirty_overridden) && box64env.dynarec_dirty) + return; + const char* folder = GetDynacacheFolder(mapping); + if(!folder) return; // no folder, no serialize... + const char* name = GetMmaplistName(mapping); + if(!name) return; + char mapname[strlen(folder)+strlen(name)+1]; + strcpy(mapname, folder); + strcat(mapname, name); + size_t filesize = FileSize(mapping->fullname); + if(!filesize) { + dynarec_log(LOG_INFO, "DynaCache will not serialize cache for %s because filesize is 0\n", mapping->fullname); + return; // mmaped file as a 0 size... + } + // prepare header + int nblocks = MmaplistNBlocks(mapping->mmaplist); + if(!nblocks) { + dynarec_log(LOG_INFO, "DynaCache will not serialize cache for %s because nblocks is 0\n", mapping->fullname); + return; //How??? + } + size_t map_len = SizeFileMapped(mapping->start); + size_t nLockAddresses = nLockAddressRange(mapping->start, map_len); + size_t nUnaligned = nUnalignedRange(mapping->start, map_len); + size_t total = sizeof(DynaCacheHeader_t) + strlen(mapping->fullname) + 1 + nblocks*sizeof(CompressedDynaCacheBlock_t) + nLockAddresses*sizeof(uintptr_t) + nUnaligned*sizeof(uintptr_t);; + total = ALIGN(total); // align on pagesize + uint8_t all_header[total]; + memset(all_header, 0, total); + void* p = all_header; + DynaCacheHeader_t* header = p; + strcpy(header->sign, HEADER_SIGN); + header->file_version = FILE_VERSION; + header->dynarec_version = DYNAREC_VERSION; + header->arch_version = ARCH_VERSION; + header->dynarec_settings = GetDynSetting(mapping); + header->cpuext = cpuext.x; + header->pagesize = box64_pagesize; + header->codesize = MmaplistTotalAlloc(mapping->mmaplist); + header->map_addr = mapping->start; + header->file_length = filesize; + header->filename_length = strlen(mapping->fullname); + header->nblocks = MmaplistNBlocks(mapping->mmaplist); + header->map_len = map_len; + header->nLockAddresses = nLockAddresses; + header->nUnalignedAddresses = nUnaligned; + size_t dynacache_min = box64env.dynacache_min; + if(mapping->env && mapping->env->is_dynacache_min_overridden) + dynacache_min = mapping->env->dynacache_min; + if(dynacache_min*1024>header->codesize) { + dynarec_log(LOG_INFO, "DynaCache will not serialize cache for %s because there is not enough usefull code (%s)\n", mapping->fullname, NicePrintSize(header->codesize)); + return; // not enugh code, do no write + } + p += sizeof(DynaCacheHeader_t); // fullname + strcpy(p, mapping->fullname); + p += strlen(p) + 1; // blocks + CompressedDynaCacheBlock_t* blocks = p; + MmaplistFillBlocks(mapping->mmaplist, blocks); + p += nblocks*sizeof(CompressedDynaCacheBlock_t); + uintptr_t* lockAddresses = p; + p += nLockAddresses*sizeof(uintptr_t); + uintptr_t* unalignedAddresses = p; + if(nLockAddresses) + getLockAddressRange(mapping->start, map_len, lockAddresses); + if(nUnaligned) + getUnalignedRange(mapping->start, map_len, unalignedAddresses); + // all done, now just create the file and write all this down... + #ifndef WIN32 + char tmpname[strlen(mapname)+64]; + int tmpfd = DynaCacheOpenTempFile(mapname, tmpname, sizeof(tmpname)); + if(tmpfd<0) { + dynarec_log(LOG_INFO, "Cannot create cache file %s\n", mapname); + return; + } + FILE* f = fdopen(tmpfd, "wb"); + if(!f) { + close(tmpfd); + unlink(tmpname); + dynarec_log(LOG_INFO, "Cannot create cache file %s\n", mapname); + return; + } + int write_error = 0; + if(fwrite(all_header, total, 1, f)!=1) + write_error = 1; + int rewrite_header = 0; + int type = BOX64ENV(dynacache_compress); + if(type!=COMP_NONE) { + for(int i=0; ienv && mapping->env->is_dynacache_limit_overridden) + dynacache_limit = mapping->env->dynacache_limit; + + if(rename(tmpname, mapname)) { + dynarec_log(LOG_INFO, "Cannot publish cache file %s\n", mapname); + unlink(tmpname); + return; + } + SyncDynaCacheFolder(folder); + if(dynacache_limit>0) { + PruneDynaCacheFolder(folder, (uint64_t)dynacache_limit*1024*1024); + SyncDynaCacheFolder(folder); + } + #else + // TODO? + #endif +} + +#define DCERR_OK 0 +#define DCERR_NEXIST 1 +#define DCERR_TOOSMALL 2 +#define DCERR_FERROR 3 +#define DCERR_BADHEADER 4 +#define DCERR_FILEVER 5 +#define DCERR_DYNVER 6 +#define DCERR_DYNARCHVER 7 +#define DCERR_PAGESIZE 8 +#define DCERR_MAPNEXIST 9 +#define DCERR_MAPCHG 10 +#define DCERR_RELOC 11 +#define DCERR_BADNAME 12 +#define DCERR_CPUEXT 13 + +#ifndef WIN32 +int ReadDynaCache(const char* folder, const char* name, mapping_t* mapping, int verbose) +{ + char filename[strlen(folder)+strlen(name)+1]; + strcpy(filename, folder); + strcat(filename, name); + if(verbose) printf_log(LOG_NONE, "File %s:\t", name); + FILE *f = fopen(filename, "rb"); + if(!f) { + int exists = FileExist(filename, IS_FILE); + if(verbose) printf_log_prefix(0, LOG_NONE, "%s\n", exists?"Cannot open file":"Invalid file"); + return exists?DCERR_FERROR:DCERR_NEXIST; + } + struct stat st = {0}; + if(fstat(fileno(f), &st) || !S_ISREG(st.st_mode) || st.st_size<0) { + if(verbose) printf_log_prefix(0, LOG_NONE, "Cannot stat file\n"); + fclose(f); + return DCERR_FERROR; + } + size_t filesize = st.st_size; + if(filesizestart - header.map_addr; + dynarec_log(LOG_INFO, "Trying to load DynaCache for %s, with a delta_map=%zx\n", mapping->fullname, delta_map); + if(!mapping->mmaplist) + mapping->mmaplist = NewMmaplist(); + MmaplistAddNBlocks(mapping->mmaplist, header.nblocks); + for(int i=0; immaplist, fd, p, blocks[i].block.block, blocks[i].block.size, delta_map, mapping->start); + else { + void* src = box_malloc(blocks[i].compsize); + fseek(f, p, SEEK_SET); + if(!src || (fread(src, blocks[i].compsize, 1, f)!=1)) { + ret = -10; + } else + ret = MmaplistAddCompressedBlock(mapping->mmaplist, blocks[i].type, src, blocks[i].compsize, blocks[i].block.block, blocks[i].block.size, delta_map, mapping->start); + box_free(src); + } + if(ret) { + printf_log(LOG_NONE, "Error while doing relocation on a DynaCache (block %d)\n", i); + fclose(f); + return DCERR_RELOC; + } + p+=(blocks[i].type==COMP_NONE)?blocks[i].block.size:ALIGN(blocks[i].compsize); + } + for(size_t i=0; ifullname, header.nblocks); + } + fclose(f); + return DCERR_OK; +} +#endif + +void DynaCacheList(const char* filter) +{ + #ifndef WIN32 + const char* folder = GetDynacacheFolder(NULL); + if(!folder) { + printf_log(LOG_NONE, "DynaCache folder not found\n"); + return; + } + DIR* dir = opendir(folder); + if(!dir) { + printf_log(LOG_NONE, "Cannot open DynaCache folder\n"); + return; + } + struct dirent* d = NULL; + int need_filter = (filter && strlen(filter)); + while((d = readdir(dir))) { + size_t l = strlen(d->d_name); + if(l>6 && !strcmp(d->d_name+l-6, ".box64")) { + if(need_filter && !strstr(d->d_name, filter)) + continue; + ReadDynaCache(folder, d->d_name, NULL, 1); + printf_log_prefix(0, LOG_NONE, "\n"); + } + } + closedir(dir); + #endif +} +void DynaCacheClean() +{ + #ifndef WIN32 + const char* folder = GetDynacacheFolder(NULL); + if(!folder) { + printf_log(LOG_NONE, "DynaCache folder not found\n"); + return; + } + DIR* dir = opendir(folder); + if(!dir) { + printf_log(LOG_NONE, "Cannot open DynaCache folder\n"); + return; + } + struct dirent* d = NULL; + while((d = readdir(dir))) { + size_t l = strlen(d->d_name); + if(RemoveStaleDynaCacheTempFile(folder, d->d_name)) + continue; + if(l>6 && !strcmp(d->d_name+l-6, ".box64")) { + int ret = ReadDynaCache(folder, d->d_name, NULL, 0); + if(ret) { + char filename[strlen(folder)+strlen(d->d_name)+1]; + strcpy(filename, folder); + strcat(filename, d->d_name); + size_t filesize = FileSize(filename); + if(!unlink(filename)) { + printf_log(LOG_NONE, "Removed %s for %s\n", d->d_name, NicePrintSize(filesize)); + } else { + printf_log(LOG_NONE, "Could not remove %s\n", d->d_name); + } + } + } + } + closedir(dir); + SyncDynaCacheFolder(folder); + #endif +} +#ifndef WIN32 +void MmapDynaCache(mapping_t* mapping) +{ + if(!DYNAREC_VERSION) + return; + // no need to test dynacache enabled or not, it has already been done before this call + const char* folder = GetDynacacheFolder(mapping); + if(!folder) return; + const char* name = GetMmaplistName(mapping); + if(!name) return; + dynarec_log(LOG_DEBUG, "Looking for DynaCache %s in %s\n", name, folder); + ReadDynaCache(folder, name, mapping, 0); +} +#endif +#else +void SerializeMmaplist(mapping_t* mapping) {} +void DynaCacheList(const char* filter) { printf_log(LOG_NONE, "Dynarec not enable\n"); } +void DynaCacheClean() {} +#endif \ No newline at end of file diff --git a/src/tools/env.c b/src/tools/env.c index 0ce4e8b7a..500368b23 100644 --- a/src/tools/env.c +++ b/src/tools/env.c @@ -6,14 +6,9 @@ #include #include #include -#if defined(DYNAREC) && !defined(WIN32) #include -#include -#include #include #include -#include -#endif #include "os.h" #include "env.h" @@ -25,10 +20,8 @@ #include "rbtree.h" #include "wine_tools.h" #include "pe_tools.h" -#ifdef DYNAREC -#include "dynacache_hashes.h" -#include "dynacache_compress.h" -#endif +#include "dynacache.h" +#include "env_private.h" box64env_t box64env = { 0 }; @@ -36,23 +29,10 @@ KHASH_MAP_INIT_STR(box64env_entry, box64env_t) static kh_box64env_entry_t* box64env_entries = NULL; static kh_box64env_entry_t* box64env_entries_wildcard = NULL; -mmaplist_t* NewMmaplist(); -void DelMmaplist(mmaplist_t* list); #ifdef DYNAREC +void DelMmaplist(mmaplist_t* list); int MmaplistHasNew(mmaplist_t* list, int clear); int MmaplistIsDirty(mmaplist_t* list); -int MmaplistNBlocks(mmaplist_t* list); -size_t MmaplistTotalAlloc(mmaplist_t* list); -void MmaplistFillBlocks(mmaplist_t* list, CompressedDynaCacheBlock_t* blocks); -void MmaplistAddNBlocks(mmaplist_t* list, int nblocks); -int MmaplistAddBlock(mmaplist_t* list, int fd, off_t offset, void* orig, size_t size, intptr_t delta_map, uintptr_t mapping_start); -int MmaplistAddCompressedBlock(mmaplist_t* list, int type, void* src, size_t src_size, void* orig, size_t size, intptr_t delta_map, uintptr_t mapping_start); -int nLockAddressRange(uintptr_t start, size_t size); -void getLockAddressRange(uintptr_t start, size_t size, uintptr_t addrs[]); -void addLockAddress(uintptr_t addr); -int nUnalignedRange(uintptr_t start, size_t size); -void getUnalignedRange(uintptr_t start, size_t size, uintptr_t addrs[]); -void add_unaligned_address(uintptr_t addr); #endif static rbtree_t* envmap = NULL; @@ -782,22 +762,9 @@ void PrintEnvVariables(box64env_t* env, int level) } -typedef struct mapping_s { - char* filename; - char* fullname; - box64env_t* env; - uintptr_t start; //lower address of the map for this file - mmaplist_t* mmaplist; -} mapping_t; - KHASH_MAP_INIT_STR(mapping_entry, mapping_t*); static kh_mapping_entry_t* mapping_entries = NULL; -#if defined(DYNAREC) && !defined(WIN32) -void MmapDynaCache(mapping_t* mapping); -#endif - - void RecordEnvMappings(uintptr_t addr, size_t length, int fd) { #ifndef _WIN32 @@ -885,776 +852,6 @@ void RecordEnvMappings(uintptr_t addr, size_t length, int fd) #endif } -#ifdef DYNAREC -const char* GetDynacacheFolder(mapping_t* mapping) -{ - static char folder[4096] = { 0 }; - if(mapping && mapping->env && mapping->env->is_dynacache_folder_overridden && mapping->env->dynacache_folder) { - if (FileExist(mapping->env->dynacache_folder, 0) || MakeDir(mapping->env->dynacache_folder)) { - strcpy(folder, mapping->env->dynacache_folder); - goto done; - } - } else if (box64env.dynacache_folder) { - if (FileExist(box64env.dynacache_folder, 0) || MakeDir(box64env.dynacache_folder)) { - strcpy(folder, box64env.dynacache_folder); - goto done; - } - } - - if(GetEnv("XDG_CACHE_HOME")) - strcpy(folder, GetEnv("XDG_CACHE_HOME")); - else if(GetEnv(HOME)) { - strcpy(folder, GetEnv(HOME)); - strcat(folder, PATHSEP); - strcat(folder, ".cache"); - if (!FileExist(folder, 0) && !MakeDir(folder)) - return NULL; - - } else - return NULL; - strcat(folder, PATHSEP); - strcat(folder, "box64"); - if (!FileExist(folder, 0) && !MakeDir(folder)) - return NULL; - -done: - if (folder[strlen(folder) - 1] != PATHSEP[0]) { - strcat(folder, PATHSEP); - } - return folder; -} - -/* - 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 - - 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 3 -#define HEADER_SIGN "DynaCache" - -typedef struct DynaCacheHeader_s { - char sign[10]; //"DynaCache\0" - uint64_t file_version:16; - uint64_t dynarec_version:24; - uint64_t arch_version:24; - uint64_t cpuext; - uint64_t dynarec_settings; - size_t pagesize; - size_t codesize; - uintptr_t map_addr; - size_t map_len; - size_t file_length; - uint32_t filename_length; - uint32_t nblocks; - uint32_t nLockAddresses; - uint32_t nUnalignedAddresses; - char filename[]; -} DynaCacheHeader_t; - -#define DYNAREC_SETTINGS() \ - DS_GO(BOX64_DYNAREC_ALIGNED_ATOMICS, dynarec_aligned_atomics, 1) \ - DS_GO(BOX64_DYNAREC_BIGBLOCK, dynarec_bigblock, 2) \ - DS_GO(BOX64_DYNAREC_CALLRET, dynarec_callret, 2) \ - DS_GO(BOX64_DYNAREC_DF, dynarec_df, 1) \ - DS_GO(BOX64_DYNAREC_DIRTY, dynarec_dirty, 2) \ - DS_GO(BOX64_DYNAREC_DIV0, dynarec_div0, 1) \ - DS_GO(BOX64_DYNAREC_FASTNAN, dynarec_fastnan, 1) \ - DS_GO(BOX64_DYNAREC_FASTROUND, dynarec_fastround, 2) \ - DS_GO(BOX64_DYNAREC_FORWARD, dynarec_forward, 10) \ - DS_GO(BOX64_DYNAREC_NATIVEFLAGS, dynarec_nativeflags, 1) \ - DS_GO(BOX64_DYNAREC_SAFEFLAGS, dynarec_safeflags, 2) \ - DS_GO(BOX64_DYNAREC_STRONGMEM, dynarec_strongmem, 2) \ - DS_GO(BOX64_DYNAREC_VOLATILE_METADATA, dynarec_volatile_metadata, 1)\ - DS_GO(BOX64_DYNAREC_WEAKBARRIER, dynarec_weakbarrier, 2) \ - DS_GO(BOX64_DYNAREC_X87DOUBLE, dynarec_x87double, 2) \ - DS_GO(BOX64_DYNAREC_NOARCH, dynarec_noarch, 2) \ - DS_GO(BOX64_aes, aes, 1) \ - DS_GO(BOX64_PCLMULQDQ, pclmulqdq, 1) \ - DS_GO(BOX64_SHAEXT, shaext, 1) \ - DS_GO(BOX64_SSE42, sse42, 1) \ - DS_GO(BOX64_AVX, avx, 2) \ - DS_GO(BOX64_X87_NO80BITS, x87_no80bits, 1) \ - DS_GO(BOX64_RDTSC_1GHZ, rdtsc_1ghz, 1) \ - DS_GO(BOX64_SSE_FLUSHTO0, sse_flushto0, 1) \ - -#define DS_GO(A, B, C) uint64_t B:C; -typedef union dynarec_settings_s { - struct { - DYNAREC_SETTINGS() - }; - uint64_t x; -} dynarec_settings_t; -#undef DS_GO -uint64_t GetDynSetting(mapping_t* mapping) -{ - dynarec_settings_t settings = {0}; - #define DS_GO(A, B, C) settings.B = (mapping->env && mapping->env->is_##B##_overridden)?mapping->env->B:box64env.B; - DYNAREC_SETTINGS() - #undef DS_GO - return settings.x; -} -void PrintDynfSettings(int level, uint64_t s) -{ - dynarec_settings_t settings = {0}; - settings.x = s; - #define DS_GO(A, B, C) if(settings.B) printf_log_prefix(0, level, "\t\t" #A "=%d\n", settings.B); - DYNAREC_SETTINGS() - #undef DS_GO -} -#undef DYNAREC_SETTINGS - -char* MmaplistName(const char* filename, uint64_t dynarec_settings, const char* fullname) -{ - // names are FOLDER/filename-YYYYY-XXXXX.box64 - // Where XXXXX is the hash of the full name - // and YYYY is the Dynarec optim (in hex) - static char mapname[4096]; - snprintf(mapname, 4095-6, "%s-%llx-%u", filename, dynarec_settings, __ac_X31_hash_string(fullname)); - strcat(mapname, ".box64"); - return mapname; -} - -char* GetMmaplistName(mapping_t* mapping) -{ - return MmaplistName(mapping->filename+1, GetDynSetting(mapping), mapping->fullname); -} - -const char* NicePrintSize(size_t sz) -{ - static char buf[256]; - const char* units[] = {"", "kb", "Mb", "Gb"}; - size_t u, d; - int idx = 0; - size_t ratio = 0; - while(idx>ratio)<50) { - snprintf(buf, 255, "%zd.%zd%s", sz>>ratio, (sz>>(ratio-1))%10, units[idx]); - } else { - snprintf(buf, 255, "%zd%s", sz>>ratio, units[idx]); - } - return buf; -} - -#ifndef WIN32 -typedef struct DynaCacheFileEntry_s { - char* filename; - uint64_t size; - time_t mtime; - int valid; -} DynaCacheFileEntry_t; - -int ReadDynaCache(const char* folder, const char* name, mapping_t* mapping, int verbose); - -#ifndef O_CLOEXEC -#define O_CLOEXEC 0 -#endif - -#define DYNACACHE_TMP_STALE_SECONDS 60 - -static void SyncDynaCacheFolder(const char* folder) -{ -#ifdef O_DIRECTORY - int fd = open(folder, O_RDONLY|O_DIRECTORY|O_CLOEXEC); -#else - int fd = open(folder, O_RDONLY|O_CLOEXEC); -#endif - if(fd>=0) { - fsync(fd); - close(fd); - } -} - -static int DynaCacheOpenTempFile(const char* mapname, char* tmpname, size_t tmpname_size) -{ - for(int i=0; i<100; ++i) { - snprintf(tmpname, tmpname_size, "%s.tmp.%ld.%d", mapname, (long)getpid(), i); - int fd = open(tmpname, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, 0644); - if(fd>=0) - return fd; - if(errno!=EEXIST) - return -1; - } - errno = EEXIST; - return -1; -} - -static int RemoveStaleDynaCacheTempFile(const char* folder, const char* name) -{ - if(!strstr(name, ".box64.tmp.")) - return 0; - - char filename[strlen(folder)+strlen(name)+1]; - strcpy(filename, folder); - strcat(filename, name); - - struct stat st = {0}; - if(lstat(filename, &st) || !S_ISREG(st.st_mode) || st.st_size<0) - return 0; - - time_t now = time(NULL); - if(now==(time_t)-1 || st.st_mtime > now-DYNACACHE_TMP_STALE_SECONDS) - return 0; - - size_t size = st.st_size; - if(unlink(filename)) - return 0; - - dynarec_log(LOG_INFO, "DynaCache removed stale temporary cache file %s (%s)\n", filename, NicePrintSize(size)); - return 1; -} - -static int DynaCacheFileEntryCmp(const void* a, const void* b) -{ - const DynaCacheFileEntry_t* fa = a; - const DynaCacheFileEntry_t* fb = b; - if(fa->mtime < fb->mtime) - return -1; - if(fa->mtime > fb->mtime) - return 1; - return strcmp(fa->filename, fb->filename); -} - -static void PruneDynaCacheFolder(const char* folder, uint64_t max_size) -{ - if(!max_size) - return; - DIR* dir = opendir(folder); - if(!dir) return; - - DynaCacheFileEntry_t* files = NULL; - size_t count = 0; - size_t capacity = 0; - uint64_t total = 0; - - // collect all dynacache files in the folder - struct dirent* d = NULL; - while((d = readdir(dir))) { - size_t l = strlen(d->d_name); - if(RemoveStaleDynaCacheTempFile(folder, d->d_name)) - continue; - if(l<=6 || strcmp(d->d_name+l-6, ".box64")) continue; - - char* filename = box_malloc(strlen(folder)+l+1); - strcpy(filename, folder); - strcat(filename, d->d_name); - - struct stat st = {0}; - if(lstat(filename, &st) || !S_ISREG(st.st_mode) || st.st_size<0) { - box_free(filename); - continue; - } - - if(count == capacity) { - size_t new_capacity = capacity ? capacity*2 : 16; - DynaCacheFileEntry_t* new_files = box_realloc(files, new_capacity*sizeof(*files)); - files = new_files; - capacity = new_capacity; - } - - files[count].filename = filename; - files[count].size = st.st_size; - files[count].mtime = st.st_mtime; - files[count].valid = -1; - total += files[count].size; - ++count; - } - closedir(dir); - - // start pruning, invalid files first, then older ones until we are under 80% of set max size - if(total > max_size && count) { - uint64_t target_size = (max_size/5) * 4; - size_t folder_len = strlen(folder); - for(size_t i=0; itarget_size; ++i) { - files[i].valid = (ReadDynaCache(folder, files[i].filename+folder_len, NULL, 0)==0); - if(files[i].valid) continue; - uint64_t size = files[i].size; - if(!unlink(files[i].filename)) { - dynarec_log(LOG_INFO, "DynaCache removed invalid cache file %s (%s)\n", files[i].filename, NicePrintSize(size)); - files[i].size = 0; - total = (size>total)?0:total-size; - } - } - qsort(files, count, sizeof(*files), DynaCacheFileEntryCmp); - for(size_t i=0; itarget_size; ++i) { - if(files[i].valid!=1 || !files[i].size) - continue; - uint64_t size = files[i].size; - if(!unlink(files[i].filename)) { - dynarec_log(LOG_INFO, "DynaCache removed old cache file %s (%s)\n", files[i].filename, NicePrintSize(size)); - total = (size>total)?0:total-size; - } - } - } - - for(size_t i=0; ienv && mapping->env->is_dynacache_overridden && (mapping->env->dynacache!=1)) - return; - if((!mapping->env || !mapping->env->is_dynacache_overridden) && box64env.dynacache!=1) - return; - // don't do serialize for program that needs purge=1 - if(mapping->env && mapping->env->is_dynarec_purge_overridden && mapping->env->dynarec_purge) - return; - if((!mapping->env || !mapping->env->is_dynarec_purge_overridden) && box64env.dynarec_purge) - return; - // don't do serialize for program that needs dirty=1 - if(mapping->env && mapping->env->is_dynarec_dirty_overridden && mapping->env->dynarec_dirty) - return; - if((!mapping->env || !mapping->env->is_dynarec_dirty_overridden) && box64env.dynarec_dirty) - return; - const char* folder = GetDynacacheFolder(mapping); - if(!folder) return; // no folder, no serialize... - const char* name = GetMmaplistName(mapping); - if(!name) return; - char mapname[strlen(folder)+strlen(name)+1]; - strcpy(mapname, folder); - strcat(mapname, name); - size_t filesize = FileSize(mapping->fullname); - if(!filesize) { - dynarec_log(LOG_INFO, "DynaCache will not serialize cache for %s because filesize is 0\n", mapping->fullname); - return; // mmaped file as a 0 size... - } - // prepare header - int nblocks = MmaplistNBlocks(mapping->mmaplist); - if(!nblocks) { - dynarec_log(LOG_INFO, "DynaCache will not serialize cache for %s because nblocks is 0\n", mapping->fullname); - return; //How??? - } - size_t map_len = SizeFileMapped(mapping->start); - size_t nLockAddresses = nLockAddressRange(mapping->start, map_len); - size_t nUnaligned = nUnalignedRange(mapping->start, map_len); - size_t total = sizeof(DynaCacheHeader_t) + strlen(mapping->fullname) + 1 + nblocks*sizeof(CompressedDynaCacheBlock_t) + nLockAddresses*sizeof(uintptr_t) + nUnaligned*sizeof(uintptr_t);; - total = ALIGN(total); // align on pagesize - uint8_t all_header[total]; - memset(all_header, 0, total); - void* p = all_header; - DynaCacheHeader_t* header = p; - strcpy(header->sign, HEADER_SIGN); - header->file_version = FILE_VERSION; - header->dynarec_version = DYNAREC_VERSION; - header->arch_version = ARCH_VERSION; - header->dynarec_settings = GetDynSetting(mapping); - header->cpuext = cpuext.x; - header->pagesize = box64_pagesize; - header->codesize = MmaplistTotalAlloc(mapping->mmaplist); - header->map_addr = mapping->start; - header->file_length = filesize; - header->filename_length = strlen(mapping->fullname); - header->nblocks = MmaplistNBlocks(mapping->mmaplist); - header->map_len = map_len; - header->nLockAddresses = nLockAddresses; - header->nUnalignedAddresses = nUnaligned; - size_t dynacache_min = box64env.dynacache_min; - if(mapping->env && mapping->env->is_dynacache_min_overridden) - dynacache_min = mapping->env->dynacache_min; - if(dynacache_min*1024>header->codesize) { - dynarec_log(LOG_INFO, "DynaCache will not serialize cache for %s because there is not enough usefull code (%s)\n", mapping->fullname, NicePrintSize(header->codesize)); - return; // not enugh code, do no write - } - p += sizeof(DynaCacheHeader_t); // fullname - strcpy(p, mapping->fullname); - p += strlen(p) + 1; // blocks - CompressedDynaCacheBlock_t* blocks = p; - MmaplistFillBlocks(mapping->mmaplist, blocks); - p += nblocks*sizeof(CompressedDynaCacheBlock_t); - uintptr_t* lockAddresses = p; - p += nLockAddresses*sizeof(uintptr_t); - uintptr_t* unalignedAddresses = p; - if(nLockAddresses) - getLockAddressRange(mapping->start, map_len, lockAddresses); - if(nUnaligned) - getUnalignedRange(mapping->start, map_len, unalignedAddresses); - // all done, now just create the file and write all this down... - #ifndef WIN32 - char tmpname[strlen(mapname)+64]; - int tmpfd = DynaCacheOpenTempFile(mapname, tmpname, sizeof(tmpname)); - if(tmpfd<0) { - dynarec_log(LOG_INFO, "Cannot create cache file %s\n", mapname); - return; - } - FILE* f = fdopen(tmpfd, "wb"); - if(!f) { - close(tmpfd); - unlink(tmpname); - dynarec_log(LOG_INFO, "Cannot create cache file %s\n", mapname); - return; - } - int write_error = 0; - if(fwrite(all_header, total, 1, f)!=1) - write_error = 1; - int rewrite_header = 0; - int type = BOX64ENV(dynacache_compress); - if(type!=COMP_NONE) { - for(int i=0; ienv && mapping->env->is_dynacache_limit_overridden) - dynacache_limit = mapping->env->dynacache_limit; - - if(rename(tmpname, mapname)) { - dynarec_log(LOG_INFO, "Cannot publish cache file %s\n", mapname); - unlink(tmpname); - return; - } - SyncDynaCacheFolder(folder); - if(dynacache_limit>0) { - PruneDynaCacheFolder(folder, (uint64_t)dynacache_limit*1024*1024); - SyncDynaCacheFolder(folder); - } - #else - // TODO? - #endif -} - -#define DCERR_OK 0 -#define DCERR_NEXIST 1 -#define DCERR_TOOSMALL 2 -#define DCERR_FERROR 3 -#define DCERR_BADHEADER 4 -#define DCERR_FILEVER 5 -#define DCERR_DYNVER 6 -#define DCERR_DYNARCHVER 7 -#define DCERR_PAGESIZE 8 -#define DCERR_MAPNEXIST 9 -#define DCERR_MAPCHG 10 -#define DCERR_RELOC 11 -#define DCERR_BADNAME 12 -#define DCERR_CPUEXT 13 - -#ifndef WIN32 -int ReadDynaCache(const char* folder, const char* name, mapping_t* mapping, int verbose) -{ - char filename[strlen(folder)+strlen(name)+1]; - strcpy(filename, folder); - strcat(filename, name); - if(verbose) printf_log(LOG_NONE, "File %s:\t", name); - FILE *f = fopen(filename, "rb"); - if(!f) { - int exists = FileExist(filename, IS_FILE); - if(verbose) printf_log_prefix(0, LOG_NONE, "%s\n", exists?"Cannot open file":"Invalid file"); - return exists?DCERR_FERROR:DCERR_NEXIST; - } - struct stat st = {0}; - if(fstat(fileno(f), &st) || !S_ISREG(st.st_mode) || st.st_size<0) { - if(verbose) printf_log_prefix(0, LOG_NONE, "Cannot stat file\n"); - fclose(f); - return DCERR_FERROR; - } - size_t filesize = st.st_size; - if(filesizestart - header.map_addr; - dynarec_log(LOG_INFO, "Trying to load DynaCache for %s, with a delta_map=%zx\n", mapping->fullname, delta_map); - if(!mapping->mmaplist) - mapping->mmaplist = NewMmaplist(); - MmaplistAddNBlocks(mapping->mmaplist, header.nblocks); - for(int i=0; immaplist, fd, p, blocks[i].block.block, blocks[i].block.size, delta_map, mapping->start); - else { - void* src = box_malloc(blocks[i].compsize); - fseek(f, p, SEEK_SET); - if(!src || (fread(src, blocks[i].compsize, 1, f)!=1)) { - ret = -10; - } else - ret = MmaplistAddCompressedBlock(mapping->mmaplist, blocks[i].type, src, blocks[i].compsize, blocks[i].block.block, blocks[i].block.size, delta_map, mapping->start); - box_free(src); - } - if(ret) { - printf_log(LOG_NONE, "Error while doing relocation on a DynaCache (block %d)\n", i); - fclose(f); - return DCERR_RELOC; - } - p+=(blocks[i].type==COMP_NONE)?blocks[i].block.size:ALIGN(blocks[i].compsize); - } - for(size_t i=0; ifullname, header.nblocks); - } - fclose(f); - return DCERR_OK; -} -#endif - -void DynaCacheList(const char* filter) -{ - #ifndef WIN32 - const char* folder = GetDynacacheFolder(NULL); - if(!folder) { - printf_log(LOG_NONE, "DynaCache folder not found\n"); - return; - } - DIR* dir = opendir(folder); - if(!dir) { - printf_log(LOG_NONE, "Cannot open DynaCache folder\n"); - return; - } - struct dirent* d = NULL; - int need_filter = (filter && strlen(filter)); - while((d = readdir(dir))) { - size_t l = strlen(d->d_name); - if(l>6 && !strcmp(d->d_name+l-6, ".box64")) { - if(need_filter && !strstr(d->d_name, filter)) - continue; - ReadDynaCache(folder, d->d_name, NULL, 1); - printf_log_prefix(0, LOG_NONE, "\n"); - } - } - closedir(dir); - #endif -} -void DynaCacheClean() -{ - #ifndef WIN32 - const char* folder = GetDynacacheFolder(NULL); - if(!folder) { - printf_log(LOG_NONE, "DynaCache folder not found\n"); - return; - } - DIR* dir = opendir(folder); - if(!dir) { - printf_log(LOG_NONE, "Cannot open DynaCache folder\n"); - return; - } - struct dirent* d = NULL; - while((d = readdir(dir))) { - size_t l = strlen(d->d_name); - if(RemoveStaleDynaCacheTempFile(folder, d->d_name)) - continue; - if(l>6 && !strcmp(d->d_name+l-6, ".box64")) { - int ret = ReadDynaCache(folder, d->d_name, NULL, 0); - if(ret) { - char filename[strlen(folder)+strlen(d->d_name)+1]; - strcpy(filename, folder); - strcat(filename, d->d_name); - size_t filesize = FileSize(filename); - if(!unlink(filename)) { - printf_log(LOG_NONE, "Removed %s for %s\n", d->d_name, NicePrintSize(filesize)); - } else { - printf_log(LOG_NONE, "Could not remove %s\n", d->d_name); - } - } - } - } - closedir(dir); - SyncDynaCacheFolder(folder); - #endif -} -#ifndef WIN32 -void MmapDynaCache(mapping_t* mapping) -{ - if(!DYNAREC_VERSION) - return; - // no need to test dynacache enabled or not, it has already been done before this call - const char* folder = GetDynacacheFolder(mapping); - if(!folder) return; - const char* name = GetMmaplistName(mapping); - if(!name) return; - dynarec_log(LOG_DEBUG, "Looking for DynaCache %s in %s\n", name, folder); - ReadDynaCache(folder, name, mapping, 0); -} -#endif -#else -void SerializeMmaplist(mapping_t* mapping) {} -void DynaCacheList(const char* filter) { printf_log(LOG_NONE, "Dynarec not enable\n"); } -void DynaCacheClean() {} -#endif - void WillRemoveMapping(uintptr_t addr, size_t length) { #ifdef DYNAREC diff --git a/src/tools/env_private.h b/src/tools/env_private.h new file mode 100644 index 000000000..57c8228ab --- /dev/null +++ b/src/tools/env_private.h @@ -0,0 +1,18 @@ +#ifndef __ENV_PRIVATE_H__ +#define __ENV_PRIVATE_H__ + +#include + +#include "env.h" + +typedef struct mapping_s { + char* filename; + char* fullname; + box64env_t* env; + uintptr_t start; //lower address of the map for this file + mmaplist_t* mmaplist; +} mapping_t; + +char* LowerCase(const char* s); + +#endif // __ENV_PRIVATE_H__ diff --git a/wine/wow64/CMakeLists.txt b/wine/wow64/CMakeLists.txt index 86e048216..fe145955e 100644 --- a/wine/wow64/CMakeLists.txt +++ b/wine/wow64/CMakeLists.txt @@ -100,6 +100,7 @@ set(WOW64_BOX64CPU_SRC "${BOX64_ROOT}/src/tools/alternate.c" "${BOX64_ROOT}/src/tools/bitutils.c" "${BOX64_ROOT}/src/tools/env.c" + "${BOX64_ROOT}/src/tools/dynacache.c" "${BOX64_ROOT}/src/tools/rbtree.c" )