From c817ae0692bbdc2a3cebf9d7c7dcf12f031f11bd Mon Sep 17 00:00:00 2001 From: Hanlu Li Date: Sat, 15 Aug 2026 08:25:54 +0800 Subject: [PATCH 01/11] LATX, refactor: Share wrapped-library ELF preflight Move the ELF64 guest export and host symbol checks out of the Cairo-specific preflight into a reusable helper. Cairo retains the same strict activation policy and rejection reasons. Validated by a full LoongArch latx-x86_64 product build. Signed-off-by: Hanlu Li --- target/i386/latx/context/meson.build | 1 + .../latx/context/wrappedcairo-preflight.c | 216 +---------------- .../i386/latx/context/wrappedlib-preflight.c | 227 ++++++++++++++++++ .../i386/latx/include/wrappedlib-preflight.h | 21 ++ 4 files changed, 258 insertions(+), 207 deletions(-) create mode 100644 target/i386/latx/context/wrappedlib-preflight.c create mode 100644 target/i386/latx/include/wrappedlib-preflight.h diff --git a/target/i386/latx/context/meson.build b/target/i386/latx/context/meson.build index 2586140630..9bf8ef94e1 100644 --- a/target/i386/latx/context/meson.build +++ b/target/i386/latx/context/meson.build @@ -25,6 +25,7 @@ my_file = files( 'wrappedlibxcbutil.c', 'wrappedlibxcbxinerama.c', 'wrappedlibxcbxinput.c', + 'wrappedlib-preflight.c', 'wrappedcairo-preflight.c', 'wrappedcairo.c', 'wrappedlibx11xcb.c', diff --git a/target/i386/latx/context/wrappedcairo-preflight.c b/target/i386/latx/context/wrappedcairo-preflight.c index 0f4b614ce8..bc93814b1a 100644 --- a/target/i386/latx/context/wrappedcairo-preflight.c +++ b/target/i386/latx/context/wrappedcairo-preflight.c @@ -4,15 +4,12 @@ * SPDX-License-Identifier: GPL-2.0-only */ -#include -#include -#include -#include -#include -#include +#include "qemu/osdep.h" + #include #include "wrappedcairo-preflight.h" +#include "wrappedlib-preflight.h" #define GO(name, signature) #name, #define GOM(name, signature) #name, @@ -38,212 +35,17 @@ static const char *const cairo_supported_symbols[] = { #undef DATAB #undef DATAM -static bool cairo_symbol_is_supported(const char *name) -{ - size_t count = sizeof(cairo_supported_symbols) / - sizeof(cairo_supported_symbols[0]); - - for (size_t i = 0; i < count; i++) { - if (!strcmp(name, cairo_supported_symbols[i])) { - return true; - } - } - return false; -} - -static bool read_exact(FILE *file, void *buffer, size_t size, long offset) -{ - return fseek(file, offset, SEEK_SET) == 0 && - fread(buffer, 1, size, file) == size; -} - -static bool range_is_valid(uint64_t offset, uint64_t size, - uint64_t file_size) -{ - return offset <= file_size && size <= file_size - offset; -} - -static bool reject(char *reason, size_t reason_size, const char *format, - const char *detail) +static bool cairo_symbol_filter(const char *name) { - snprintf(reason, reason_size, format, detail ? detail : ""); - return false; + return !strncmp(name, "cairo_", 6); } bool latx_cairo_preflight_guest(const char *guest_path, const char *host_soname, char *reason, size_t reason_size) { - Elf64_Ehdr ehdr; - Elf64_Shdr *sections = NULL; - FILE *file = NULL; - void *host = NULL; - bool found_dynsym = false; - bool found_cairo_symbol = false; - bool ok = false; - long file_size; - - if (!reason || !reason_size) { - return false; - } - reason[0] = '\0'; - if (!guest_path || !host_soname) { - return reject(reason, reason_size, - "guest or host Cairo path is unavailable%s", NULL); - } - - file = fopen(guest_path, "rb"); - if (!file) { - snprintf(reason, reason_size, "cannot open guest ELF '%s': %s", - guest_path, strerror(errno)); - goto out; - } - if (fseek(file, 0, SEEK_END) != 0 || - (file_size = ftell(file)) < 0 || - fseek(file, 0, SEEK_SET) != 0) { - reject(reason, reason_size, - "cannot determine guest Cairo ELF size%s", NULL); - goto out; - } - if (fread(&ehdr, 1, sizeof(ehdr), file) != sizeof(ehdr) || - memcmp(ehdr.e_ident, ELFMAG, SELFMAG) || - ehdr.e_ident[EI_CLASS] != ELFCLASS64 || - ehdr.e_ident[EI_DATA] != ELFDATA2LSB || - ehdr.e_type != ET_DYN || - ehdr.e_machine != EM_X86_64 || - ehdr.e_shentsize != sizeof(Elf64_Shdr) || - !ehdr.e_shoff || !ehdr.e_shnum || - !range_is_valid(ehdr.e_shoff, - (uint64_t)ehdr.e_shnum * sizeof(Elf64_Shdr), - file_size)) { - reject(reason, reason_size, - "guest Cairo is not a supported ELF64 little-endian shared object%s", - NULL); - goto out; - } - sections = calloc(ehdr.e_shnum, sizeof(*sections)); - if (!sections || - !read_exact(file, sections, ehdr.e_shnum * sizeof(*sections), - (long)ehdr.e_shoff)) { - reject(reason, reason_size, - "cannot read guest Cairo section table%s", NULL); - goto out; - } - - host = dlopen(host_soname, RTLD_LAZY | RTLD_LOCAL); - if (!host) { - reject(reason, reason_size, "cannot load host Cairo: %s", dlerror()); - goto out; - } - - for (size_t section_index = 0; section_index < ehdr.e_shnum; - section_index++) { - const Elf64_Shdr *dynsym = §ions[section_index]; - const Elf64_Shdr *strtab; - Elf64_Sym *symbols = NULL; - char *strings = NULL; - size_t symbol_count; - - if (dynsym->sh_type != SHT_DYNSYM) { - continue; - } - found_dynsym = true; - if (dynsym->sh_link >= ehdr.e_shnum || - dynsym->sh_entsize != sizeof(Elf64_Sym) || - dynsym->sh_size % sizeof(Elf64_Sym) || - !range_is_valid(dynsym->sh_offset, dynsym->sh_size, - file_size)) { - reject(reason, reason_size, - "guest Cairo has an invalid dynamic symbol table%s", NULL); - goto out; - } - strtab = §ions[dynsym->sh_link]; - if (strtab->sh_type != SHT_STRTAB || !strtab->sh_size || - !range_is_valid(strtab->sh_offset, strtab->sh_size, - file_size)) { - reject(reason, reason_size, - "guest Cairo has an invalid dynamic string table%s", - NULL); - goto out; - } - symbols = malloc(dynsym->sh_size); - strings = malloc(strtab->sh_size); - if (!symbols || !strings || - !read_exact(file, symbols, dynsym->sh_size, - (long)dynsym->sh_offset) || - !read_exact(file, strings, strtab->sh_size, - (long)strtab->sh_offset)) { - free(symbols); - free(strings); - reject(reason, reason_size, - "cannot read guest Cairo dynamic symbols%s", NULL); - goto out; - } - - symbol_count = dynsym->sh_size / sizeof(Elf64_Sym); - for (size_t i = 0; i < symbol_count; i++) { - const Elf64_Sym *symbol = &symbols[i]; - const char *name; - unsigned type = ELF64_ST_TYPE(symbol->st_info); - unsigned binding = ELF64_ST_BIND(symbol->st_info); - - if (symbol->st_shndx == SHN_UNDEF || - (type != STT_FUNC && type != STT_GNU_IFUNC) || - (binding != STB_GLOBAL && binding != STB_WEAK) || - symbol->st_name >= strtab->sh_size) { - continue; - } - name = strings + symbol->st_name; - if (!memchr(name, '\0', strtab->sh_size - symbol->st_name)) { - free(symbols); - free(strings); - reject(reason, reason_size, - "guest Cairo has an unterminated dynamic symbol%s", - NULL); - goto out; - } - if (strncmp(name, "cairo_", 6)) { - continue; - } - found_cairo_symbol = true; - if (!cairo_symbol_is_supported(name)) { - snprintf(reason, reason_size, - "guest symbol '%s' has no safe wrapper", name); - free(symbols); - free(strings); - goto out; - } - if (!dlsym(host, name)) { - snprintf(reason, reason_size, - "host Cairo is missing guest symbol '%s'", name); - free(symbols); - free(strings); - goto out; - } - } - free(symbols); - free(strings); - } - - if (!found_dynsym) { - reject(reason, reason_size, - "guest Cairo has no inspectable dynamic symbol table%s", NULL); - goto out; - } - if (!found_cairo_symbol) { - reject(reason, reason_size, - "guest Cairo exports no Cairo functions%s", NULL); - goto out; - } - ok = true; - -out: - if (host) { - dlclose(host); - } - free(sections); - if (file) { - fclose(file); - } - return ok; + return latx_wrappedlib_preflight_guest( + guest_path, host_soname, "Cairo", cairo_symbol_filter, + cairo_supported_symbols, ARRAY_SIZE(cairo_supported_symbols), + reason, reason_size); } diff --git a/target/i386/latx/context/wrappedlib-preflight.c b/target/i386/latx/context/wrappedlib-preflight.c new file mode 100644 index 0000000000..2146e48c64 --- /dev/null +++ b/target/i386/latx/context/wrappedlib-preflight.c @@ -0,0 +1,227 @@ +/* + * SPDX-FileCopyrightText: 2026 LAT Project Authors + * + * SPDX-License-Identifier: GPL-2.0-only + */ + +#include "qemu/osdep.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "wrappedlib-preflight.h" + +static bool wrapped_symbol_is_supported( + const char *name, const char *const *supported_symbols, + size_t supported_symbol_count) +{ + for (size_t i = 0; i < supported_symbol_count; i++) { + if (!strcmp(name, supported_symbols[i])) { + return true; + } + } + return false; +} + +static bool read_exact(FILE *file, void *buffer, size_t size, long offset) +{ + return fseek(file, offset, SEEK_SET) == 0 && + fread(buffer, 1, size, file) == size; +} + +static bool range_is_valid(uint64_t offset, uint64_t size, + uint64_t file_size) +{ + return offset <= file_size && size <= file_size - offset; +} + +bool latx_wrappedlib_preflight_guest( + const char *guest_path, const char *host_soname, + const char *library_label, LatxWrappedSymbolFilter symbol_filter, + const char *const *supported_symbols, size_t supported_symbol_count, + char *reason, size_t reason_size) +{ + Elf64_Ehdr ehdr; + Elf64_Shdr *sections = NULL; + FILE *file = NULL; + void *host = NULL; + bool found_dynsym = false; + bool found_library_symbol = false; + bool ok = false; + long file_size; + + if (!reason || !reason_size) { + return false; + } + reason[0] = '\0'; + if (!guest_path || !host_soname || !library_label || !symbol_filter || + !supported_symbols || !supported_symbol_count) { + snprintf(reason, reason_size, + "wrapped-library preflight configuration is incomplete"); + return false; + } + + file = fopen(guest_path, "rb"); + if (!file) { + snprintf(reason, reason_size, "cannot open guest ELF '%s': %s", + guest_path, strerror(errno)); + goto out; + } + if (fseek(file, 0, SEEK_END) != 0 || + (file_size = ftell(file)) < 0 || + fseek(file, 0, SEEK_SET) != 0) { + snprintf(reason, reason_size, + "cannot determine guest %s ELF size", library_label); + goto out; + } + if (fread(&ehdr, 1, sizeof(ehdr), file) != sizeof(ehdr) || + memcmp(ehdr.e_ident, ELFMAG, SELFMAG) || + ehdr.e_ident[EI_CLASS] != ELFCLASS64 || + ehdr.e_ident[EI_DATA] != ELFDATA2LSB || + ehdr.e_type != ET_DYN || ehdr.e_machine != EM_X86_64 || + ehdr.e_shentsize != sizeof(Elf64_Shdr) || !ehdr.e_shoff || + !ehdr.e_shnum || + !range_is_valid(ehdr.e_shoff, + (uint64_t)ehdr.e_shnum * sizeof(Elf64_Shdr), + file_size)) { + snprintf(reason, reason_size, + "guest %s is not a supported ELF64 little-endian shared object", + library_label); + goto out; + } + sections = calloc(ehdr.e_shnum, sizeof(*sections)); + if (!sections || + !read_exact(file, sections, ehdr.e_shnum * sizeof(*sections), + (long)ehdr.e_shoff)) { + snprintf(reason, reason_size, "cannot read guest %s section table", + library_label); + goto out; + } + + host = dlopen(host_soname, RTLD_LAZY | RTLD_LOCAL); + if (!host) { + snprintf(reason, reason_size, "cannot load host %s: %s", + library_label, dlerror()); + goto out; + } + + for (size_t section_index = 0; section_index < ehdr.e_shnum; + section_index++) { + const Elf64_Shdr *dynsym = §ions[section_index]; + const Elf64_Shdr *strtab; + Elf64_Sym *symbols = NULL; + char *strings = NULL; + size_t symbol_count; + + if (dynsym->sh_type != SHT_DYNSYM) { + continue; + } + found_dynsym = true; + if (dynsym->sh_link >= ehdr.e_shnum || + dynsym->sh_entsize != sizeof(Elf64_Sym) || + dynsym->sh_size % sizeof(Elf64_Sym) || + !range_is_valid(dynsym->sh_offset, dynsym->sh_size, + file_size)) { + snprintf(reason, reason_size, + "guest %s has an invalid dynamic symbol table", + library_label); + goto out; + } + strtab = §ions[dynsym->sh_link]; + if (strtab->sh_type != SHT_STRTAB || !strtab->sh_size || + !range_is_valid(strtab->sh_offset, strtab->sh_size, + file_size)) { + snprintf(reason, reason_size, + "guest %s has an invalid dynamic string table", + library_label); + goto out; + } + symbols = malloc(dynsym->sh_size); + strings = malloc(strtab->sh_size); + if (!symbols || !strings || + !read_exact(file, symbols, dynsym->sh_size, + (long)dynsym->sh_offset) || + !read_exact(file, strings, strtab->sh_size, + (long)strtab->sh_offset)) { + free(symbols); + free(strings); + snprintf(reason, reason_size, + "cannot read guest %s dynamic symbols", library_label); + goto out; + } + + symbol_count = dynsym->sh_size / sizeof(Elf64_Sym); + for (size_t i = 0; i < symbol_count; i++) { + const Elf64_Sym *symbol = &symbols[i]; + const char *name; + unsigned type = ELF64_ST_TYPE(symbol->st_info); + unsigned binding = ELF64_ST_BIND(symbol->st_info); + + if (symbol->st_shndx == SHN_UNDEF || + (type != STT_FUNC && type != STT_GNU_IFUNC) || + (binding != STB_GLOBAL && binding != STB_WEAK) || + symbol->st_name >= strtab->sh_size) { + continue; + } + name = strings + symbol->st_name; + if (!memchr(name, '\0', strtab->sh_size - symbol->st_name)) { + free(symbols); + free(strings); + snprintf(reason, reason_size, + "guest %s has an unterminated dynamic symbol", + library_label); + goto out; + } + if (!symbol_filter(name)) { + continue; + } + found_library_symbol = true; + if (!wrapped_symbol_is_supported(name, supported_symbols, + supported_symbol_count)) { + snprintf(reason, reason_size, + "guest symbol '%s' has no safe wrapper", name); + free(symbols); + free(strings); + goto out; + } + if (!dlsym(host, name)) { + snprintf(reason, reason_size, + "host %s is missing guest symbol '%s'", + library_label, name); + free(symbols); + free(strings); + goto out; + } + } + free(symbols); + free(strings); + } + + if (!found_dynsym) { + snprintf(reason, reason_size, + "guest %s has no inspectable dynamic symbol table", + library_label); + goto out; + } + if (!found_library_symbol) { + snprintf(reason, reason_size, "guest %s exports no wrapped functions", + library_label); + goto out; + } + ok = true; + +out: + if (host) { + dlclose(host); + } + free(sections); + if (file) { + fclose(file); + } + return ok; +} diff --git a/target/i386/latx/include/wrappedlib-preflight.h b/target/i386/latx/include/wrappedlib-preflight.h new file mode 100644 index 0000000000..389308d529 --- /dev/null +++ b/target/i386/latx/include/wrappedlib-preflight.h @@ -0,0 +1,21 @@ +/* + * SPDX-FileCopyrightText: 2026 LAT Project Authors + * + * SPDX-License-Identifier: GPL-2.0-only + */ + +#ifndef LATX_WRAPPEDLIB_PREFLIGHT_H +#define LATX_WRAPPEDLIB_PREFLIGHT_H + +#include +#include + +typedef bool (*LatxWrappedSymbolFilter)(const char *name); + +bool latx_wrappedlib_preflight_guest( + const char *guest_path, const char *host_soname, + const char *library_label, LatxWrappedSymbolFilter symbol_filter, + const char *const *supported_symbols, size_t supported_symbol_count, + char *reason, size_t reason_size); + +#endif /* LATX_WRAPPEDLIB_PREFLIGHT_H */ From f800f7daec2fb6214a5cb043295d0756b82f36da Mon Sep 17 00:00:00 2001 From: Hanlu Li Date: Sat, 15 Aug 2026 08:26:00 +0800 Subject: [PATCH 02/11] LATX, feat: Add font-library ABI dispatch signatures Add the integer, floating-point, aggregate, stack-argument, and variadic dispatch shapes required by the FreeType, Fontconfig, and Xft public ABIs. The signatures were checked against the x86_64 guest headers and compiled as part of the LoongArch product build. Signed-off-by: Hanlu Li --- target/i386/latx/context/wrapper.c | 101 +++++++++++++++++++++++++++++ target/i386/latx/include/wrapper.h | 51 +++++++++++++++ 2 files changed, 152 insertions(+) diff --git a/target/i386/latx/context/wrapper.c b/target/i386/latx/context/wrapper.c index d98cbe0abf..0127e7eaeb 100644 --- a/target/i386/latx/context/wrapper.c +++ b/target/i386/latx/context/wrapper.c @@ -295,6 +295,7 @@ typedef void* (*pFEiV_t)(int32_t, void*); typedef void* (*pFEpi_t)(void*, int32_t); typedef void* (*pFEpp_t)(void*, void*); typedef void* (*pFEpV_t)(void*, void*); +typedef void* (*pFEpiV_t)(void*, int32_t, void*); typedef void* (*pFuii_t)(uint32_t, int32_t, int32_t); typedef void* (*pFpii_t)(void*, int32_t, int32_t); typedef void* (*pFpip_t)(void*, int32_t, void*); @@ -1519,6 +1520,56 @@ typedef int32_t (*iFpippu_t)(void*, int32_t, void*, void*, uint32_t); typedef uint32_t (*uFpupppp_t)(void*, uint32_t, void*, void*, void*, void*); typedef uint64_t (*UFpu_t)(void*, uint32_t); typedef int32_t (*iFpupppp_t)(void*, uint32_t, void*, void*, void*, void*); +/* FreeType, Fontconfig, and Xft public ABI signatures. */ +typedef uint8_t (*CFp_t)(void*); +typedef uint8_t (*CFpC_t)(void*, uint8_t); +typedef uintptr_t (*LFpLp_t)(void*, uintptr_t, void*); +typedef double (*dFd_t)(double); +typedef int32_t (*iFHH_t)(unsigned __int128, unsigned __int128); +typedef int32_t (*iFpLlpp_t)(void*, uintptr_t, intptr_t, void*, void*); +typedef int32_t (*iFpWp_t)(void*, uint16_t, void*); +typedef int32_t (*iFplip_t)(void*, intptr_t, int32_t, void*); +typedef int32_t (*iFpll_t)(void*, intptr_t, intptr_t); +typedef int32_t (*iFplluu_t)(void*, intptr_t, intptr_t, uint32_t, uint32_t); +typedef int32_t (*iFppC_t)(void*, void*, uint8_t); +typedef int32_t (*iFppCC_t)(void*, void*, uint8_t, uint8_t); +typedef int32_t (*iFppHi_t)(void*, void*, unsigned __int128, int32_t); +typedef int32_t (*iFppLLppu_t)(void*, void*, uintptr_t, uintptr_t, void*, void*, uint32_t); +typedef uint8_t (*CFpup_t)(void*, uint32_t, void*); +typedef uint8_t (*CFpuppp_t)(void*, uint32_t, void*, void*, void*); +typedef uint8_t (*CFpuip_t)(void*, uint32_t, int32_t, void*); +typedef int32_t (*iFppLpp_t)(void*, void*, uintptr_t, void*, void*); +typedef int32_t (*iFppll_t)(void*, void*, intptr_t, intptr_t); +typedef int32_t (*iFppllp_t)(void*, void*, intptr_t, intptr_t, void*); +typedef int32_t (*iFppppL_t)(void*, void*, void*, void*, uintptr_t); +typedef int32_t (*iFpuip_t)(void*, uint32_t, int32_t, void*); +typedef int32_t (*iFpuipp_t)(void*, uint32_t, int32_t, void*, void*); +typedef int32_t (*iFpupC_t)(void*, uint32_t, void*, uint8_t); +typedef int32_t (*iFpupu_t)(void*, uint32_t, void*, uint32_t); +typedef int32_t (*iFpuuLppp_t)(void*, uint32_t, uint32_t, uintptr_t, void*, void*, void*); +typedef int32_t (*iFpuuip_t)(void*, uint32_t, uint32_t, int32_t, void*); +typedef intptr_t (*lFl_t)(intptr_t); +typedef intptr_t (*lFll_t)(intptr_t, intptr_t); +typedef intptr_t (*lFlll_t)(intptr_t, intptr_t, intptr_t); +typedef intptr_t (*lFpiupl_t)(void*, int32_t, uint32_t, void*, intptr_t); +typedef void* (*pFdd_t)(double, double); +typedef void* (*pFpLpL_t)(void*, uintptr_t, void*, uintptr_t); +typedef void* (*pFppipipp_t)(void*, void*, int32_t, void*, int32_t, void*, void*); +typedef uint32_t (*uFpL_t)(void*, uintptr_t); +typedef uint32_t (*uFpLL_t)(void*, uintptr_t, uintptr_t); +typedef uint32_t (*uFppip_t)(void*, void*, int32_t, void*); +typedef uint32_t (*uFppiu_t)(void*, void*, int32_t, uint32_t); +typedef uint32_t (*uFppu_t)(void*, void*, uint32_t); +typedef uint32_t (*uFpuppp_t)(void*, uint32_t, void*, void*, void*); +typedef void (*vFH_t)(unsigned __int128); +typedef void (*vFpiLLiipi_t)(void*, int32_t, uintptr_t, uintptr_t, int32_t, int32_t, void*, int32_t); +typedef void (*vFpiLpLiiiipi_t)(void*, int32_t, uintptr_t, void*, uintptr_t, int32_t, int32_t, int32_t, int32_t, void*, int32_t); +typedef void (*vFpiLpLiiiipui_t)(void*, int32_t, uintptr_t, void*, uintptr_t, int32_t, int32_t, int32_t, int32_t, void*, uint32_t, int32_t); +typedef void (*vFpiLpLiipi_t)(void*, int32_t, uintptr_t, void*, uintptr_t, int32_t, int32_t, void*, int32_t); +typedef void (*vFpll_t)(void*, intptr_t, intptr_t); +typedef void (*vFpluul_t)(void*, intptr_t, uint32_t, uint32_t, intptr_t); +typedef void (*vFpppiipui_t)(void*, void*, void*, int32_t, int32_t, void*, uint32_t, int32_t); +typedef void (*vFpppuip_t)(void*, void*, void*, uint32_t, int32_t, void*); //endxcbV2 typedef void (*vFpLi_t)(void*, uintptr_t, int32_t); typedef void (*vFpLL_t)(void*, uintptr_t, uintptr_t); @@ -1827,6 +1878,7 @@ void pFEiV(uintptr_t fcn) { __CPU; pFEiV_t fn = (pFEiV_t)fcn; R_RAX=(uintptr_t) void pFEpi(uintptr_t fcn) { __CPU; pFEpi_t fn = (pFEpi_t)fcn; R_RAX=(uintptr_t)fn((void*)R_RDI, (int32_t)R_RSI); DEBUG_LOG; (void)cpu; } void pFEpp(uintptr_t fcn) { __CPU; pFEpp_t fn = (pFEpp_t)fcn; R_RAX=(uintptr_t)fn((void*)R_RDI, (void*)R_RSI); DEBUG_LOG; (void)cpu; } void pFEpV(uintptr_t fcn) { __CPU; pFEpV_t fn = (pFEpV_t)fcn; R_RAX=(uintptr_t)fn((void*)R_RDI, (void*)(R_RSP + 8)); DEBUG_LOG; (void)cpu; } +void pFEpiV(uintptr_t fcn) { __CPU; pFEpiV_t fn = (pFEpiV_t)fcn; R_RAX=(uintptr_t)fn((void*)R_RDI, (int32_t)R_RSI, (void*)(R_RSP + 8)); DEBUG_LOG; (void)cpu; } void pFuii(uintptr_t fcn) { __CPU; pFuii_t fn = (pFuii_t)fcn; R_RAX=(uintptr_t)fn((uint32_t)R_RDI, (int32_t)R_RSI, (int32_t)R_RDX); DEBUG_LOG; (void)cpu; } void pFpii(uintptr_t fcn) { __CPU; pFpii_t fn = (pFpii_t)fcn; R_RAX=(uintptr_t)fn((void*)R_RDI, (int32_t)R_RSI, (int32_t)R_RDX); DEBUG_LOG; (void)cpu; } void pFpip(uintptr_t fcn) { __CPU; pFpip_t fn = (pFpip_t)fcn; R_RAX=(uintptr_t)fn((void*)R_RDI, (int32_t)R_RSI, (void*)R_RDX); DEBUG_LOG; (void)cpu; } @@ -3159,6 +3211,55 @@ void iFpippu(uintptr_t fcn) { __CPU; iFpippu_t fn = (iFpippu_t)fcn; R_RAX=(int32 void uFpupppp(uintptr_t fcn) { __CPU; uFpupppp_t fn = (uFpupppp_t)fcn; R_RAX=(uint32_t)fn((void*)R_RDI, (uint32_t)R_RSI, (void*)R_RDX, (void*)R_RCX, (void*)R_R8, (void*)R_R9); DEBUG_LOG; (void)cpu; } void UFpu(uintptr_t fcn) { __CPU; UFpu_t fn = (UFpu_t)fcn; R_RAX=fn((void*)R_RDI, (uint32_t)R_RSI); DEBUG_LOG; (void)cpu; } void iFpupppp(uintptr_t fcn) { __CPU; iFpupppp_t fn = (iFpupppp_t)fcn; R_RAX=(int32_t)fn((void*)R_RDI, (uint32_t)R_RSI, (void*)R_RDX, (void*)R_RCX, (void*)R_R8, (void*)R_R9); DEBUG_LOG; (void)cpu; } +void CFp(uintptr_t fcn) { __CPU; CFp_t fn = (CFp_t)fcn; R_RAX=(uint8_t)fn((void*)R_RDI); DEBUG_LOG; (void)cpu; } +void CFpC(uintptr_t fcn) { __CPU; CFpC_t fn = (CFpC_t)fcn; R_RAX=(uint8_t)fn((void*)R_RDI, (uint8_t)R_RSI); DEBUG_LOG; (void)cpu; } +void LFpLp(uintptr_t fcn) { __CPU; LFpLp_t fn = (LFpLp_t)fcn; R_RAX=(uintptr_t)fn((void*)R_RDI, (uintptr_t)R_RSI, (void*)R_RDX); DEBUG_LOG; (void)cpu; } +void dFd(uintptr_t fcn) { __CPU; dFd_t fn = (dFd_t)fcn; R_XMMD(0)=fn(R_XMMD(0)); DEBUG_LOG; (void)cpu; } +void iFHH(uintptr_t fcn) { __CPU; iFHH_t fn = (iFHH_t)fcn; R_RAX=(int32_t)fn(((unsigned __int128)(uint64_t)R_RDI | ((unsigned __int128)(uint64_t)R_RSI << 64)), ((unsigned __int128)(uint64_t)R_RDX | ((unsigned __int128)(uint64_t)R_RCX << 64))); DEBUG_LOG; (void)cpu; } +void iFpLlpp(uintptr_t fcn) { __CPU; iFpLlpp_t fn = (iFpLlpp_t)fcn; R_RAX=(int32_t)fn((void*)R_RDI, (uintptr_t)R_RSI, (intptr_t)R_RDX, (void*)R_RCX, (void*)R_R8); DEBUG_LOG; (void)cpu; } +void iFpWp(uintptr_t fcn) { __CPU; iFpWp_t fn = (iFpWp_t)fcn; R_RAX=(int32_t)fn((void*)R_RDI, (uint16_t)R_RSI, (void*)R_RDX); DEBUG_LOG; (void)cpu; } +void iFplip(uintptr_t fcn) { __CPU; iFplip_t fn = (iFplip_t)fcn; R_RAX=(int32_t)fn((void*)R_RDI, (intptr_t)R_RSI, (int32_t)R_RDX, (void*)R_RCX); DEBUG_LOG; (void)cpu; } +void iFpll(uintptr_t fcn) { __CPU; iFpll_t fn = (iFpll_t)fcn; R_RAX=(int32_t)fn((void*)R_RDI, (intptr_t)R_RSI, (intptr_t)R_RDX); DEBUG_LOG; (void)cpu; } +void iFplluu(uintptr_t fcn) { __CPU; iFplluu_t fn = (iFplluu_t)fcn; R_RAX=(int32_t)fn((void*)R_RDI, (intptr_t)R_RSI, (intptr_t)R_RDX, (uint32_t)R_RCX, (uint32_t)R_R8); DEBUG_LOG; (void)cpu; } +void iFppC(uintptr_t fcn) { __CPU; iFppC_t fn = (iFppC_t)fcn; R_RAX=(int32_t)fn((void*)R_RDI, (void*)R_RSI, (uint8_t)R_RDX); DEBUG_LOG; (void)cpu; } +void iFppCC(uintptr_t fcn) { __CPU; iFppCC_t fn = (iFppCC_t)fcn; R_RAX=(int32_t)fn((void*)R_RDI, (void*)R_RSI, (uint8_t)R_RDX, (uint8_t)R_RCX); DEBUG_LOG; (void)cpu; } +void iFppHi(uintptr_t fcn) { __CPU; iFppHi_t fn = (iFppHi_t)fcn; R_RAX=(int32_t)fn((void*)R_RDI, (void*)R_RSI, ((unsigned __int128)(uint64_t)R_RDX | ((unsigned __int128)(uint64_t)R_RCX << 64)), (int32_t)R_R8); DEBUG_LOG; (void)cpu; } +void iFppLLppu(uintptr_t fcn) { __CPU; iFppLLppu_t fn = (iFppLLppu_t)fcn; R_RAX=(int32_t)fn((void*)R_RDI, (void*)R_RSI, (uintptr_t)R_RDX, (uintptr_t)R_RCX, (void*)R_R8, (void*)R_R9, (uint32_t)*(uint32_t*)(R_RSP + 8)); DEBUG_LOG; (void)cpu; } +void CFpup(uintptr_t fcn) { __CPU; CFpup_t fn = (CFpup_t)fcn; R_RAX=(uint8_t)fn((void*)R_RDI, (uint32_t)R_RSI, (void*)R_RDX); DEBUG_LOG; (void)cpu; } +void CFpuppp(uintptr_t fcn) { __CPU; CFpuppp_t fn = (CFpuppp_t)fcn; R_RAX=(uint8_t)fn((void*)R_RDI, (uint32_t)R_RSI, (void*)R_RDX, (void*)R_RCX, (void*)R_R8); DEBUG_LOG; (void)cpu; } +void CFpuip(uintptr_t fcn) { __CPU; CFpuip_t fn = (CFpuip_t)fcn; R_RAX=(uint8_t)fn((void*)R_RDI, (uint32_t)R_RSI, (int32_t)R_RDX, (void*)R_RCX); DEBUG_LOG; (void)cpu; } +void iFppLpp(uintptr_t fcn) { __CPU; iFppLpp_t fn = (iFppLpp_t)fcn; R_RAX=(int32_t)fn((void*)R_RDI, (void*)R_RSI, (uintptr_t)R_RDX, (void*)R_RCX, (void*)R_R8); DEBUG_LOG; (void)cpu; } +void iFppll(uintptr_t fcn) { __CPU; iFppll_t fn = (iFppll_t)fcn; R_RAX=(int32_t)fn((void*)R_RDI, (void*)R_RSI, (intptr_t)R_RDX, (intptr_t)R_RCX); DEBUG_LOG; (void)cpu; } +void iFppllp(uintptr_t fcn) { __CPU; iFppllp_t fn = (iFppllp_t)fcn; R_RAX=(int32_t)fn((void*)R_RDI, (void*)R_RSI, (intptr_t)R_RDX, (intptr_t)R_RCX, (void*)R_R8); DEBUG_LOG; (void)cpu; } +void iFppppL(uintptr_t fcn) { __CPU; iFppppL_t fn = (iFppppL_t)fcn; R_RAX=(int32_t)fn((void*)R_RDI, (void*)R_RSI, (void*)R_RDX, (void*)R_RCX, (uintptr_t)R_R8); DEBUG_LOG; (void)cpu; } +void iFpuip(uintptr_t fcn) { __CPU; iFpuip_t fn = (iFpuip_t)fcn; R_RAX=(int32_t)fn((void*)R_RDI, (uint32_t)R_RSI, (int32_t)R_RDX, (void*)R_RCX); DEBUG_LOG; (void)cpu; } +void iFpuipp(uintptr_t fcn) { __CPU; iFpuipp_t fn = (iFpuipp_t)fcn; R_RAX=(int32_t)fn((void*)R_RDI, (uint32_t)R_RSI, (int32_t)R_RDX, (void*)R_RCX, (void*)R_R8); DEBUG_LOG; (void)cpu; } +void iFpupC(uintptr_t fcn) { __CPU; iFpupC_t fn = (iFpupC_t)fcn; R_RAX=(int32_t)fn((void*)R_RDI, (uint32_t)R_RSI, (void*)R_RDX, (uint8_t)R_RCX); DEBUG_LOG; (void)cpu; } +void iFpupu(uintptr_t fcn) { __CPU; iFpupu_t fn = (iFpupu_t)fcn; R_RAX=(int32_t)fn((void*)R_RDI, (uint32_t)R_RSI, (void*)R_RDX, (uint32_t)R_RCX); DEBUG_LOG; (void)cpu; } +void iFpuuLppp(uintptr_t fcn) { __CPU; iFpuuLppp_t fn = (iFpuuLppp_t)fcn; R_RAX=(int32_t)fn((void*)R_RDI, (uint32_t)R_RSI, (uint32_t)R_RDX, (uintptr_t)R_RCX, (void*)R_R8, (void*)R_R9, (void*)*(void**)(R_RSP + 8)); DEBUG_LOG; (void)cpu; } +void iFpuuip(uintptr_t fcn) { __CPU; iFpuuip_t fn = (iFpuuip_t)fcn; R_RAX=(int32_t)fn((void*)R_RDI, (uint32_t)R_RSI, (uint32_t)R_RDX, (int32_t)R_RCX, (void*)R_R8); DEBUG_LOG; (void)cpu; } +void lFl(uintptr_t fcn) { __CPU; lFl_t fn = (lFl_t)fcn; R_RAX=(intptr_t)fn((intptr_t)R_RDI); DEBUG_LOG; (void)cpu; } +void lFll(uintptr_t fcn) { __CPU; lFll_t fn = (lFll_t)fcn; R_RAX=(intptr_t)fn((intptr_t)R_RDI, (intptr_t)R_RSI); DEBUG_LOG; (void)cpu; } +void lFlll(uintptr_t fcn) { __CPU; lFlll_t fn = (lFlll_t)fcn; R_RAX=(intptr_t)fn((intptr_t)R_RDI, (intptr_t)R_RSI, (intptr_t)R_RDX); DEBUG_LOG; (void)cpu; } +void lFpiupl(uintptr_t fcn) { __CPU; lFpiupl_t fn = (lFpiupl_t)fcn; R_RAX=(intptr_t)fn((void*)R_RDI, (int32_t)R_RSI, (uint32_t)R_RDX, (void*)R_RCX, (intptr_t)R_R8); DEBUG_LOG; (void)cpu; } +void pFdd(uintptr_t fcn) { __CPU; pFdd_t fn = (pFdd_t)fcn; R_RAX=(uintptr_t)fn(R_XMMD(0), R_XMMD(1)); DEBUG_LOG; (void)cpu; } +void pFpLpL(uintptr_t fcn) { __CPU; pFpLpL_t fn = (pFpLpL_t)fcn; R_RAX=(uintptr_t)fn((void*)R_RDI, (uintptr_t)R_RSI, (void*)R_RDX, (uintptr_t)R_RCX); DEBUG_LOG; (void)cpu; } +void pFppipipp(uintptr_t fcn) { __CPU; pFppipipp_t fn = (pFppipipp_t)fcn; R_RAX=(uintptr_t)fn((void*)R_RDI, (void*)R_RSI, (int32_t)R_RDX, (void*)R_RCX, (int32_t)R_R8, (void*)R_R9, (void*)*(void**)(R_RSP + 8)); DEBUG_LOG; (void)cpu; } +void uFpL(uintptr_t fcn) { __CPU; uFpL_t fn = (uFpL_t)fcn; R_RAX=(uint32_t)fn((void*)R_RDI, (uintptr_t)R_RSI); DEBUG_LOG; (void)cpu; } +void uFpLL(uintptr_t fcn) { __CPU; uFpLL_t fn = (uFpLL_t)fcn; R_RAX=(uint32_t)fn((void*)R_RDI, (uintptr_t)R_RSI, (uintptr_t)R_RDX); DEBUG_LOG; (void)cpu; } +void uFppip(uintptr_t fcn) { __CPU; uFppip_t fn = (uFppip_t)fcn; R_RAX=(uint32_t)fn((void*)R_RDI, (void*)R_RSI, (int32_t)R_RDX, (void*)R_RCX); DEBUG_LOG; (void)cpu; } +void uFppiu(uintptr_t fcn) { __CPU; uFppiu_t fn = (uFppiu_t)fcn; R_RAX=(uint32_t)fn((void*)R_RDI, (void*)R_RSI, (int32_t)R_RDX, (uint32_t)R_RCX); DEBUG_LOG; (void)cpu; } +void uFppu(uintptr_t fcn) { __CPU; uFppu_t fn = (uFppu_t)fcn; R_RAX=(uint32_t)fn((void*)R_RDI, (void*)R_RSI, (uint32_t)R_RDX); DEBUG_LOG; (void)cpu; } +void uFpuppp(uintptr_t fcn) { __CPU; uFpuppp_t fn = (uFpuppp_t)fcn; R_RAX=(uint32_t)fn((void*)R_RDI, (uint32_t)R_RSI, (void*)R_RDX, (void*)R_RCX, (void*)R_R8); DEBUG_LOG; (void)cpu; } +void vFH(uintptr_t fcn) { __CPU; vFH_t fn = (vFH_t)fcn; fn(((unsigned __int128)(uint64_t)R_RDI | ((unsigned __int128)(uint64_t)R_RSI << 64))); DEBUG_LOG; (void)cpu; } +void vFpiLLiipi(uintptr_t fcn) { __CPU; vFpiLLiipi_t fn = (vFpiLLiipi_t)fcn; fn((void*)R_RDI, (int32_t)R_RSI, (uintptr_t)R_RDX, (uintptr_t)R_RCX, (int32_t)R_R8, (int32_t)R_R9, (void*)*(void**)(R_RSP + 8), (int32_t)*(int32_t*)(R_RSP + 16)); DEBUG_LOG; (void)cpu; } +void vFpiLpLiiiipi(uintptr_t fcn) { __CPU; vFpiLpLiiiipi_t fn = (vFpiLpLiiiipi_t)fcn; fn((void*)R_RDI, (int32_t)R_RSI, (uintptr_t)R_RDX, (void*)R_RCX, (uintptr_t)R_R8, (int32_t)R_R9, (int32_t)*(int32_t*)(R_RSP + 8), (int32_t)*(int32_t*)(R_RSP + 16), (int32_t)*(int32_t*)(R_RSP + 24), (void*)*(void**)(R_RSP + 32), (int32_t)*(int32_t*)(R_RSP + 40)); DEBUG_LOG; (void)cpu; } +void vFpiLpLiiiipui(uintptr_t fcn) { __CPU; vFpiLpLiiiipui_t fn = (vFpiLpLiiiipui_t)fcn; fn((void*)R_RDI, (int32_t)R_RSI, (uintptr_t)R_RDX, (void*)R_RCX, (uintptr_t)R_R8, (int32_t)R_R9, (int32_t)*(int32_t*)(R_RSP + 8), (int32_t)*(int32_t*)(R_RSP + 16), (int32_t)*(int32_t*)(R_RSP + 24), (void*)*(void**)(R_RSP + 32), (uint32_t)*(uint32_t*)(R_RSP + 40), (int32_t)*(int32_t*)(R_RSP + 48)); DEBUG_LOG; (void)cpu; } +void vFpiLpLiipi(uintptr_t fcn) { __CPU; vFpiLpLiipi_t fn = (vFpiLpLiipi_t)fcn; fn((void*)R_RDI, (int32_t)R_RSI, (uintptr_t)R_RDX, (void*)R_RCX, (uintptr_t)R_R8, (int32_t)R_R9, (int32_t)*(int32_t*)(R_RSP + 8), (void*)*(void**)(R_RSP + 16), (int32_t)*(int32_t*)(R_RSP + 24)); DEBUG_LOG; (void)cpu; } +void vFpll(uintptr_t fcn) { __CPU; vFpll_t fn = (vFpll_t)fcn; fn((void*)R_RDI, (intptr_t)R_RSI, (intptr_t)R_RDX); DEBUG_LOG; (void)cpu; } +void vFpluul(uintptr_t fcn) { __CPU; vFpluul_t fn = (vFpluul_t)fcn; fn((void*)R_RDI, (intptr_t)R_RSI, (uint32_t)R_RDX, (uint32_t)R_RCX, (intptr_t)R_R8); DEBUG_LOG; (void)cpu; } +void vFpppiipui(uintptr_t fcn) { __CPU; vFpppiipui_t fn = (vFpppiipui_t)fcn; fn((void*)R_RDI, (void*)R_RSI, (void*)R_RDX, (int32_t)R_RCX, (int32_t)R_R8, (void*)R_R9, (uint32_t)*(uint32_t*)(R_RSP + 8), (int32_t)*(int32_t*)(R_RSP + 16)); DEBUG_LOG; (void)cpu; } +void vFpppuip(uintptr_t fcn) { __CPU; vFpppuip_t fn = (vFpppuip_t)fcn; fn((void*)R_RDI, (void*)R_RSI, (void*)R_RDX, (uint32_t)R_RCX, (int32_t)R_R8, (void*)R_R9); DEBUG_LOG; (void)cpu; } //xcbV2end #undef R_RAX #undef R_RDI diff --git a/target/i386/latx/include/wrapper.h b/target/i386/latx/include/wrapper.h index 0b4eea23c7..889a784667 100644 --- a/target/i386/latx/include/wrapper.h +++ b/target/i386/latx/include/wrapper.h @@ -214,6 +214,7 @@ void pFEiV(uintptr_t fnc); void pFEpi(uintptr_t fnc); void pFEpp(uintptr_t fnc); void pFEpV(uintptr_t fnc); +void pFEpiV(uintptr_t fnc); void pFuii(uintptr_t fnc); void pFpii(uintptr_t fnc); void pFpip(uintptr_t fnc); @@ -1556,5 +1557,55 @@ void iFpippu(uintptr_t fcn); void uFpupppp(uintptr_t fcn); void UFpu(uintptr_t fcn); void iFpupppp(uintptr_t fcn); +/* FreeType, Fontconfig, and Xft public ABI signatures. */ +void CFp(uintptr_t fnc); +void CFpC(uintptr_t fnc); +void LFpLp(uintptr_t fnc); +void dFd(uintptr_t fnc); +void iFHH(uintptr_t fnc); +void iFpLlpp(uintptr_t fnc); +void iFpWp(uintptr_t fnc); +void iFplip(uintptr_t fnc); +void iFpll(uintptr_t fnc); +void iFplluu(uintptr_t fnc); +void iFppC(uintptr_t fnc); +void iFppCC(uintptr_t fnc); +void iFppHi(uintptr_t fnc); +void iFppLLppu(uintptr_t fnc); +void CFpup(uintptr_t fnc); +void CFpuppp(uintptr_t fnc); +void CFpuip(uintptr_t fnc); +void iFppLpp(uintptr_t fnc); +void iFppll(uintptr_t fnc); +void iFppllp(uintptr_t fnc); +void iFppppL(uintptr_t fnc); +void iFpuip(uintptr_t fnc); +void iFpuipp(uintptr_t fnc); +void iFpupC(uintptr_t fnc); +void iFpupu(uintptr_t fnc); +void iFpuuLppp(uintptr_t fnc); +void iFpuuip(uintptr_t fnc); +void lFl(uintptr_t fnc); +void lFll(uintptr_t fnc); +void lFlll(uintptr_t fnc); +void lFpiupl(uintptr_t fnc); +void pFdd(uintptr_t fnc); +void pFpLpL(uintptr_t fnc); +void pFppipipp(uintptr_t fnc); +void uFpL(uintptr_t fnc); +void uFpLL(uintptr_t fnc); +void uFppip(uintptr_t fnc); +void uFppiu(uintptr_t fnc); +void uFppu(uintptr_t fnc); +void uFpuppp(uintptr_t fnc); +void vFH(uintptr_t fnc); +void vFpiLLiipi(uintptr_t fnc); +void vFpiLpLiiiipi(uintptr_t fnc); +void vFpiLpLiiiipui(uintptr_t fnc); +void vFpiLpLiipi(uintptr_t fnc); +void vFpll(uintptr_t fnc); +void vFpluul(uintptr_t fnc); +void vFpppiipui(uintptr_t fnc); +void vFpppuip(uintptr_t fnc); //endxcbV2 #endif // __WRAPPER_H_ From 780f89699efca7246e4377e7e4b0d8fad76fd3af Mon Sep 17 00:00:00 2001 From: Hanlu Li Date: Sat, 15 Aug 2026 08:26:28 +0800 Subject: [PATCH 03/11] LATX, feat: Add experimental font wrapper family Add complete guest export catalogs for FreeType, Fontconfig, and Xft, and register them as one experimental font group. A whole-family preflight requires matching guest exports and host symbols before any member is activated. Handle x86_64 variadics and by-value aggregates explicitly. Bridge synchronous FreeType outline and list callbacks, reject unsupported callback-bearing APIs without passing guest addresses to host code, and clear wrapper tracking state on unload. Xft variadic entry points are rebuilt through native Fontconfig and Xft APIs. The implementation is derived in part from Box64's MIT-licensed wrappers with attribution retained, but corrects signatures against public headers and avoids its fixed-slot and global-stream fallback hazards. Validated with a full LoongArch latx-x86_64 product build, direct FreeType/Fontconfig/Xft probes, a FreeType callback round trip, atomic preflight rejection, and real-display gtkperf. Signed-off-by: Hanlu Li --- target/i386/latx/context/meson.build | 3 + .../i386/latx/context/wrappedfont-preflight.c | 133 +++ target/i386/latx/context/wrappedfontconfig.c | 332 ++++++ target/i386/latx/context/wrappedfreetype.c | 959 ++++++++++++++++++ target/i386/latx/context/wrappedlibxft.c | 121 ++- .../include/generated/wrappedfontconfigdefs.h | 14 + .../generated/wrappedfontconfigtypes.h | 36 + .../generated/wrappedfontconfigundefs.h | 11 + .../include/generated/wrappedfreetypedefs.h | 22 + .../include/generated/wrappedfreetypetypes.h | 60 ++ .../include/generated/wrappedfreetypeundefs.h | 21 + .../include/generated/wrappedlibxfttypes.h | 7 +- target/i386/latx/include/kzt-group-list.h | 1 + target/i386/latx/include/library_list.h | 8 +- .../i386/latx/include/wrappedfont-preflight.h | 20 + target/i386/latx/include/wrappedfont-vaargs.h | 98 ++ .../latx/include/wrappedfontconfig_private.h | 251 +++++ .../latx/include/wrappedfreetype_private.h | 235 +++++ .../i386/latx/include/wrappedlibxft_private.h | 137 +-- 19 files changed, 2383 insertions(+), 86 deletions(-) create mode 100644 target/i386/latx/context/wrappedfont-preflight.c create mode 100644 target/i386/latx/context/wrappedfontconfig.c create mode 100644 target/i386/latx/context/wrappedfreetype.c create mode 100644 target/i386/latx/include/generated/wrappedfontconfigdefs.h create mode 100644 target/i386/latx/include/generated/wrappedfontconfigtypes.h create mode 100644 target/i386/latx/include/generated/wrappedfontconfigundefs.h create mode 100644 target/i386/latx/include/generated/wrappedfreetypedefs.h create mode 100644 target/i386/latx/include/generated/wrappedfreetypetypes.h create mode 100644 target/i386/latx/include/generated/wrappedfreetypeundefs.h create mode 100644 target/i386/latx/include/wrappedfont-preflight.h create mode 100644 target/i386/latx/include/wrappedfont-vaargs.h create mode 100644 target/i386/latx/include/wrappedfontconfig_private.h create mode 100644 target/i386/latx/include/wrappedfreetype_private.h diff --git a/target/i386/latx/context/meson.build b/target/i386/latx/context/meson.build index 9bf8ef94e1..92a506ecf0 100644 --- a/target/i386/latx/context/meson.build +++ b/target/i386/latx/context/meson.build @@ -27,6 +27,7 @@ my_file = files( 'wrappedlibxcbxinput.c', 'wrappedlib-preflight.c', 'wrappedcairo-preflight.c', + 'wrappedfont-preflight.c', 'wrappedcairo.c', 'wrappedlibx11xcb.c', 'wrappedxkbcommon.c', @@ -37,6 +38,8 @@ my_file = files( 'wrappedlibxcomposite.c', 'wrappedlibxrender.c', 'wrappedlibxft.c', + 'wrappedfontconfig.c', + 'wrappedfreetype.c', 'wrappedlibxtst.c', 'wrappedlibxt.c', 'wrappedlibxss.c', diff --git a/target/i386/latx/context/wrappedfont-preflight.c b/target/i386/latx/context/wrappedfont-preflight.c new file mode 100644 index 0000000000..ffa6551b09 --- /dev/null +++ b/target/i386/latx/context/wrappedfont-preflight.c @@ -0,0 +1,133 @@ +/* + * SPDX-FileCopyrightText: 2026 LAT Project Authors + * + * SPDX-License-Identifier: GPL-2.0-only + */ + +#include "qemu/osdep.h" + +#include + +#include "debug.h" +#include "fileutils.h" +#include "kzt-groups.h" +#include "wrappedfont-preflight.h" +#include "wrappedlib-preflight.h" + +#define GO(name, signature) #name, +#define GOM(name, signature) #name, +#define GOW(name, signature) #name, +#define GOWM(name, signature) #name, +#define GO2(name, signature, alias) #name, +#define GOS(name, signature) #name, +#define DATA(name, size) +#define DATAV(name, size) +#define DATAB(name, size) +#define DATAM(name, size) +static const char *const freetype_supported_symbols[] = { +#include "wrappedfreetype_private.h" +}; +static const char *const fontconfig_supported_symbols[] = { +#include "wrappedfontconfig_private.h" +}; +static const char *const libxft_supported_symbols[] = { +#include "wrappedlibxft_private.h" +}; +#undef GO +#undef GOM +#undef GOW +#undef GOWM +#undef GO2 +#undef GOS +#undef DATA +#undef DATAV +#undef DATAB +#undef DATAM + +typedef struct FontLibraryPreflight { + const char *guest_soname; + const char *host_soname; + const char *label; + LatxWrappedSymbolFilter symbol_filter; + const char *const *supported_symbols; + size_t supported_symbol_count; +} FontLibraryPreflight; + +static bool freetype_symbol_filter(const char *name) +{ + return !strncmp(name, "FT_", 3) || !strncmp(name, "FTC_", 4) || + !strncmp(name, "TT_", 3); +} + +static bool fontconfig_symbol_filter(const char *name) +{ + return !strncmp(name, "Fc", 2); +} + +static bool xft_symbol_filter(const char *name) +{ + return !strncmp(name, "Xft", 3); +} + +bool latx_font_preflight_guest(path_collection_t *guest_paths, + char *reason, size_t reason_size) +{ + static const FontLibraryPreflight libraries[] = { + { + .guest_soname = "libfreetype.so.6", + .host_soname = "libfreetype.so.6", + .label = "FreeType", + .symbol_filter = freetype_symbol_filter, + .supported_symbols = freetype_supported_symbols, + .supported_symbol_count = ARRAY_SIZE(freetype_supported_symbols), + }, + { + .guest_soname = "libfontconfig.so.1", + .host_soname = "libfontconfig.so.1", + .label = "Fontconfig", + .symbol_filter = fontconfig_symbol_filter, + .supported_symbols = fontconfig_supported_symbols, + .supported_symbol_count = ARRAY_SIZE(fontconfig_supported_symbols), + }, + { + .guest_soname = "libXft.so.2", + .host_soname = "libXft.so.2", + .label = "Xft", + .symbol_filter = xft_symbol_filter, + .supported_symbols = libxft_supported_symbols, + .supported_symbol_count = ARRAY_SIZE(libxft_supported_symbols), + }, + }; + + if (!guest_paths || !reason || !reason_size) { + return false; + } + reason[0] = '\0'; + for (size_t i = 0; i < ARRAY_SIZE(libraries); i++) { + const FontLibraryPreflight *library = &libraries[i]; + char *guest_path = ResolveFile(library->guest_soname, guest_paths); + bool safe = latx_wrappedlib_preflight_guest( + guest_path, library->host_soname, library->label, + library->symbol_filter, library->supported_symbols, + library->supported_symbol_count, reason, reason_size); + + box_free(guest_path); + if (!safe) { + return false; + } + } + return true; +} + +int latx_font_preflight_or_disable(path_collection_t *guest_paths, + const char *requesting_soname) +{ + char reason[256]; + + if (latx_font_preflight_guest(guest_paths, reason, sizeof(reason))) { + return 0; + } + kzt_groups_log_wrapper_rejection(requesting_soname, reason); + kzt_group_disable(KZT_GROUP_FONT, reason); + return -1; +} diff --git a/target/i386/latx/context/wrappedfontconfig.c b/target/i386/latx/context/wrappedfontconfig.c new file mode 100644 index 0000000000..6bb3ab0655 --- /dev/null +++ b/target/i386/latx/context/wrappedfontconfig.c @@ -0,0 +1,332 @@ +/* + * This file is derived from Box64. + * + * SPDX-FileCopyrightText: 2020 ptitSeb + * SPDX-FileCopyrightText: 2026 LAT Project Authors + * + * SPDX-License-Identifier: MIT + */ + +#include "qemu/osdep.h" + +#include +#include +#include +#include + +#include "wrappedlibs.h" + +#include "box64context.h" +#include "bridge.h" +#include "callback.h" +#include "debug.h" +#include "kzt-groups.h" +#include "library_private.h" +#include "wrappedfont-vaargs.h" +#include "wrappedfont-preflight.h" +#include "wrapper.h" + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wmissing-prototypes" + +const char *fontconfigName = "libfontconfig.so.1"; +#define LIBNAME fontconfig + +typedef union FcValueUnionAbi { + const char *string; + int32_t integer; + int32_t boolean; + double real; + const void *pointer; +} FcValueUnionAbi; + +typedef struct FcValueAbi { + int32_t type; + FcValueUnionAbi value; +} FcValueAbi; + +_Static_assert(sizeof(FcValueAbi) == 16, "FcValue ABI"); + +typedef void *(*fc_pFv_t)(void); +typedef void *(*fc_pFp_t)(void *); +typedef void *(*fc_pFpp_t)(void *, void *); +typedef int32_t (*fc_iFpp_t)(void *, void *); +typedef int32_t (*fc_iFppi_t)(void *, void *, int32_t); +typedef int32_t (*fc_iFppd_t)(void *, void *, double); +typedef int32_t (*fc_iFppp_t)(void *, void *, void *); +typedef void (*fc_vFp_t)(void *); + +#define ADDED_FUNCTIONS() \ + GO(FcObjectSetAdd, fc_iFpp_t) \ + GO(FcObjectSetCreate, fc_pFv_t) \ + GO(FcObjectSetDestroy, fc_vFp_t) \ + GO(FcPatternAddBool, fc_iFppi_t) \ + GO(FcPatternAddCharSet, fc_iFppp_t) \ + GO(FcPatternAddDouble, fc_iFppd_t) \ + GO(FcPatternAddFTFace, fc_iFppp_t) \ + GO(FcPatternAddInteger, fc_iFppi_t) \ + GO(FcPatternAddLangSet, fc_iFppp_t) \ + GO(FcPatternAddMatrix, fc_iFppp_t) \ + GO(FcPatternAddRange, fc_iFppp_t) \ + GO(FcPatternAddString, fc_iFppp_t) \ + GO(FcPatternCreate, fc_pFv_t) \ + GO(FcPatternDestroy, fc_vFp_t) \ + GO(FcStrCopy, fc_pFp_t) + +#include "generated/wrappedfontconfigtypes.h" + +#include "wrappercallback.h" + +enum { + FC_TYPE_VOID = 0, + FC_TYPE_INTEGER = 1, + FC_TYPE_DOUBLE = 2, + FC_TYPE_STRING = 3, + FC_TYPE_BOOL = 4, + FC_TYPE_MATRIX = 5, + FC_TYPE_CHARSET = 6, + FC_TYPE_FT_FACE = 7, + FC_TYPE_LANGSET = 8, + FC_TYPE_RANGE = 9, +}; + +static bool fontconfig_pattern_add_value(void *pattern, const char *object, + int32_t type, + LatxX64VaReader *reader) +{ + switch (type) { + case FC_TYPE_INTEGER: + return my->FcPatternAddInteger( + pattern, (void *)object, (int32_t)latx_x64_va_gp(reader)); + case FC_TYPE_DOUBLE: + return my->FcPatternAddDouble(pattern, (void *)object, + latx_x64_va_double(reader)); + case FC_TYPE_STRING: + return my->FcPatternAddString( + pattern, (void *)object, + (void *)(uintptr_t)latx_x64_va_gp(reader)); + case FC_TYPE_BOOL: + return my->FcPatternAddBool( + pattern, (void *)object, (int32_t)latx_x64_va_gp(reader)); + case FC_TYPE_MATRIX: + return my->FcPatternAddMatrix( + pattern, (void *)object, + (void *)(uintptr_t)latx_x64_va_gp(reader)); + case FC_TYPE_CHARSET: + return my->FcPatternAddCharSet( + pattern, (void *)object, + (void *)(uintptr_t)latx_x64_va_gp(reader)); + case FC_TYPE_FT_FACE: + return my->FcPatternAddFTFace( + pattern, (void *)object, + (void *)(uintptr_t)latx_x64_va_gp(reader)); + case FC_TYPE_LANGSET: + return my->FcPatternAddLangSet( + pattern, (void *)object, + (void *)(uintptr_t)latx_x64_va_gp(reader)); + case FC_TYPE_RANGE: + return my->FcPatternAddRange( + pattern, (void *)object, + (void *)(uintptr_t)latx_x64_va_gp(reader)); + default: + kzt_groups_log_wrapper_limitation( + fontconfigName, "unsupported FcPatternBuild value type"); + return false; + } +} + +void *latx_fontconfig_pattern_build(LatxX64VaReader *reader, + void *pattern) +{ + bool created = pattern == NULL; + + if (created) { + pattern = my->FcPatternCreate(); + if (!pattern) { + return NULL; + } + } + for (;;) { + const char *object = + (const char *)(uintptr_t)latx_x64_va_gp(reader); + int32_t type; + + if (!object) { + return pattern; + } + type = (int32_t)latx_x64_va_gp(reader); + if (type == FC_TYPE_VOID || + !fontconfig_pattern_add_value(pattern, object, type, reader)) { + if (created) { + my->FcPatternDestroy(pattern); + } + return NULL; + } + } +} + +void *latx_fontconfig_object_set_build(LatxX64VaReader *reader, + const char *first) +{ + void *set = my->FcObjectSetCreate(); + const char *object = first; + + if (!set) { + return NULL; + } + while (object) { + if (!my->FcObjectSetAdd(set, (void *)object)) { + my->FcObjectSetDestroy(set); + return NULL; + } + object = (const char *)(uintptr_t)latx_x64_va_gp(reader); + } + return set; +} + +EXPORT void *my_FcPatternBuild(void *pattern, void *stack) +{ + LatxX64VaReader reader; + + (void)stack; + latx_x64_va_reader_live(&reader, 1); + return latx_fontconfig_pattern_build(&reader, pattern); +} + +EXPORT void *my_FcPatternVaBuild(void *pattern, x64_va_list_t list) +{ + LatxX64VaReader reader; + + latx_x64_va_reader_list(&reader, list); + return latx_fontconfig_pattern_build(&reader, pattern); +} + +EXPORT void *my_FcObjectSetBuild(void *first, void *stack) +{ + LatxX64VaReader reader; + + (void)stack; + latx_x64_va_reader_live(&reader, 1); + return latx_fontconfig_object_set_build(&reader, first); +} + +EXPORT void *my_FcObjectSetVaBuild(void *first, x64_va_list_t list) +{ + LatxX64VaReader reader; + + latx_x64_va_reader_list(&reader, list); + return latx_fontconfig_object_set_build(&reader, first); +} + +EXPORT int32_t my_FcPatternAdd(void *pattern, const char *object, + unsigned __int128 bits, int32_t append) +{ + FcValueAbi value; + + memcpy(&value, &bits, sizeof(value)); + return my->FcPatternAdd(pattern, (void *)object, value, append); +} + +EXPORT int32_t my_FcPatternAddWeak(void *pattern, const char *object, + unsigned __int128 bits, int32_t append) +{ + FcValueAbi value; + + memcpy(&value, &bits, sizeof(value)); + return my->FcPatternAddWeak(pattern, (void *)object, value, append); +} + +EXPORT void my_FcValueDestroy(unsigned __int128 bits) +{ + FcValueAbi value; + + memcpy(&value, &bits, sizeof(value)); + my->FcValueDestroy(value); +} + +EXPORT int32_t my_FcValueEqual(unsigned __int128 left_bits, + unsigned __int128 right_bits) +{ + FcValueAbi left; + FcValueAbi right; + + memcpy(&left, &left_bits, sizeof(left)); + memcpy(&right, &right_bits, sizeof(right)); + return my->FcValueEqual(left, right); +} + +EXPORT void my_FcValuePrint(unsigned __int128 bits) +{ + FcValueAbi value; + + memcpy(&value, &bits, sizeof(value)); + my->FcValuePrint(value); +} + +EXPORT unsigned __int128 my_FcValueSave(unsigned __int128 bits) +{ + FcValueAbi value; + FcValueAbi result; + unsigned __int128 result_bits; + + memcpy(&value, &bits, sizeof(value)); + result = my->FcValueSave(value); + memcpy(&result_bits, &result, sizeof(result_bits)); + return result_bits; +} + +EXPORT int32_t my_FcScandir(void *directory, void *namelist, + void *select_callback, void *compare_callback) +{ + void *native_select = GetNativeFnc((uintptr_t)select_callback); + void *native_compare = GetNativeFnc((uintptr_t)compare_callback); + + if ((select_callback && !native_select) || + (compare_callback && !native_compare)) { + kzt_groups_log_wrapper_limitation( + fontconfigName, "FcScandir guest callbacks are unsupported"); + return -1; + } + return my->FcScandir(directory, namelist, native_select, native_compare); +} + +EXPORT void *my_FcStrBuildFilename(const char *first, void *stack) +{ + LatxX64VaReader reader; + GString *path; + const char *part; + void *result; + + (void)stack; + if (!first) { + return NULL; + } + latx_x64_va_reader_live(&reader, 1); + path = g_string_new(first); + while ((part = (const char *)(uintptr_t)latx_x64_va_gp(&reader))) { + g_string_append_c(path, '/'); + g_string_append(path, part); + } + result = my->FcStrCopy(path->str); + g_string_free(path, true); + return result; +} + +#define PRE_INIT_GUEST \ + do { \ + if (latx_font_preflight_or_disable(&box64->box64_ld_lib, \ + fontconfigName) != 0) { \ + return -1; \ + } \ + } while (0); + +#define CUSTOM_INIT \ + getMy(lib); \ + setNeededLibs(lib, 2, "libexpat.so.1", "libfreetype.so.6"); + +#define CUSTOM_FINI \ + freeMy(); + +#include "wrappedlib_init.h" + +#pragma GCC diagnostic pop diff --git a/target/i386/latx/context/wrappedfreetype.c b/target/i386/latx/context/wrappedfreetype.c new file mode 100644 index 0000000000..eaad9c6f10 --- /dev/null +++ b/target/i386/latx/context/wrappedfreetype.c @@ -0,0 +1,959 @@ +/* + * This file is derived from Box64. + * + * SPDX-FileCopyrightText: 2020 ptitSeb + * SPDX-FileCopyrightText: 2026 LAT Project Authors + * + * SPDX-License-Identifier: MIT + */ + +#include "qemu/osdep.h" + +#include +#include +#include +#include +#include + +#include "wrappedlibs.h" + +#include "box64context.h" +#include "bridge.h" +#include "callback.h" +#include "debug.h" +#include "kzt-groups.h" +#include "library_private.h" +#include "wrappedfont-preflight.h" +#include "wrapper.h" + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wmissing-prototypes" + +const char *freetypeName = "libfreetype.so.6"; +#define LIBNAME freetype + +#define PRE_INIT_GUEST \ + do { \ + if (latx_font_preflight_or_disable(&box64->box64_ld_lib, \ + freetypeName) != 0) { \ + return -1; \ + } \ + } while (0); + +typedef struct FtVectorAbi { + intptr_t x; + intptr_t y; +} FtVectorAbi; + +typedef struct FtColorAbi { + uint8_t blue; + uint8_t green; + uint8_t red; + uint8_t alpha; +} FtColorAbi; + +typedef struct FtOpaquePaintAbi { + uint8_t *paint; + int32_t insert_root_transform; +} FtOpaquePaintAbi; + +typedef struct FtGenericAbi { + void *data; + void *finalizer; +} FtGenericAbi; + +typedef struct FtBBoxAbi { + intptr_t x_min; + intptr_t y_min; + intptr_t x_max; + intptr_t y_max; +} FtBBoxAbi; + +typedef struct FtFaceAbi { + intptr_t num_faces; + intptr_t face_index; + intptr_t face_flags; + intptr_t style_flags; + intptr_t num_glyphs; + char *family_name; + char *style_name; + int32_t num_fixed_sizes; + void *available_sizes; + int32_t num_charmaps; + void *charmaps; + FtGenericAbi generic; + FtBBoxAbi bbox; + uint16_t units_per_em; + int16_t ascender; + int16_t descender; + int16_t height; + int16_t max_advance_width; + int16_t max_advance_height; + int16_t underline_position; + int16_t underline_thickness; + void *glyph; + void *size; + void *charmap; +} FtFaceAbi; + +typedef struct FtGlyphSlotAbi { + void *library; + void *face; + void *next; + uint32_t glyph_index; + FtGenericAbi generic; +} FtGlyphSlotAbi; + +typedef struct FtSizeAbi { + void *face; + FtGenericAbi generic; +} FtSizeAbi; + +typedef union FtStreamDescAbi { + intptr_t value; + void *pointer; +} FtStreamDescAbi; + +typedef struct FtStreamAbi { + uint8_t *base; + uintptr_t size; + uintptr_t pos; + FtStreamDescAbi descriptor; + FtStreamDescAbi pathname; + void *read; + void *close; + void *memory; + uint8_t *cursor; + uint8_t *limit; +} FtStreamAbi; + +typedef struct FtOpenArgsAbi { + uint32_t flags; + const uint8_t *memory_base; + intptr_t memory_size; + char *pathname; + FtStreamAbi *stream; + void *driver; + int32_t num_params; + void *params; +} FtOpenArgsAbi; + +typedef struct FtOutlineFuncsAbi { + void *move_to; + void *line_to; + void *conic_to; + void *cubic_to; + int32_t shift; + intptr_t delta; +} FtOutlineFuncsAbi; + +typedef struct FtRasterParamsAbi { + const void *target; + const void *source; + int32_t flags; + void *gray_spans; + void *black_spans; + void *bit_test; + void *bit_set; + void *user; + FtBBoxAbi clip_box; +} FtRasterParamsAbi; + +_Static_assert(sizeof(FtVectorAbi) == 16, "FT_Vector ABI"); +_Static_assert(sizeof(FtColorAbi) == 4, "FT_Color ABI"); +_Static_assert(sizeof(FtOpaquePaintAbi) == 16, "FT_OpaquePaint ABI"); +_Static_assert(offsetof(FtFaceAbi, generic) == 88, "FT_Face generic ABI"); +_Static_assert(offsetof(FtFaceAbi, glyph) == 152, "FT_Face glyph ABI"); +_Static_assert(offsetof(FtGlyphSlotAbi, generic) == 32, + "FT_GlyphSlot generic ABI"); +_Static_assert(offsetof(FtSizeAbi, generic) == 8, "FT_Size generic ABI"); +_Static_assert(offsetof(FtRasterParamsAbi, gray_spans) == 24, + "FT_Raster_Params callback ABI"); +_Static_assert(sizeof(FtRasterParamsAbi) == 96, + "FT_Raster_Params ABI"); + +#include "generated/wrappedfreetypetypes.h" + +#include "wrappercallback.h" + +enum { + FT_ERROR_SUCCESS = 0, + FT_ERROR_INVALID_ARGUMENT = 0x06, + FT_ERROR_UNIMPLEMENTED_FEATURE = 0x07, + FT_ERROR_OUT_OF_MEMORY = 0x40, + FT_OPEN_STREAM = 0x02, +}; + +typedef struct FtFaceTracker { + void *face; + void *library; + unsigned references; + struct FtFaceTracker *next; +} FtFaceTracker; + +typedef struct FtSizeTracker { + void *size; + void *face; + struct FtSizeTracker *next; +} FtSizeTracker; + +static GMutex freetype_lock; +static FtFaceTracker *freetype_faces; +static FtSizeTracker *freetype_sizes; + +static bool freetype_generic_is_safe(const FtGenericAbi *generic) +{ + return !generic || !generic->finalizer || + GetNativeFnc((uintptr_t)generic->finalizer) != NULL; +} + +static bool freetype_face_is_safe(void *face) +{ + FtFaceAbi *record = face; + + if (!record || !freetype_generic_is_safe(&record->generic)) { + return record == NULL; + } + if (record->glyph && + !freetype_generic_is_safe( + &((FtGlyphSlotAbi *)record->glyph)->generic)) { + return false; + } + if (record->size && + !freetype_generic_is_safe(&((FtSizeAbi *)record->size)->generic)) { + return false; + } + for (FtSizeTracker *size = freetype_sizes; size; size = size->next) { + if (size->face == face && + !freetype_generic_is_safe(&((FtSizeAbi *)size->size)->generic)) { + return false; + } + } + return true; +} + +static bool freetype_library_is_safe(void *library) +{ + for (FtFaceTracker *face = freetype_faces; face; face = face->next) { + if (face->library == library && !freetype_face_is_safe(face->face)) { + return false; + } + } + return true; +} + +static bool freetype_track_face(void *library, void *face) +{ + FtFaceTracker *tracker = calloc(1, sizeof(*tracker)); + + if (!tracker) { + return false; + } + tracker->library = library; + tracker->face = face; + tracker->references = 1; + g_mutex_lock(&freetype_lock); + tracker->next = freetype_faces; + freetype_faces = tracker; + g_mutex_unlock(&freetype_lock); + return true; +} + +static bool freetype_track_size(void *face, void *size) +{ + FtSizeTracker *tracker = calloc(1, sizeof(*tracker)); + + if (!tracker) { + return false; + } + tracker->face = face; + tracker->size = size; + g_mutex_lock(&freetype_lock); + tracker->next = freetype_sizes; + freetype_sizes = tracker; + g_mutex_unlock(&freetype_lock); + return true; +} + +static void freetype_untrack_size_locked(void *size) +{ + FtSizeTracker **cursor = &freetype_sizes; + + while (*cursor) { + FtSizeTracker *tracker = *cursor; + + if (tracker->size != size) { + cursor = &tracker->next; + continue; + } + *cursor = tracker->next; + free(tracker); + return; + } +} + +static void freetype_untrack_face_locked(void *face) +{ + FtFaceTracker **cursor = &freetype_faces; + FtSizeTracker **size_cursor = &freetype_sizes; + + while (*size_cursor) { + FtSizeTracker *size = *size_cursor; + + if (size->face != face) { + size_cursor = &size->next; + continue; + } + *size_cursor = size->next; + free(size); + } + while (*cursor) { + FtFaceTracker *tracker = *cursor; + + if (tracker->face != face) { + cursor = &tracker->next; + continue; + } + *cursor = tracker->next; + free(tracker); + return; + } +} + +static void freetype_untrack_library_locked(void *library) +{ + FtFaceTracker *face = freetype_faces; + + while (face) { + FtFaceTracker *next = face->next; + + if (face->library == library) { + freetype_untrack_face_locked(face->face); + } + face = next; + } +} + +#define FT_CALLBACK_SLOTS 16 +#define FT_SLOT_LIST(X) \ + X(0) X(1) X(2) X(3) X(4) X(5) X(6) X(7) \ + X(8) X(9) X(10) X(11) X(12) X(13) X(14) X(15) + +typedef struct FtCallbackSlot { + uintptr_t guest; + unsigned references; +} FtCallbackSlot; + +static FtCallbackSlot ft_move_slots[FT_CALLBACK_SLOTS]; +static FtCallbackSlot ft_line_slots[FT_CALLBACK_SLOTS]; +static FtCallbackSlot ft_conic_slots[FT_CALLBACK_SLOTS]; +static FtCallbackSlot ft_cubic_slots[FT_CALLBACK_SLOTS]; +static FtCallbackSlot ft_list_iterator_slots[FT_CALLBACK_SLOTS]; +static FtCallbackSlot ft_list_destroy_slots[FT_CALLBACK_SLOTS]; + +static uintptr_t freetype_slot_guest(FtCallbackSlot *slots, size_t index) +{ + uintptr_t guest; + + g_mutex_lock(&freetype_lock); + guest = slots[index].guest; + g_mutex_unlock(&freetype_lock); + return guest; +} + +static void freetype_slot_release(FtCallbackSlot *slots, uintptr_t guest) +{ + g_mutex_lock(&freetype_lock); + for (size_t i = 0; i < FT_CALLBACK_SLOTS; i++) { + if (slots[i].guest != guest) { + continue; + } + if (slots[i].references && --slots[i].references == 0) { + slots[i].guest = 0; + } + break; + } + g_mutex_unlock(&freetype_lock); +} + +static void *freetype_slot_acquire(FtCallbackSlot *slots, + void *const *thunks, void *callback, + const char *kind) +{ + uintptr_t guest = (uintptr_t)callback; + void *native; + + if (!callback) { + return NULL; + } + native = GetNativeFnc(guest); + if (native) { + return native; + } + g_mutex_lock(&freetype_lock); + for (size_t i = 0; i < FT_CALLBACK_SLOTS; i++) { + if (slots[i].guest == guest) { + slots[i].references++; + native = thunks[i]; + g_mutex_unlock(&freetype_lock); + return native; + } + } + for (size_t i = 0; i < FT_CALLBACK_SLOTS; i++) { + if (!slots[i].guest) { + slots[i].guest = guest; + slots[i].references = 1; + native = thunks[i]; + g_mutex_unlock(&freetype_lock); + return native; + } + } + g_mutex_unlock(&freetype_lock); + kzt_groups_log_wrapper_limitation(freetypeName, kind); + return NULL; +} + +#define DEFINE_MOVE_THUNK(N) \ + static int32_t ft_move_thunk_##N(void *to, void *user) \ + { \ + uintptr_t guest = freetype_slot_guest(ft_move_slots, N); \ + return guest ? (int32_t)RunFunctionFmt(guest, "pp", to, user) : 0; \ + } +FT_SLOT_LIST(DEFINE_MOVE_THUNK) +#undef DEFINE_MOVE_THUNK + +#define DEFINE_LINE_THUNK(N) \ + static int32_t ft_line_thunk_##N(void *to, void *user) \ + { \ + uintptr_t guest = freetype_slot_guest(ft_line_slots, N); \ + return guest ? (int32_t)RunFunctionFmt(guest, "pp", to, user) : 0; \ + } +FT_SLOT_LIST(DEFINE_LINE_THUNK) +#undef DEFINE_LINE_THUNK + +#define DEFINE_CONIC_THUNK(N) \ + static int32_t ft_conic_thunk_##N(void *control, void *to, void *user) \ + { \ + uintptr_t guest = freetype_slot_guest(ft_conic_slots, N); \ + return guest ? (int32_t)RunFunctionFmt(guest, "ppp", control, to, user) : 0; \ + } +FT_SLOT_LIST(DEFINE_CONIC_THUNK) +#undef DEFINE_CONIC_THUNK + +#define DEFINE_CUBIC_THUNK(N) \ + static int32_t ft_cubic_thunk_##N(void *control1, void *control2, \ + void *to, void *user) \ + { \ + uintptr_t guest = freetype_slot_guest(ft_cubic_slots, N); \ + return guest ? (int32_t)RunFunctionFmt(guest, "pppp", control1, \ + control2, to, user) : 0; \ + } +FT_SLOT_LIST(DEFINE_CUBIC_THUNK) +#undef DEFINE_CUBIC_THUNK + +#define DEFINE_LIST_ITERATOR_THUNK(N) \ + static int32_t ft_list_iterator_thunk_##N(void *node, void *user) \ + { \ + uintptr_t guest = freetype_slot_guest(ft_list_iterator_slots, N); \ + return guest ? (int32_t)RunFunctionFmt(guest, "pp", node, user) : 0; \ + } +FT_SLOT_LIST(DEFINE_LIST_ITERATOR_THUNK) +#undef DEFINE_LIST_ITERATOR_THUNK + +#define DEFINE_LIST_DESTROY_THUNK(N) \ + static void ft_list_destroy_thunk_##N(void *memory, void *data, void *user) \ + { \ + uintptr_t guest = freetype_slot_guest(ft_list_destroy_slots, N); \ + if (guest) RunFunctionFmt(guest, "ppp", memory, data, user); \ + } +FT_SLOT_LIST(DEFINE_LIST_DESTROY_THUNK) +#undef DEFINE_LIST_DESTROY_THUNK + +#define THUNK_ADDRESS(kind, N) (void *)ft_##kind##_thunk_##N, +#define MOVE_ADDRESS(N) THUNK_ADDRESS(move, N) +static void *const ft_move_thunks[] = { FT_SLOT_LIST(MOVE_ADDRESS) }; +#undef MOVE_ADDRESS +#define LINE_ADDRESS(N) THUNK_ADDRESS(line, N) +static void *const ft_line_thunks[] = { FT_SLOT_LIST(LINE_ADDRESS) }; +#undef LINE_ADDRESS +#define CONIC_ADDRESS(N) THUNK_ADDRESS(conic, N) +static void *const ft_conic_thunks[] = { FT_SLOT_LIST(CONIC_ADDRESS) }; +#undef CONIC_ADDRESS +#define CUBIC_ADDRESS(N) THUNK_ADDRESS(cubic, N) +static void *const ft_cubic_thunks[] = { FT_SLOT_LIST(CUBIC_ADDRESS) }; +#undef CUBIC_ADDRESS +#define LIST_ITERATOR_ADDRESS(N) THUNK_ADDRESS(list_iterator, N) +static void *const ft_list_iterator_thunks[] = { + FT_SLOT_LIST(LIST_ITERATOR_ADDRESS) +}; +#undef LIST_ITERATOR_ADDRESS +#define LIST_DESTROY_ADDRESS(N) THUNK_ADDRESS(list_destroy, N) +static void *const ft_list_destroy_thunks[] = { + FT_SLOT_LIST(LIST_DESTROY_ADDRESS) +}; +#undef LIST_DESTROY_ADDRESS +#undef THUNK_ADDRESS + +static int32_t freetype_finish_face_creation(void *library, void **face, + int32_t status) +{ + if (status != FT_ERROR_SUCCESS || !face || !*face) { + return status; + } + if (freetype_track_face(library, *face)) { + return status; + } + my->FT_Done_Face(*face); + *face = NULL; + return FT_ERROR_OUT_OF_MEMORY; +} + +EXPORT int32_t my_FT_New_Face(void *library, void *path, intptr_t index, + void **face) +{ + return freetype_finish_face_creation( + library, face, my->FT_New_Face(library, path, index, face)); +} + +EXPORT int32_t my_FT_New_Memory_Face(void *library, void *base, + intptr_t size, intptr_t index, + void **face) +{ + return freetype_finish_face_creation( + library, face, + my->FT_New_Memory_Face(library, base, size, index, face)); +} + +EXPORT int32_t my_FT_Open_Face(void *library, FtOpenArgsAbi *args, + intptr_t index, void **face) +{ + int32_t status; + + if (args && (args->flags & FT_OPEN_STREAM) && args->stream && + (args->stream->read || args->stream->close)) { + kzt_groups_log_wrapper_limitation( + freetypeName, "FT_Open_Face guest stream callbacks are unsupported"); + return FT_ERROR_UNIMPLEMENTED_FEATURE; + } + status = my->FT_Open_Face(library, args, index, face); + return freetype_finish_face_creation(library, face, status); +} + +EXPORT int32_t my_FT_Attach_Stream(void *face, FtOpenArgsAbi *args) +{ + if (args && (args->flags & FT_OPEN_STREAM) && args->stream && + (args->stream->read || args->stream->close)) { + kzt_groups_log_wrapper_limitation( + freetypeName, + "FT_Attach_Stream guest stream callbacks are unsupported"); + return FT_ERROR_UNIMPLEMENTED_FEATURE; + } + return my->FT_Attach_Stream(face, args); +} + +EXPORT int32_t my_FT_New_Size(void *face, void **size) +{ + int32_t status = my->FT_New_Size(face, size); + + if (status != FT_ERROR_SUCCESS || !size || !*size) { + return status; + } + if (freetype_track_size(face, *size)) { + return status; + } + my->FT_Done_Size(*size); + *size = NULL; + return FT_ERROR_OUT_OF_MEMORY; +} + +EXPORT int32_t my_FT_Done_Size(void *size) +{ + int32_t status; + + g_mutex_lock(&freetype_lock); + if (size && + !freetype_generic_is_safe(&((FtSizeAbi *)size)->generic)) { + g_mutex_unlock(&freetype_lock); + kzt_groups_log_wrapper_limitation( + freetypeName, "guest FT_Size generic finalizer is unsupported"); + return FT_ERROR_UNIMPLEMENTED_FEATURE; + } + g_mutex_unlock(&freetype_lock); + status = my->FT_Done_Size(size); + if (status == FT_ERROR_SUCCESS) { + g_mutex_lock(&freetype_lock); + freetype_untrack_size_locked(size); + g_mutex_unlock(&freetype_lock); + } + return status; +} + +EXPORT int32_t my_FT_Done_Face(void *face) +{ + int32_t status; + + g_mutex_lock(&freetype_lock); + if (!freetype_face_is_safe(face)) { + g_mutex_unlock(&freetype_lock); + kzt_groups_log_wrapper_limitation( + freetypeName, "guest FT_Face generic finalizer is unsupported"); + return FT_ERROR_UNIMPLEMENTED_FEATURE; + } + g_mutex_unlock(&freetype_lock); + status = my->FT_Done_Face(face); + if (status == FT_ERROR_SUCCESS) { + g_mutex_lock(&freetype_lock); + for (FtFaceTracker *tracker = freetype_faces; tracker; + tracker = tracker->next) { + if (tracker->face != face) { + continue; + } + if (tracker->references > 1) { + tracker->references--; + } else { + freetype_untrack_face_locked(face); + } + break; + } + g_mutex_unlock(&freetype_lock); + } + return status; +} + +EXPORT int32_t my_FT_Reference_Face(void *face) +{ + int32_t status = my->FT_Reference_Face(face); + + if (status != FT_ERROR_SUCCESS || !face) { + return status; + } + g_mutex_lock(&freetype_lock); + for (FtFaceTracker *tracker = freetype_faces; tracker; + tracker = tracker->next) { + if (tracker->face == face) { + tracker->references++; + g_mutex_unlock(&freetype_lock); + return status; + } + } + g_mutex_unlock(&freetype_lock); + + FtFaceAbi *record = face; + void *library = record->glyph + ? ((FtGlyphSlotAbi *)record->glyph)->library : NULL; + + if (library && freetype_track_face(library, face)) { + return status; + } + my->FT_Done_Face(face); + return FT_ERROR_OUT_OF_MEMORY; +} + +static int32_t freetype_done_library(void *library, + int32_t (*finish)(void *)) +{ + int32_t status; + + g_mutex_lock(&freetype_lock); + if (!freetype_library_is_safe(library)) { + g_mutex_unlock(&freetype_lock); + kzt_groups_log_wrapper_limitation( + freetypeName, "guest FreeType generic finalizer is unsupported"); + return FT_ERROR_UNIMPLEMENTED_FEATURE; + } + g_mutex_unlock(&freetype_lock); + status = finish(library); + if (status == FT_ERROR_SUCCESS) { + g_mutex_lock(&freetype_lock); + freetype_untrack_library_locked(library); + g_mutex_unlock(&freetype_lock); + } + return status; +} + +EXPORT int32_t my_FT_Done_FreeType(void *library) +{ + return freetype_done_library(library, my->FT_Done_FreeType); +} + +EXPORT int32_t my_FT_Done_Library(void *library) +{ + return freetype_done_library(library, my->FT_Done_Library); +} + +EXPORT int32_t my_FT_Bitmap_Blend(void *library, void *source, + uintptr_t source_x, uintptr_t source_y, + void *target, void *target_offset, + uint32_t color_bits) +{ + FtVectorAbi source_offset = {(intptr_t)source_x, (intptr_t)source_y}; + FtColorAbi color; + + memcpy(&color, &color_bits, sizeof(color)); + return my->FT_Bitmap_Blend(library, source, source_offset, target, + target_offset, color); +} + +EXPORT int32_t my_FT_Palette_Set_Foreground_Color(void *face, + uint32_t color_bits) +{ + FtColorAbi color; + + memcpy(&color, &color_bits, sizeof(color)); + return my->FT_Palette_Set_Foreground_Color(face, color); +} + +EXPORT int32_t my_FT_Get_Paint(void *face, void *paint, + int32_t insert_root_transform, void *result) +{ + FtOpaquePaintAbi opaque = { + .paint = paint, + .insert_root_transform = insert_root_transform, + }; + + return my->FT_Get_Paint(face, opaque, result); +} + +EXPORT int32_t my_FT_Outline_Decompose(void *outline, + FtOutlineFuncsAbi *functions, + void *user) +{ + FtOutlineFuncsAbi native = {0}; + void *callbacks[4] = {0}; + FtCallbackSlot *slots[4] = { + ft_move_slots, ft_line_slots, ft_conic_slots, ft_cubic_slots, + }; + void *const *thunks[4] = { + ft_move_thunks, ft_line_thunks, ft_conic_thunks, ft_cubic_thunks, + }; + const char *limitations[4] = { + "outline move callback slots exhausted", + "outline line callback slots exhausted", + "outline conic callback slots exhausted", + "outline cubic callback slots exhausted", + }; + void **guest_callbacks; + int32_t status; + + if (!functions) { + return FT_ERROR_INVALID_ARGUMENT; + } + native = *functions; + guest_callbacks = &functions->move_to; + for (size_t i = 0; i < 4; i++) { + callbacks[i] = freetype_slot_acquire(slots[i], thunks[i], + guest_callbacks[i], + limitations[i]); + if (guest_callbacks[i] && !callbacks[i]) { + for (size_t j = 0; j < i; j++) { + if (GetNativeFnc((uintptr_t)guest_callbacks[j]) == NULL) { + freetype_slot_release(slots[j], + (uintptr_t)guest_callbacks[j]); + } + } + return FT_ERROR_OUT_OF_MEMORY; + } + } + native.move_to = callbacks[0]; + native.line_to = callbacks[1]; + native.conic_to = callbacks[2]; + native.cubic_to = callbacks[3]; + status = my->FT_Outline_Decompose(outline, &native, user); + for (size_t i = 0; i < 4; i++) { + if (guest_callbacks[i] && + GetNativeFnc((uintptr_t)guest_callbacks[i]) == NULL) { + freetype_slot_release(slots[i], + (uintptr_t)guest_callbacks[i]); + } + } + return status; +} + +EXPORT int32_t my_FT_Outline_Render(void *library, void *outline, + FtRasterParamsAbi *params) +{ + FtRasterParamsAbi native; + void **callbacks; + + if (!params) { + return FT_ERROR_INVALID_ARGUMENT; + } + native = *params; + callbacks = &native.gray_spans; + for (size_t i = 0; i < 4; i++) { + void *callback = callbacks[i]; + + if (!callback) { + continue; + } + callbacks[i] = GetNativeFnc((uintptr_t)callback); + if (!callbacks[i]) { + kzt_groups_log_wrapper_limitation( + freetypeName, + "FT_Outline_Render guest callbacks are unsupported"); + return FT_ERROR_UNIMPLEMENTED_FEATURE; + } + } + return my->FT_Outline_Render(library, outline, &native); +} + +EXPORT int32_t my_FT_List_Iterate(void *list, void *iterator, void *user) +{ + void *native = freetype_slot_acquire( + ft_list_iterator_slots, ft_list_iterator_thunks, iterator, + "list iterator callback slots exhausted"); + int32_t status; + + if (iterator && !native) { + return FT_ERROR_OUT_OF_MEMORY; + } + status = my->FT_List_Iterate(list, native, user); + if (iterator && GetNativeFnc((uintptr_t)iterator) == NULL) { + freetype_slot_release(ft_list_iterator_slots, (uintptr_t)iterator); + } + return status; +} + +EXPORT void my_FT_List_Finalize(void *list, void *destroy, void *memory, + void *user) +{ + void *native = freetype_slot_acquire( + ft_list_destroy_slots, ft_list_destroy_thunks, destroy, + "list destructor callback slots exhausted"); + + if (destroy && !native) { + return; + } + my->FT_List_Finalize(list, native, memory, user); + if (destroy && GetNativeFnc((uintptr_t)destroy) == NULL) { + freetype_slot_release(ft_list_destroy_slots, (uintptr_t)destroy); + } +} + +EXPORT int32_t my_FT_Add_Module(void *library, void *module_class) +{ + (void)library; + (void)module_class; + kzt_groups_log_wrapper_limitation( + freetypeName, "FT_Add_Module callback-bearing class is unsupported"); + return FT_ERROR_UNIMPLEMENTED_FEATURE; +} + +EXPORT int32_t my_FT_New_Library(void *memory, void **library) +{ + (void)memory; + if (library) { + *library = NULL; + } + kzt_groups_log_wrapper_limitation( + freetypeName, "FT_New_Library guest memory callbacks are unsupported"); + return FT_ERROR_UNIMPLEMENTED_FEATURE; +} + +EXPORT int32_t my_FTC_Manager_New(void *library, uint32_t max_faces, + uint32_t max_sizes, uintptr_t max_bytes, + void *requester, void *request_data, + void **manager) +{ + void *native = GetNativeFnc((uintptr_t)requester); + + if (requester && !native) { + if (manager) { + *manager = NULL; + } + kzt_groups_log_wrapper_limitation( + freetypeName, "FTC_Manager_New guest requester is unsupported"); + return FT_ERROR_UNIMPLEMENTED_FEATURE; + } + return my->FTC_Manager_New(library, max_faces, max_sizes, max_bytes, + native, request_data, manager); +} + +EXPORT void my_FT_Set_Debug_Hook(void *library, uint32_t index, + void *callback) +{ + void *native = GetNativeFnc((uintptr_t)callback); + + if (callback && !native) { + kzt_groups_log_wrapper_limitation( + freetypeName, "FT_Set_Debug_Hook guest callback is unsupported"); + return; + } + my->FT_Set_Debug_Hook(library, index, native); +} + +EXPORT void my_FT_Set_Log_Handler(void *callback) +{ + void *native = GetNativeFnc((uintptr_t)callback); + + if (callback && !native) { + kzt_groups_log_wrapper_limitation( + freetypeName, + "FT_Set_Log_Handler guest va_list callback is unsupported"); + return; + } + my->FT_Set_Log_Handler(native); +} + +#define DEFINE_UNSUPPORTED_STREAM(name) \ + EXPORT int32_t my_##name(void *target, void *source) \ + { \ + (void)target; \ + (void)source; \ + kzt_groups_log_wrapper_limitation( \ + freetypeName, #name " callback-bearing stream is unsupported"); \ + return FT_ERROR_UNIMPLEMENTED_FEATURE; \ + } +DEFINE_UNSUPPORTED_STREAM(FT_Stream_OpenBzip2) +DEFINE_UNSUPPORTED_STREAM(FT_Stream_OpenGzip) +DEFINE_UNSUPPORTED_STREAM(FT_Stream_OpenLZW) +#undef DEFINE_UNSUPPORTED_STREAM + +EXPORT void *my_TT_New_Context(void) +{ + kzt_groups_log_wrapper_limitation( + freetypeName, "internal TT_New_Context ABI is unsupported"); + return NULL; +} + +EXPORT int32_t my_TT_RunIns(void) +{ + kzt_groups_log_wrapper_limitation( + freetypeName, "internal TT_RunIns ABI is unsupported"); + return FT_ERROR_UNIMPLEMENTED_FEATURE; +} + +static void freetype_state_clear(void) +{ + g_mutex_lock(&freetype_lock); + while (freetype_faces) { + FtFaceTracker *next = freetype_faces->next; + + free(freetype_faces); + freetype_faces = next; + } + while (freetype_sizes) { + FtSizeTracker *next = freetype_sizes->next; + + free(freetype_sizes); + freetype_sizes = next; + } + memset(ft_move_slots, 0, sizeof(ft_move_slots)); + memset(ft_line_slots, 0, sizeof(ft_line_slots)); + memset(ft_conic_slots, 0, sizeof(ft_conic_slots)); + memset(ft_cubic_slots, 0, sizeof(ft_cubic_slots)); + memset(ft_list_iterator_slots, 0, sizeof(ft_list_iterator_slots)); + memset(ft_list_destroy_slots, 0, sizeof(ft_list_destroy_slots)); + g_mutex_unlock(&freetype_lock); +} + +#define CUSTOM_INIT \ + getMy(lib); + +#define CUSTOM_FINI \ + freetype_state_clear(); \ + freeMy(); + +#include "wrappedlib_init.h" + +#pragma GCC diagnostic pop diff --git a/target/i386/latx/context/wrappedlibxft.c b/target/i386/latx/context/wrappedlibxft.c index dbe415128e..1abd03a05d 100644 --- a/target/i386/latx/context/wrappedlibxft.c +++ b/target/i386/latx/context/wrappedlibxft.c @@ -2,44 +2,129 @@ * This file is derived from Box64. * * SPDX-FileCopyrightText: 2020 ptitSeb + * SPDX-FileCopyrightText: 2026 LAT Project Authors * * SPDX-License-Identifier: MIT */ -#include -#include -#include +#include "qemu/osdep.h" + #include +#include #include "wrappedlibs.h" -#include "wrapper.h" +#include "box64context.h" #include "bridge.h" #include "library_private.h" +#include "wrappedfont-vaargs.h" +#include "wrappedfont-preflight.h" +#include "wrapper.h" + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wmissing-prototypes" #ifdef ANDROID - const char* libxftName = "libXft.so"; +const char *libxftName = "libXft.so"; #else - const char* libxftName = "libXft.so.2"; +const char *libxftName = "libXft.so.2"; #endif #define LIBNAME libxft + +#define PRE_INIT_GUEST \ + do { \ + if (latx_font_preflight_or_disable(&box64->box64_ld_lib, \ + libxftName) != 0) { \ + return -1; \ + } \ + } while (0); + +typedef void (*xft_vFp_t)(void *); +typedef void *(*xft_pFpp_t)(void *, void *); +typedef void *(*xft_pFpipp_t)(void *, int32_t, void *, void *); +typedef void *(*xft_pFppp_t)(void *, void *, void *); + +#define ADDED_FUNCTIONS() \ + GO(FcFontList, xft_pFppp_t) \ + GO(FcObjectSetDestroy, xft_vFp_t) \ + GO(FcPatternDestroy, xft_vFp_t) \ + GO(XftFontMatch, xft_pFpipp_t) \ + GO(XftFontOpenPattern, xft_pFpp_t) + +#include "generated/wrappedlibxfttypes.h" + +#include "wrappercallback.h" + +EXPORT void *my_XftFontOpen(void *display, int32_t screen, void *stack) +{ + LatxX64VaReader reader; + void *pattern; + void *match; + void *font; + int32_t result; + + (void)stack; + latx_x64_va_reader_live(&reader, 2); + pattern = latx_fontconfig_pattern_build(&reader, NULL); + if (!pattern) { + return NULL; + } + match = my->XftFontMatch(display, screen, pattern, &result); + my->FcPatternDestroy(pattern); + if (!match) { + return NULL; + } + font = my->XftFontOpenPattern(display, match); + if (!font) { + my->FcPatternDestroy(match); + } + return font; +} + +EXPORT void *my_XftListFonts(void *display, int32_t screen, void *stack) +{ + LatxX64VaReader reader; + void *pattern; + void *objects; + void *fonts; + const char *first; + + (void)display; + (void)screen; + (void)stack; + latx_x64_va_reader_live(&reader, 2); + pattern = latx_fontconfig_pattern_build(&reader, NULL); + if (!pattern) { + return NULL; + } + first = (const char *)(uintptr_t)latx_x64_va_gp(&reader); + objects = latx_fontconfig_object_set_build(&reader, first); + if (!objects) { + my->FcPatternDestroy(pattern); + return NULL; + } + fonts = my->FcFontList(NULL, pattern, objects); + my->FcPatternDestroy(pattern); + my->FcObjectSetDestroy(objects); + return fonts; +} + #ifdef ANDROID - #define CUSTOM_INIT \ - setNeededLibs(lib, 4, \ - "libX11.so", \ - "libfontconfig.so", \ - "libXrender.so", \ - "libfreetype.so"); +#define CUSTOM_INIT \ + getMy(lib); \ + setNeededLibs(lib, 4, "libX11.so", "libfontconfig.so", \ + "libXrender.so", "libfreetype.so"); #else - #define CUSTOM_INIT \ - setNeededLibs(lib, 4, \ - "libX11.so.6", \ - "libfontconfig.so.1", \ - "libXrender.so.1", \ - "libfreetype.so.6"); +#define CUSTOM_INIT \ + getMy(lib); \ + setNeededLibs(lib, 4, "libX11.so.6", "libfontconfig.so.1", \ + "libXrender.so.1", "libfreetype.so.6"); #endif +#define CUSTOM_FINI \ + freeMy(); #include "wrappedlib_init.h" +#pragma GCC diagnostic pop diff --git a/target/i386/latx/include/generated/wrappedfontconfigdefs.h b/target/i386/latx/include/generated/wrappedfontconfigdefs.h new file mode 100644 index 0000000000..1554fe55c7 --- /dev/null +++ b/target/i386/latx/include/generated/wrappedfontconfigdefs.h @@ -0,0 +1,14 @@ +/******************************************************************* + * File automatically generated by rebuild_wrappers.py (v2.2.0.18) * + *******************************************************************/ +#ifndef __wrappedfontconfigDEFS_H_ +#define __wrappedfontconfigDEFS_H_ + +#define pFEpA pFpp +#define iFEppHi iFppHi +#define iFEpppp iFpppp +#define vFEH vFH +#define iFEHH iFHH +#define HFEH HFH + +#endif // __wrappedfontconfigDEFS_H_ diff --git a/target/i386/latx/include/generated/wrappedfontconfigtypes.h b/target/i386/latx/include/generated/wrappedfontconfigtypes.h new file mode 100644 index 0000000000..9a58c1bacf --- /dev/null +++ b/target/i386/latx/include/generated/wrappedfontconfigtypes.h @@ -0,0 +1,36 @@ +/******************************************************************* + * File automatically generated by rebuild_wrappers.py (v2.2.0.18) * + *******************************************************************/ +#ifndef __wrappedfontconfigTYPES_H_ +#define __wrappedfontconfigTYPES_H_ + +#ifndef LIBNAME +#error You should only #include this file inside a wrapped*.c file +#endif +#ifndef ADDED_FUNCTIONS +#define ADDED_FUNCTIONS() +#endif + +typedef void *(*pFpV_t)(void *, ...); +typedef void *(*pFpA_t)(void *, va_list); +typedef int32_t (*iFppHi_t)(void *, void *, FcValueAbi, int32_t); +typedef int32_t (*iFpppp_t)(void *, void *, void *, void *); +typedef void (*vFH_t)(FcValueAbi); +typedef int32_t (*iFHH_t)(FcValueAbi, FcValueAbi); +typedef FcValueAbi (*HFH_t)(FcValueAbi); + +#define SUPER() ADDED_FUNCTIONS() \ + GO(FcObjectSetBuild, pFpV_t) \ + GO(FcObjectSetVaBuild, pFpA_t) \ + GO(FcPatternAdd, iFppHi_t) \ + GO(FcPatternAddWeak, iFppHi_t) \ + GO(FcPatternBuild, pFpV_t) \ + GO(FcPatternVaBuild, pFpA_t) \ + GO(FcScandir, iFpppp_t) \ + GO(FcStrBuildFilename, pFpV_t) \ + GO(FcValueDestroy, vFH_t) \ + GO(FcValueEqual, iFHH_t) \ + GO(FcValuePrint, vFH_t) \ + GO(FcValueSave, HFH_t) + +#endif // __wrappedfontconfigTYPES_H_ diff --git a/target/i386/latx/include/generated/wrappedfontconfigundefs.h b/target/i386/latx/include/generated/wrappedfontconfigundefs.h new file mode 100644 index 0000000000..8af18bc18d --- /dev/null +++ b/target/i386/latx/include/generated/wrappedfontconfigundefs.h @@ -0,0 +1,11 @@ +/******************************************************************* + * File automatically generated by rebuild_wrappers.py (v2.2.0.18) * + *******************************************************************/ +#ifndef __wrappedfontconfigUNDEFS_H_ +#define __wrappedfontconfigUNDEFS_H_ + +#undef pFEpA +#undef iFEppHi +#undef iFEpppp + +#endif // __wrappedfontconfigUNDEFS_H_ diff --git a/target/i386/latx/include/generated/wrappedfreetypedefs.h b/target/i386/latx/include/generated/wrappedfreetypedefs.h new file mode 100644 index 0000000000..9974e00d8a --- /dev/null +++ b/target/i386/latx/include/generated/wrappedfreetypedefs.h @@ -0,0 +1,22 @@ +/******************************************************************* + * File automatically generated by rebuild_wrappers.py (v2.2.0.18) * + *******************************************************************/ +#ifndef __wrappedfreetypeDEFS_H_ +#define __wrappedfreetypeDEFS_H_ + +#define iFEpp iFpp +#define iFEppLLppu iFppLLppu +#define iFEp iFp +#define vFEpppp vFpppp +#define iFEppp iFppp +#define iFEpplp iFpplp +#define iFEppllp iFppllp +#define iFEppip iFppip +#define iFEpu iFpu +#define vFEpup vFpup +#define vFEp vFp +#define iFEpuuLppp iFpuuLppp +#define pFEv pFv +#define iFEv iFv + +#endif // __wrappedfreetypeDEFS_H_ diff --git a/target/i386/latx/include/generated/wrappedfreetypetypes.h b/target/i386/latx/include/generated/wrappedfreetypetypes.h new file mode 100644 index 0000000000..1125919c7e --- /dev/null +++ b/target/i386/latx/include/generated/wrappedfreetypetypes.h @@ -0,0 +1,60 @@ +/******************************************************************* + * File automatically generated by rebuild_wrappers.py (v2.2.0.18) * + *******************************************************************/ +#ifndef __wrappedfreetypeTYPES_H_ +#define __wrappedfreetypeTYPES_H_ + +#ifndef LIBNAME +#error You should only #include this file inside a wrapped*.c file +#endif +#ifndef ADDED_FUNCTIONS +#define ADDED_FUNCTIONS() +#endif + +typedef int32_t (*iFpp_t)(void *, void *); +typedef void (*vFp_t)(void *); +typedef int32_t (*iFppLLppu_t)(void *, void *, FtVectorAbi, void *, + void *, FtColorAbi); +typedef int32_t (*iFp_t)(void *); +typedef void (*vFpppp_t)(void *, void *, void *, void *); +typedef int32_t (*iFppp_t)(void *, void *, void *); +typedef int32_t (*iFpplp_t)(void *, void *, intptr_t, void *); +typedef int32_t (*iFppllp_t)(void *, void *, intptr_t, intptr_t, void *); +typedef int32_t (*iFppip_t)(void *, FtOpaquePaintAbi, void *); +typedef int32_t (*iFpu_t)(void *, FtColorAbi); +typedef void (*vFpup_t)(void *, uint32_t, void *); +typedef int32_t (*iFpuuLppp_t)(void *, uint32_t, uint32_t, uintptr_t, + void *, void *, void *); +typedef void *(*pFv_t)(void); +typedef int32_t (*iFv_t)(void); + +#define SUPER() ADDED_FUNCTIONS() \ + GO(FT_Add_Module, iFpp_t) \ + GO(FT_Attach_Stream, iFpp_t) \ + GO(FT_Bitmap_Blend, iFppLLppu_t) \ + GO(FT_Done_Face, iFp_t) \ + GO(FT_Done_FreeType, iFp_t) \ + GO(FT_Done_Library, iFp_t) \ + GO(FT_Done_Size, iFp_t) \ + GO(FT_List_Finalize, vFpppp_t) \ + GO(FT_List_Iterate, iFppp_t) \ + GO(FT_New_Library, iFpp_t) \ + GO(FT_New_Face, iFpplp_t) \ + GO(FT_New_Memory_Face, iFppllp_t) \ + GO(FT_New_Size, iFpp_t) \ + GO(FT_Open_Face, iFpplp_t) \ + GO(FT_Outline_Decompose, iFppp_t) \ + GO(FT_Outline_Render, iFppp_t) \ + GO(FT_Get_Paint, iFppip_t) \ + GO(FT_Palette_Set_Foreground_Color, iFpu_t) \ + GO(FT_Reference_Face, iFp_t) \ + GO(FT_Set_Debug_Hook, vFpup_t) \ + GO(FT_Set_Log_Handler, vFp_t) \ + GO(FT_Stream_OpenBzip2, iFpp_t) \ + GO(FT_Stream_OpenGzip, iFpp_t) \ + GO(FT_Stream_OpenLZW, iFpp_t) \ + GO(FTC_Manager_New, iFpuuLppp_t) \ + GO(TT_New_Context, pFv_t) \ + GO(TT_RunIns, iFv_t) + +#endif // __wrappedfreetypeTYPES_H_ diff --git a/target/i386/latx/include/generated/wrappedfreetypeundefs.h b/target/i386/latx/include/generated/wrappedfreetypeundefs.h new file mode 100644 index 0000000000..6d8558091a --- /dev/null +++ b/target/i386/latx/include/generated/wrappedfreetypeundefs.h @@ -0,0 +1,21 @@ +/******************************************************************* + * File automatically generated by rebuild_wrappers.py (v2.2.0.18) * + *******************************************************************/ +#ifndef __wrappedfreetypeUNDEFS_H_ +#define __wrappedfreetypeUNDEFS_H_ + +#undef iFEpp +#undef iFEppLLppu +#undef iFEp +#undef vFEpppp +#undef iFEppp +#undef iFEpplp +#undef iFEppllp +#undef iFEpu +#undef vFEpup +#undef vFEp +#undef iFEpuuLppp +#undef pFEv +#undef iFEv + +#endif // __wrappedfreetypeUNDEFS_H_ diff --git a/target/i386/latx/include/generated/wrappedlibxfttypes.h b/target/i386/latx/include/generated/wrappedlibxfttypes.h index 02257e5a14..dc61b14283 100644 --- a/target/i386/latx/include/generated/wrappedlibxfttypes.h +++ b/target/i386/latx/include/generated/wrappedlibxfttypes.h @@ -8,10 +8,13 @@ #error You should only #include this file inside a wrapped*.c file #endif #ifndef ADDED_FUNCTIONS -#define ADDED_FUNCTIONS() +#define ADDED_FUNCTIONS() #endif +typedef void *(*pFpiV_t)(void *, int32_t, ...); -#define SUPER() ADDED_FUNCTIONS() +#define SUPER() ADDED_FUNCTIONS() \ + GO(XftFontOpen, pFpiV_t) \ + GO(XftListFonts, pFpiV_t) #endif // __wrappedlibxftTYPES_H_ diff --git a/target/i386/latx/include/kzt-group-list.h b/target/i386/latx/include/kzt-group-list.h index 69f02499db..fbbb96913d 100644 --- a/target/i386/latx/include/kzt-group-list.h +++ b/target/i386/latx/include/kzt-group-list.h @@ -24,6 +24,7 @@ X(GL, "gl", 2, STABLE, KZT_GROUP_CORE | KZT_GROUP_X11) \ X(VULKAN, "vulkan", 3, STABLE, KZT_GROUP_CORE | KZT_GROUP_X11) \ X(VAAPI, "vaapi", 4, STABLE, KZT_GROUP_CORE | KZT_GROUP_X11) \ + X(FONT, "font", 6, EXPERIMENTAL, KZT_GROUP_CORE | KZT_GROUP_X11) \ X(CAIRO, "cairo", 5, EXPERIMENTAL, KZT_GROUP_CORE | KZT_GROUP_X11) #endif /* LATX_KZT_GROUP_LIST_H */ diff --git a/target/i386/latx/include/library_list.h b/target/i386/latx/include/library_list.h index e80675e1bc..ade1441189 100755 --- a/target/i386/latx/include/library_list.h +++ b/target/i386/latx/include/library_list.h @@ -26,7 +26,9 @@ GO("libXcursor.so.1", libxcursor, KZT_GROUP_X11) GO("libXinerama.so.1", xinerama, KZT_GROUP_X11) GO("libXrandr.so.2", libxrandr, KZT_GROUP_X11) GO("libXss.so.1", libxss, KZT_GROUP_X11) -GO("libXft.so.2", libxft, KZT_GROUP_X11) +GO("libXft.so.2", libxft, KZT_GROUP_FONT) +GO("libfontconfig.so.1", fontconfig, KZT_GROUP_FONT) +GO("libfreetype.so.6", freetype, KZT_GROUP_FONT) GO("libXtst.so.6", libxtst, KZT_GROUP_X11) GO("libXt.so.6", libxt, KZT_GROUP_X11) GO("libXrender.so.1", libxrender, KZT_GROUP_X11) @@ -98,7 +100,9 @@ GOALIAS("libXcursor.so", libxcursor, KZT_GROUP_X11) GOALIAS("libXinerama.so", xinerama, KZT_GROUP_X11) GOALIAS("libXrandr.so", libxrandr, KZT_GROUP_X11) GOALIAS("libXss.so", libxss, KZT_GROUP_X11) -GOALIAS("libXft.so", libxft, KZT_GROUP_X11) +GOALIAS("libXft.so", libxft, KZT_GROUP_FONT) +GOALIAS("libfontconfig.so", fontconfig, KZT_GROUP_FONT) +GOALIAS("libfreetype.so", freetype, KZT_GROUP_FONT) GOALIAS("libXtst.so", libxtst, KZT_GROUP_X11) GOALIAS("libXt.so", libxt, KZT_GROUP_X11) GOALIAS("libXrender.so", libxrender, KZT_GROUP_X11) diff --git a/target/i386/latx/include/wrappedfont-preflight.h b/target/i386/latx/include/wrappedfont-preflight.h new file mode 100644 index 0000000000..4d3770ea21 --- /dev/null +++ b/target/i386/latx/include/wrappedfont-preflight.h @@ -0,0 +1,20 @@ +/* + * SPDX-FileCopyrightText: 2026 LAT Project Authors + * + * SPDX-License-Identifier: GPL-2.0-only + */ + +#ifndef LATX_WRAPPEDFONT_PREFLIGHT_H +#define LATX_WRAPPEDFONT_PREFLIGHT_H + +#include +#include + +#include "pathcoll.h" + +bool latx_font_preflight_guest(path_collection_t *guest_paths, + char *reason, size_t reason_size); +int latx_font_preflight_or_disable(path_collection_t *guest_paths, + const char *requesting_soname); + +#endif /* LATX_WRAPPEDFONT_PREFLIGHT_H */ diff --git a/target/i386/latx/include/wrappedfont-vaargs.h b/target/i386/latx/include/wrappedfont-vaargs.h new file mode 100644 index 0000000000..e80db098a9 --- /dev/null +++ b/target/i386/latx/include/wrappedfont-vaargs.h @@ -0,0 +1,98 @@ +/* + * SPDX-FileCopyrightText: 2026 LAT Project Authors + * + * SPDX-License-Identifier: GPL-2.0-only + */ + +#ifndef LATX_WRAPPEDFONT_VAARGS_H +#define LATX_WRAPPEDFONT_VAARGS_H + +#include +#include +#include + +#include "myalign.h" + +typedef struct LatxX64VaReader { + CPUX86State *cpu; + uint32_t gp_offset; + uint32_t fp_offset; + uint8_t *overflow_arg_area; + uint8_t *reg_save_area; + bool live_registers; +} LatxX64VaReader; + +static inline void latx_x64_va_reader_live(LatxX64VaReader *reader, + unsigned fixed_gp) +{ + reader->cpu = (CPUX86State *)lsenv->cpu_state; + reader->gp_offset = fixed_gp * 8; + reader->fp_offset = X64_VA_MAX_REG; + reader->overflow_arg_area = + (uint8_t *)(reader->cpu->regs[R_ESP] + 8); + reader->reg_save_area = NULL; + reader->live_registers = true; +} + +static inline void latx_x64_va_reader_list(LatxX64VaReader *reader, + x64_va_list_t list) +{ + reader->cpu = (CPUX86State *)lsenv->cpu_state; + reader->gp_offset = list->gp_offset; + reader->fp_offset = list->fp_offset; + reader->overflow_arg_area = list->overflow_arg_area; + reader->reg_save_area = list->reg_save_area; + reader->live_registers = false; +} + +static inline uint64_t latx_x64_va_gp(LatxX64VaReader *reader) +{ + static const int registers[] = { + R_EDI, R_ESI, R_EDX, R_ECX, R_R8, R_R9, + }; + uint64_t value; + + if (reader->gp_offset < X64_VA_MAX_REG) { + unsigned index = reader->gp_offset / 8; + + if (reader->live_registers) { + value = reader->cpu->regs[registers[index]]; + } else { + memcpy(&value, reader->reg_save_area + reader->gp_offset, + sizeof(value)); + } + reader->gp_offset += 8; + return value; + } + memcpy(&value, reader->overflow_arg_area, sizeof(value)); + reader->overflow_arg_area += 8; + return value; +} + +static inline double latx_x64_va_double(LatxX64VaReader *reader) +{ + double value; + + if (reader->fp_offset < X64_VA_MAX_XMM) { + unsigned index = (reader->fp_offset - X64_VA_MAX_REG) / 16; + + if (reader->live_registers) { + value = *(double *)&reader->cpu->xmm_regs[index].ZMM_Q(0); + } else { + memcpy(&value, reader->reg_save_area + reader->fp_offset, + sizeof(value)); + } + reader->fp_offset += 16; + return value; + } + memcpy(&value, reader->overflow_arg_area, sizeof(value)); + reader->overflow_arg_area += 8; + return value; +} + +void *latx_fontconfig_pattern_build(LatxX64VaReader *reader, + void *pattern); +void *latx_fontconfig_object_set_build(LatxX64VaReader *reader, + const char *first); + +#endif /* LATX_WRAPPEDFONT_VAARGS_H */ diff --git a/target/i386/latx/include/wrappedfontconfig_private.h b/target/i386/latx/include/wrappedfontconfig_private.h new file mode 100644 index 0000000000..55a3a9ecea --- /dev/null +++ b/target/i386/latx/include/wrappedfontconfig_private.h @@ -0,0 +1,251 @@ +/* + * This file is derived from Box64. + * + * SPDX-FileCopyrightText: 2020 ptitSeb + * SPDX-FileCopyrightText: 2026 LAT Project Authors + * + * SPDX-License-Identifier: MIT + */ + +#if !(defined(GO) && defined(GOM) && defined(GO2) && defined(DATA)) +#error Meh... +#endif + +GO(FcAtomicCreate, pFp) +GO(FcAtomicDeleteNew, vFp) +GO(FcAtomicDestroy, vFp) +GO(FcAtomicLock, iFp) +GO(FcAtomicNewFile, pFp) +GO(FcAtomicOrigFile, pFp) +GO(FcAtomicReplaceOrig, iFp) +GO(FcAtomicUnlock, vFp) +GO(FcBlanksAdd, iFpu) +GO(FcBlanksCreate, pFv) +GO(FcBlanksDestroy, vFp) +GO(FcBlanksIsMember, iFpu) +GO(FcCacheCopySet, pFp) +GO(FcCacheCreateTagFile, vFp) +GO(FcCacheDir, pFp) +GO(FcCacheNumFont, iFp) +GO(FcCacheNumSubdir, iFp) +GO(FcCacheSubdir, pFpi) +GO(FcCharSetAddChar, iFpu) +GO(FcCharSetCopy, pFp) +GO(FcCharSetCount, uFp) +GO(FcCharSetCoverage, uFpup) +GO(FcCharSetCreate, pFv) +GO(FcCharSetDelChar, iFpu) +GO(FcCharSetDestroy, vFp) +GO(FcCharSetEqual, iFpp) +GO(FcCharSetFirstPage, uFppp) +GO(FcCharSetHasChar, iFpu) +GO(FcCharSetIntersect, pFpp) +GO(FcCharSetIntersectCount, uFpp) +GO(FcCharSetIsSubset, iFpp) +GO(FcCharSetMerge, iFppp) +GO(FcCharSetNew, pFv) +GO(FcCharSetNextPage, uFppp) +GO(FcCharSetSubtract, pFpp) +GO(FcCharSetSubtractCount, uFpp) +GO(FcCharSetUnion, pFpp) +GO(FcConfigAddRule, iFppi) +GO(FcConfigAppFontAddDir, iFpp) +GO(FcConfigAppFontAddFile, iFpp) +GO(FcConfigAppFontClear, vFp) +GO(FcConfigBuildFonts, iFp) +GO(FcConfigCreate, pFv) +GO(FcConfigDestroy, vFp) +GO(FcConfigEnableHome, iFi) +GO(FcConfigFileInfoIterGet, iFppppp) +GO(FcConfigFileInfoIterInit, vFpp) +GO(FcConfigFileInfoIterNext, iFpp) +GO(FcConfigFilename, pFp) +GO(FcConfigGetBlanks, pFp) +GO(FcConfigGetCache, pFp) +GO(FcConfigGetCacheDirs, pFp) +GO(FcConfigGetConfigDirs, pFp) +GO(FcConfigGetConfigFiles, pFp) +GO(FcConfigGetCurrent, pFv) +GO(FcConfigGetFilename, pFpp) +GO(FcConfigGetFontDirs, pFp) +GO(FcConfigGetFonts, pFpu) +GO(FcConfigGetRescanInterval, iFp) +GO(FcConfigGetRescanInverval, iFp) +GO(FcConfigGetSysRoot, pFp) +GO(FcConfigHome, pFv) +GO(FcConfigParseAndLoad, iFppi) +GO(FcConfigParseAndLoadFromMemory, iFppi) +GO(FcConfigReference, pFp) +GO(FcConfigSetCurrent, iFp) +GO(FcConfigSetRescanInterval, iFpi) +GO(FcConfigSetRescanInverval, iFpi) +GO(FcConfigSetSysRoot, vFpp) +GO(FcConfigSubstitute, iFppu) +GO(FcConfigSubstituteWithPat, iFpppu) +GO(FcConfigUptoDate, iFp) +GO(FcDefaultSubstitute, vFp) +GO(FcDirCacheClean, iFpi) +GO(FcDirCacheCreateUUID, iFpip) +GO(FcDirCacheDeleteUUID, iFpp) +GO(FcDirCacheLoad, pFppp) +GO(FcDirCacheLoadFile, pFpp) +GO(FcDirCacheRead, pFpip) +GO(FcDirCacheRescan, pFpp) +GO(FcDirCacheUnlink, iFpp) +GO(FcDirCacheUnload, vFp) +GO(FcDirCacheValid, iFp) +GO(FcDirSave, iFppp) +GO(FcDirScan, iFpppppi) +GO(FcFileIsDir, iFp) +GO(FcFileScan, iFpppppi) +GO(FcFini, vFv) +GO(FcFontList, pFppp) +GO(FcFontMatch, pFppp) +GO(FcFontRenderPrepare, pFppp) +GO(FcFontSetAdd, iFpp) +GO(FcFontSetCreate, pFv) +GO(FcFontSetDestroy, vFp) +GO(FcFontSetList, pFppipp) +GO(FcFontSetMatch, pFppipp) +GO(FcFontSetPrint, vFp) +GO(FcFontSetSort, pFppipipp) +GO(FcFontSetSortDestroy, vFp) +GO(FcFontSort, pFppipp) +GO(FcFreeTypeCharIndex, uFpu) +GO(FcFreeTypeCharSet, pFpp) +GO(FcFreeTypeCharSetAndSpacing, pFppp) +GO(FcFreeTypeQuery, pFpupp) +GO(FcFreeTypeQueryAll, uFpuppp) +GO(FcFreeTypeQueryFace, pFppip) +GO(FcGetDefaultLangs, pFv) +GO(FcGetLangs, pFv) +GO(FcGetVersion, iFv) +GO(FcInit, iFv) +GO(FcInitBringUptoDate, iFv) +GO(FcInitLoadConfig, pFv) +GO(FcInitLoadConfigAndFonts, pFv) +GO(FcInitReinitialize, iFv) +GO(FcLangGetCharSet, pFp) +GO(FcLangNormalize, pFp) +GO(FcLangSetAdd, iFpp) +GO(FcLangSetCompare, uFpp) +GO(FcLangSetContains, iFpp) +GO(FcLangSetCopy, pFp) +GO(FcLangSetCreate, pFv) +GO(FcLangSetDel, iFpp) +GO(FcLangSetDestroy, vFp) +GO(FcLangSetEqual, iFpp) +GO(FcLangSetGetLangs, pFp) +GO(FcLangSetHash, uFp) +GO(FcLangSetHasLang, uFpp) +GO(FcLangSetSubtract, pFpp) +GO(FcLangSetUnion, pFpp) +GO(FcMatrixCopy, pFp) +GO(FcMatrixEqual, iFpp) +GO(FcMatrixMultiply, vFppp) +GO(FcMatrixRotate, vFpdd) +GO(FcMatrixScale, vFpdd) +GO(FcMatrixShear, vFpdd) +GO(FcNameConstant, iFpp) +GO(FcNameGetConstant, pFp) +GO(FcNameGetConstantFor, pFpp) +GO(FcNameGetObjectType, pFp) +GO(FcNameParse, pFp) +GO(FcNameRegisterConstants, iFpi) +GO(FcNameRegisterObjectTypes, iFpi) +GO(FcNameUnparse, pFp) +GO(FcNameUnregisterConstants, iFpi) +GO(FcNameUnregisterObjectTypes, iFpi) +GO(FcObjectSetAdd, iFpp) +GOM(FcObjectSetBuild, pFEpV) +GO(FcObjectSetCreate, pFv) +GO(FcObjectSetDestroy, vFp) +GOM(FcObjectSetVaBuild, pFEpA) +GOM(FcPatternAdd, iFEppHi) +GO(FcPatternAddBool, iFppi) +GO(FcPatternAddCharSet, iFppp) +GO(FcPatternAddDouble, iFppd) +GO(FcPatternAddFTFace, iFppp) +GO(FcPatternAddInteger, iFppi) +GO(FcPatternAddLangSet, iFppp) +GO(FcPatternAddMatrix, iFppp) +GO(FcPatternAddRange, iFppp) +GO(FcPatternAddString, iFppp) +GOM(FcPatternAddWeak, iFEppHi) +GOM(FcPatternBuild, pFEpV) +GO(FcPatternCreate, pFv) +GO(FcPatternDel, iFpp) +GO(FcPatternDestroy, vFp) +GO(FcPatternDuplicate, pFp) +GO(FcPatternEqual, iFpp) +GO(FcPatternEqualSubset, iFppp) +GO(FcPatternFilter, pFpp) +GO(FcPatternFindIter, iFppp) +GO(FcPatternFormat, pFpp) +GO(FcPatternGet, uFppip) +GO(FcPatternGetBool, uFppip) +GO(FcPatternGetCharSet, uFppip) +GO(FcPatternGetDouble, uFppip) +GO(FcPatternGetFTFace, iFppip) +GO(FcPatternGetInteger, uFppip) +GO(FcPatternGetLangSet, uFppip) +GO(FcPatternGetMatrix, uFppip) +GO(FcPatternGetRange, iFppip) +GO(FcPatternGetString, uFppip) +GO(FcPatternGetWithBinding, iFppipp) +GO(FcPatternHash, uFp) +GO(FcPatternIterEqual, iFpppp) +GO(FcPatternIterGetObject, pFpp) +GO(FcPatternIterGetValue, iFppipp) +GO(FcPatternIterIsValid, iFpp) +GO(FcPatternIterNext, iFpp) +GO(FcPatternIterStart, vFpp) +GO(FcPatternIterValueCount, iFpp) +GO(FcPatternObjectCount, iFp) +GO(FcPatternPrint, vFp) +GO(FcPatternReference, vFp) +GO(FcPatternRemove, iFppi) +GOM(FcPatternVaBuild, pFEpA) +GO(FcRangeCopy, pFp) +GO(FcRangeCreateDouble, pFdd) +GO(FcRangeCreateInteger, pFuu) +GO(FcRangeDestroy, vFp) +GO(FcRangeGetDouble, iFppp) +GO(FcRuleDestroy, vFp) +GOM(FcScandir, iFEpppp) +GO(FcStrBasename, pFp) +GOM(FcStrBuildFilename, pFEpV) +GO(FcStrCmp, iFpp) +GO(FcStrCmpIgnoreCase, iFpp) +GO(FcStrCopy, pFp) +GO(FcStrCopyFilename, pFp) +GO(FcStrDirname, pFp) +GO(FcStrDowncase, pFp) +GO(FcStrFree, vFp) +GO(FcStrListCreate, pFp) +GO(FcStrListDone, vFp) +GO(FcStrListFirst, vFp) +GO(FcStrListNext, pFp) +GO(FcStrPlus, pFpp) +GO(FcStrSetAdd, iFpp) +GO(FcStrSetAddFilename, iFpp) +GO(FcStrSetCreate, pFv) +GO(FcStrSetDel, iFpp) +GO(FcStrSetDestroy, vFp) +GO(FcStrSetEqual, iFpp) +GO(FcStrSetMember, iFpp) +GO(FcStrStr, pFpp) +GO(FcStrStrIgnoreCase, pFpp) +GO(FcUcs4ToUtf8, iFup) +GO(FcUtf16Len, iFpuipp) +GO(FcUtf16ToUcs4, iFpupi) +GO(FcUtf8Len, iFpipp) +GO(FcUtf8ToUcs4, iFppi) +GOM(FcValueDestroy, vFEH) +GOM(FcValueEqual, iFEHH) +GOM(FcValuePrint, vFEH) +GOM(FcValueSave, HFEH) +GO(FcWeightFromOpenType, iFi) +GO(FcWeightFromOpenTypeDouble, dFd) +GO(FcWeightToOpenType, iFi) +GO(FcWeightToOpenTypeDouble, dFd) diff --git a/target/i386/latx/include/wrappedfreetype_private.h b/target/i386/latx/include/wrappedfreetype_private.h new file mode 100644 index 0000000000..5e3e47b3b4 --- /dev/null +++ b/target/i386/latx/include/wrappedfreetype_private.h @@ -0,0 +1,235 @@ +/* + * This file is derived from Box64. + * + * SPDX-FileCopyrightText: 2020 ptitSeb + * SPDX-FileCopyrightText: 2026 LAT Project Authors + * + * SPDX-License-Identifier: MIT + */ + +#if !(defined(GO) && defined(GOM) && defined(GO2) && defined(DATA)) +#error Meh... +#endif + +GO(FT_Activate_Size, iFp) +GO(FT_Add_Default_Modules, vFp) +GOM(FT_Add_Module, iFEpp) +GO(FT_Angle_Diff, lFll) +GO(FT_Atan2, lFll) +GO(FT_Attach_File, iFpp) +GOM(FT_Attach_Stream, iFEpp) +GOM(FT_Bitmap_Blend, iFEppLLppu) +GO(FT_Bitmap_Convert, iFpppi) +GO(FT_Bitmap_Copy, iFppp) +GO(FT_Bitmap_Done, iFpp) +GO(FT_Bitmap_Embolden, iFppll) +GO(FT_Bitmap_Init, vFp) +GO(FT_Bitmap_New, vFp) +GO(FT_CeilFix, lFl) +GO(FT_ClassicKern_Free, vFpp) +GO(FT_ClassicKern_Validate, iFpup) +GO(FT_Cos, lFl) +GO(FT_DivFix, lFll) +GOM(FT_Done_Face, iFEp) +GOM(FT_Done_FreeType, iFEp) +GO(FT_Done_Glyph, vFp) +GOM(FT_Done_Library, iFEp) +GO(FT_Done_MM_Var, iFpp) +GOM(FT_Done_Size, iFEp) +GO(FT_Error_String, pFi) +GO(FT_Face_CheckTrueTypePatents, CFp) +GO(FT_Face_GetCharsOfVariant, pFpL) +GO(FT_Face_GetCharVariantIndex, uFpLL) +GO(FT_Face_GetCharVariantIsDefault, iFpLL) +GO(FT_Face_GetVariantSelectors, pFp) +GO(FT_Face_GetVariantsOfChar, pFpL) +GO(FT_Face_Properties, iFpup) +GO(FT_Face_SetUnpatentedHinting, CFpC) +GO(FT_FloorFix, lFl) +GO(FT_Get_Advance, iFpuip) +GO(FT_Get_Advances, iFpuuip) +GO(FT_Get_BDF_Charset_ID, iFppp) +GO(FT_Get_BDF_Property, iFppp) +GO(FT_Get_Char_Index, uFpL) +GO(FT_Get_Charmap_Index, iFp) +GO(FT_Get_CID_From_Glyph_Index, iFpup) +GO(FT_Get_CID_Is_Internally_CID_Keyed, iFpp) +GO(FT_Get_CID_Registry_Ordering_Supplement, iFpppp) +GO(FT_Get_CMap_Format, lFp) +GO(FT_Get_CMap_Language_ID, LFp) +GO(FT_Get_Color_Glyph_ClipBox, CFpup) +GO(FT_Get_Color_Glyph_Layer, CFpuppp) +GO(FT_Get_Color_Glyph_Paint, CFpuip) +GO(FT_Get_Colorline_Stops, CFppp) +GO(FT_Get_Default_Named_Instance, iFpp) +GO(FT_Get_First_Char, LFpp) +GO(FT_Get_Font_Format, pFp) +GO(FT_Get_FSType_Flags, WFp) +GO(FT_Get_Gasp, iFpu) +GO(FT_Get_Glyph, iFpp) +GO(FT_Get_Glyph_Name, iFpupu) +GO(FT_Get_Kerning, iFpuuup) +GO(FT_Get_MM_Blend_Coordinates, iFpup) +GO(FT_Get_MM_Var, iFpp) +GO(FT_Get_MM_WeightVector, iFppp) +GO(FT_Get_Module, pFpp) +GO(FT_Get_Multi_Master, iFpp) +GO(FT_Get_Name_Index, uFpp) +GO(FT_Get_Next_Char, LFpLp) +GOM(FT_Get_Paint, iFEppip) +GO(FT_Get_Paint_Layers, CFppp) +GO(FT_Get_PFR_Advance, iFpup) +GO(FT_Get_PFR_Kerning, iFpuup) +GO(FT_Get_PFR_Metrics, iFppppp) +GO(FT_Get_Postscript_Name, pFp) +GO(FT_Get_PS_Font_Info, iFpp) +GO(FT_Get_PS_Font_Private, iFpp) +GO(FT_Get_PS_Font_Value, lFpiupl) +GO(FT_Get_Renderer, pFpu) +GO(FT_Get_Sfnt_LangTag, iFpup) +GO(FT_Get_Sfnt_Name, iFpip) +GO(FT_Get_Sfnt_Name_Count, uFp) +GO(FT_Get_Sfnt_Table, pFpi) +GO(FT_Get_SubGlyph_Info, iFpuppppp) +GO(FT_Get_Track_Kerning, iFplip) +GO(FT_Get_Transform, vFppp) +GO(FT_Get_TrueType_Engine_Type, uFp) +GO(FT_Get_Var_Axis_Flags, iFpup) +GO(FT_Get_Var_Blend_Coordinates, iFpup) +GO(FT_Get_Var_Design_Coordinates, iFpup) +GO(FT_Get_WinFNT_Header, iFpp) +GO(FT_Get_X11_Font_Format, pFp) +GO(FT_Glyph_Copy, iFpp) +GO(FT_Glyph_Get_CBox, vFpup) +GO(FT_GlyphSlot_AdjustWeight, vFpll) +GO(FT_GlyphSlot_Embolden, vFp) +GO(FT_GlyphSlot_Oblique, vFp) +GO(FT_GlyphSlot_Own_Bitmap, iFp) +GO(FT_GlyphSlot_Slant, vFpll) +GO(FT_Glyph_Stroke, iFppC) +GO(FT_Glyph_StrokeBorder, iFppCC) +GO(FT_Glyph_To_Bitmap, iFpupC) +GO(FT_Glyph_Transform, iFppp) +GO(FT_Gzip_Uncompress, iFppppL) +GO(FT_Has_PS_Glyph_Names, iFp) +GO(FT_Init_FreeType, iFp) +GO(FT_Library_SetLcdFilter, iFpu) +GO(FT_Library_SetLcdFilterWeights, iFpp) +GO(FT_Library_SetLcdGeometry, iFpp) +GO(FT_Library_Version, vFpppp) +GO(FT_List_Add, vFpp) +GOM(FT_List_Finalize, vFEpppp) +GO(FT_List_Find, pFpp) +GO(FT_List_Insert, vFpp) +GOM(FT_List_Iterate, iFEppp) +GO(FT_List_Remove, vFpp) +GO(FT_List_Up, vFpp) +GO(FT_Load_Char, iFpLi) +GO(FT_Load_Glyph, iFpui) +GO(FT_Load_Sfnt_Table, iFpLlpp) +GO(FT_Matrix_Invert, iFp) +GO(FT_Matrix_Multiply, vFpp) +GO(FT_MulDiv, lFlll) +GO(FT_MulFix, lFll) +GOM(FT_New_Face, iFEpplp) +GO(FT_New_Glyph, iFpup) +GOM(FT_New_Library, iFEpp) +GOM(FT_New_Memory_Face, iFEppllp) +GOM(FT_New_Size, iFEpp) +GOM(FT_Open_Face, iFEpplp) +GO(FT_OpenType_Free, vFpp) +GO(FT_OpenType_Validate, iFpuppppp) +GO(FT_Outline_Check, iFp) +GO(FT_Outline_Copy, iFpp) +GOM(FT_Outline_Decompose, iFEppp) +GO(FT_Outline_Done, iFpp) +GO(FT_Outline_Embolden, iFpl) +GO(FT_Outline_EmboldenXY, iFpll) +GO(FT_Outline_Get_BBox, iFpp) +GO(FT_Outline_Get_Bitmap, iFppp) +GO(FT_Outline_Get_CBox, vFpp) +GO(FT_Outline_GetInsideBorder, uFp) +GO(FT_Outline_Get_Orientation, uFp) +GO(FT_Outline_GetOutsideBorder, uFp) +GO(FT_Outline_New, iFpuip) +GOM(FT_Outline_Render, iFEppp) +GO(FT_Outline_Reverse, vFp) +GO(FT_Outline_Transform, vFpp) +GO(FT_Outline_Translate, vFpll) +GO(FT_Palette_Data_Get, iFpp) +GO(FT_Palette_Select, iFpWp) +GOM(FT_Palette_Set_Foreground_Color, iFEpu) +GO(FT_Property_Get, iFpppp) +GO(FT_Property_Set, iFpppp) +GOM(FT_Reference_Face, iFEp) +GO(FT_Reference_Library, iFp) +GO(FT_Remove_Module, iFpp) +GO(FT_Render_Glyph, iFpu) +GO(FT_Request_Size, iFpp) +GO(FT_RoundFix, lFl) +GO(FT_Select_Charmap, iFpu) +GO(FT_Select_Size, iFpi) +GO(FT_Set_Charmap, iFpp) +GO(FT_Set_Char_Size, iFplluu) +GOM(FT_Set_Debug_Hook, vFEpup) +GO(FT_Set_Default_Log_Handler, vFv) +GO(FT_Set_Default_Properties, vFp) +GOM(FT_Set_Log_Handler, vFEp) +GO(FT_Set_MM_Blend_Coordinates, iFpup) +GO(FT_Set_MM_Design_Coordinates, iFpup) +GO(FT_Set_MM_WeightVector, iFpup) +GO(FT_Set_Named_Instance, iFpu) +GO(FT_Set_Pixel_Sizes, iFpuu) +GO(FT_Set_Renderer, iFppup) +GO(FT_Set_Transform, vFppp) +GO(FT_Set_Var_Blend_Coordinates, iFpup) +GO(FT_Set_Var_Design_Coordinates, iFpup) +GO(FT_Sfnt_Table_Info, iFpupp) +GO(FT_Sin, lFl) +GOM(FT_Stream_OpenBzip2, iFEpp) +GOM(FT_Stream_OpenGzip, iFEpp) +GOM(FT_Stream_OpenLZW, iFEpp) +GO(FT_Stroker_BeginSubPath, iFppC) +GO(FT_Stroker_ConicTo, iFppp) +GO(FT_Stroker_CubicTo, iFpppp) +GO(FT_Stroker_Done, vFp) +GO(FT_Stroker_EndSubPath, iFp) +GO(FT_Stroker_Export, vFpp) +GO(FT_Stroker_ExportBorder, vFpup) +GO(FT_Stroker_GetBorderCounts, iFpupp) +GO(FT_Stroker_GetCounts, iFppp) +GO(FT_Stroker_LineTo, iFpp) +GO(FT_Stroker_New, iFpp) +GO(FT_Stroker_ParseOutline, iFppC) +GO(FT_Stroker_Rewind, vFp) +GO(FT_Stroker_Set, vFpluul) +GO(FT_Tan, lFl) +GO(FT_Trace_Set_Default_Level, vFv) +GO(FT_Trace_Set_Level, vFp) +GO(FT_TrueTypeGX_Free, vFpp) +GO(FT_TrueTypeGX_Validate, iFpupu) +GO(FT_Vector_From_Polar, vFpll) +GO(FT_Vector_Length, lFp) +GO(FT_Vector_Polarize, vFppp) +GO(FT_Vector_Rotate, vFpl) +GO(FT_Vector_Transform, vFpp) +GO(FT_Vector_Unit, vFpl) + +GO(FTC_CMapCache_Lookup, uFppiu) +GO(FTC_CMapCache_New, iFpp) +GO(FTC_ImageCache_Lookup, iFppupp) +GO(FTC_ImageCache_LookupScaler, iFppLupp) +GO(FTC_ImageCache_New, iFpp) +GO(FTC_Manager_Done, vFp) +GO(FTC_Manager_LookupFace, iFppp) +GO(FTC_Manager_LookupSize, iFppp) +GOM(FTC_Manager_New, iFEpuuLppp) +GO(FTC_Manager_RemoveFaceID, vFpp) +GO(FTC_Manager_Reset, vFp) +GO(FTC_Node_Unref, vFpp) +GO(FTC_SBitCache_Lookup, iFppupp) +GO(FTC_SBitCache_LookupScaler, iFppLupp) +GO(FTC_SBitCache_New, iFpp) + +GOM(TT_New_Context, pFEv) +GOM(TT_RunIns, iFEv) diff --git a/target/i386/latx/include/wrappedlibxft_private.h b/target/i386/latx/include/wrappedlibxft_private.h index af4910792a..3ef40fedb7 100644 --- a/target/i386/latx/include/wrappedlibxft_private.h +++ b/target/i386/latx/include/wrappedlibxft_private.h @@ -1,81 +1,90 @@ +/* + * This file is derived from Box64. + * + * SPDX-FileCopyrightText: 2020 ptitSeb + * SPDX-FileCopyrightText: 2026 LAT Project Authors + * + * SPDX-License-Identifier: MIT + */ + #if !(defined(GO) && defined(GOM) && defined(GO2) && defined(DATA)) -#error Meh.... +#error Meh... #endif -//GO(XftCharExists, -//GO(XftCharFontSpecRender, -//GO(XftCharIndex, -//GO(XftCharSpecRender, -GO(XftColorAllocName, iFppupp) -//GO(XftColorAllocValue, -GO(XftColorFree, vFppup) -//GO(XftDefaultHasRender, -//GO(XftDefaultSet, -//GO(XftDefaultSubstitute, -GO(XftDrawChange, vFpp) -//GO(XftDrawCharFontSpec, -//GO(XftDrawCharSpec, -//GO(XftDrawColormap, -GO(XftDrawCreate, pFpppp) -//GO(XftDrawCreateAlpha, -//GO(XftDrawCreateBitmap, +GO(XftCharExists, iFppu) +GO(XftCharFontSpecRender, vFpiLLiipi) +GO(XftCharIndex, uFppu) +GO(XftCharSpecRender, vFpiLpLiipi) +GO(XftColorAllocName, iFppLpp) +GO(XftColorAllocValue, iFppLpp) +GO(XftColorFree, vFppLp) +GO(XftDefaultHasRender, iFp) +GO(XftDefaultSet, iFpp) +GO(XftDefaultSubstitute, vFpip) +GO(XftDrawChange, vFpL) +GO(XftDrawCharFontSpec, vFpppi) +GO(XftDrawCharSpec, vFppppi) +GO(XftDrawColormap, LFp) +GO(XftDrawCreate, pFpLpL) +GO(XftDrawCreateAlpha, pFpLi) +GO(XftDrawCreateBitmap, pFpL) GO(XftDrawDestroy, vFp) -//GO(XftDrawDisplay, -//GO(XftDrawDrawable, -//GO(XftDrawGlyphFontSpec, -//GO(XftDrawGlyphs, -//GO(XftDrawGlyphSpec, -//GO(XftDrawPicture, +GO(XftDrawDisplay, pFp) +GO(XftDrawDrawable, LFp) +GO(XftDrawGlyphFontSpec, vFpppi) +GO(XftDrawGlyphs, vFpppiipi) +GO(XftDrawGlyphSpec, vFppppi) +GO(XftDrawPicture, LFp) GO(XftDrawRect, vFppiiuu) GO(XftDrawSetClip, iFpp) -//GO(XftDrawSetClipRectangles, -//GO(XftDrawSetSubwindowMode, -//GO(XftDrawSrcPicture, +GO(XftDrawSetClipRectangles, iFpiipi) +GO(XftDrawSetSubwindowMode, vFpi) +GO(XftDrawSrcPicture, LFpp) GO(XftDrawString16, vFpppiipi) GO(XftDrawString32, vFpppiipi) -//GO(XftDrawString8, -//GO(XftDrawStringUtf16, -//GO(XftDrawStringUtf8, -//GO(XftDrawVisual, -//GO(XftFontCheckGlyph, +GO(XftDrawString8, vFpppiipi) +GO(XftDrawStringUtf16, vFpppiipui) +GO(XftDrawStringUtf8, vFpppiipi) +GO(XftDrawVisual, pFp) +GO(XftFontCheckGlyph, iFppiupp) GO(XftFontClose, vFpp) GO(XftFontCopy, pFpp) -//GO(XftFontInfoCreate, -//GO(XftFontInfoDestroy, -//GO(XftFontInfoEqual, -//GO(XftFontInfoHash, -//GO(XftFontLoadGlyphs, +GO(XftFontInfoCreate, pFpp) +GO(XftFontInfoDestroy, vFpp) +GO(XftFontInfoEqual, iFpp) +GO(XftFontInfoHash, uFp) +GO(XftFontLoadGlyphs, vFppipi) GO(XftFontMatch, pFpipp) -GO(XftFontOpen, pFpippppppppppppppppp) // use ... -//GO(XftFontOpenInfo, +GOM(XftFontOpen, pFEpiV) +GO(XftFontOpenInfo, pFppp) GO(XftFontOpenName, pFpip) GO(XftFontOpenPattern, pFpp) GO(XftFontOpenXlfd, pFpip) -//GO(XftFontUnloadGlyphs, -//GO(XftGetVersion, -//GO(XftGlyphExtents, -//GO(XftGlyphFontSpecRender, -//GO(XftGlyphRender, -//GO(XftGlyphSpecRender, -//GO(XftInit, -//GO(XftInitFtLibrary, -//GO(XftListFonts, -//GO(XftLockFace, -//GO(XftNameParse, +GO(XftFontUnloadGlyphs, vFpppi) +GO(XftGetVersion, iFv) +GO(XftGlyphExtents, vFpppip) +GO(XftGlyphFontSpecRender, vFpiLLiipi) +GO(XftGlyphRender, vFpiLpLiiiipi) +GO(XftGlyphSpecRender, vFpiLpLiipi) +GO(XftInit, iFp) +GO(XftInitFtLibrary, iFv) +GOM(XftListFonts, pFEpiV) +GO(XftLockFace, pFp) +GO(XftNameParse, pFp) GO(XftNameUnparse, iFppi) GO(XftTextExtents16, vFpppip) GO(XftTextExtents32, vFpppip) -//GO(XftTextExtents8, -//GO(XftTextExtentsUtf16, -//GO(XftTextExtentsUtf8, -//GO(XftTextRender16, -//GO(XftTextRender16BE, -//GO(XftTextRender16LE, -//GO(XftTextRender32, -//GO(XftTextRender32BE, -//GO(XftTextRender32LE, -//GO(XftTextRender8, -//GO(XftTextRenderUtf16, -//GO(XftTextRenderUtf8, -//GO(XftUnlockFace, -//GO(XftXlfdParse, +GO(XftTextExtents8, vFpppip) +GO(XftTextExtentsUtf16, vFpppuip) +GO(XftTextExtentsUtf8, vFpppip) +GO(XftTextRender16, vFpiLpLiiiipi) +GO(XftTextRender16BE, vFpiLpLiiiipi) +GO(XftTextRender16LE, vFpiLpLiiiipi) +GO(XftTextRender32, vFpiLpLiiiipi) +GO(XftTextRender32BE, vFpiLpLiiiipi) +GO(XftTextRender32LE, vFpiLpLiiiipi) +GO(XftTextRender8, vFpiLpLiiiipi) +GO(XftTextRenderUtf16, vFpiLpLiiiipui) +GO(XftTextRenderUtf8, vFpiLpLiiiipi) +GO(XftUnlockFace, vFp) +GO(XftXlfdParse, pFpii) From cac1006e047a0f486ce422a92d7cd2efe7ea6bdc Mon Sep 17 00:00:00 2001 From: Hanlu Li Date: Sat, 15 Aug 2026 08:26:57 +0800 Subject: [PATCH 04/11] LATX, feat: Require the font group for Cairo Make Cairo activation depend on the experimental font family and run the same whole-family preflight before loading the native Cairo wrapper. If FreeType, Fontconfig, or Xft is incompatible, disable font and let dependency pruning disable Cairo before host objects can cross into a partial native stack. The cairo_ft_* object crossings remain fail-closed until FT_Face and FcPattern ownership and lifetime rules are wired across the wrappers. Validated by the LoongArch product build and atomic preflight rejection probe. Signed-off-by: Hanlu Li --- target/i386/latx/context/wrappedcairo.c | 19 ++++++++++++------- target/i386/latx/include/kzt-group-list.h | 2 +- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/target/i386/latx/context/wrappedcairo.c b/target/i386/latx/context/wrappedcairo.c index 08ce60051b..88b35c5984 100644 --- a/target/i386/latx/context/wrappedcairo.c +++ b/target/i386/latx/context/wrappedcairo.c @@ -26,6 +26,7 @@ #include "library_private.h" #include "myalign.h" #include "wrappedcairo-preflight.h" +#include "wrappedfont-preflight.h" #include "wrapper.h" #pragma GCC diagnostic push @@ -845,9 +846,10 @@ static uint32_t cairo_ft_interop_failure(void *font, void *cr, void *extents) } /* - * LAT does not yet wrap FreeType or Fontconfig. Their opaque guest objects - * cannot be passed to host Cairo, and a host FT_Face cannot be returned to - * guest code. Return a valid host Cairo face that fails explicitly on use. + * The font wrapper family is available, but Cairo object interoperation stays + * closed until FT_Face and FcPattern ownership and lifetime rules are wired + * across both wrappers. Return a valid host Cairo face that fails explicitly + * on use instead of mixing guest and host opaque objects. */ static void *cairo_ft_unsupported_font_face(void) { @@ -858,8 +860,7 @@ static void *cairo_ft_unsupported_font_face(void) cairo_ft_interop_failure); } kzt_groups_log_wrapper_limitation( - cairoName, "Cairo FreeType/Fontconfig interop requires native " - "FreeType and Fontconfig wrappers"); + cairoName, "Cairo FreeType/Fontconfig object interop is not enabled"); return font_face; } @@ -887,8 +888,7 @@ EXPORT void my_cairo_ft_font_options_substitute(void *pattern, void *options) (void)pattern; (void)options; kzt_groups_log_wrapper_limitation( - cairoName, "Cairo Fontconfig interop requires a native Fontconfig " - "wrapper"); + cairoName, "Cairo Fontconfig object interop is not enabled"); } EXPORT void *my_cairo_ft_scaled_font_lock_face(void *scaled_font) @@ -1330,12 +1330,17 @@ static void cairo_callbacks_clear(void) #define PRE_INIT_GUEST \ do { \ char reason[256]; \ + if (latx_font_preflight_or_disable(&box64->box64_ld_lib, \ + cairoName) != 0) { \ + return -1; \ + } \ char *guest_path = ResolveFile(lib->path, &box64->box64_ld_lib); \ bool safe = latx_cairo_preflight_guest(guest_path, cairoName, reason, \ sizeof(reason)); \ box_free(guest_path); \ if (!safe) { \ kzt_groups_log_wrapper_rejection(cairoName, reason); \ + kzt_group_disable(KZT_GROUP_CAIRO, reason); \ return -1; \ } \ } while (0); diff --git a/target/i386/latx/include/kzt-group-list.h b/target/i386/latx/include/kzt-group-list.h index fbbb96913d..083fb7b1bf 100644 --- a/target/i386/latx/include/kzt-group-list.h +++ b/target/i386/latx/include/kzt-group-list.h @@ -25,6 +25,6 @@ X(VULKAN, "vulkan", 3, STABLE, KZT_GROUP_CORE | KZT_GROUP_X11) \ X(VAAPI, "vaapi", 4, STABLE, KZT_GROUP_CORE | KZT_GROUP_X11) \ X(FONT, "font", 6, EXPERIMENTAL, KZT_GROUP_CORE | KZT_GROUP_X11) \ - X(CAIRO, "cairo", 5, EXPERIMENTAL, KZT_GROUP_CORE | KZT_GROUP_X11) + X(CAIRO, "cairo", 5, EXPERIMENTAL, KZT_GROUP_FONT) #endif /* LATX_KZT_GROUP_LIST_H */ From eefc9d05b267da481925ba52293bda17ac6f097e Mon Sep 17 00:00:00 2001 From: Hanlu Li Date: Sat, 15 Aug 2026 09:55:01 +0800 Subject: [PATCH 05/11] LATX, feat: Preflight native wrapper prerequisites Custom wrappers can require host helper symbols that are not guaranteed by scanning the guest library exports alone. Let each wrapped library declare unconditional native prerequisites and reject the whole library family before activation when one is missing. Cover the Cairo font-face helpers, the FreeType face lifetime calls, the Fontconfig FT_Face query, and the Xft helper calls used by custom wrappers. Validated by the LoongArch product build. Signed-off-by: Hanlu Li --- .../latx/context/wrappedcairo-preflight.c | 7 +++++ .../i386/latx/context/wrappedfont-preflight.c | 29 ++++++++++++++++++- .../i386/latx/context/wrappedlib-preflight.c | 13 ++++++++- .../i386/latx/include/wrappedlib-preflight.h | 2 ++ 4 files changed, 49 insertions(+), 2 deletions(-) diff --git a/target/i386/latx/context/wrappedcairo-preflight.c b/target/i386/latx/context/wrappedcairo-preflight.c index bc93814b1a..f05608093d 100644 --- a/target/i386/latx/context/wrappedcairo-preflight.c +++ b/target/i386/latx/context/wrappedcairo-preflight.c @@ -24,6 +24,11 @@ static const char *const cairo_supported_symbols[] = { #include "wrappedcairo_private.h" }; +static const char *const cairo_required_host_symbols[] = { + "cairo_font_face_destroy", + "cairo_font_face_get_user_data", + "cairo_font_face_status", +}; #undef GO #undef GOM #undef GOW @@ -47,5 +52,7 @@ bool latx_cairo_preflight_guest(const char *guest_path, return latx_wrappedlib_preflight_guest( guest_path, host_soname, "Cairo", cairo_symbol_filter, cairo_supported_symbols, ARRAY_SIZE(cairo_supported_symbols), + cairo_required_host_symbols, + ARRAY_SIZE(cairo_required_host_symbols), reason, reason_size); } diff --git a/target/i386/latx/context/wrappedfont-preflight.c b/target/i386/latx/context/wrappedfont-preflight.c index ffa6551b09..84c5da19f7 100644 --- a/target/i386/latx/context/wrappedfont-preflight.c +++ b/target/i386/latx/context/wrappedfont-preflight.c @@ -33,6 +33,20 @@ static const char *const fontconfig_supported_symbols[] = { static const char *const libxft_supported_symbols[] = { #include "wrappedlibxft_private.h" }; +static const char *const freetype_required_host_symbols[] = { + "FT_Done_Face", + "FT_Reference_Face", +}; +static const char *const fontconfig_required_host_symbols[] = { + "FcPatternGetFTFace", +}; +static const char *const libxft_required_host_symbols[] = { + "FcFontList", + "FcObjectSetDestroy", + "FcPatternDestroy", + "XftFontMatch", + "XftFontOpenPattern", +}; #undef GO #undef GOM #undef GOW @@ -51,6 +65,8 @@ typedef struct FontLibraryPreflight { LatxWrappedSymbolFilter symbol_filter; const char *const *supported_symbols; size_t supported_symbol_count; + const char *const *required_host_symbols; + size_t required_host_symbol_count; } FontLibraryPreflight; static bool freetype_symbol_filter(const char *name) @@ -80,6 +96,9 @@ bool latx_font_preflight_guest(path_collection_t *guest_paths, .symbol_filter = freetype_symbol_filter, .supported_symbols = freetype_supported_symbols, .supported_symbol_count = ARRAY_SIZE(freetype_supported_symbols), + .required_host_symbols = freetype_required_host_symbols, + .required_host_symbol_count = + ARRAY_SIZE(freetype_required_host_symbols), }, { .guest_soname = "libfontconfig.so.1", @@ -88,6 +107,9 @@ bool latx_font_preflight_guest(path_collection_t *guest_paths, .symbol_filter = fontconfig_symbol_filter, .supported_symbols = fontconfig_supported_symbols, .supported_symbol_count = ARRAY_SIZE(fontconfig_supported_symbols), + .required_host_symbols = fontconfig_required_host_symbols, + .required_host_symbol_count = + ARRAY_SIZE(fontconfig_required_host_symbols), }, { .guest_soname = "libXft.so.2", @@ -96,6 +118,9 @@ bool latx_font_preflight_guest(path_collection_t *guest_paths, .symbol_filter = xft_symbol_filter, .supported_symbols = libxft_supported_symbols, .supported_symbol_count = ARRAY_SIZE(libxft_supported_symbols), + .required_host_symbols = libxft_required_host_symbols, + .required_host_symbol_count = + ARRAY_SIZE(libxft_required_host_symbols), }, }; @@ -109,7 +134,9 @@ bool latx_font_preflight_guest(path_collection_t *guest_paths, bool safe = latx_wrappedlib_preflight_guest( guest_path, library->host_soname, library->label, library->symbol_filter, library->supported_symbols, - library->supported_symbol_count, reason, reason_size); + library->supported_symbol_count, + library->required_host_symbols, + library->required_host_symbol_count, reason, reason_size); box_free(guest_path); if (!safe) { diff --git a/target/i386/latx/context/wrappedlib-preflight.c b/target/i386/latx/context/wrappedlib-preflight.c index 2146e48c64..8a273ad3b5 100644 --- a/target/i386/latx/context/wrappedlib-preflight.c +++ b/target/i386/latx/context/wrappedlib-preflight.c @@ -44,6 +44,8 @@ bool latx_wrappedlib_preflight_guest( const char *guest_path, const char *host_soname, const char *library_label, LatxWrappedSymbolFilter symbol_filter, const char *const *supported_symbols, size_t supported_symbol_count, + const char *const *required_host_symbols, + size_t required_host_symbol_count, char *reason, size_t reason_size) { Elf64_Ehdr ehdr; @@ -60,7 +62,8 @@ bool latx_wrappedlib_preflight_guest( } reason[0] = '\0'; if (!guest_path || !host_soname || !library_label || !symbol_filter || - !supported_symbols || !supported_symbol_count) { + !supported_symbols || !supported_symbol_count || + (required_host_symbol_count && !required_host_symbols)) { snprintf(reason, reason_size, "wrapped-library preflight configuration is incomplete"); return false; @@ -109,6 +112,14 @@ bool latx_wrappedlib_preflight_guest( library_label, dlerror()); goto out; } + for (size_t i = 0; i < required_host_symbol_count; i++) { + if (!dlsym(host, required_host_symbols[i])) { + snprintf(reason, reason_size, + "host %s is missing wrapper prerequisite '%s'", + library_label, required_host_symbols[i]); + goto out; + } + } for (size_t section_index = 0; section_index < ehdr.e_shnum; section_index++) { diff --git a/target/i386/latx/include/wrappedlib-preflight.h b/target/i386/latx/include/wrappedlib-preflight.h index 389308d529..72ca45d7cd 100644 --- a/target/i386/latx/include/wrappedlib-preflight.h +++ b/target/i386/latx/include/wrappedlib-preflight.h @@ -16,6 +16,8 @@ bool latx_wrappedlib_preflight_guest( const char *guest_path, const char *host_soname, const char *library_label, LatxWrappedSymbolFilter symbol_filter, const char *const *supported_symbols, size_t supported_symbol_count, + const char *const *required_host_symbols, + size_t required_host_symbol_count, char *reason, size_t reason_size); #endif /* LATX_WRAPPEDLIB_PREFLIGHT_H */ From 09e6e44615713021843b831771cdab0bb9b7c99a Mon Sep 17 00:00:00 2001 From: Hanlu Li Date: Sat, 15 Aug 2026 09:55:08 +0800 Subject: [PATCH 06/11] LATX, feat: Share native font object lifetime state Expose narrow inter-wrapper helpers for retaining host FT_Face objects, marking Cairo-borrowed faces, and reading an FcPattern FC_FT_FACE value. Keep borrowed faces separate from caller-owned FreeType faces. FT_Done_Face now rejects a face while Cairo has lent it to guest code, and wrapper teardown clears the associated bookkeeping without handing guest pointers to native code. Validated by the LoongArch product build and the FT_Face ownership probe. Signed-off-by: Hanlu Li --- target/i386/latx/context/wrappedfontconfig.c | 15 +++ target/i386/latx/context/wrappedfreetype.c | 95 +++++++++++++++++++ .../i386/latx/include/wrappedfont-interop.h | 19 ++++ 3 files changed, 129 insertions(+) create mode 100644 target/i386/latx/include/wrappedfont-interop.h diff --git a/target/i386/latx/context/wrappedfontconfig.c b/target/i386/latx/context/wrappedfontconfig.c index 6bb3ab0655..27a0051544 100644 --- a/target/i386/latx/context/wrappedfontconfig.c +++ b/target/i386/latx/context/wrappedfontconfig.c @@ -24,6 +24,7 @@ #include "library_private.h" #include "wrappedfont-vaargs.h" #include "wrappedfont-preflight.h" +#include "wrappedfont-interop.h" #include "wrapper.h" #pragma GCC diagnostic push @@ -54,6 +55,7 @@ typedef int32_t (*fc_iFpp_t)(void *, void *); typedef int32_t (*fc_iFppi_t)(void *, void *, int32_t); typedef int32_t (*fc_iFppd_t)(void *, void *, double); typedef int32_t (*fc_iFppp_t)(void *, void *, void *); +typedef int32_t (*fc_iFppip_t)(void *, void *, int32_t, void *); typedef void (*fc_vFp_t)(void *); #define ADDED_FUNCTIONS() \ @@ -71,6 +73,7 @@ typedef void (*fc_vFp_t)(void *); GO(FcPatternAddString, fc_iFppp_t) \ GO(FcPatternCreate, fc_pFv_t) \ GO(FcPatternDestroy, fc_vFp_t) \ + GO(FcPatternGetFTFace, fc_iFppip_t) \ GO(FcStrCopy, fc_pFp_t) #include "generated/wrappedfontconfigtypes.h" @@ -90,6 +93,18 @@ enum { FC_TYPE_RANGE = 9, }; +void *latx_fontconfig_pattern_ft_face(void *pattern) +{ + static const char fc_ft_face[] = "ftface"; + void *face = NULL; + + if (my_lib && my->FcPatternGetFTFace(pattern, (void *)fc_ft_face, 0, + &face) == 0) { + return face; + } + return NULL; +} + static bool fontconfig_pattern_add_value(void *pattern, const char *object, int32_t type, LatxX64VaReader *reader) diff --git a/target/i386/latx/context/wrappedfreetype.c b/target/i386/latx/context/wrappedfreetype.c index eaad9c6f10..2cf6331eac 100644 --- a/target/i386/latx/context/wrappedfreetype.c +++ b/target/i386/latx/context/wrappedfreetype.c @@ -24,6 +24,7 @@ #include "kzt-groups.h" #include "library_private.h" #include "wrappedfont-preflight.h" +#include "wrappedfont-interop.h" #include "wrapper.h" #pragma GCC diagnostic push @@ -197,9 +198,16 @@ typedef struct FtSizeTracker { struct FtSizeTracker *next; } FtSizeTracker; +typedef struct FtBorrowTracker { + void *face; + unsigned references; + struct FtBorrowTracker *next; +} FtBorrowTracker; + static GMutex freetype_lock; static FtFaceTracker *freetype_faces; static FtSizeTracker *freetype_sizes; +static FtBorrowTracker *freetype_borrowed_faces; static bool freetype_generic_is_safe(const FtGenericAbi *generic) { @@ -334,6 +342,68 @@ static void freetype_untrack_library_locked(void *library) } } +static bool freetype_face_is_borrowed_locked(void *face) +{ + for (FtBorrowTracker *tracker = freetype_borrowed_faces; tracker; + tracker = tracker->next) { + if (tracker->face == face) { + return true; + } + } + return false; +} + +bool latx_freetype_face_borrow(void *face) +{ + FtBorrowTracker *tracker; + + if (!face) { + return false; + } + g_mutex_lock(&freetype_lock); + for (tracker = freetype_borrowed_faces; tracker; + tracker = tracker->next) { + if (tracker->face == face) { + tracker->references++; + g_mutex_unlock(&freetype_lock); + return true; + } + } + tracker = calloc(1, sizeof(*tracker)); + if (!tracker) { + g_mutex_unlock(&freetype_lock); + return false; + } + tracker->face = face; + tracker->references = 1; + tracker->next = freetype_borrowed_faces; + freetype_borrowed_faces = tracker; + g_mutex_unlock(&freetype_lock); + return true; +} + +void latx_freetype_face_return(void *face) +{ + FtBorrowTracker **cursor; + + g_mutex_lock(&freetype_lock); + cursor = &freetype_borrowed_faces; + while (*cursor) { + FtBorrowTracker *tracker = *cursor; + + if (tracker->face != face) { + cursor = &tracker->next; + continue; + } + if (--tracker->references == 0) { + *cursor = tracker->next; + free(tracker); + } + break; + } + g_mutex_unlock(&freetype_lock); +} + #define FT_CALLBACK_SLOTS 16 #define FT_SLOT_LIST(X) \ X(0) X(1) X(2) X(3) X(4) X(5) X(6) X(7) \ @@ -593,6 +663,12 @@ EXPORT int32_t my_FT_Done_Face(void *face) int32_t status; g_mutex_lock(&freetype_lock); + if (freetype_face_is_borrowed_locked(face)) { + g_mutex_unlock(&freetype_lock); + kzt_groups_log_wrapper_limitation( + freetypeName, "cannot destroy a Cairo-borrowed FT_Face"); + return FT_ERROR_INVALID_ARGUMENT; + } if (!freetype_face_is_safe(face)) { g_mutex_unlock(&freetype_lock); kzt_groups_log_wrapper_limitation( @@ -649,6 +725,19 @@ EXPORT int32_t my_FT_Reference_Face(void *face) return FT_ERROR_OUT_OF_MEMORY; } +bool latx_freetype_face_reference(void *face) +{ + return my_lib && my_FT_Reference_Face(face) == FT_ERROR_SUCCESS; +} + +void latx_freetype_face_release(void *face) +{ + if (!my_lib || my_FT_Done_Face(face) != FT_ERROR_SUCCESS) { + kzt_groups_log_wrapper_limitation( + freetypeName, "cannot release Cairo-owned FT_Face reference"); + } +} + static int32_t freetype_done_library(void *library, int32_t (*finish)(void *)) { @@ -938,6 +1027,12 @@ static void freetype_state_clear(void) free(freetype_sizes); freetype_sizes = next; } + while (freetype_borrowed_faces) { + FtBorrowTracker *next = freetype_borrowed_faces->next; + + free(freetype_borrowed_faces); + freetype_borrowed_faces = next; + } memset(ft_move_slots, 0, sizeof(ft_move_slots)); memset(ft_line_slots, 0, sizeof(ft_line_slots)); memset(ft_conic_slots, 0, sizeof(ft_conic_slots)); diff --git a/target/i386/latx/include/wrappedfont-interop.h b/target/i386/latx/include/wrappedfont-interop.h new file mode 100644 index 0000000000..f10dad7168 --- /dev/null +++ b/target/i386/latx/include/wrappedfont-interop.h @@ -0,0 +1,19 @@ +/* + * SPDX-FileCopyrightText: 2026 LAT Project Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef LATX_WRAPPEDFONT_INTEROP_H +#define LATX_WRAPPEDFONT_INTEROP_H + +#include + +bool latx_freetype_face_reference(void *face); +void latx_freetype_face_release(void *face); +bool latx_freetype_face_borrow(void *face); +void latx_freetype_face_return(void *face); + +void *latx_fontconfig_pattern_ft_face(void *pattern); + +#endif From 01305e359fea55784a3464b9abdec23ae8c89d6b Mon Sep 17 00:00:00 2001 From: Hanlu Li Date: Sat, 15 Aug 2026 09:55:21 +0800 Subject: [PATCH 07/11] LATX, feat: Bridge Cairo font backend objects Pass native cairo_font_options_t and FcPattern objects through cairo_ft_font_options_substitute and cairo_ft_font_face_create_for_pattern using the public Cairo argument order. Retain pre-opened FT_Face objects with Cairo user data, and track the ownership and thread of faces borrowed by cairo_ft_scaled_font_lock_face until the matching unlock. Fail with a Cairo error face when the required lifetime reference cannot be established. Validated by the LoongArch product build, native entry probes for the Cairo FT APIs, and three gtkperf runs without Pango warnings or X errors. Signed-off-by: Hanlu Li --- target/i386/latx/context/wrappedcairo.c | 201 +++++++++++++++++++++--- 1 file changed, 179 insertions(+), 22 deletions(-) diff --git a/target/i386/latx/context/wrappedcairo.c b/target/i386/latx/context/wrappedcairo.c index 88b35c5984..ea941e833b 100644 --- a/target/i386/latx/context/wrappedcairo.c +++ b/target/i386/latx/context/wrappedcairo.c @@ -26,6 +26,7 @@ #include "library_private.h" #include "myalign.h" #include "wrappedcairo-preflight.h" +#include "wrappedfont-interop.h" #include "wrappedfont-preflight.h" #include "wrapper.h" @@ -35,6 +36,15 @@ const char *cairoName = "libcairo.so.2"; #define LIBNAME cairo +typedef void (*cairo_native_vFp_t)(void *); +typedef void *(*cairo_native_pFpp_t)(void *, void *); +typedef uint32_t (*cairo_native_uFp_t)(void *); + +#define ADDED_FUNCTIONS() \ + GO(cairo_font_face_destroy, cairo_native_vFp_t) \ + GO(cairo_font_face_get_user_data, cairo_native_pFpp_t) \ + GO(cairo_font_face_status, cairo_native_uFp_t) + #include "generated/wrappedcairotypes.h" #include "wrappercallback.h" @@ -128,13 +138,23 @@ typedef struct CairoObjectTracker { struct CairoObjectTracker *next; } CairoObjectTracker; +typedef struct CairoFtFaceLock { + void *scaled_font; + void *face; + GThread *owner; + unsigned references; + struct CairoFtFaceLock *next; +} CairoFtFaceLock; + static GMutex cairo_callback_lock; static CairoCallbackBinding *cairo_bindings; static CairoObjectTracker *cairo_trackers; +static CairoFtFaceLock *cairo_ft_face_locks; static char cairo_surface_tracker_key; static char cairo_device_tracker_key; static char cairo_font_face_tracker_key; static char cairo_pattern_tracker_key; +static char cairo_ft_face_key; static CairoCallbackSlot cairo_destroy_slots[CAIRO_CALLBACK_SLOT_COUNT]; static CairoCallbackSlot cairo_io_slots[CAIRO_CALLBACK_SLOT_COUNT]; @@ -845,13 +865,7 @@ static uint32_t cairo_ft_interop_failure(void *font, void *cr, void *extents) return CAIRO_STATUS_FONT_TYPE_MISMATCH; } -/* - * The font wrapper family is available, but Cairo object interoperation stays - * closed until FT_Face and FcPattern ownership and lifetime rules are wired - * across both wrappers. Return a valid host Cairo face that fails explicitly - * on use instead of mixing guest and host opaque objects. - */ -static void *cairo_ft_unsupported_font_face(void) +static void *cairo_ft_error_font_face(const char *reason) { void *font_face = my->cairo_user_font_face_create(); @@ -859,11 +873,48 @@ static void *cairo_ft_unsupported_font_face(void) my->cairo_user_font_face_set_init_func(font_face, cairo_ft_interop_failure); } - kzt_groups_log_wrapper_limitation( - cairoName, "Cairo FreeType/Fontconfig object interop is not enabled"); + kzt_groups_log_wrapper_limitation(cairoName, reason); return font_face; } +static void cairo_ft_release_face(void *face) +{ + latx_freetype_face_release(face); +} + +/* + * Cairo does not own a pre-opened FT_Face, so retain one wrapper-managed + * reference until the last cached cairo_font_face_t reference is released. + */ +static bool cairo_ft_keep_face_alive(void *font_face, void *face) +{ + void *retained; + uint32_t status; + + if (!face) { + return true; + } + g_mutex_lock(&cairo_callback_lock); + retained = my->cairo_font_face_get_user_data(font_face, + &cairo_ft_face_key); + if (retained) { + g_mutex_unlock(&cairo_callback_lock); + return retained == face; + } + if (!latx_freetype_face_reference(face)) { + g_mutex_unlock(&cairo_callback_lock); + return false; + } + status = my->cairo_font_face_set_user_data( + font_face, &cairo_ft_face_key, face, cairo_ft_release_face); + g_mutex_unlock(&cairo_callback_lock); + if (status != CAIRO_STATUS_SUCCESS) { + latx_freetype_face_release(face); + return false; + } + return true; +} + EXPORT void *my_cairo_user_font_face_create(void) { return my->cairo_user_font_face_create(); @@ -872,36 +923,133 @@ EXPORT void *my_cairo_user_font_face_create(void) EXPORT void *my_cairo_ft_font_face_create_for_ft_face(void *face, int32_t load_flags) { - (void)face; - (void)load_flags; - return cairo_ft_unsupported_font_face(); + void *font_face = my->cairo_ft_font_face_create_for_ft_face(face, + load_flags); + + if (!font_face || + my->cairo_font_face_status(font_face) != CAIRO_STATUS_SUCCESS) { + return font_face; + } + if (cairo_ft_keep_face_alive(font_face, face)) { + return font_face; + } + my->cairo_font_face_destroy(font_face); + return cairo_ft_error_font_face( + "cannot retain FT_Face for Cairo font-face lifetime"); } EXPORT void *my_cairo_ft_font_face_create_for_pattern(void *pattern) { - (void)pattern; - return cairo_ft_unsupported_font_face(); + void *face = latx_fontconfig_pattern_ft_face(pattern); + void *font_face = my->cairo_ft_font_face_create_for_pattern(pattern); + + if (!font_face || !face || + my->cairo_font_face_status(font_face) != CAIRO_STATUS_SUCCESS) { + return font_face; + } + if (cairo_ft_keep_face_alive(font_face, face)) { + return font_face; + } + my->cairo_font_face_destroy(font_face); + return cairo_ft_error_font_face( + "cannot retain FcPattern FT_Face for Cairo font-face lifetime"); } -EXPORT void my_cairo_ft_font_options_substitute(void *pattern, void *options) +EXPORT void my_cairo_ft_font_options_substitute(void *options, void *pattern) { - (void)pattern; - (void)options; - kzt_groups_log_wrapper_limitation( - cairoName, "Cairo Fontconfig object interop is not enabled"); + my->cairo_ft_font_options_substitute(options, pattern); } EXPORT void *my_cairo_ft_scaled_font_lock_face(void *scaled_font) { - (void)scaled_font; + CairoFtFaceLock *tracker; + GThread *owner = g_thread_self(); + void *face = my->cairo_ft_scaled_font_lock_face(scaled_font); + + if (!face) { + return NULL; + } + /* The returned face is owned by Cairo and is valid only until unlock. */ + if (!latx_freetype_face_borrow(face)) { + my->cairo_ft_scaled_font_unlock_face(scaled_font); + kzt_groups_log_wrapper_limitation( + cairoName, "cannot track Cairo-borrowed FT_Face"); + return NULL; + } + + g_mutex_lock(&cairo_callback_lock); + for (tracker = cairo_ft_face_locks; tracker; tracker = tracker->next) { + if (tracker->scaled_font != scaled_font) { + continue; + } + if (tracker->face == face && tracker->owner == owner) { + tracker->references++; + g_mutex_unlock(&cairo_callback_lock); + return face; + } + g_mutex_unlock(&cairo_callback_lock); + latx_freetype_face_return(face); + my->cairo_ft_scaled_font_unlock_face(scaled_font); + kzt_groups_log_wrapper_limitation( + cairoName, "inconsistent nested Cairo FT_Face lock"); + return NULL; + } + + tracker = calloc(1, sizeof(*tracker)); + if (tracker) { + tracker->scaled_font = scaled_font; + tracker->face = face; + tracker->owner = owner; + tracker->references = 1; + tracker->next = cairo_ft_face_locks; + cairo_ft_face_locks = tracker; + } + g_mutex_unlock(&cairo_callback_lock); + if (tracker) { + return face; + } + + latx_freetype_face_return(face); + my->cairo_ft_scaled_font_unlock_face(scaled_font); kzt_groups_log_wrapper_limitation( - cairoName, "returning a host FT_Face to guest code is unsupported"); + cairoName, "cannot allocate Cairo FT_Face lock tracker"); return NULL; } EXPORT void my_cairo_ft_scaled_font_unlock_face(void *scaled_font) { - (void)scaled_font; + CairoFtFaceLock **cursor; + CairoFtFaceLock *tracker = NULL; + GThread *owner = g_thread_self(); + void *face; + + g_mutex_lock(&cairo_callback_lock); + cursor = &cairo_ft_face_locks; + while (*cursor) { + if ((*cursor)->scaled_font != scaled_font || + (*cursor)->owner != owner) { + cursor = &(*cursor)->next; + continue; + } + tracker = *cursor; + if (--tracker->references == 0) { + *cursor = tracker->next; + } + break; + } + g_mutex_unlock(&cairo_callback_lock); + if (!tracker) { + kzt_groups_log_wrapper_limitation( + cairoName, "Cairo FT_Face unlock has no matching guest lock"); + return; + } + + face = tracker->face; + my->cairo_ft_scaled_font_unlock_face(scaled_font); + latx_freetype_face_return(face); + if (tracker->references == 0) { + free(tracker); + } } #define DEFINE_USER_DATA_WRAPPER(name, object_type) \ @@ -1287,6 +1435,7 @@ static void cairo_callbacks_clear(void) { CairoCallbackBinding *binding; CairoObjectTracker *tracker; + CairoFtFaceLock *face_lock; for (;;) { g_mutex_lock(&cairo_callback_lock); @@ -1304,6 +1453,14 @@ static void cairo_callbacks_clear(void) } g_mutex_lock(&cairo_callback_lock); + while ((face_lock = cairo_ft_face_locks)) { + cairo_ft_face_locks = face_lock->next; + while (face_lock->references > 0) { + face_lock->references--; + latx_freetype_face_return(face_lock->face); + } + free(face_lock); + } while ((binding = cairo_bindings)) { cairo_bindings = binding->next; free(binding); From 33bf72eb0a2bd8f787de3c63b47e856e5b7f216c Mon Sep 17 00:00:00 2001 From: Hanlu Li Date: Sat, 15 Aug 2026 14:51:04 +0800 Subject: [PATCH 08/11] LATX, fix: Preserve Fontconfig container ownership Pango can build FcFontSet containers with the guest allocator while storing host-native FcPattern pointers. Passing those containers to native Fontconfig lets the host allocator free guest memory. Transfer guest-owned containers into host-owned storage before native destruction, and keep native-owned sets on the direct path. Validated with the normal LAT product build and repeated LoongArch gtkperf runs using the font and Cairo groups. Signed-off-by: Hanlu Li --- target/i386/latx/context/wrappedfontconfig.c | 94 +++++++++++++++++++ .../latx/include/wrappedfontconfig_private.h | 4 +- 2 files changed, 96 insertions(+), 2 deletions(-) diff --git a/target/i386/latx/context/wrappedfontconfig.c b/target/i386/latx/context/wrappedfontconfig.c index 27a0051544..9f4f570cd4 100644 --- a/target/i386/latx/context/wrappedfontconfig.c +++ b/target/i386/latx/context/wrappedfontconfig.c @@ -48,6 +48,15 @@ typedef struct FcValueAbi { _Static_assert(sizeof(FcValueAbi) == 16, "FcValue ABI"); +typedef struct FcFontSetAbi { + int32_t nfont; + int32_t sfont; + void **fonts; +} FcFontSetAbi; + +_Static_assert(sizeof(FcFontSetAbi) == 16, "FcFontSet ABI"); +_Static_assert(offsetof(FcFontSetAbi, fonts) == 8, "FcFontSet fonts ABI"); + typedef void *(*fc_pFv_t)(void); typedef void *(*fc_pFp_t)(void *); typedef void *(*fc_pFpp_t)(void *, void *); @@ -62,6 +71,8 @@ typedef void (*fc_vFp_t)(void *); GO(FcObjectSetAdd, fc_iFpp_t) \ GO(FcObjectSetCreate, fc_pFv_t) \ GO(FcObjectSetDestroy, fc_vFp_t) \ + GO(FcFontSetDestroy, fc_vFp_t) \ + GO(FcFontSetSortDestroy, fc_vFp_t) \ GO(FcPatternAddBool, fc_iFppi_t) \ GO(FcPatternAddCharSet, fc_iFppp_t) \ GO(FcPatternAddDouble, fc_iFppd_t) \ @@ -278,6 +289,89 @@ EXPORT void my_FcValuePrint(unsigned __int128 bits) my->FcValuePrint(value); } +extern void *x86free; + +static void fontconfig_free_container(void *pointer) +{ + if (!pointer) { + return; + } + if ((uintptr_t)pointer >= reserved_va) { + free(pointer); + return; + } + if (!x86free) { + kzt_groups_log_wrapper_limitation( + fontconfigName, "guest FcFontSet container cannot be freed"); + return; + } + RunFunctionFmt((uintptr_t)x86free, "p", pointer); +} + +static void fontconfig_font_set_destroy(void *pointer, fc_vFp_t destroy) +{ + FcFontSetAbi *font_set = pointer; + FcFontSetAbi *native_set; + void **native_fonts = NULL; + size_t font_count; + + if (!font_set) { + destroy(NULL); + return; + } + if ((uintptr_t)font_set >= reserved_va && + (!font_set->fonts || (uintptr_t)font_set->fonts >= reserved_va)) { + destroy(font_set); + return; + } + if (font_set->nfont < 0 || font_set->sfont < font_set->nfont || + (font_set->nfont && !font_set->fonts)) { + kzt_groups_log_wrapper_limitation( + fontconfigName, "invalid guest FcFontSet container"); + return; + } + + font_count = (size_t)font_set->nfont; + if (font_count > SIZE_MAX / sizeof(*native_fonts)) { + kzt_groups_log_wrapper_limitation( + fontconfigName, "oversized guest FcFontSet container"); + return; + } + native_set = malloc(sizeof(*native_set)); + if (font_count) { + native_fonts = malloc(font_count * sizeof(*native_fonts)); + } + if (!native_set || (font_count && !native_fonts)) { + free(native_fonts); + free(native_set); + kzt_groups_log_wrapper_limitation( + fontconfigName, "guest FcFontSet transfer allocation failed"); + return; + } + + if (font_count) { + memcpy(native_fonts, font_set->fonts, + font_count * sizeof(*native_fonts)); + } + native_set->nfont = font_set->nfont; + native_set->sfont = font_set->nfont; + native_set->fonts = native_fonts; + + fontconfig_free_container(font_set->fonts); + fontconfig_free_container(font_set); + destroy(native_set); +} + +EXPORT void my_FcFontSetDestroy(void *font_set) +{ + fontconfig_font_set_destroy(font_set, my->FcFontSetDestroy); +} + +EXPORT void my_FcFontSetSortDestroy(void *font_set) +{ + fontconfig_font_set_destroy(font_set, my->FcFontSetSortDestroy); +} + EXPORT unsigned __int128 my_FcValueSave(unsigned __int128 bits) { FcValueAbi value; diff --git a/target/i386/latx/include/wrappedfontconfig_private.h b/target/i386/latx/include/wrappedfontconfig_private.h index 55a3a9ecea..ac779ac1d7 100644 --- a/target/i386/latx/include/wrappedfontconfig_private.h +++ b/target/i386/latx/include/wrappedfontconfig_private.h @@ -104,12 +104,12 @@ GO(FcFontMatch, pFppp) GO(FcFontRenderPrepare, pFppp) GO(FcFontSetAdd, iFpp) GO(FcFontSetCreate, pFv) -GO(FcFontSetDestroy, vFp) +GOM(FcFontSetDestroy, vFEp) GO(FcFontSetList, pFppipp) GO(FcFontSetMatch, pFppipp) GO(FcFontSetPrint, vFp) GO(FcFontSetSort, pFppipipp) -GO(FcFontSetSortDestroy, vFp) +GOM(FcFontSetSortDestroy, vFEp) GO(FcFontSort, pFppipp) GO(FcFreeTypeCharIndex, uFpu) GO(FcFreeTypeCharSet, pFpp) From c68fdd302fc64ad27db78900652f7e3cb784fb0d Mon Sep 17 00:00:00 2001 From: Hanlu Li Date: Sat, 15 Aug 2026 15:56:23 +0800 Subject: [PATCH 09/11] LATX, feat: Bridge Fontconfig scandir callbacks FcScandir executes its selector and comparator inside host Fontconfig. Passing guest function addresses directly would make LoongArch execute x86 code. Use a thread-local call context and native thunks for guest callbacks while preserving native callbacks on the direct path. Nested scans restore the previous context. Validated with the normal LAT product build and a LoongArch probe covering both callbacks with KZT disabled and the font group enabled. Signed-off-by: Hanlu Li --- target/i386/latx/context/wrappedfontconfig.c | 65 ++++++++++++++++++-- 1 file changed, 59 insertions(+), 6 deletions(-) diff --git a/target/i386/latx/context/wrappedfontconfig.c b/target/i386/latx/context/wrappedfontconfig.c index 9f4f570cd4..0b5ac94ede 100644 --- a/target/i386/latx/context/wrappedfontconfig.c +++ b/target/i386/latx/context/wrappedfontconfig.c @@ -67,6 +67,18 @@ typedef int32_t (*fc_iFppp_t)(void *, void *, void *); typedef int32_t (*fc_iFppip_t)(void *, void *, int32_t, void *); typedef void (*fc_vFp_t)(void *); +typedef int32_t (*fc_scandir_select_t)(const void *entry); +typedef int32_t (*fc_scandir_compare_t)(const void *left, + const void *right); + +typedef struct FcScandirCallbackContext { + void *callbacks[2]; + void *native_callbacks[2]; + struct FcScandirCallbackContext *previous; +} FcScandirCallbackContext; + +static __thread FcScandirCallbackContext *fontconfig_scandir_context; + #define ADDED_FUNCTIONS() \ GO(FcObjectSetAdd, fc_iFpp_t) \ GO(FcObjectSetCreate, fc_pFv_t) \ @@ -384,19 +396,60 @@ EXPORT unsigned __int128 my_FcValueSave(unsigned __int128 bits) return result_bits; } +static int32_t fontconfig_scandir_select(const void *entry) +{ + FcScandirCallbackContext *context = fontconfig_scandir_context; + + if (!context || !context->callbacks[0]) { + return 0; + } + if (context->native_callbacks[0]) { + return ((fc_scandir_select_t)context->native_callbacks[0])(entry); + } + return (int32_t)RunFunctionFmt((uintptr_t)context->callbacks[0], "p", + entry); +} + +static int32_t fontconfig_scandir_compare(const void *left, + const void *right) +{ + FcScandirCallbackContext *context = fontconfig_scandir_context; + + if (!context || !context->callbacks[1]) { + return 0; + } + if (context->native_callbacks[1]) { + return ((fc_scandir_compare_t)context->native_callbacks[1])( + left, right); + } + return (int32_t)RunFunctionFmt((uintptr_t)context->callbacks[1], "pp", + left, right); +} + EXPORT int32_t my_FcScandir(void *directory, void *namelist, void *select_callback, void *compare_callback) { - void *native_select = GetNativeFnc((uintptr_t)select_callback); - void *native_compare = GetNativeFnc((uintptr_t)compare_callback); + FcScandirCallbackContext context = { + .callbacks = {select_callback, compare_callback}, + .native_callbacks = { + GetNativeFnc((uintptr_t)select_callback), + GetNativeFnc((uintptr_t)compare_callback), + }, + .previous = fontconfig_scandir_context, + }; + void *native_select = context.native_callbacks[0]; + void *native_compare = context.native_callbacks[1]; + int32_t result; if ((select_callback && !native_select) || (compare_callback && !native_compare)) { - kzt_groups_log_wrapper_limitation( - fontconfigName, "FcScandir guest callbacks are unsupported"); - return -1; + native_select = select_callback ? fontconfig_scandir_select : NULL; + native_compare = compare_callback ? fontconfig_scandir_compare : NULL; + fontconfig_scandir_context = &context; } - return my->FcScandir(directory, namelist, native_select, native_compare); + result = my->FcScandir(directory, namelist, native_select, native_compare); + fontconfig_scandir_context = context.previous; + return result; } EXPORT void *my_FcStrBuildFilename(const char *first, void *stack) From 0adece586d42a946014a97ac9e52a05802aee044 Mon Sep 17 00:00:00 2001 From: Hanlu Li Date: Sat, 15 Aug 2026 15:56:33 +0800 Subject: [PATCH 10/11] LATX, feat: Bridge FreeType callback lifetimes Native FreeType can retain stream, memory, cache requester, debug hook, and FT_Generic callbacks after a wrapper returns. Synchronous raster callbacks also require native thunks. Guest function addresses cannot cross those boundaries directly. Add per-object callback state, reference and destruction tracking, safe callback-slot exhaustion, and a translator bridge for guest-visible generic finalizer pointers. Reject module classes, log handlers, compressed streams, and internal TrueType entry points that still lack a safe ABI. Validated with the normal LAT product build, deterministic reference-lifetime RED/GREEN coverage, GDB thunk-to-host evidence, and 20 repeated LoongArch callback-probe runs. Signed-off-by: Hanlu Li --- target/i386/latx/context/wrappedfreetype.c | 1071 +++++++++++++++-- .../include/generated/wrappedfreetypetypes.h | 3 + .../i386/latx/include/wrappedfont-interop.h | 1 + .../latx/include/wrappedfreetype_private.h | 6 +- 4 files changed, 1000 insertions(+), 81 deletions(-) diff --git a/target/i386/latx/context/wrappedfreetype.c b/target/i386/latx/context/wrappedfreetype.c index 2cf6331eac..6f1754a18d 100644 --- a/target/i386/latx/context/wrappedfreetype.c +++ b/target/i386/latx/context/wrappedfreetype.c @@ -26,6 +26,7 @@ #include "wrappedfont-preflight.h" #include "wrappedfont-interop.h" #include "wrapper.h" +#include "wrappertbbridge.h" #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wmissing-prototypes" @@ -139,6 +140,13 @@ typedef struct FtOpenArgsAbi { void *params; } FtOpenArgsAbi; +typedef struct FtMemoryAbi { + void *user; + void *alloc; + void *free; + void *realloc; +} FtMemoryAbi; + typedef struct FtOutlineFuncsAbi { void *move_to; void *line_to; @@ -160,6 +168,43 @@ typedef struct FtRasterParamsAbi { FtBBoxAbi clip_box; } FtRasterParamsAbi; +typedef void (*FtSpanFuncAbi)(int32_t y, int32_t count, const void *spans, + void *user); +typedef int32_t (*FtRasterBitTestFuncAbi)(int32_t y, int32_t x, void *user); +typedef void (*FtRasterBitSetFuncAbi)(int32_t y, int32_t x, void *user); + +typedef struct FtRasterCallbackContext { + void *callbacks[4]; + void *native_callbacks[4]; + void *guest_user; +} FtRasterCallbackContext; + +typedef uintptr_t (*FtStreamIoFuncAbi)(FtStreamAbi *stream, + uintptr_t offset, uint8_t *buffer, + uintptr_t count); +typedef void (*FtStreamCloseFuncAbi)(FtStreamAbi *stream); + +typedef struct FtStreamBridge { + FtStreamAbi native; + FtStreamAbi *guest; + void *callbacks[2]; + void *native_callbacks[2]; +} FtStreamBridge; + +typedef void *(*FtAllocFuncAbi)(FtMemoryAbi *memory, intptr_t size); +typedef void (*FtFreeFuncAbi)(FtMemoryAbi *memory, void *block); +typedef void *(*FtReallocFuncAbi)(FtMemoryAbi *memory, intptr_t current_size, + intptr_t new_size, void *block); + +typedef struct FtMemoryBridge { + FtMemoryAbi native; + FtMemoryAbi *guest; + void *callbacks[3]; + void *native_callbacks[3]; + void *library; + struct FtMemoryBridge *next; +} FtMemoryBridge; + _Static_assert(sizeof(FtVectorAbi) == 16, "FT_Vector ABI"); _Static_assert(sizeof(FtColorAbi) == 4, "FT_Color ABI"); _Static_assert(sizeof(FtOpaquePaintAbi) == 16, "FT_OpaquePaint ABI"); @@ -168,6 +213,10 @@ _Static_assert(offsetof(FtFaceAbi, glyph) == 152, "FT_Face glyph ABI"); _Static_assert(offsetof(FtGlyphSlotAbi, generic) == 32, "FT_GlyphSlot generic ABI"); _Static_assert(offsetof(FtSizeAbi, generic) == 8, "FT_Size generic ABI"); +_Static_assert(offsetof(FtStreamAbi, read) == 40, "FT_Stream read ABI"); +_Static_assert(offsetof(FtStreamAbi, close) == 48, "FT_Stream close ABI"); +_Static_assert(sizeof(FtStreamAbi) == 80, "FT_Stream ABI"); +_Static_assert(sizeof(FtMemoryAbi) == 32, "FT_Memory ABI"); _Static_assert(offsetof(FtRasterParamsAbi, gray_spans) == 24, "FT_Raster_Params callback ABI"); _Static_assert(sizeof(FtRasterParamsAbi) == 96, @@ -189,9 +238,16 @@ typedef struct FtFaceTracker { void *face; void *library; unsigned references; + FtStreamBridge *stream_bridge; struct FtFaceTracker *next; } FtFaceTracker; +typedef struct FtLibraryTracker { + void *library; + unsigned references; + struct FtLibraryTracker *next; +} FtLibraryTracker; + typedef struct FtSizeTracker { void *size; void *face; @@ -204,53 +260,518 @@ typedef struct FtBorrowTracker { struct FtBorrowTracker *next; } FtBorrowTracker; +typedef struct FtCacheManagerTracker { + void *manager; + uintptr_t guest_requester; + struct FtCacheManagerTracker *next; +} FtCacheManagerTracker; + +typedef struct FtDebugHookTracker { + void *library; + uint32_t index; + uintptr_t guest_callback; + struct FtDebugHookTracker *next; +} FtDebugHookTracker; + +typedef struct FtGenericPatch { + FtGenericAbi *generic; + void *object; + void *guest_finalizer; + void *native_finalizer; + bool persistent; + bool invoked; + bool retire_face; + struct FtGenericPatch *global_next; + struct FtGenericPatch *context_next; +} FtGenericPatch; + +typedef struct FtGenericContext { + FtGenericPatch *patches; +} FtGenericContext; + static GMutex freetype_lock; +static FtLibraryTracker *freetype_libraries; static FtFaceTracker *freetype_faces; static FtSizeTracker *freetype_sizes; static FtBorrowTracker *freetype_borrowed_faces; +static FtCacheManagerTracker *freetype_cache_managers; +static FtDebugHookTracker *freetype_debug_hooks; +static FtGenericPatch *freetype_generic_patches; +static FtMemoryBridge *freetype_memory_bridges; + +static uintptr_t freetype_stream_read(FtStreamAbi *stream, uintptr_t offset, + uint8_t *buffer, uintptr_t count); +static void freetype_stream_close(FtStreamAbi *stream); +static FtStreamBridge *freetype_untrack_face_locked(void *face); -static bool freetype_generic_is_safe(const FtGenericAbi *generic) +static void freetype_stream_guest_view(FtStreamBridge *bridge) { - return !generic || !generic->finalizer || - GetNativeFnc((uintptr_t)generic->finalizer) != NULL; + *bridge->guest = bridge->native; + bridge->guest->read = bridge->callbacks[0]; + bridge->guest->close = bridge->callbacks[1]; } -static bool freetype_face_is_safe(void *face) +static void freetype_stream_native_view(FtStreamBridge *bridge) { - FtFaceAbi *record = face; + bridge->native = *bridge->guest; + bridge->native.read = bridge->callbacks[0] + ? freetype_stream_read : NULL; + bridge->native.close = bridge->callbacks[1] + ? freetype_stream_close : NULL; +} - if (!record || !freetype_generic_is_safe(&record->generic)) { - return record == NULL; +static uintptr_t freetype_stream_read(FtStreamAbi *stream, uintptr_t offset, + uint8_t *buffer, uintptr_t count) +{ + FtStreamBridge *bridge = (FtStreamBridge *)((char *)stream - + offsetof(FtStreamBridge, native)); + uintptr_t result; + + freetype_stream_guest_view(bridge); + if (bridge->native_callbacks[0]) { + result = ((FtStreamIoFuncAbi)bridge->native_callbacks[0])( + bridge->guest, offset, buffer, count); + } else { + result = (uintptr_t)RunFunctionFmt( + (uintptr_t)bridge->callbacks[0], "pLpL", bridge->guest, + offset, buffer, count); } - if (record->glyph && - !freetype_generic_is_safe( - &((FtGlyphSlotAbi *)record->glyph)->generic)) { + freetype_stream_native_view(bridge); + return result; +} + +static void freetype_stream_close(FtStreamAbi *stream) +{ + FtStreamBridge *bridge = (FtStreamBridge *)((char *)stream - + offsetof(FtStreamBridge, native)); + + freetype_stream_guest_view(bridge); + if (bridge->native_callbacks[1]) { + ((FtStreamCloseFuncAbi)bridge->native_callbacks[1])(bridge->guest); + } else { + RunFunctionFmt((uintptr_t)bridge->callbacks[1], "p", bridge->guest); + } +} + +static bool freetype_stream_requires_bridge(const FtOpenArgsAbi *args) +{ + if (!args || !(args->flags & FT_OPEN_STREAM) || !args->stream) { return false; } - if (record->size && - !freetype_generic_is_safe(&((FtSizeAbi *)record->size)->generic)) { + return (args->stream->read && + !GetNativeFnc((uintptr_t)args->stream->read)) || + (args->stream->close && + !GetNativeFnc((uintptr_t)args->stream->close)); +} + +static FtStreamBridge *freetype_stream_bridge_new(FtOpenArgsAbi *args) +{ + FtStreamBridge *bridge; + + if (!freetype_stream_requires_bridge(args)) { + return NULL; + } + bridge = calloc(1, sizeof(*bridge)); + if (!bridge) { + return NULL; + } + bridge->guest = args->stream; + bridge->callbacks[0] = args->stream->read; + bridge->callbacks[1] = args->stream->close; + bridge->native_callbacks[0] = + GetNativeFnc((uintptr_t)bridge->callbacks[0]); + bridge->native_callbacks[1] = + GetNativeFnc((uintptr_t)bridge->callbacks[1]); + freetype_stream_native_view(bridge); + return bridge; +} + +static void *freetype_memory_alloc(FtMemoryAbi *memory, intptr_t size) +{ + FtMemoryBridge *bridge = (FtMemoryBridge *)((char *)memory - + offsetof(FtMemoryBridge, native)); + + if (bridge->native_callbacks[0]) { + return ((FtAllocFuncAbi)bridge->native_callbacks[0])( + bridge->guest, size); + } + return (void *)(uintptr_t)RunFunctionFmt( + (uintptr_t)bridge->callbacks[0], "pl", bridge->guest, size); +} + +static void freetype_memory_free(FtMemoryAbi *memory, void *block) +{ + FtMemoryBridge *bridge = (FtMemoryBridge *)((char *)memory - + offsetof(FtMemoryBridge, native)); + + if (bridge->native_callbacks[1]) { + ((FtFreeFuncAbi)bridge->native_callbacks[1])(bridge->guest, block); + return; + } + RunFunctionFmt((uintptr_t)bridge->callbacks[1], "pp", bridge->guest, + block); +} + +static void *freetype_memory_realloc(FtMemoryAbi *memory, + intptr_t current_size, + intptr_t new_size, void *block) +{ + FtMemoryBridge *bridge = (FtMemoryBridge *)((char *)memory - + offsetof(FtMemoryBridge, native)); + + if (bridge->native_callbacks[2]) { + return ((FtReallocFuncAbi)bridge->native_callbacks[2])( + bridge->guest, current_size, new_size, block); + } + return (void *)(uintptr_t)RunFunctionFmt( + (uintptr_t)bridge->callbacks[2], "pllp", bridge->guest, + current_size, new_size, block); +} + +static bool freetype_memory_requires_bridge(const FtMemoryAbi *memory) +{ + if (!memory) { return false; } + return (memory->alloc && !GetNativeFnc((uintptr_t)memory->alloc)) || + (memory->free && !GetNativeFnc((uintptr_t)memory->free)) || + (memory->realloc && !GetNativeFnc((uintptr_t)memory->realloc)); +} + +static FtMemoryBridge *freetype_memory_bridge_new(FtMemoryAbi *memory) +{ + FtMemoryBridge *bridge = calloc(1, sizeof(*bridge)); + + if (!bridge) { + return NULL; + } + bridge->guest = memory; + bridge->callbacks[0] = memory->alloc; + bridge->callbacks[1] = memory->free; + bridge->callbacks[2] = memory->realloc; + for (size_t i = 0; i < ARRAY_SIZE(bridge->callbacks); i++) { + bridge->native_callbacks[i] = + GetNativeFnc((uintptr_t)bridge->callbacks[i]); + } + bridge->native.user = memory->user; + bridge->native.alloc = memory->alloc ? freetype_memory_alloc : NULL; + bridge->native.free = memory->free ? freetype_memory_free : NULL; + bridge->native.realloc = memory->realloc + ? freetype_memory_realloc : NULL; + return bridge; +} + +static void freetype_memory_bridge_track(FtMemoryBridge *bridge, + void *library) +{ + bridge->library = library; + g_mutex_lock(&freetype_lock); + bridge->next = freetype_memory_bridges; + freetype_memory_bridges = bridge; + g_mutex_unlock(&freetype_lock); +} + +static FtMemoryBridge *freetype_memory_bridge_untrack_locked(void *library) +{ + for (FtMemoryBridge **link = &freetype_memory_bridges; *link; + link = &(*link)->next) { + FtMemoryBridge *bridge = *link; + + if (bridge->library != library) { + continue; + } + *link = bridge->next; + return bridge; + } + return NULL; +} + +static FtDebugHookTracker *freetype_debug_hooks_take_locked(void *library) +{ + FtDebugHookTracker *result = NULL; + FtDebugHookTracker **link = &freetype_debug_hooks; + + while (*link) { + FtDebugHookTracker *tracker = *link; + + if (tracker->library != library) { + link = &tracker->next; + continue; + } + *link = tracker->next; + tracker->next = result; + result = tracker; + } + return result; +} + +static void freetype_generic_finalizer(void *object) +{ + FtGenericPatch **link; + FtGenericPatch *patch = NULL; + void *guest_finalizer = NULL; + void *native_finalizer = NULL; + FtStreamBridge *stream_bridge = NULL; + + g_mutex_lock(&freetype_lock); + for (link = &freetype_generic_patches; *link; + link = &(*link)->global_next) { + if ((*link)->object != object) { + continue; + } + patch = *link; + patch->invoked = true; + guest_finalizer = patch->guest_finalizer; + native_finalizer = patch->native_finalizer; + if (patch->retire_face) { + stream_bridge = freetype_untrack_face_locked(object); + } + if (patch->persistent) { + *link = patch->global_next; + } + break; + } + g_mutex_unlock(&freetype_lock); + + if (native_finalizer) { + ((void (*)(void *))native_finalizer)(object); + } else if (guest_finalizer) { + RunFunctionFmt((uintptr_t)guest_finalizer, "p", object); + } else if (!patch) { + kzt_groups_log_wrapper_limitation( + freetypeName, "untracked FT_Generic finalizer invocation"); + } + free(stream_bridge); + if (patch && patch->persistent) { + free(patch); + } +} + +static bool freetype_generic_patch_locked(FtGenericContext *context, + void *object, + FtGenericAbi *generic, + bool persistent, + bool force, + bool retire_face) +{ + FtGenericPatch *patch; + void *native_finalizer; + + if (!generic) { + return true; + } + for (patch = freetype_generic_patches; patch; + patch = patch->global_next) { + if (patch->generic == generic) { + if (generic->finalizer != freetype_generic_finalizer) { + patch->guest_finalizer = generic->finalizer; + patch->native_finalizer = generic->finalizer + ? GetNativeFnc((uintptr_t)generic->finalizer) : NULL; + generic->finalizer = freetype_generic_finalizer; + } + patch->retire_face |= retire_face; + return true; + } + } + if (!generic->finalizer && !force) { + return true; + } + native_finalizer = generic->finalizer + ? GetNativeFnc((uintptr_t)generic->finalizer) : NULL; + if (native_finalizer && !force) { + return true; + } + if (kzt_tbbridge_insert((uintptr_t)freetype_generic_finalizer, + (ADDR)freetype_generic_finalizer, vFp) < 0) { + return false; + } + patch = calloc(1, sizeof(*patch)); + if (!patch) { + return false; + } + patch->generic = generic; + patch->object = object; + patch->guest_finalizer = generic->finalizer; + patch->native_finalizer = native_finalizer; + patch->persistent = persistent; + patch->retire_face = retire_face; + patch->global_next = freetype_generic_patches; + freetype_generic_patches = patch; + if (context) { + patch->context_next = context->patches; + context->patches = patch; + } + generic->finalizer = freetype_generic_finalizer; + return true; +} + +static bool freetype_generic_patch_face_locked(FtGenericContext *context, + void *face, + bool persistent) +{ + FtFaceAbi *record = face; + FtGenericPatch *old_global = freetype_generic_patches; + FtGenericPatch *old_context = context ? context->patches : NULL; + + if (!record) { + return true; + } + if (!freetype_generic_patch_locked(context, face, &record->generic, + persistent, persistent, + persistent)) { + goto fail; + } + for (FtGlyphSlotAbi *slot = record->glyph; slot; slot = slot->next) { + if (!freetype_generic_patch_locked( + context, slot, &slot->generic, persistent, false, false)) { + goto fail; + } + } + if (record->size && + !freetype_generic_patch_locked( + context, record->size, + &((FtSizeAbi *)record->size)->generic, persistent, + false, false)) { + goto fail; + } for (FtSizeTracker *size = freetype_sizes; size; size = size->next) { if (size->face == face && - !freetype_generic_is_safe(&((FtSizeAbi *)size->size)->generic)) { - return false; + !freetype_generic_patch_locked( + context, size->size, + &((FtSizeAbi *)size->size)->generic, persistent, + false, false)) { + goto fail; } } return true; + +fail: + while (freetype_generic_patches != old_global) { + FtGenericPatch *patch = freetype_generic_patches; + + freetype_generic_patches = patch->global_next; + if (patch->generic->finalizer == freetype_generic_finalizer) { + patch->generic->finalizer = patch->guest_finalizer; + } + free(patch); + } + if (context) { + context->patches = old_context; + } + return false; } -static bool freetype_library_is_safe(void *library) +static bool freetype_generic_patch_library_locked(FtGenericContext *context, + void *library) { for (FtFaceTracker *face = freetype_faces; face; face = face->next) { - if (face->library == library && !freetype_face_is_safe(face->face)) { + if (face->library == library && + !freetype_generic_patch_face_locked(context, face->face, + false)) { return false; } } return true; } -static bool freetype_track_face(void *library, void *face) +static void freetype_generic_context_begin(FtGenericContext *context) +{ + (void)context; +} + +static void freetype_generic_context_finish(FtGenericContext *context) +{ + FtGenericPatch *patch = context->patches; + + g_mutex_lock(&freetype_lock); + while (patch) { + FtGenericPatch *next = patch->context_next; + FtGenericPatch **link; + + for (link = &freetype_generic_patches; *link; + link = &(*link)->global_next) { + if (*link == patch) { + *link = patch->global_next; + break; + } + } + + if (!patch->invoked && + patch->generic->finalizer == freetype_generic_finalizer) { + patch->generic->finalizer = patch->guest_finalizer; + } + free(patch); + patch = next; + } + g_mutex_unlock(&freetype_lock); + context->patches = NULL; +} + +static bool freetype_track_library(void *library) +{ + FtLibraryTracker *tracker = calloc(1, sizeof(*tracker)); + + if (!tracker) { + return false; + } + tracker->library = library; + tracker->references = 1; + g_mutex_lock(&freetype_lock); + tracker->next = freetype_libraries; + freetype_libraries = tracker; + g_mutex_unlock(&freetype_lock); + return true; +} + +static bool freetype_library_is_last_reference_locked(void *library) +{ + for (FtLibraryTracker *tracker = freetype_libraries; tracker; + tracker = tracker->next) { + if (tracker->library == library) { + return tracker->references == 1; + } + } + return true; +} + +static void freetype_library_release_locked(void *library) +{ + FtLibraryTracker **link = &freetype_libraries; + + while (*link) { + FtLibraryTracker *tracker = *link; + + if (tracker->library != library) { + link = &tracker->next; + continue; + } + if (--tracker->references == 0) { + *link = tracker->next; + free(tracker); + } + return; + } +} + +static void freetype_library_remove_locked(void *library) +{ + FtLibraryTracker **link = &freetype_libraries; + + while (*link) { + FtLibraryTracker *tracker = *link; + + if (tracker->library != library) { + link = &tracker->next; + continue; + } + *link = tracker->next; + free(tracker); + return; + } +} + +static bool freetype_track_face(void *library, void *face, + FtStreamBridge *stream_bridge) { FtFaceTracker *tracker = calloc(1, sizeof(*tracker)); @@ -260,6 +781,7 @@ static bool freetype_track_face(void *library, void *face) tracker->library = library; tracker->face = face; tracker->references = 1; + tracker->stream_bridge = stream_bridge; g_mutex_lock(&freetype_lock); tracker->next = freetype_faces; freetype_faces = tracker; @@ -300,7 +822,7 @@ static void freetype_untrack_size_locked(void *size) } } -static void freetype_untrack_face_locked(void *face) +static FtStreamBridge *freetype_untrack_face_locked(void *face) { FtFaceTracker **cursor = &freetype_faces; FtSizeTracker **size_cursor = &freetype_sizes; @@ -323,9 +845,12 @@ static void freetype_untrack_face_locked(void *face) continue; } *cursor = tracker->next; + FtStreamBridge *stream_bridge = tracker->stream_bridge; + free(tracker); - return; + return stream_bridge; } + return NULL; } static void freetype_untrack_library_locked(void *library) @@ -336,7 +861,7 @@ static void freetype_untrack_library_locked(void *library) FtFaceTracker *next = face->next; if (face->library == library) { - freetype_untrack_face_locked(face->face); + free(freetype_untrack_face_locked(face->face)); } face = next; } @@ -382,6 +907,20 @@ bool latx_freetype_face_borrow(void *face) return true; } +bool latx_freetype_face_prepare_host_destruction(void *face) +{ + bool safe; + + g_mutex_lock(&freetype_lock); + safe = freetype_generic_patch_face_locked(NULL, face, true); + g_mutex_unlock(&freetype_lock); + if (!safe) { + kzt_groups_log_wrapper_limitation( + freetypeName, "cannot bridge host-owned FT_Face finalizers"); + } + return safe; +} + void latx_freetype_face_return(void *face) { FtBorrowTracker **cursor; @@ -420,6 +959,10 @@ static FtCallbackSlot ft_conic_slots[FT_CALLBACK_SLOTS]; static FtCallbackSlot ft_cubic_slots[FT_CALLBACK_SLOTS]; static FtCallbackSlot ft_list_iterator_slots[FT_CALLBACK_SLOTS]; static FtCallbackSlot ft_list_destroy_slots[FT_CALLBACK_SLOTS]; +static FtCallbackSlot ft_face_requester_slots[FT_CALLBACK_SLOTS]; +static FtCallbackSlot ft_debug_hook_slots[FT_CALLBACK_SLOTS]; + +int32_t my_FT_Done_Face(void *face); static uintptr_t freetype_slot_guest(FtCallbackSlot *slots, size_t index) { @@ -539,6 +1082,40 @@ FT_SLOT_LIST(DEFINE_LIST_ITERATOR_THUNK) FT_SLOT_LIST(DEFINE_LIST_DESTROY_THUNK) #undef DEFINE_LIST_DESTROY_THUNK +#define DEFINE_FACE_REQUESTER_THUNK(N) \ + static int32_t ft_face_requester_thunk_##N( \ + void *face_id, void *library, void *request_data, void **face) \ + { \ + uintptr_t guest = freetype_slot_guest(ft_face_requester_slots, N); \ + int32_t status = guest \ + ? (int32_t)RunFunctionFmt(guest, "pppp", face_id, library, \ + request_data, face) \ + : FT_ERROR_UNIMPLEMENTED_FEATURE; \ + if (status == FT_ERROR_SUCCESS && face && *face) { \ + bool safe; \ + g_mutex_lock(&freetype_lock); \ + safe = freetype_generic_patch_face_locked(NULL, *face, true); \ + g_mutex_unlock(&freetype_lock); \ + if (!safe) { \ + my_FT_Done_Face(*face); \ + *face = NULL; \ + return FT_ERROR_OUT_OF_MEMORY; \ + } \ + } \ + return status; \ + } +FT_SLOT_LIST(DEFINE_FACE_REQUESTER_THUNK) +#undef DEFINE_FACE_REQUESTER_THUNK + +#define DEFINE_DEBUG_HOOK_THUNK(N) \ + static void ft_debug_hook_thunk_##N(void *argument) \ + { \ + uintptr_t guest = freetype_slot_guest(ft_debug_hook_slots, N); \ + if (guest) RunFunctionFmt(guest, "p", argument); \ + } +FT_SLOT_LIST(DEFINE_DEBUG_HOOK_THUNK) +#undef DEFINE_DEBUG_HOOK_THUNK + #define THUNK_ADDRESS(kind, N) (void *)ft_##kind##_thunk_##N, #define MOVE_ADDRESS(N) THUNK_ADDRESS(move, N) static void *const ft_move_thunks[] = { FT_SLOT_LIST(MOVE_ADDRESS) }; @@ -562,27 +1139,55 @@ static void *const ft_list_destroy_thunks[] = { FT_SLOT_LIST(LIST_DESTROY_ADDRESS) }; #undef LIST_DESTROY_ADDRESS +#define FACE_REQUESTER_ADDRESS(N) THUNK_ADDRESS(face_requester, N) +static void *const ft_face_requester_thunks[] = { + FT_SLOT_LIST(FACE_REQUESTER_ADDRESS) +}; +#undef FACE_REQUESTER_ADDRESS +#define DEBUG_HOOK_ADDRESS(N) THUNK_ADDRESS(debug_hook, N) +static void *const ft_debug_hook_thunks[] = { + FT_SLOT_LIST(DEBUG_HOOK_ADDRESS) +}; +#undef DEBUG_HOOK_ADDRESS #undef THUNK_ADDRESS static int32_t freetype_finish_face_creation(void *library, void **face, - int32_t status) + int32_t status, + FtStreamBridge *stream_bridge) { if (status != FT_ERROR_SUCCESS || !face || !*face) { + free(stream_bridge); return status; } - if (freetype_track_face(library, *face)) { + if (freetype_track_face(library, *face, stream_bridge)) { return status; } my->FT_Done_Face(*face); + free(stream_bridge); *face = NULL; return FT_ERROR_OUT_OF_MEMORY; } +EXPORT int32_t my_FT_Init_FreeType(void **library) +{ + int32_t status = my->FT_Init_FreeType(library); + + if (status != FT_ERROR_SUCCESS || !library || !*library) { + return status; + } + if (freetype_track_library(*library)) { + return status; + } + my->FT_Done_FreeType(*library); + *library = NULL; + return FT_ERROR_OUT_OF_MEMORY; +} + EXPORT int32_t my_FT_New_Face(void *library, void *path, intptr_t index, void **face) { return freetype_finish_face_creation( - library, face, my->FT_New_Face(library, path, index, face)); + library, face, my->FT_New_Face(library, path, index, face), NULL); } EXPORT int32_t my_FT_New_Memory_Face(void *library, void *base, @@ -591,34 +1196,48 @@ EXPORT int32_t my_FT_New_Memory_Face(void *library, void *base, { return freetype_finish_face_creation( library, face, - my->FT_New_Memory_Face(library, base, size, index, face)); + my->FT_New_Memory_Face(library, base, size, index, face), NULL); } EXPORT int32_t my_FT_Open_Face(void *library, FtOpenArgsAbi *args, intptr_t index, void **face) { + FtOpenArgsAbi native_args; + FtStreamBridge *stream_bridge = NULL; int32_t status; - if (args && (args->flags & FT_OPEN_STREAM) && args->stream && - (args->stream->read || args->stream->close)) { - kzt_groups_log_wrapper_limitation( - freetypeName, "FT_Open_Face guest stream callbacks are unsupported"); - return FT_ERROR_UNIMPLEMENTED_FEATURE; + if (freetype_stream_requires_bridge(args)) { + stream_bridge = freetype_stream_bridge_new(args); + if (!stream_bridge) { + return FT_ERROR_OUT_OF_MEMORY; + } + native_args = *args; + native_args.stream = &stream_bridge->native; + args = &native_args; } status = my->FT_Open_Face(library, args, index, face); - return freetype_finish_face_creation(library, face, status); + return freetype_finish_face_creation(library, face, status, + stream_bridge); } EXPORT int32_t my_FT_Attach_Stream(void *face, FtOpenArgsAbi *args) { - if (args && (args->flags & FT_OPEN_STREAM) && args->stream && - (args->stream->read || args->stream->close)) { - kzt_groups_log_wrapper_limitation( - freetypeName, - "FT_Attach_Stream guest stream callbacks are unsupported"); - return FT_ERROR_UNIMPLEMENTED_FEATURE; + FtOpenArgsAbi native_args; + FtStreamBridge *stream_bridge = NULL; + int32_t status; + + if (freetype_stream_requires_bridge(args)) { + stream_bridge = freetype_stream_bridge_new(args); + if (!stream_bridge) { + return FT_ERROR_OUT_OF_MEMORY; + } + native_args = *args; + native_args.stream = &stream_bridge->native; + args = &native_args; } - return my->FT_Attach_Stream(face, args); + status = my->FT_Attach_Stream(face, args); + free(stream_bridge); + return status; } EXPORT int32_t my_FT_New_Size(void *face, void **size) @@ -638,18 +1257,21 @@ EXPORT int32_t my_FT_New_Size(void *face, void **size) EXPORT int32_t my_FT_Done_Size(void *size) { + FtGenericContext context = {0}; int32_t status; g_mutex_lock(&freetype_lock); - if (size && - !freetype_generic_is_safe(&((FtSizeAbi *)size)->generic)) { + if (size && !freetype_generic_patch_locked( + &context, size, &((FtSizeAbi *)size)->generic, false, + false, false)) { g_mutex_unlock(&freetype_lock); - kzt_groups_log_wrapper_limitation( - freetypeName, "guest FT_Size generic finalizer is unsupported"); - return FT_ERROR_UNIMPLEMENTED_FEATURE; + freetype_generic_context_finish(&context); + return FT_ERROR_OUT_OF_MEMORY; } g_mutex_unlock(&freetype_lock); + freetype_generic_context_begin(&context); status = my->FT_Done_Size(size); + freetype_generic_context_finish(&context); if (status == FT_ERROR_SUCCESS) { g_mutex_lock(&freetype_lock); freetype_untrack_size_locked(size); @@ -660,6 +1282,8 @@ EXPORT int32_t my_FT_Done_Size(void *size) EXPORT int32_t my_FT_Done_Face(void *face) { + FtGenericContext context = {0}; + FtStreamBridge *stream_bridge = NULL; int32_t status; g_mutex_lock(&freetype_lock); @@ -669,14 +1293,15 @@ EXPORT int32_t my_FT_Done_Face(void *face) freetypeName, "cannot destroy a Cairo-borrowed FT_Face"); return FT_ERROR_INVALID_ARGUMENT; } - if (!freetype_face_is_safe(face)) { + if (!freetype_generic_patch_face_locked(&context, face, false)) { g_mutex_unlock(&freetype_lock); - kzt_groups_log_wrapper_limitation( - freetypeName, "guest FT_Face generic finalizer is unsupported"); - return FT_ERROR_UNIMPLEMENTED_FEATURE; + freetype_generic_context_finish(&context); + return FT_ERROR_OUT_OF_MEMORY; } g_mutex_unlock(&freetype_lock); + freetype_generic_context_begin(&context); status = my->FT_Done_Face(face); + freetype_generic_context_finish(&context); if (status == FT_ERROR_SUCCESS) { g_mutex_lock(&freetype_lock); for (FtFaceTracker *tracker = freetype_faces; tracker; @@ -687,11 +1312,12 @@ EXPORT int32_t my_FT_Done_Face(void *face) if (tracker->references > 1) { tracker->references--; } else { - freetype_untrack_face_locked(face); + stream_bridge = freetype_untrack_face_locked(face); } break; } g_mutex_unlock(&freetype_lock); + free(stream_bridge); } return status; } @@ -718,13 +1344,46 @@ EXPORT int32_t my_FT_Reference_Face(void *face) void *library = record->glyph ? ((FtGlyphSlotAbi *)record->glyph)->library : NULL; - if (library && freetype_track_face(library, face)) { + if (library && freetype_track_face(library, face, NULL)) { return status; } my->FT_Done_Face(face); return FT_ERROR_OUT_OF_MEMORY; } +EXPORT int32_t my_FT_Reference_Library(void *library) +{ + FtLibraryTracker *new_tracker = calloc(1, sizeof(*new_tracker)); + int32_t status; + + if (!new_tracker) { + return FT_ERROR_OUT_OF_MEMORY; + } + status = my->FT_Reference_Library(library); + if (status != FT_ERROR_SUCCESS) { + free(new_tracker); + return status; + } + + g_mutex_lock(&freetype_lock); + for (FtLibraryTracker *tracker = freetype_libraries; tracker; + tracker = tracker->next) { + if (tracker->library != library) { + continue; + } + tracker->references++; + g_mutex_unlock(&freetype_lock); + free(new_tracker); + return status; + } + new_tracker->library = library; + new_tracker->references = 2; + new_tracker->next = freetype_libraries; + freetype_libraries = new_tracker; + g_mutex_unlock(&freetype_lock); + return status; +} + bool latx_freetype_face_reference(void *face) { return my_lib && my_FT_Reference_Face(face) == FT_ERROR_SUCCESS; @@ -739,35 +1398,69 @@ void latx_freetype_face_release(void *face) } static int32_t freetype_done_library(void *library, - int32_t (*finish)(void *)) + int32_t (*finish)(void *), + bool honor_references) { + FtGenericContext context = {0}; + FtMemoryBridge *memory_bridge = NULL; + FtDebugHookTracker *debug_hooks = NULL; + bool last_reference; int32_t status; g_mutex_lock(&freetype_lock); - if (!freetype_library_is_safe(library)) { + last_reference = !honor_references || + freetype_library_is_last_reference_locked(library); + if (!last_reference) { g_mutex_unlock(&freetype_lock); - kzt_groups_log_wrapper_limitation( - freetypeName, "guest FreeType generic finalizer is unsupported"); - return FT_ERROR_UNIMPLEMENTED_FEATURE; + status = finish(library); + if (status == FT_ERROR_SUCCESS) { + g_mutex_lock(&freetype_lock); + freetype_library_release_locked(library); + g_mutex_unlock(&freetype_lock); + } + return status; + } + if (!freetype_generic_patch_library_locked(&context, library)) { + g_mutex_unlock(&freetype_lock); + freetype_generic_context_finish(&context); + return FT_ERROR_OUT_OF_MEMORY; } g_mutex_unlock(&freetype_lock); + freetype_generic_context_begin(&context); status = finish(library); + freetype_generic_context_finish(&context); if (status == FT_ERROR_SUCCESS) { g_mutex_lock(&freetype_lock); + if (honor_references) { + freetype_library_release_locked(library); + } else { + freetype_library_remove_locked(library); + } freetype_untrack_library_locked(library); + memory_bridge = freetype_memory_bridge_untrack_locked(library); + debug_hooks = freetype_debug_hooks_take_locked(library); g_mutex_unlock(&freetype_lock); + free(memory_bridge); + while (debug_hooks) { + FtDebugHookTracker *next = debug_hooks->next; + + freetype_slot_release(ft_debug_hook_slots, + debug_hooks->guest_callback); + free(debug_hooks); + debug_hooks = next; + } } return status; } EXPORT int32_t my_FT_Done_FreeType(void *library) { - return freetype_done_library(library, my->FT_Done_FreeType); + return freetype_done_library(library, my->FT_Done_FreeType, false); } EXPORT int32_t my_FT_Done_Library(void *library) { - return freetype_done_library(library, my->FT_Done_Library); + return freetype_done_library(library, my->FT_Done_Library, true); } EXPORT int32_t my_FT_Bitmap_Blend(void *library, void *source, @@ -858,11 +1551,72 @@ EXPORT int32_t my_FT_Outline_Decompose(void *outline, return status; } +static void freetype_raster_span(FtRasterCallbackContext *context, + size_t index, int32_t y, int32_t count, + const void *spans) +{ + if (!context->callbacks[index]) { + return; + } + if (context->native_callbacks[index]) { + ((FtSpanFuncAbi)context->native_callbacks[index])( + y, count, spans, context->guest_user); + return; + } + RunFunctionFmt((uintptr_t)context->callbacks[index], "iipp", y, count, + spans, context->guest_user); +} + +static void freetype_raster_gray_spans(int32_t y, int32_t count, + const void *spans, void *user) +{ + freetype_raster_span(user, 0, y, count, spans); +} + +static void freetype_raster_black_spans(int32_t y, int32_t count, + const void *spans, void *user) +{ + freetype_raster_span(user, 1, y, count, spans); +} + +static int32_t freetype_raster_bit_test(int32_t y, int32_t x, void *user) +{ + FtRasterCallbackContext *context = user; + + if (!context->callbacks[2]) { + return 0; + } + if (context->native_callbacks[2]) { + return ((FtRasterBitTestFuncAbi)context->native_callbacks[2])( + y, x, context->guest_user); + } + return (int32_t)RunFunctionFmt((uintptr_t)context->callbacks[2], "iip", + y, x, context->guest_user); +} + +static void freetype_raster_bit_set(int32_t y, int32_t x, void *user) +{ + FtRasterCallbackContext *context = user; + + if (!context->callbacks[3]) { + return; + } + if (context->native_callbacks[3]) { + ((FtRasterBitSetFuncAbi)context->native_callbacks[3])( + y, x, context->guest_user); + return; + } + RunFunctionFmt((uintptr_t)context->callbacks[3], "iip", y, x, + context->guest_user); +} + EXPORT int32_t my_FT_Outline_Render(void *library, void *outline, FtRasterParamsAbi *params) { FtRasterParamsAbi native; + FtRasterCallbackContext context = {0}; void **callbacks; + bool needs_bridge = false; if (!params) { return FT_ERROR_INVALID_ARGUMENT; @@ -870,19 +1624,31 @@ EXPORT int32_t my_FT_Outline_Render(void *library, void *outline, native = *params; callbacks = &native.gray_spans; for (size_t i = 0; i < 4; i++) { - void *callback = callbacks[i]; + context.callbacks[i] = callbacks[i]; - if (!callback) { + if (!context.callbacks[i]) { continue; } - callbacks[i] = GetNativeFnc((uintptr_t)callback); - if (!callbacks[i]) { - kzt_groups_log_wrapper_limitation( - freetypeName, - "FT_Outline_Render guest callbacks are unsupported"); - return FT_ERROR_UNIMPLEMENTED_FEATURE; + context.native_callbacks[i] = + GetNativeFnc((uintptr_t)context.callbacks[i]); + if (!context.native_callbacks[i]) { + needs_bridge = true; } } + if (!needs_bridge) { + return my->FT_Outline_Render(library, outline, &native); + } + + context.guest_user = native.user; + native.gray_spans = context.callbacks[0] + ? freetype_raster_gray_spans : NULL; + native.black_spans = context.callbacks[1] + ? freetype_raster_black_spans : NULL; + native.bit_test = context.callbacks[2] + ? freetype_raster_bit_test : NULL; + native.bit_set = context.callbacks[3] + ? freetype_raster_bit_set : NULL; + native.user = &context; return my->FT_Outline_Render(library, outline, &native); } @@ -930,13 +1696,35 @@ EXPORT int32_t my_FT_Add_Module(void *library, void *module_class) EXPORT int32_t my_FT_New_Library(void *memory, void **library) { - (void)memory; - if (library) { + FtMemoryBridge *bridge = NULL; + void *native_memory = memory; + int32_t status; + + if (freetype_memory_requires_bridge(memory)) { + bridge = freetype_memory_bridge_new(memory); + if (!bridge) { + if (library) { + *library = NULL; + } + return FT_ERROR_OUT_OF_MEMORY; + } + native_memory = &bridge->native; + } + status = my->FT_New_Library(native_memory, library); + if (status != FT_ERROR_SUCCESS || !library || !*library) { + free(bridge); + return status; + } + if (!freetype_track_library(*library)) { + my->FT_Done_Library(*library); + free(bridge); *library = NULL; + return FT_ERROR_OUT_OF_MEMORY; } - kzt_groups_log_wrapper_limitation( - freetypeName, "FT_New_Library guest memory callbacks are unsupported"); - return FT_ERROR_UNIMPLEMENTED_FEATURE; + if (bridge) { + freetype_memory_bridge_track(bridge, *library); + } + return status; } EXPORT int32_t my_FTC_Manager_New(void *library, uint32_t max_faces, @@ -944,31 +1732,119 @@ EXPORT int32_t my_FTC_Manager_New(void *library, uint32_t max_faces, void *requester, void *request_data, void **manager) { - void *native = GetNativeFnc((uintptr_t)requester); + uintptr_t guest = (uintptr_t)requester; + void *native = freetype_slot_acquire( + ft_face_requester_slots, ft_face_requester_thunks, requester, + "FTC face requester callback slots exhausted"); + FtCacheManagerTracker *tracker = NULL; + int32_t status; if (requester && !native) { if (manager) { *manager = NULL; } - kzt_groups_log_wrapper_limitation( - freetypeName, "FTC_Manager_New guest requester is unsupported"); - return FT_ERROR_UNIMPLEMENTED_FEATURE; + return FT_ERROR_OUT_OF_MEMORY; + } + if (requester && !GetNativeFnc(guest)) { + tracker = calloc(1, sizeof(*tracker)); + if (!tracker) { + freetype_slot_release(ft_face_requester_slots, guest); + if (manager) { + *manager = NULL; + } + return FT_ERROR_OUT_OF_MEMORY; + } + } + status = my->FTC_Manager_New(library, max_faces, max_sizes, max_bytes, + native, request_data, manager); + if (status != FT_ERROR_SUCCESS || !manager || !*manager || !tracker) { + if (tracker) { + freetype_slot_release(ft_face_requester_slots, guest); + free(tracker); + } + return status; + } + tracker->manager = *manager; + tracker->guest_requester = guest; + g_mutex_lock(&freetype_lock); + tracker->next = freetype_cache_managers; + freetype_cache_managers = tracker; + g_mutex_unlock(&freetype_lock); + return status; +} + +EXPORT void my_FTC_Manager_Done(void *manager) +{ + FtCacheManagerTracker *tracker = NULL; + + my->FTC_Manager_Done(manager); + g_mutex_lock(&freetype_lock); + for (FtCacheManagerTracker **link = &freetype_cache_managers; *link; + link = &(*link)->next) { + if ((*link)->manager != manager) { + continue; + } + tracker = *link; + *link = tracker->next; + break; + } + g_mutex_unlock(&freetype_lock); + if (tracker) { + freetype_slot_release(ft_face_requester_slots, + tracker->guest_requester); + free(tracker); } - return my->FTC_Manager_New(library, max_faces, max_sizes, max_bytes, - native, request_data, manager); } EXPORT void my_FT_Set_Debug_Hook(void *library, uint32_t index, void *callback) { - void *native = GetNativeFnc((uintptr_t)callback); + uintptr_t guest = (uintptr_t)callback; + void *native = GetNativeFnc(guest); + FtDebugHookTracker *tracker = NULL; + FtDebugHookTracker *old = NULL; - if (callback && !native) { - kzt_groups_log_wrapper_limitation( - freetypeName, "FT_Set_Debug_Hook guest callback is unsupported"); + if (!library || !callback || index >= 4) { + my->FT_Set_Debug_Hook(library, index, native); return; } + + if (!native) { + native = freetype_slot_acquire( + ft_debug_hook_slots, ft_debug_hook_thunks, callback, + "debug hook callback slots exhausted"); + if (!native) { + return; + } + tracker = calloc(1, sizeof(*tracker)); + if (!tracker) { + freetype_slot_release(ft_debug_hook_slots, guest); + return; + } + tracker->library = library; + tracker->index = index; + tracker->guest_callback = guest; + } my->FT_Set_Debug_Hook(library, index, native); + g_mutex_lock(&freetype_lock); + for (FtDebugHookTracker **link = &freetype_debug_hooks; *link; + link = &(*link)->next) { + if ((*link)->library != library || (*link)->index != index) { + continue; + } + old = *link; + *link = old->next; + break; + } + if (tracker) { + tracker->next = freetype_debug_hooks; + freetype_debug_hooks = tracker; + } + g_mutex_unlock(&freetype_lock); + if (old) { + freetype_slot_release(ft_debug_hook_slots, old->guest_callback); + free(old); + } } EXPORT void my_FT_Set_Log_Handler(void *callback) @@ -1015,9 +1891,16 @@ EXPORT int32_t my_TT_RunIns(void) static void freetype_state_clear(void) { g_mutex_lock(&freetype_lock); + while (freetype_libraries) { + FtLibraryTracker *next = freetype_libraries->next; + + free(freetype_libraries); + freetype_libraries = next; + } while (freetype_faces) { FtFaceTracker *next = freetype_faces->next; + free(freetype_faces->stream_bridge); free(freetype_faces); freetype_faces = next; } @@ -1033,12 +1916,44 @@ static void freetype_state_clear(void) free(freetype_borrowed_faces); freetype_borrowed_faces = next; } + while (freetype_cache_managers) { + FtCacheManagerTracker *next = freetype_cache_managers->next; + + free(freetype_cache_managers); + freetype_cache_managers = next; + } + while (freetype_debug_hooks) { + FtDebugHookTracker *next = freetype_debug_hooks->next; + + free(freetype_debug_hooks); + freetype_debug_hooks = next; + } + while (freetype_generic_patches) { + FtGenericPatch *next = freetype_generic_patches->global_next; + + if (!freetype_generic_patches->invoked && + freetype_generic_patches->generic->finalizer == + freetype_generic_finalizer) { + freetype_generic_patches->generic->finalizer = + freetype_generic_patches->guest_finalizer; + } + free(freetype_generic_patches); + freetype_generic_patches = next; + } + while (freetype_memory_bridges) { + FtMemoryBridge *next = freetype_memory_bridges->next; + + free(freetype_memory_bridges); + freetype_memory_bridges = next; + } memset(ft_move_slots, 0, sizeof(ft_move_slots)); memset(ft_line_slots, 0, sizeof(ft_line_slots)); memset(ft_conic_slots, 0, sizeof(ft_conic_slots)); memset(ft_cubic_slots, 0, sizeof(ft_cubic_slots)); memset(ft_list_iterator_slots, 0, sizeof(ft_list_iterator_slots)); memset(ft_list_destroy_slots, 0, sizeof(ft_list_destroy_slots)); + memset(ft_face_requester_slots, 0, sizeof(ft_face_requester_slots)); + memset(ft_debug_hook_slots, 0, sizeof(ft_debug_hook_slots)); g_mutex_unlock(&freetype_lock); } diff --git a/target/i386/latx/include/generated/wrappedfreetypetypes.h b/target/i386/latx/include/generated/wrappedfreetypetypes.h index 1125919c7e..a0d909c5e9 100644 --- a/target/i386/latx/include/generated/wrappedfreetypetypes.h +++ b/target/i386/latx/include/generated/wrappedfreetypetypes.h @@ -46,13 +46,16 @@ typedef int32_t (*iFv_t)(void); GO(FT_Outline_Decompose, iFppp_t) \ GO(FT_Outline_Render, iFppp_t) \ GO(FT_Get_Paint, iFppip_t) \ + GO(FT_Init_FreeType, iFp_t) \ GO(FT_Palette_Set_Foreground_Color, iFpu_t) \ GO(FT_Reference_Face, iFp_t) \ + GO(FT_Reference_Library, iFp_t) \ GO(FT_Set_Debug_Hook, vFpup_t) \ GO(FT_Set_Log_Handler, vFp_t) \ GO(FT_Stream_OpenBzip2, iFpp_t) \ GO(FT_Stream_OpenGzip, iFpp_t) \ GO(FT_Stream_OpenLZW, iFpp_t) \ + GO(FTC_Manager_Done, vFp_t) \ GO(FTC_Manager_New, iFpuuLppp_t) \ GO(TT_New_Context, pFv_t) \ GO(TT_RunIns, iFv_t) diff --git a/target/i386/latx/include/wrappedfont-interop.h b/target/i386/latx/include/wrappedfont-interop.h index f10dad7168..b56e04a70c 100644 --- a/target/i386/latx/include/wrappedfont-interop.h +++ b/target/i386/latx/include/wrappedfont-interop.h @@ -13,6 +13,7 @@ bool latx_freetype_face_reference(void *face); void latx_freetype_face_release(void *face); bool latx_freetype_face_borrow(void *face); void latx_freetype_face_return(void *face); +bool latx_freetype_face_prepare_host_destruction(void *face); void *latx_fontconfig_pattern_ft_face(void *pattern); diff --git a/target/i386/latx/include/wrappedfreetype_private.h b/target/i386/latx/include/wrappedfreetype_private.h index 5e3e47b3b4..700ccc1bbc 100644 --- a/target/i386/latx/include/wrappedfreetype_private.h +++ b/target/i386/latx/include/wrappedfreetype_private.h @@ -112,7 +112,7 @@ GO(FT_Glyph_To_Bitmap, iFpupC) GO(FT_Glyph_Transform, iFppp) GO(FT_Gzip_Uncompress, iFppppL) GO(FT_Has_PS_Glyph_Names, iFp) -GO(FT_Init_FreeType, iFp) +GOM(FT_Init_FreeType, iFEp) GO(FT_Library_SetLcdFilter, iFpu) GO(FT_Library_SetLcdFilterWeights, iFpp) GO(FT_Library_SetLcdGeometry, iFpp) @@ -162,7 +162,7 @@ GOM(FT_Palette_Set_Foreground_Color, iFEpu) GO(FT_Property_Get, iFpppp) GO(FT_Property_Set, iFpppp) GOM(FT_Reference_Face, iFEp) -GO(FT_Reference_Library, iFp) +GOM(FT_Reference_Library, iFEp) GO(FT_Remove_Module, iFpp) GO(FT_Render_Glyph, iFpu) GO(FT_Request_Size, iFpp) @@ -220,7 +220,7 @@ GO(FTC_CMapCache_New, iFpp) GO(FTC_ImageCache_Lookup, iFppupp) GO(FTC_ImageCache_LookupScaler, iFppLupp) GO(FTC_ImageCache_New, iFpp) -GO(FTC_Manager_Done, vFp) +GOM(FTC_Manager_Done, vFEp) GO(FTC_Manager_LookupFace, iFppp) GO(FTC_Manager_LookupSize, iFppp) GOM(FTC_Manager_New, iFEpuuLppp) From 5dfbab596dc3320bb18d10d74044911283df608a Mon Sep 17 00:00:00 2001 From: Hanlu Li Date: Sat, 15 Aug 2026 15:56:40 +0800 Subject: [PATCH 11/11] LATX, feat: Guard Xft-owned FreeType faces XftLockFace exposes a host FT_Face whose public generic finalizers can be replaced by guest code. Xft may later destroy that face internally, outside the FreeType wrapper path. Guard each exposed face before and after guest access, keep it borrowed while locked, and refresh the bridge before Xft closes fonts. A bounded per-thread lock stack fails safely on invalid nesting. Validated with a deterministic LoongArch RED that hung in the unguarded native path, followed by 20/20 GREEN runs and GDB evidence for both guest-invoked and host-invoked finalizers. Signed-off-by: Hanlu Li --- target/i386/latx/context/wrappedlibxft.c | 78 +++++++++++++++++++ .../include/generated/wrappedlibxfttypes.h | 5 +- .../i386/latx/include/wrappedlibxft_private.h | 6 +- 3 files changed, 85 insertions(+), 4 deletions(-) diff --git a/target/i386/latx/context/wrappedlibxft.c b/target/i386/latx/context/wrappedlibxft.c index 1abd03a05d..be7cfefe2b 100644 --- a/target/i386/latx/context/wrappedlibxft.c +++ b/target/i386/latx/context/wrappedlibxft.c @@ -16,9 +16,11 @@ #include "box64context.h" #include "bridge.h" +#include "kzt-groups.h" #include "library_private.h" #include "wrappedfont-vaargs.h" #include "wrappedfont-preflight.h" +#include "wrappedfont-interop.h" #include "wrapper.h" #pragma GCC diagnostic push @@ -41,6 +43,8 @@ const char *libxftName = "libXft.so.2"; } while (0); typedef void (*xft_vFp_t)(void *); +typedef void (*xft_vFpp_t)(void *, void *); +typedef void *(*xft_pFp_t)(void *); typedef void *(*xft_pFpp_t)(void *, void *); typedef void *(*xft_pFpipp_t)(void *, int32_t, void *, void *); typedef void *(*xft_pFppp_t)(void *, void *, void *); @@ -56,6 +60,80 @@ typedef void *(*xft_pFppp_t)(void *, void *, void *); #include "wrappercallback.h" +#define XFT_FACE_LOCK_DEPTH 16 + +typedef struct XftFaceLockEntry { + void *font; + void *face; +} XftFaceLockEntry; + +static __thread XftFaceLockEntry xft_face_locks[XFT_FACE_LOCK_DEPTH]; +static __thread size_t xft_face_lock_depth; + +EXPORT void *my_XftLockFace(void *font) +{ + void *face = my->XftLockFace(font); + + if (!face) { + return NULL; + } + if (xft_face_lock_depth == XFT_FACE_LOCK_DEPTH || + !latx_freetype_face_prepare_host_destruction(face) || + !latx_freetype_face_borrow(face)) { + my->XftUnlockFace(font); + if (xft_face_lock_depth == XFT_FACE_LOCK_DEPTH) { + kzt_groups_log_wrapper_limitation( + libxftName, "XftLockFace nesting limit reached"); + } + return NULL; + } + xft_face_locks[xft_face_lock_depth++] = (XftFaceLockEntry) { + .font = font, + .face = face, + }; + return face; +} + +EXPORT void my_XftUnlockFace(void *font) +{ + size_t index = xft_face_lock_depth; + void *face; + + while (index && xft_face_locks[index - 1].font != font) { + index--; + } + if (!index) { + kzt_groups_log_wrapper_limitation( + libxftName, "XftUnlockFace has no matching guest lock"); + return; + } + index--; + face = xft_face_locks[index].face; + if (!latx_freetype_face_prepare_host_destruction(face)) { + return; + } + memmove(&xft_face_locks[index], &xft_face_locks[index + 1], + (xft_face_lock_depth - index - 1) * sizeof(xft_face_locks[0])); + xft_face_lock_depth--; + my->XftUnlockFace(font); + latx_freetype_face_return(face); +} + +EXPORT void my_XftFontClose(void *display, void *font) +{ + void *face = my->XftLockFace(font); + + if (face) { + bool safe = latx_freetype_face_prepare_host_destruction(face); + + my->XftUnlockFace(font); + if (!safe) { + return; + } + } + my->XftFontClose(display, font); +} + EXPORT void *my_XftFontOpen(void *display, int32_t screen, void *stack) { LatxX64VaReader reader; diff --git a/target/i386/latx/include/generated/wrappedlibxfttypes.h b/target/i386/latx/include/generated/wrappedlibxfttypes.h index dc61b14283..13351b0645 100644 --- a/target/i386/latx/include/generated/wrappedlibxfttypes.h +++ b/target/i386/latx/include/generated/wrappedlibxfttypes.h @@ -14,7 +14,10 @@ typedef void *(*pFpiV_t)(void *, int32_t, ...); #define SUPER() ADDED_FUNCTIONS() \ + GO(XftFontClose, xft_vFpp_t) \ GO(XftFontOpen, pFpiV_t) \ - GO(XftListFonts, pFpiV_t) + GO(XftListFonts, pFpiV_t) \ + GO(XftLockFace, xft_pFp_t) \ + GO(XftUnlockFace, xft_vFp_t) #endif // __wrappedlibxftTYPES_H_ diff --git a/target/i386/latx/include/wrappedlibxft_private.h b/target/i386/latx/include/wrappedlibxft_private.h index 3ef40fedb7..ef133cfa11 100644 --- a/target/i386/latx/include/wrappedlibxft_private.h +++ b/target/i386/latx/include/wrappedlibxft_private.h @@ -47,7 +47,7 @@ GO(XftDrawStringUtf16, vFpppiipui) GO(XftDrawStringUtf8, vFpppiipi) GO(XftDrawVisual, pFp) GO(XftFontCheckGlyph, iFppiupp) -GO(XftFontClose, vFpp) +GOM(XftFontClose, vFEpp) GO(XftFontCopy, pFpp) GO(XftFontInfoCreate, pFpp) GO(XftFontInfoDestroy, vFpp) @@ -69,7 +69,7 @@ GO(XftGlyphSpecRender, vFpiLpLiipi) GO(XftInit, iFp) GO(XftInitFtLibrary, iFv) GOM(XftListFonts, pFEpiV) -GO(XftLockFace, pFp) +GOM(XftLockFace, pFEp) GO(XftNameParse, pFp) GO(XftNameUnparse, iFppi) GO(XftTextExtents16, vFpppip) @@ -86,5 +86,5 @@ GO(XftTextRender32LE, vFpiLpLiiiipi) GO(XftTextRender8, vFpiLpLiiiipi) GO(XftTextRenderUtf16, vFpiLpLiiiipui) GO(XftTextRenderUtf8, vFpiLpLiiiipi) -GO(XftUnlockFace, vFp) +GOM(XftUnlockFace, vFEp) GO(XftXlfdParse, pFpii)