diff --git a/target/i386/latx/context/meson.build b/target/i386/latx/context/meson.build index 2586140630..92a506ecf0 100644 --- a/target/i386/latx/context/meson.build +++ b/target/i386/latx/context/meson.build @@ -25,7 +25,9 @@ my_file = files( 'wrappedlibxcbutil.c', 'wrappedlibxcbxinerama.c', 'wrappedlibxcbxinput.c', + 'wrappedlib-preflight.c', 'wrappedcairo-preflight.c', + 'wrappedfont-preflight.c', 'wrappedcairo.c', 'wrappedlibx11xcb.c', 'wrappedxkbcommon.c', @@ -36,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/wrappedcairo-preflight.c b/target/i386/latx/context/wrappedcairo-preflight.c index 0f4b614ce8..f05608093d 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, @@ -27,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 @@ -38,212 +40,19 @@ 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) +static bool cairo_symbol_filter(const char *name) { - 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) -{ - 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), + cairo_required_host_symbols, + ARRAY_SIZE(cairo_required_host_symbols), + reason, reason_size); } diff --git a/target/i386/latx/context/wrappedcairo.c b/target/i386/latx/context/wrappedcairo.c index 08ce60051b..ea941e833b 100644 --- a/target/i386/latx/context/wrappedcairo.c +++ b/target/i386/latx/context/wrappedcairo.c @@ -26,6 +26,8 @@ #include "library_private.h" #include "myalign.h" #include "wrappedcairo-preflight.h" +#include "wrappedfont-interop.h" +#include "wrappedfont-preflight.h" #include "wrapper.h" #pragma GCC diagnostic push @@ -34,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" @@ -127,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]; @@ -844,12 +865,7 @@ static uint32_t cairo_ft_interop_failure(void *font, void *cr, void *extents) return CAIRO_STATUS_FONT_TYPE_MISMATCH; } -/* - * 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. - */ -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(); @@ -857,12 +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 interop requires native " - "FreeType and Fontconfig wrappers"); + 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(); @@ -871,37 +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 interop requires a native Fontconfig " - "wrapper"); + 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); @@ -1330,12 +1487,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/context/wrappedfont-preflight.c b/target/i386/latx/context/wrappedfont-preflight.c new file mode 100644 index 0000000000..84c5da19f7 --- /dev/null +++ b/target/i386/latx/context/wrappedfont-preflight.c @@ -0,0 +1,160 @@ +/* + * 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" +}; +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 +#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; + const char *const *required_host_symbols; + size_t required_host_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), + .required_host_symbols = freetype_required_host_symbols, + .required_host_symbol_count = + ARRAY_SIZE(freetype_required_host_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), + .required_host_symbols = fontconfig_required_host_symbols, + .required_host_symbol_count = + ARRAY_SIZE(fontconfig_required_host_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), + .required_host_symbols = libxft_required_host_symbols, + .required_host_symbol_count = + ARRAY_SIZE(libxft_required_host_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, + library->required_host_symbols, + library->required_host_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..0b5ac94ede --- /dev/null +++ b/target/i386/latx/context/wrappedfontconfig.c @@ -0,0 +1,494 @@ +/* + * 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 "wrappedfont-interop.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 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 *); +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 *); + +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) \ + 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) \ + 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(FcPatternGetFTFace, fc_iFppip_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, +}; + +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) +{ + 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); +} + +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; + 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; +} + +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) +{ + 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)) { + native_select = select_callback ? fontconfig_scandir_select : NULL; + native_compare = compare_callback ? fontconfig_scandir_compare : NULL; + fontconfig_scandir_context = &context; + } + 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) +{ + 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..6f1754a18d --- /dev/null +++ b/target/i386/latx/context/wrappedfreetype.c @@ -0,0 +1,1969 @@ +/* + * 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 "wrappedfont-interop.h" +#include "wrapper.h" +#include "wrappertbbridge.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 FtMemoryAbi { + void *user; + void *alloc; + void *free; + void *realloc; +} FtMemoryAbi; + +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; + +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"); +_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(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, + "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; + 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; + struct FtSizeTracker *next; +} FtSizeTracker; + +typedef struct FtBorrowTracker { + void *face; + unsigned references; + 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 void freetype_stream_guest_view(FtStreamBridge *bridge) +{ + *bridge->guest = bridge->native; + bridge->guest->read = bridge->callbacks[0]; + bridge->guest->close = bridge->callbacks[1]; +} + +static void freetype_stream_native_view(FtStreamBridge *bridge) +{ + bridge->native = *bridge->guest; + bridge->native.read = bridge->callbacks[0] + ? freetype_stream_read : NULL; + bridge->native.close = bridge->callbacks[1] + ? freetype_stream_close : 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); + } + 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; + } + 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_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_generic_patch_library_locked(FtGenericContext *context, + void *library) +{ + for (FtFaceTracker *face = freetype_faces; face; face = face->next) { + if (face->library == library && + !freetype_generic_patch_face_locked(context, face->face, + false)) { + return false; + } + } + return true; +} + +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)); + + if (!tracker) { + return false; + } + 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; + 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 FtStreamBridge *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; + FtStreamBridge *stream_bridge = tracker->stream_bridge; + + free(tracker); + return stream_bridge; + } + return NULL; +} + +static void freetype_untrack_library_locked(void *library) +{ + FtFaceTracker *face = freetype_faces; + + while (face) { + FtFaceTracker *next = face->next; + + if (face->library == library) { + free(freetype_untrack_face_locked(face->face)); + } + face = next; + } +} + +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; +} + +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; + + 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) \ + 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 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) +{ + 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 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) }; +#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 +#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, + FtStreamBridge *stream_bridge) +{ + if (status != FT_ERROR_SUCCESS || !face || !*face) { + free(stream_bridge); + return status; + } + 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), NULL); +} + +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), 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 (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, + stream_bridge); +} + +EXPORT int32_t my_FT_Attach_Stream(void *face, FtOpenArgsAbi *args) +{ + 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; + } + status = my->FT_Attach_Stream(face, args); + free(stream_bridge); + return status; +} + +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) +{ + FtGenericContext context = {0}; + int32_t status; + + g_mutex_lock(&freetype_lock); + if (size && !freetype_generic_patch_locked( + &context, size, &((FtSizeAbi *)size)->generic, false, + false, false)) { + 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 = 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); + g_mutex_unlock(&freetype_lock); + } + return status; +} + +EXPORT int32_t my_FT_Done_Face(void *face) +{ + FtGenericContext context = {0}; + FtStreamBridge *stream_bridge = NULL; + 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_generic_patch_face_locked(&context, face, false)) { + 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 = 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; + tracker = tracker->next) { + if (tracker->face != face) { + continue; + } + if (tracker->references > 1) { + tracker->references--; + } else { + stream_bridge = freetype_untrack_face_locked(face); + } + break; + } + g_mutex_unlock(&freetype_lock); + free(stream_bridge); + } + 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, 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; +} + +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 *), + 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); + last_reference = !honor_references || + freetype_library_is_last_reference_locked(library); + if (!last_reference) { + g_mutex_unlock(&freetype_lock); + 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, false); +} + +EXPORT int32_t my_FT_Done_Library(void *library) +{ + return freetype_done_library(library, my->FT_Done_Library, true); +} + +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; +} + +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; + } + native = *params; + callbacks = &native.gray_spans; + for (size_t i = 0; i < 4; i++) { + context.callbacks[i] = callbacks[i]; + + if (!context.callbacks[i]) { + continue; + } + 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); +} + +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) +{ + 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; + } + if (bridge) { + freetype_memory_bridge_track(bridge, *library); + } + return status; +} + +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) +{ + 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; + } + 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); + } +} + +EXPORT void my_FT_Set_Debug_Hook(void *library, uint32_t index, + void *callback) +{ + uintptr_t guest = (uintptr_t)callback; + void *native = GetNativeFnc(guest); + FtDebugHookTracker *tracker = NULL; + FtDebugHookTracker *old = NULL; + + 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) +{ + 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_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; + } + while (freetype_sizes) { + FtSizeTracker *next = freetype_sizes->next; + + free(freetype_sizes); + freetype_sizes = next; + } + while (freetype_borrowed_faces) { + FtBorrowTracker *next = freetype_borrowed_faces->next; + + 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); +} + +#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/wrappedlib-preflight.c b/target/i386/latx/context/wrappedlib-preflight.c new file mode 100644 index 0000000000..8a273ad3b5 --- /dev/null +++ b/target/i386/latx/context/wrappedlib-preflight.c @@ -0,0 +1,238 @@ +/* + * 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, + const char *const *required_host_symbols, + size_t required_host_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 || + (required_host_symbol_count && !required_host_symbols)) { + 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 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++) { + 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/context/wrappedlibxft.c b/target/i386/latx/context/wrappedlibxft.c index dbe415128e..be7cfefe2b 100644 --- a/target/i386/latx/context/wrappedlibxft.c +++ b/target/i386/latx/context/wrappedlibxft.c @@ -2,44 +2,207 @@ * 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 "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 +#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_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 *); + +#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" + +#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; + 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/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/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..a0d909c5e9 --- /dev/null +++ b/target/i386/latx/include/generated/wrappedfreetypetypes.h @@ -0,0 +1,63 @@ +/******************************************************************* + * 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_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) + +#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..13351b0645 100644 --- a/target/i386/latx/include/generated/wrappedlibxfttypes.h +++ b/target/i386/latx/include/generated/wrappedlibxfttypes.h @@ -8,10 +8,16 @@ #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(XftFontClose, xft_vFpp_t) \ + GO(XftFontOpen, 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/kzt-group-list.h b/target/i386/latx/include/kzt-group-list.h index 69f02499db..083fb7b1bf 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(CAIRO, "cairo", 5, EXPERIMENTAL, KZT_GROUP_CORE | KZT_GROUP_X11) + X(FONT, "font", 6, EXPERIMENTAL, KZT_GROUP_CORE | KZT_GROUP_X11) \ + X(CAIRO, "cairo", 5, EXPERIMENTAL, KZT_GROUP_FONT) #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-interop.h b/target/i386/latx/include/wrappedfont-interop.h new file mode 100644 index 0000000000..b56e04a70c --- /dev/null +++ b/target/i386/latx/include/wrappedfont-interop.h @@ -0,0 +1,20 @@ +/* + * 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); +bool latx_freetype_face_prepare_host_destruction(void *face); + +void *latx_fontconfig_pattern_ft_face(void *pattern); + +#endif 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..ac779ac1d7 --- /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) +GOM(FcFontSetDestroy, vFEp) +GO(FcFontSetList, pFppipp) +GO(FcFontSetMatch, pFppipp) +GO(FcFontSetPrint, vFp) +GO(FcFontSetSort, pFppipipp) +GOM(FcFontSetSortDestroy, vFEp) +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..700ccc1bbc --- /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) +GOM(FT_Init_FreeType, iFEp) +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) +GOM(FT_Reference_Library, iFEp) +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) +GOM(FTC_Manager_Done, vFEp) +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/wrappedlib-preflight.h b/target/i386/latx/include/wrappedlib-preflight.h new file mode 100644 index 0000000000..72ca45d7cd --- /dev/null +++ b/target/i386/latx/include/wrappedlib-preflight.h @@ -0,0 +1,23 @@ +/* + * 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, + const char *const *required_host_symbols, + size_t required_host_symbol_count, + char *reason, size_t reason_size); + +#endif /* LATX_WRAPPEDLIB_PREFLIGHT_H */ diff --git a/target/i386/latx/include/wrappedlibxft_private.h b/target/i386/latx/include/wrappedlibxft_private.h index af4910792a..ef133cfa11 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(XftFontClose, vFpp) +GO(XftDrawString8, vFpppiipi) +GO(XftDrawStringUtf16, vFpppiipui) +GO(XftDrawStringUtf8, vFpppiipi) +GO(XftDrawVisual, pFp) +GO(XftFontCheckGlyph, iFppiupp) +GOM(XftFontClose, vFEpp) 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) +GOM(XftLockFace, pFEp) +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) +GOM(XftUnlockFace, vFEp) +GO(XftXlfdParse, pFpii) 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_