From 135916c7d75559f0b5193b74e10e1219242ef358 Mon Sep 17 00:00:00 2001 From: Yang Liu Date: Fri, 13 Nov 2026 16:47:14 +0800 Subject: [PATCH 1/3] [DYNAREC] Refined libtbb detection and optimization --- CMakeLists.txt | 1 + docs/USAGE.md | 6 +- docs/USAGE_CN.md | 6 +- docs/box64.pod | 6 +- docs/gen/usage.json | 6 +- docs/gen/usage_cn.json | 6 +- src/dynarec/arm64/dynarec_arm64_pass0.h | 4 +- src/dynarec/la64/dynarec_la64_pass0.h | 47 ++-- src/dynarec/rv64/dynarec_rv64_pass0.h | 38 +-- src/dynarec/tbb.c | 315 ++++++++++++++++++++++++ src/include/env.h | 2 +- src/include/tbb.h | 17 ++ src/librarian/library.c | 14 +- 13 files changed, 408 insertions(+), 60 deletions(-) create mode 100644 src/dynarec/tbb.c create mode 100644 src/include/tbb.h diff --git a/CMakeLists.txt b/CMakeLists.txt index c74391b616..dd8cfe186d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1033,6 +1033,7 @@ if(DYNAREC) "${BOX64_ROOT}/src/dynarec/dynarec_native_functions.c" "${BOX64_ROOT}/src/dynarec/dynacache_reloc.c" "${BOX64_ROOT}/src/dynarec/dynacache_compress.c" + "${BOX64_ROOT}/src/dynarec/tbb.c" "${BOX64_ROOT}/src/emu/x64test.c" ) endif() diff --git a/docs/USAGE.md b/docs/USAGE.md index 2bbd6ab62c..e6dfa29038 100644 --- a/docs/USAGE.md +++ b/docs/USAGE.md @@ -321,10 +321,10 @@ Enable or disable the generation of division-by-zero exception. Available in Wow ### BOX64_DYNAREC_TBB -Enable or disable libtbb detection. +Detect oneTBB (statically linked or libtbb) and emulate x86 TSO for its scheduler code range. - * 0: Do not detect libtbb. - * 1: Detect libtbb and apply conservative settings. [Default] + * 0: Do not detect oneTBB. + * 1: Detect oneTBB and apply strongmem=4 to its scheduler code range. [Default] ### BOX64_DYNAREC_X87DOUBLE diff --git a/docs/USAGE_CN.md b/docs/USAGE_CN.md index f93c8f083c..eceaef8668 100644 --- a/docs/USAGE_CN.md +++ b/docs/USAGE_CN.md @@ -308,10 +308,10 @@ Skip N first CPU cores (Is apply before MAXCPU) 在 WowBox64 中可用。 ### BOX64_DYNAREC_TBB -启用或禁用 libtbb 检测。 +检测 oneTBB(静态链接或 libtbb),并为其调度器代码范围模拟 x86 TSO。 - * 0: 不检测 libtbb。 - * 1: 检测 libtbb 并应用保守设置。 [默认值] + * 0: 不检测 oneTBB。 + * 1: 检测 oneTBB 并对其调度器代码范围应用 strongmem=4。 [默认值] ### BOX64_DYNAREC_X87DOUBLE diff --git a/docs/box64.pod b/docs/box64.pod index 40fc9f3e96..3b73cef763 100644 --- a/docs/box64.pod +++ b/docs/box64.pod @@ -351,10 +351,10 @@ Enable the emulation of x86 strong memory model. Available in WowBox64. =item B =I<0|1> -Enable or disable libtbb detection. +Detect oneTBB (statically linked or libtbb) and emulate x86 TSO for its scheduler code range. - * 0 : Do not detect libtbb. - * 1 : Detect libtbb and apply conservative settings. [Default] + * 0 : Do not detect oneTBB. + * 1 : Detect oneTBB and apply strongmem=4 to its scheduler code range. [Default] =item B =I<0|1|0xXXXXXXXX-0xYYYYYYYY> diff --git a/docs/gen/usage.json b/docs/gen/usage.json index 1d0f49e17c..931a49f4b3 100644 --- a/docs/gen/usage.json +++ b/docs/gen/usage.json @@ -772,19 +772,19 @@ }, { "name": "BOX64_DYNAREC_TBB", - "description": "Enable or disable libtbb detection.", + "description": "Detect oneTBB (statically linked or libtbb) and emulate x86 TSO for its scheduler code range.", "category": "Compatibility", "wine": false, "configurator": false, "options": [ { "key": "0", - "description": "Do not detect libtbb.", + "description": "Do not detect oneTBB.", "default": false }, { "key": "1", - "description": "Detect libtbb and apply conservative settings.", + "description": "Detect oneTBB and apply strongmem=4 to its scheduler code range.", "default": true } ] diff --git a/docs/gen/usage_cn.json b/docs/gen/usage_cn.json index 7833debcf6..63f223b730 100644 --- a/docs/gen/usage_cn.json +++ b/docs/gen/usage_cn.json @@ -263,10 +263,10 @@ } }, "BOX64_DYNAREC_TBB": { - "description": "启用或禁用 libtbb 检测。", + "description": "检测 oneTBB(静态链接或 libtbb),并为其调度器代码范围模拟 x86 TSO。", "options": { - "0": "不检测 libtbb。", - "1": "检测 libtbb 并应用保守设置。" + "0": "不检测 oneTBB。", + "1": "检测 oneTBB 并对其调度器代码范围应用 strongmem=4。" } }, "BOX64_DYNAREC_TEST": { diff --git a/src/dynarec/arm64/dynarec_arm64_pass0.h b/src/dynarec/arm64/dynarec_arm64_pass0.h index 2f194e72ab..d003755e0f 100644 --- a/src/dynarec/arm64/dynarec_arm64_pass0.h +++ b/src/dynarec/arm64/dynarec_arm64_pass0.h @@ -1,5 +1,7 @@ // SPDX-License-Identifier: MIT +#include "tbb.h" + #define INIT uintptr_t sav_addr=addr #define FINI \ dyn->isize = addr-sav_addr; \ @@ -40,7 +42,7 @@ dyn->insts[ninst].f_entry = dyn->f; \ dyn->insts[ninst].host_call = 0; \ dyn->insts[ninst].host_call_clobber = 0;\ - dyn->insts[ninst].x64.strongmem = ((box64_wine && VolatileRangesContains(ip)) ? 0 : BOX64DRENV(dynarec_strongmem)); \ + dyn->insts[ninst].x64.strongmem = ((box64_wine && VolatileRangesContains(ip)) ? 0 : (TBBTSORangesContains(ip) ? 4 : BOX64DRENV(dynarec_strongmem))); \ if(dyn->insts[ninst].x64.strongmem && cpuext.lrcpc && cpuext.lrcpc2 && cpuext.uscat) dyn->insts[ninst].use_lrcpc = 1; \ if(ninst) {dyn->insts[ninst-1].x64.size = dyn->insts[ninst].x64.addr - dyn->insts[ninst-1].x64.addr;} \ AREFLAGSNEEDED() diff --git a/src/dynarec/la64/dynarec_la64_pass0.h b/src/dynarec/la64/dynarec_la64_pass0.h index 001215d0b2..a5064cedfb 100644 --- a/src/dynarec/la64/dynarec_la64_pass0.h +++ b/src/dynarec/la64/dynarec_la64_pass0.h @@ -1,4 +1,7 @@ // SPDX-License-Identifier: MIT + +#include "tbb.h" + #define INIT uintptr_t sav_addr = addr #define FINI \ dyn->isize = addr - sav_addr; \ @@ -70,27 +73,29 @@ } else \ dyn->insts[ninst].barrier_maybe = 1 #define SET_HASCALLRET() dyn->insts[ninst].x64.has_callret = 1 -#define NEW_INST \ - ++dyn->size; \ - dyn->insts[ninst].x64.addr = ip; \ - dyn->insts[ninst].rsp_class = 0; \ - dyn->lsx.combined1 = dyn->lsx.combined2 = 0; \ - dyn->lsx.swapped = 0; \ - dyn->lsx.barrier = 0; \ - dyn->insts[ninst].up32_read = 0; \ - dyn->insts[ninst].up32_write64 = 0; \ - dyn->insts[ninst].up32_write32 = 0; \ - dyn->insts[ninst].up32_zero = 0; \ - dyn->insts[ninst].up32_skip = 0; \ - dyn->insts[ninst].up32_pending = 0; \ - dyn->insts[ninst].vector_liveness = (vector_liveness_t) { 0 }; \ - dyn->insts[ninst].comis_fusion = -1; \ - dyn->insts[ninst].comis_mark = 0; \ - dyn->insts[ninst].host_call = 0; \ - dyn->insts[ninst].call_window = 0; \ - dyn->insts[ninst].f_entry = dyn->f; \ - dyn->insts[ninst].x64.strongmem = ((box64_wine && VolatileRangesContains(ip)) ? 0 : BOX64DRENV(dynarec_strongmem)); \ - if (ninst) { dyn->insts[ninst - 1].x64.size = dyn->insts[ninst].x64.addr - dyn->insts[ninst - 1].x64.addr; } \ +#define NEW_INST \ + ++dyn->size; \ + dyn->insts[ninst].x64.addr = ip; \ + dyn->insts[ninst].rsp_class = 0; \ + dyn->lsx.combined1 = dyn->lsx.combined2 = 0; \ + dyn->lsx.swapped = 0; \ + dyn->lsx.barrier = 0; \ + dyn->insts[ninst].up32_read = 0; \ + dyn->insts[ninst].up32_write64 = 0; \ + dyn->insts[ninst].up32_write32 = 0; \ + dyn->insts[ninst].up32_zero = 0; \ + dyn->insts[ninst].up32_skip = 0; \ + dyn->insts[ninst].up32_pending = 0; \ + dyn->insts[ninst].vector_liveness = (vector_liveness_t) { 0 }; \ + dyn->insts[ninst].comis_fusion = -1; \ + dyn->insts[ninst].comis_mark = 0; \ + dyn->insts[ninst].host_call = 0; \ + dyn->insts[ninst].call_window = 0; \ + dyn->insts[ninst].f_entry = dyn->f; \ + dyn->insts[ninst].x64.strongmem = ((box64_wine && VolatileRangesContains(ip)) \ + ? 0 \ + : (TBBTSORangesContains(ip) ? 4 : BOX64DRENV(dynarec_strongmem))); \ + if (ninst) { dyn->insts[ninst - 1].x64.size = dyn->insts[ninst].x64.addr - dyn->insts[ninst - 1].x64.addr; } \ AREFLAGSNEEDED() #define INST_EPILOG \ diff --git a/src/dynarec/rv64/dynarec_rv64_pass0.h b/src/dynarec/rv64/dynarec_rv64_pass0.h index 4924afab63..ec0dca4abd 100644 --- a/src/dynarec/rv64/dynarec_rv64_pass0.h +++ b/src/dynarec/rv64/dynarec_rv64_pass0.h @@ -1,5 +1,7 @@ // SPDX-License-Identifier: MIT +#include "tbb.h" + #define INIT uintptr_t sav_addr = addr #define FINI \ dyn->isize = addr - sav_addr; \ @@ -69,23 +71,25 @@ } else \ dyn->insts[ninst].barrier_maybe = 1 #define SET_HASCALLRET() dyn->insts[ninst].x64.has_callret = 1 -#define NEW_INST \ - ++dyn->size; \ - memset(&dyn->insts[ninst], 0, sizeof(instruction_native_t)); \ - dyn->insts[ninst].x64.addr = ip; \ - dyn->e.combined1 = dyn->e.combined2 = 0; \ - dyn->e.swapped = 0; \ - dyn->e.barrier = 0; \ - for (int i = 0; i < 24; ++i) \ - dyn->e.olds[i].v = 0; \ - dyn->insts[ninst].f_entry = dyn->f; \ - dyn->insts[ninst].vector_sew_entry = dyn->vector_sew; \ - dyn->inst_sew = dyn->vector_sew; \ - dyn->inst_vlmul = VECTOR_LMUL1; \ - dyn->inst_vl = 0; \ - dyn->insts[ninst].x64.strongmem = ((box64_wine && VolatileRangesContains(ip)) ? 0 : BOX64DRENV(dynarec_strongmem));\ - if (ninst) \ - dyn->insts[ninst - 1].x64.size = dyn->insts[ninst].x64.addr - dyn->insts[ninst - 1].x64.addr;\ +#define NEW_INST \ + ++dyn->size; \ + memset(&dyn->insts[ninst], 0, sizeof(instruction_native_t)); \ + dyn->insts[ninst].x64.addr = ip; \ + dyn->e.combined1 = dyn->e.combined2 = 0; \ + dyn->e.swapped = 0; \ + dyn->e.barrier = 0; \ + for (int i = 0; i < 24; ++i) \ + dyn->e.olds[i].v = 0; \ + dyn->insts[ninst].f_entry = dyn->f; \ + dyn->insts[ninst].vector_sew_entry = dyn->vector_sew; \ + dyn->inst_sew = dyn->vector_sew; \ + dyn->inst_vlmul = VECTOR_LMUL1; \ + dyn->inst_vl = 0; \ + dyn->insts[ninst].x64.strongmem = ((box64_wine && VolatileRangesContains(ip)) \ + ? 0 \ + : (TBBTSORangesContains(ip) ? 4 : BOX64DRENV(dynarec_strongmem))); \ + if (ninst) \ + dyn->insts[ninst - 1].x64.size = dyn->insts[ninst].x64.addr - dyn->insts[ninst - 1].x64.addr; \ AREFLAGSNEEDED() #define INST_EPILOG \ diff --git a/src/dynarec/tbb.c b/src/dynarec/tbb.c new file mode 100644 index 0000000000..33e6ae41e4 --- /dev/null +++ b/src/dynarec/tbb.c @@ -0,0 +1,315 @@ +// SPDX-License-Identifier: MIT +// +// oneTBB publishes stack-allocated tasks with plain release stores, e.g. arena_slot::spawn() does: +// task_pool_ptr[T] = &t; +// tail.store(new_tail, std::memory_order_release); +// On x86 the release store is a plain MOV, and TSO store->store ordering makes the task's vptr visible before the publication. +// RISC architectures does not, so a thief can read the pointer but stale stack bytes at [task] and crash in task->execute(). +// Detect oneTBB by its RTTI and emulate x86 TSO (strongmem=4) on its code range only. +// This is a self-maintained range list, similar to the volatile metadata ranges. + +#include +#include + +#include "box64context.h" +#include "elfs/elfloader_private.h" +#include "debug.h" +#include "env.h" +#include "tbb.h" + +#define TBB_MAX_RANGES 8 +#define TBB_MAX_CODE 64 +#define TBB_MAX_SEGS 16 +#define TBB_CLUSTER_GAP 0x10000ULL +#define TBB_RANGE_MARGIN 0x20000ULL + +typedef struct tbb_range_s { + uintptr_t start; + uintptr_t end; +} tbb_range_t; + +typedef struct tbb_seg_s { + uintptr_t start; + uintptr_t end; + uint32_t flags; +} tbb_seg_t; + +static tbb_range_t tbb_ranges[TBB_MAX_RANGES]; +static int tbb_nranges = 0; +static int tbb_main_scanned = 0; +static int tbb_lock = 0; + +// Itanium C++ ABI RTTI names of polymorphic oneTBB classes whose virtual functions live in the scheduler code (task pool, wait context). +static const char* tbb_rtti_names[] = { "N3tbb6detail2r110task_proxyE", "N3tbb6detail2d119wait_context_vertexE" }; + +#define TBB_ALIGN_UP(x, a) (((x) + (uintptr_t)((a) - 1)) & ~(uintptr_t)((a) - 1)) +#define TBB_ALIGN_DOWN(x, a) ((x) & ~(uintptr_t)((a) - 1)) + +static void tbb_lock_acquire(void) +{ + while (__atomic_exchange_n(&tbb_lock, 1, __ATOMIC_ACQUIRE)) { + while (__atomic_load_n(&tbb_lock, __ATOMIC_RELAXED)) + ; + } +} + +static void tbb_lock_release(void) +{ + __atomic_store_n(&tbb_lock, 0, __ATOMIC_RELEASE); +} + +static void* tbb_memmem(const void* haystack, size_t haystack_len, const void* needle, size_t needle_len) +{ + if (!needle_len) + return (void*)haystack; + if (haystack_len < needle_len) + return NULL; + const uint8_t* h = (const uint8_t*)haystack; + const uint8_t* n = (const uint8_t*)needle; + const uint8_t* last = h + haystack_len - needle_len; + for (const uint8_t* p = h; p <= last; ++p) { + p = memchr(p, n[0], last - p + 1); + if (!p) + return NULL; + if (!memcmp(p, n, needle_len)) + return (void*)p; + } + return NULL; +} + +static int tbb_seg_find(const tbb_seg_t* segs, int n, uintptr_t addr) +{ + for (int i = 0; i < n; ++i) + if (addr >= segs[i].start && addr < segs[i].end) + return i; + return -1; +} + +static int tbb_seg_is_exec(const tbb_seg_t* segs, int n, uint64_t addr) +{ + for (int i = 0; i < n; ++i) + if ((segs[i].flags & PF_X) && addr >= segs[i].start && addr < segs[i].end) + return 1; + return 0; +} + +static int tbb_collect_segs(elfheader_t* h, tbb_seg_t* segs, int max) +{ + int n = 0; + for (int i = 0; i < h->numPHEntries && n < max; ++i) { + Elf64_Phdr* p = &h->PHEntries._64[i]; + if (p->p_type != PT_LOAD || !p->p_filesz) + continue; + uintptr_t start = (uintptr_t)h->image + (p->p_vaddr - h->vaddr); + segs[n].start = start; + segs[n].end = start + p->p_filesz; + segs[n].flags = p->p_flags; + ++n; + } + return n; +} + +// Find a qword with the given value in 8-byte aligned positions. +// Returns the number of hits found, up to max. +static int tbb_find_qword(const tbb_seg_t* segs, int n, uint64_t value, uint32_t need_flags, uintptr_t* hits, int max) +{ + int count = 0; + for (int i = 0; i < n && count < max; ++i) { + if ((segs[i].flags & need_flags) != need_flags) + continue; + uintptr_t addr = TBB_ALIGN_UP(segs[i].start, 8); + for (; addr + 8 <= segs[i].end && count < max; addr += 8) + if (*(const uint64_t*)addr == value) + hits[count++] = addr; + } + return count; +} + +// Search for the vtable referencing this typeinfo and collect its code pointers. +// The typeinfo's own vptr may live in another module (e.g. libstdc++). +static int tbb_collect_vtable(const tbb_seg_t* segs, int nsegs, uintptr_t typeinfo, uintptr_t* code, int max) +{ + uintptr_t hits[8]; + int nhits = tbb_find_qword(segs, nsegs, typeinfo, PF_W, hits, 8); + if (!nhits) + nhits = tbb_find_qword(segs, nsegs, typeinfo, PF_R, hits, 8); + for (int i = 0; i < nhits; ++i) { + uintptr_t vtable = hits[i] + 8; + int si = tbb_seg_find(segs, nsegs, hits[i]); + if (si < 0) + continue; + uintptr_t candidates[8]; + int ncand = 0; + int k = 0; + for (; k < 8; ++k) { + if (vtable + 8 * k + 8 > segs[si].end) + break; + uintptr_t fn = *(const uint64_t*)(vtable + 8 * k); + if (!tbb_seg_is_exec(segs, nsegs, fn)) + break; + candidates[ncand++] = fn; + } + printf_log(LOG_DEBUG, "TBB: vtable=%p entries=%d\n", (void*)vtable, k); + // A vtable with less than 2 entries is not trustworthy. + if (k < 2) + continue; + int ncode = 0; + for (int m = 0; m < ncand && ncode < max; ++m) { + int dup = 0; + for (int c = 0; c < ncode; ++c) + if (code[c] == candidates[m]) + dup = 1; + if (!dup) + code[ncode++] = candidates[m]; + } + return ncode; + } + return 0; +} + +// Locate the RTTI typeinfo of name, then its vtable, and collect the code pointers of the virtual functions. +static int tbb_collect_code(elfheader_t* h, const char* name, uintptr_t* code, int max) +{ + tbb_seg_t segs[TBB_MAX_SEGS]; + int nsegs = tbb_collect_segs(h, segs, TBB_MAX_SEGS); + if (nsegs < 2) + return 0; + + size_t name_len = strlen(name); + uintptr_t str = 0; + // Prefer non-executable read-only data, but fall back to any readable segment if the RTTI name ended up in an executable one. + for (int pass = 0; pass < 2 && !str; ++pass) { + for (int i = 0; i < nsegs && !str; ++i) { + if (!(segs[i].flags & PF_R)) + continue; + if (!pass && (segs[i].flags & PF_X)) + continue; + void* p = tbb_memmem((void*)segs[i].start, segs[i].end - segs[i].start, name, name_len); + if (p) + str = (uintptr_t)p; + } + } + if (!str) + return 0; + + printf_log(LOG_DEBUG, "TBB: %s str=%p\n", name, (void*)str); + + // Try every qword that looks like this typeinfo's __name pointer. + // Some false positives can precede the real typeinfo object. + uintptr_t hits[8]; + int nhits = tbb_find_qword(segs, nsegs, str, PF_W, hits, 8); + if (!nhits) + nhits = tbb_find_qword(segs, nsegs, str, PF_R, hits, 8); + for (int i = 0; i < nhits; ++i) { + int si = tbb_seg_find(segs, nsegs, hits[i]); + if (si < 0 || hits[i] < segs[si].start + 8) + continue; + uintptr_t typeinfo = hits[i] - 8; + if (*(const uint64_t*)(typeinfo + 8) != str) + continue; + printf_log(LOG_DEBUG, "TBB: %s typecand=%p\n", name, (void*)typeinfo); + int ncode = tbb_collect_vtable(segs, nsegs, typeinfo, code, max); + if (ncode) + return ncode; + } + return 0; +} + +static void tbb_add_range(uintptr_t start, uintptr_t end) +{ + start = TBB_ALIGN_DOWN(start, 0x1000); + end = TBB_ALIGN_UP(end, 0x1000); + if (end <= start) + return; + int nranges = __atomic_load_n(&tbb_nranges, __ATOMIC_RELAXED); + for (int i = 0; i < nranges; ++i) + if (start >= tbb_ranges[i].start && end <= tbb_ranges[i].end) + return; + if (nranges >= TBB_MAX_RANGES) + return; + tbb_ranges[nranges].start = start; + tbb_ranges[nranges].end = end; + __atomic_store_n(&tbb_nranges, nranges + 1, __ATOMIC_RELEASE); + printf_log(LOG_DEBUG, "TBB detected, applying strongmem=4 to %p-%p\n", (void*)start, (void*)end); +} + +static int tbb_add_cluster_ranges(uintptr_t* code, int ncode) +{ + if (ncode < 2) + return 0; + for (int i = 1; i < ncode; ++i) { + uintptr_t value = code[i]; + int j = i - 1; + while (j >= 0 && code[j] > value) { + code[j + 1] = code[j]; + --j; + } + code[j + 1] = value; + } + int added = 0; + int i = 0; + while (i < ncode) { + int j = i; + while (j + 1 < ncode && code[j + 1] - code[i] <= TBB_CLUSTER_GAP) + ++j; + if (j > i) { + uintptr_t start = code[i] > TBB_RANGE_MARGIN ? code[i] - TBB_RANGE_MARGIN : 0; + uintptr_t end = code[j] + TBB_RANGE_MARGIN; + int nranges = __atomic_load_n(&tbb_nranges, __ATOMIC_RELAXED); + tbb_add_range(start, end); + if (__atomic_load_n(&tbb_nranges, __ATOMIC_RELAXED) > nranges) + ++added; + } + i = j + 1; + } + return added; +} + +// Must be called with tbb_lock held. +static int tbb_scan_elf(elfheader_t* h) +{ + if (!h || box64_is32bits || !h->PHEntries._64) + return 0; + int added = 0; + for (size_t i = 0; i < sizeof(tbb_rtti_names) / sizeof(tbb_rtti_names[0]); ++i) { + uintptr_t code[TBB_MAX_CODE]; + int ncode = tbb_collect_code(h, tbb_rtti_names[i], code, TBB_MAX_CODE); + added += tbb_add_cluster_ranges(code, ncode); + } + return added; +} + +int TBBTSOAddElf(elfheader_t* h) +{ + if (!BOX64ENV(dynarec_tbb) || !h) + return 0; + tbb_lock_acquire(); + int added = tbb_scan_elf(h); + tbb_lock_release(); + return added; +} + +int TBBTSORangesContains(uintptr_t ip) +{ + if (!BOX64ENV(dynarec_tbb)) + return 0; + + // Lazily scan the main executable once for static oneTBB. + // A dynamically loaded libtbb is added by TBBTSOAddElf when it is loaded. + if (!__atomic_load_n(&tbb_main_scanned, __ATOMIC_ACQUIRE)) { + tbb_lock_acquire(); + if (!__atomic_load_n(&tbb_main_scanned, __ATOMIC_RELAXED)) { + if (my_context && my_context->elfs && my_context->elfsize > 0 && my_context->elfs[0]) { + tbb_scan_elf(my_context->elfs[0]); + __atomic_store_n(&tbb_main_scanned, 1, __ATOMIC_RELEASE); + } + } + tbb_lock_release(); + } + + int nranges = __atomic_load_n(&tbb_nranges, __ATOMIC_ACQUIRE); + for (int i = 0; i < nranges; ++i) + if (ip >= tbb_ranges[i].start && ip < tbb_ranges[i].end) + return 1; + return 0; +} diff --git a/src/include/env.h b/src/include/env.h index d719c70741..16f7626811 100644 --- a/src/include/env.h +++ b/src/include/env.h @@ -70,7 +70,7 @@ extern char* ftrace_name; BOOLEAN(BOX64_DYNAREC_PERFMAP, dynarec_perf_map, 0, 0, 0) \ INTEGER(BOX64_DYNAREC_SAFEFLAGS, dynarec_safeflags, 1, 0, 2, 1, 2) \ INTEGER(BOX64_DYNAREC_STRONGMEM, dynarec_strongmem, 0, 0, 4, 1, 3) \ - BOOLEAN(BOX64_DYNAREC_TBB, dynarec_tbb, 1, 0, 0) \ + BOOLEAN(BOX64_DYNAREC_TBB, dynarec_tbb, 1, 0, 1) \ STRING(BOX64_DYNAREC_TEST, dynarec_test_str, 1, 0) \ BOOLEAN(BOX64_DYNAREC_TEST_NODUP, dynarec_test_nodup, 0, 1, 0) \ BOOLEAN(BOX64_DYNAREC_TEST_NODUMP, dynarec_test_nodump, 1, 1, 0) \ diff --git a/src/include/tbb.h b/src/include/tbb.h new file mode 100644 index 0000000000..03bc5c0e7b --- /dev/null +++ b/src/include/tbb.h @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: MIT +#ifndef __TBB_H__ +#define __TBB_H__ + +#include + +typedef struct elfheader_s elfheader_t; + +// Scan one loaded ELF for oneTBB and add the TSO ranges it needs. +// Returns the number of ranges added. +int TBBTSOAddElf(elfheader_t* h); + +// Non-zero if ip belongs to a detected oneTBB code range that needs x86 TSO +// (strongmem=4) emulation. +int TBBTSORangesContains(uintptr_t ip); + +#endif diff --git a/src/librarian/library.c b/src/librarian/library.c index 8aa9ad605e..e8278780ac 100644 --- a/src/librarian/library.c +++ b/src/librarian/library.c @@ -27,6 +27,7 @@ #include "symbols.h" #include "elfs/elfloader_private.h" #include "library_inner.h" +#include "tbb.h" #include "wrappedlibs.h" // create the native lib list @@ -381,11 +382,6 @@ static int loadEmulatedLib(const char* libname, library_t *lib, box64context_t* SET_BOX64ENV(unity, 1); env_changed = 1; } - if(libname && BOX64ENV(dynarec_tbb) && strstr(libname, "libtbb.so")) { - printf_dump(LOG_INFO, "libtbb detected, enable Dynarec StrongMem\n"); - SET_BOX64ENV(dynarec_strongmem, 1); - env_changed = 1; - } #endif if(libname && BOX64ENV(jvm) && strstr(libname, "libjvm.so")) { #ifdef DYNAREC @@ -610,6 +606,14 @@ int FinalizeLibrary(library_t* lib, lib_t* local_maplib, int bindnow, int deepbi printf_log(LOG_NONE, "Error: relocating Plt symbols in elf %s\n", lib->name); return 1; } + if (BOX64ENV(dynarec_tbb) && lib->name && strstr(lib->name, "libtbb.so")) { + if (TBBTSOAddElf(elf_header)) { + printf_log(LOG_INFO, "libtbb detected, applying safe StrongMem emulation to its code range\n"); + } else { + printf_log(LOG_INFO, "libtbb detected but oneTBB pattern matching failed, falling back to conservative but inaccurate StrongMem emulation settings\n"); + SET_BOX64ENV(dynarec_strongmem, 1); + } + } #ifdef HAVE_TRACE if(trace_func) { int weak; From bc012b09b9a237e92d2fe702e4579c8fd233eb4f Mon Sep 17 00:00:00 2001 From: Yang Liu Date: Fri, 13 Nov 2026 16:56:02 +0800 Subject: [PATCH 2/3] update --- system/box64.box64rc | 1 - 1 file changed, 1 deletion(-) diff --git a/system/box64.box64rc b/system/box64.box64rc index 76719c2dbb..8a29f2b3b5 100644 --- a/system/box64.box64rc +++ b/system/box64.box64rc @@ -215,7 +215,6 @@ BOX64_DYNAREC_BIGBLOCK=3 BOX64_DYNAREC_CALLRET=1 [geekbench6] -BOX64_ENV=BOX64_DYNAREC_STRONGMEM=1 BOX64_ENV1=BOX64_RESERVE_HIGH=1 BOX64_ENV2=BOX64_DYNAREC_CALLRET=1 From 08a691595823aaf482b6101c7bbd7e685015aec3 Mon Sep 17 00:00:00 2001 From: Yang Liu Date: Fri, 13 Nov 2026 17:10:17 +0800 Subject: [PATCH 3/3] fix build --- src/include/tbb.h | 14 ++++++++++++++ src/librarian/library.c | 2 ++ 2 files changed, 16 insertions(+) diff --git a/src/include/tbb.h b/src/include/tbb.h index 03bc5c0e7b..3d28db401b 100644 --- a/src/include/tbb.h +++ b/src/include/tbb.h @@ -6,6 +6,19 @@ typedef struct elfheader_s elfheader_t; +#ifdef _WIN32 +static inline int TBBTSOAddElf(elfheader_t* h) +{ + (void)h; + return 0; +} + +static inline int TBBTSORangesContains(uintptr_t ip) +{ + (void)ip; + return 0; +} +#else // Scan one loaded ELF for oneTBB and add the TSO ranges it needs. // Returns the number of ranges added. int TBBTSOAddElf(elfheader_t* h); @@ -13,5 +26,6 @@ int TBBTSOAddElf(elfheader_t* h); // Non-zero if ip belongs to a detected oneTBB code range that needs x86 TSO // (strongmem=4) emulation. int TBBTSORangesContains(uintptr_t ip); +#endif #endif diff --git a/src/librarian/library.c b/src/librarian/library.c index e8278780ac..45676eb4c9 100644 --- a/src/librarian/library.c +++ b/src/librarian/library.c @@ -606,6 +606,7 @@ int FinalizeLibrary(library_t* lib, lib_t* local_maplib, int bindnow, int deepbi printf_log(LOG_NONE, "Error: relocating Plt symbols in elf %s\n", lib->name); return 1; } +#ifdef DYNAREC if (BOX64ENV(dynarec_tbb) && lib->name && strstr(lib->name, "libtbb.so")) { if (TBBTSOAddElf(elf_header)) { printf_log(LOG_INFO, "libtbb detected, applying safe StrongMem emulation to its code range\n"); @@ -614,6 +615,7 @@ int FinalizeLibrary(library_t* lib, lib_t* local_maplib, int bindnow, int deepbi SET_BOX64ENV(dynarec_strongmem, 1); } } +#endif #ifdef HAVE_TRACE if(trace_func) { int weak;