#define _LARGEFILE_SOURCE 1
#define _FILE_OFFSET_BITS 64
#define _GNU_SOURCE         /* See feature_test_macros(7) */
#if !defined(TERMUX) && !defined(ANDROID)
# if defined(__has_include)
#  if __has_include(<argp.h>)
#   include <argp.h>
#   define HAVE_ARGP 1
#  endif
# else
#  include <argp.h>
#  define HAVE_ARGP 1
# endif
#endif
#ifndef HAVE_ARGP
struct argp;
struct argp_state;
#endif
#ifdef STATICBUILD
extern int _nl_msg_cat_cntr __attribute__((weak));
extern const char *const _sys_siglist[] __asm__("__sys_siglist");
extern const char *const sys_siglist[] __asm__("__sys_siglist");
#endif
#include <stdlib.h>
#include <stdio.h>
#include <stddef.h>
#include <string.h>
#include <wchar.h>
#include <dlfcn.h>
#include <signal.h>
#include <errno.h>
#include <err.h>
#include <stdarg.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/select.h>
#include <unistd.h>
#include <fcntl.h>
#include <glob.h>
#include <ctype.h>
#include <dirent.h>
#include <search.h>
#include <sys/types.h>
#include <poll.h>
#include <sys/epoll.h>
#include <arpa/inet.h>
#include <ftw.h>
#include <sys/syscall.h>
#include <sys/socket.h>
#include <sys/utsname.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <setjmp.h>
#include <sys/vfs.h>
#include <spawn.h>
#include <fts.h>
#include <syslog.h>
#include <malloc.h>
#include <getopt.h>
#include <sys/resource.h>
#include <sys/prctl.h>
#include <sys/ptrace.h>
#include <sys/uio.h>
#include <sys/wait.h>
#include <error.h>
#include <sched.h>
#undef LOG_INFO
#undef LOG_DEBUG

#include "wrappedlibs.h"

#include "os.h"
#include "box64stack.h"
#include "x64emu.h"
#include "box64cpu.h"
#include "debug.h"
#include "wrapper.h"
#include "bridge.h"
#include "callback.h"
#include "librarian.h"
#include "librarian/library_private.h"
#include "emu/x64emu_private.h"
#include "box64context.h"
#include "syscall_user_dispatch.h"
#include "myalign.h"
#include "signals.h"
#include "fileutils.h"
#include "auxval.h"
#include "elfloader.h"
#include "bridge.h"
#include "globalsymbols.h"
#include "env.h"
#include "wine_tools.h"
#include "pe_tools.h"
#include "cleanup.h"
#include "random.h"
#include "cpumask.h"
#include "x64tls.h"
#ifndef LOG_INFO
#define LOG_INFO 1
#endif
#ifndef LOG_DEBUG
#define LOG_DEBUG 2
#endif


#define LIBNAME libc
const char* libcName = "libc.so.6";
#define ALTNAME "libc.so"

typedef int (*iFi_t)(int);
typedef int (*iFp_t)(void*);
typedef int (*iFL_t)(unsigned long);
typedef void (*vFpp_t)(void*, void*);
typedef void (*vFipp_t)(int32_t, void*, void*);
typedef int32_t (*iFpi_t)(void*, int32_t);
typedef int32_t (*iFpp_t)(void*, void*);
typedef int32_t (*iFpL_t)(void*, size_t);
typedef int32_t (*iFiip_t)(int32_t, int32_t, void*);
typedef int32_t (*iFipp_t)(int32_t, void*, void*);
typedef int32_t (*iFppi_t)(void*, void*, int32_t);
typedef int32_t (*iFpup_t)(void*, uint32_t, void*);
typedef int32_t (*iFpuu_t)(void*, uint32_t, uint32_t);
typedef int32_t (*iFiiII_t)(int, int, int64_t, int64_t);
typedef int32_t (*iFiiiV_t)(int, int, int, ...);
typedef int32_t (*iFippi_t)(int32_t, void*, void*, int32_t);
typedef int32_t (*iFpppp_t)(void*, void*, void*, void*);
typedef int32_t (*iFpipp_t)(void*, int32_t, void*, void*);
typedef int32_t (*iFppii_t)(void*, void*, int32_t, int32_t);
typedef int32_t (*iFipiup_t)(int, void*, int, uint32_t, void*);
typedef int32_t (*iFipiI_t)(int32_t, void*, int32_t, int64_t);
typedef int32_t (*iFipuu_t)(int32_t, void*, uint32_t, uint32_t);
typedef int32_t (*iFipuup_t)(int32_t, void*, uint32_t, uint32_t, void*);
typedef int32_t (*iFiiV_t)(int32_t, int32_t, ...);
typedef void* (*pFp_t)(void*);
typedef void* (*pFpip_t)(void*, int, void*);

#define ADDED_FUNCTIONS() \

#include "generated/wrappedlibctypes.h"

EXPORT uintptr_t my_error_print_progname = 0;
static void (*native_error_print_progname)(void) = NULL;

static void my_wrap_error_print_progname(void)
{
    if (my_error_print_progname) {
        RunFunctionFmt(my_error_print_progname, "v");
        return;
    }
    if (native_error_print_progname)
        native_error_print_progname();
}

#define ADDED_INIT()                                                                  \
    void (**p)(void);                                                                 \
    p = (void (**)(void))dlsym(lib->w.lib, "error_print_progname");                   \
    if (p) {                                                                          \
        native_error_print_progname = *p;                                             \
        *p = my_wrap_error_print_progname;                                            \
    }

#include "wrappercallback.h"

static int regs_abi[] = {_DI, _SI, _DX, _CX, _R8, _R9};
void* getVargN(x64emu_t *emu, int n)
{
    if(n<6)
        return (void*)emu->regs[regs_abi[n]].q[0];
    return ((void**)R_RSP)[1+n-6];
}
typedef int (*inet_pton_chk_f)(int, const char*, void*, size_t);
static inet_pton_chk_f real___inet_pton_chk = NULL;
static int real___inet_pton_chk_resolved = 0;

EXPORT int my___inet_pton_chk(x64emu_t* emu, int af, const char* src, void* dst, size_t dstlen)
{
    (void)emu;
    if(!real___inet_pton_chk_resolved) {
        real___inet_pton_chk_resolved = 1;
        real___inet_pton_chk = (inet_pton_chk_f)dlsym(RTLD_DEFAULT, "__inet_pton_chk");
    }

    if(real___inet_pton_chk)
        return real___inet_pton_chk(af, src, dst, dstlen);

    (void)dstlen;
    return inet_pton(af, src, dst);
}

// utility functions
#define SUPER() \
GO(0)   \
GO(1)   \
GO(2)   \
GO(3)   \
GO(4)   \
GO(5)   \
GO(6)   \
GO(7)   \
GO(8)   \
GO(9)   \
GO(10)  \
GO(11)  \
GO(12)  \
GO(13)  \
GO(14)  \
GO(15)

// compare
#define GO(A)   \
static uintptr_t my_compare_fct_##A = 0;                                    \
static int my_compare_##A(void* a, void* b)                                 \
{                                                                           \
    return (int)RunFunctionFmt(my_compare_fct_##A, "pp", a, b);       \
}
SUPER()
#undef GO
static void* findcompareFct(void* fct)
{
    if(!fct) return NULL;
    void* p;
    if((p = GetNativeFnc((uintptr_t)fct))) return p;
    #define GO(A) if(my_compare_fct_##A == (uintptr_t)fct) return my_compare_##A;
    SUPER()
    #undef GO
    #define GO(A) if(my_compare_fct_##A == 0) {my_compare_fct_##A = (uintptr_t)fct; return my_compare_##A; }
    SUPER()
    #undef GO
    printf_log(LOG_NONE, "Warning, no more slot for libc compare callback\n");
    return NULL;
}
// tss dtor
#define GO(A)                                    \
    static uintptr_t my_Dtor_fct_##A = 0;        \
    static void my_Dtor_##A(void* a)             \
    {                                            \
        RunFunctionFmt(my_Dtor_fct_##A, "p", a); \
    }
SUPER()
#undef GO
static void* findDtorFct(void* fct)
{
    if (!fct) return NULL;
    void* p;
    if ((p = GetNativeFnc((uintptr_t)fct))) return p;
#define GO(A) \
    if (my_Dtor_fct_##A == (uintptr_t)fct) return my_Dtor_##A;
    SUPER()
#undef GO
#define GO(A)                             \
    if (my_Dtor_fct_##A == 0) {           \
        my_Dtor_fct_##A = (uintptr_t)fct; \
        return my_Dtor_##A;               \
    }
    SUPER()
#undef GO
    printf_log(LOG_NONE, "Warning, no more slot for libc tss dtor callback\n");
    return NULL;
}
// action
#define GO(A)   \
static uintptr_t my_action_fct_##A = 0;                 \
static void my_action_##A(void* a, uint32_t b, int c)   \
{                                                       \
    RunFunctionFmt(my_action_fct_##A, "pui", a, b, c);  \
}
SUPER()
#undef GO
static void* findactionFct(void* fct)
{
    if(!fct) return NULL;
    void* p;
    if((p = GetNativeFnc((uintptr_t)fct))) return p;
    #define GO(A) if(my_action_fct_##A == (uintptr_t)fct) return my_action_##A;
    SUPER()
    #undef GO
    #define GO(A) if(my_action_fct_##A == 0) {my_action_fct_##A = (uintptr_t)fct; return my_action_##A; }
    SUPER()
    #undef GO
    printf_log(LOG_NONE, "Warning, no more slot for libc action callback\n");
    return NULL;
}

// ftw64
#define GO(A)   \
static uintptr_t my_ftw64_fct_##A = 0;                      \
static int my_ftw64_##A(void* fpath, void* sb, int flag)    \
{                                                           \
    struct x64_stat64 x64st;                                \
    UnalignStat64(sb, &x64st);                              \
    return (int)RunFunctionFmt(my_ftw64_fct_##A, "ppi", fpath, &x64st, flag);         \
}
SUPER()
#undef GO
static void* findftw64Fct(void* fct)
{
    if(!fct) return NULL;
    #define GO(A) if(my_ftw64_fct_##A == (uintptr_t)fct) return my_ftw64_##A;
    SUPER()
    #undef GO
    #define GO(A) if(my_ftw64_fct_##A == 0) {my_ftw64_fct_##A = (uintptr_t)fct; return my_ftw64_##A; }
    SUPER()
    #undef GO
    printf_log(LOG_NONE, "Warning, no more slot for libc ftw64 callback\n");
    return NULL;
}

// nftw64
#define GO(A)   \
static uintptr_t my_nftw64_fct_##A = 0;                                     \
static int my_nftw64_##A(void* fpath, void* sb, int flag, void* ftwbuff)    \
{                                                                           \
    struct x64_stat64 x64st;                                                \
    UnalignStat64(sb, &x64st);                                              \
    return (int)RunFunctionFmt(my_nftw64_fct_##A, "ppip", fpath, &x64st, flag, ftwbuff);          \
}
SUPER()
#undef GO
static void* findnftw64Fct(void* fct)
{
    if(!fct) return NULL;
    #define GO(A) if(my_nftw64_fct_##A == (uintptr_t)fct) return my_nftw64_##A;
    SUPER()
    #undef GO
    #define GO(A) if(my_nftw64_fct_##A == 0) {my_nftw64_fct_##A = (uintptr_t)fct; return my_nftw64_##A; }
    SUPER()
    #undef GO
    printf_log(LOG_NONE, "Warning, no more slot for libc nftw64 callback\n");
    return NULL;
}
// globerr
#define GO(A)   \
static uintptr_t my_globerr_fct_##A = 0;                                                \
static int my_globerr_##A(void* epath, int eerrno)                                      \
{                                                                                       \
    return (int)RunFunctionFmt(my_globerr_fct_##A, "pi", epath, eerrno);          \
}
SUPER()
#undef GO
static void* findgloberrFct(void* fct)
{
    if(!fct) return NULL;
    void* p;
    if((p = GetNativeFnc((uintptr_t)fct))) return p;
    #define GO(A) if(my_globerr_fct_##A == (uintptr_t)fct) return my_globerr_##A;
    SUPER()
    #undef GO
    #define GO(A) if(my_globerr_fct_##A == 0) {my_globerr_fct_##A = (uintptr_t)fct; return my_globerr_##A; }
    SUPER()
    #undef GO
    printf_log(LOG_NONE, "Warning, no more slot for libc globerr callback\n");
    return NULL;
}
// free
#define GO(A)   \
static uintptr_t my_free_fct_##A = 0;                       \
static void my_free_##A(void* p)                            \
{                                                           \
    RunFunctionFmt(my_free_fct_##A, "p", p);          \
}
SUPER()
#undef GO
static void* findfreeFct(void* fct)
{
    if(!fct) return NULL;
    void* p;
    if((p = GetNativeFnc((uintptr_t)fct))) return p;
    #define GO(A) if(my_free_fct_##A == (uintptr_t)fct) return my_free_##A;
    SUPER()
    #undef GO
    #define GO(A) if(my_free_fct_##A == 0) {my_free_fct_##A = (uintptr_t)fct; return my_free_##A; }
    SUPER()
    #undef GO
    printf_log(LOG_NONE, "Warning, no more slot for libc free callback\n");
    return NULL;
}

#if 0
#undef dirent
// filter_dir
#define GO(A)   \
static uintptr_t my_filter_dir_fct_##A = 0;                                 \
static int my_filter_dir_##A(const struct dirent* a)                        \
{                                                                           \
    return (int)RunFunctionFmt(my_filter_dir_fct_##A, "p", a);        \
}
SUPER()
#undef GO
static void* findfilter_dirFct(void* fct)
{
    if(!fct) return NULL;
    void* p;
    if((p = GetNativeFnc((uintptr_t)fct))) return p;
    #define GO(A) if(my_filter_dir_fct_##A == (uintptr_t)fct) return my_filter_dir_##A;
    SUPER()
    #undef GO
    #define GO(A) if(my_filter_dir_fct_##A == 0) {my_filter_dir_fct_##A = (uintptr_t)fct; return my_filter_dir_##A; }
    SUPER()
    #undef GO
    printf_log(LOG_NONE, "Warning, no more slot for libc filter_dir callback\n");
    return NULL;
}
// compare_dir
#define GO(A)   \
static uintptr_t my_compare_dir_fct_##A = 0;                                    \
static int my_compare_dir_##A(const struct dirent* a, const struct dirent* b)   \
{                                                                               \
    return (int)RunFunctionFmt(my_compare_dir_fct_##A, "pp", a, b);       \
}
SUPER()
#undef GO
static void* findcompare_dirFct(void* fct)
{
    if(!fct) return NULL;
    void* p;
    if((p = GetNativeFnc((uintptr_t)fct))) return p;
    #define GO(A) if(my_compare_dir_fct_##A == (uintptr_t)fct) return my_compare_dir_##A;
    SUPER()
    #undef GO
    #define GO(A) if(my_compare_dir_fct_##A == 0) {my_compare_dir_fct_##A = (uintptr_t)fct; return my_compare_dir_##A; }
    SUPER()
    #undef GO
    printf_log(LOG_NONE, "Warning, no more slot for libc compare_dir callback\n");
    return NULL;
}
#endif

// filter64
#define GO(A)   \
static uintptr_t my_filter64_fct_##A = 0;                                   \
static int my_filter64_##A(const struct dirent64* a)                        \
{                                                                           \
    return (int)RunFunctionFmt(my_filter64_fct_##A, "p", a);          \
}
SUPER()
#undef GO
static void* findfilter64Fct(void* fct)
{
    if(!fct) return NULL;
    void* p;
    if((p = GetNativeFnc((uintptr_t)fct))) return p;
    #define GO(A) if(my_filter64_fct_##A == (uintptr_t)fct) return my_filter64_##A;
    SUPER()
    #undef GO
    #define GO(A) if(my_filter64_fct_##A == 0) {my_filter64_fct_##A = (uintptr_t)fct; return my_filter64_##A; }
    SUPER()
    #undef GO
    printf_log(LOG_NONE, "Warning, no more slot for libc filter64 callback\n");
    return NULL;
}
// compare64
#define GO(A)   \
static uintptr_t my_compare64_fct_##A = 0;                                      \
static int my_compare64_##A(const struct dirent64* a, const struct dirent64* b) \
{                                                                               \
    return (int)RunFunctionFmt(my_compare64_fct_##A, "pp", a, b);         \
}
SUPER()
#undef GO
static void* findcompare64Fct(void* fct)
{
    if(!fct) return NULL;
    void* p;
    if((p = GetNativeFnc((uintptr_t)fct))) return p;
    #define GO(A) if(my_compare64_fct_##A == (uintptr_t)fct) return my_compare64_##A;
    SUPER()
    #undef GO
    #define GO(A) if(my_compare64_fct_##A == 0) {my_compare64_fct_##A = (uintptr_t)fct; return my_compare64_##A; }
    SUPER()
    #undef GO
    printf_log(LOG_NONE, "Warning, no more slot for libc compare64 callback\n");
    return NULL;
}
// printf_output
#define GO(A)   \
static uintptr_t my_printf_output_fct_##A = 0;                                          \
static int my_printf_output_##A(void* a, void* b, void* c)                              \
{                                                                                       \
    return (int)RunFunctionFmt(my_printf_output_fct_##A, "ppp", a, b, c);         \
}
SUPER()
#undef GO
static void* findprintf_outputFct(void* fct)
{
    if(!fct) return NULL;
    void* p;
    if((p = GetNativeFnc((uintptr_t)fct))) return p;
    #define GO(A) if(my_printf_output_fct_##A == (uintptr_t)fct) return my_printf_output_##A;
    SUPER()
    #undef GO
    #define GO(A) if(my_printf_output_fct_##A == 0) {my_printf_output_fct_##A = (uintptr_t)fct; return my_printf_output_##A; }
    SUPER()
    #undef GO
    printf_log(LOG_NONE, "Warning, no more slot for libc printf_output callback\n");
    return NULL;
}
// printf_arginfo
#define GO(A)   \
static uintptr_t my_printf_arginfo_fct_##A = 0;                                             \
static int my_printf_arginfo_##A(void* a, size_t b, void* c, void* d)                       \
{                                                                                           \
    return (int)RunFunctionFmt(my_printf_arginfo_fct_##A, "pLpp", a, b, c, d);        \
}
SUPER()
#undef GO
static void* findprintf_arginfoFct(void* fct)
{
    if(!fct) return NULL;
    void* p;
    if((p = GetNativeFnc((uintptr_t)fct))) return p;
    #define GO(A) if(my_printf_arginfo_fct_##A == (uintptr_t)fct) return my_printf_arginfo_##A;
    SUPER()
    #undef GO
    #define GO(A) if(my_printf_arginfo_fct_##A == 0) {my_printf_arginfo_fct_##A = (uintptr_t)fct; return my_printf_arginfo_##A; }
    SUPER()
    #undef GO
    printf_log(LOG_NONE, "Warning, no more slot for libc printf_arginfo callback\n");
    return NULL;
}
// printf_type
#define GO(A)   \
static uintptr_t my_printf_type_fct_##A = 0;                        \
static void my_printf_type_##A(void* a, va_list* b)                 \
{                                                                   \
    RunFunctionFmt(my_printf_type_fct_##A, "pp", a, b);       \
}
SUPER()
#undef GO
static void* findprintf_typeFct(void* fct)
{
    if(!fct) return NULL;
    void* p;
    if((p = GetNativeFnc((uintptr_t)fct))) return p;
    #define GO(A) if(my_printf_type_fct_##A == (uintptr_t)fct) return my_printf_type_##A;
    SUPER()
    #undef GO
    #define GO(A) if(my_printf_type_fct_##A == 0) {my_printf_type_fct_##A = (uintptr_t)fct; return my_printf_type_##A; }
    SUPER()
    #undef GO
    printf_log(LOG_NONE, "Warning, no more slot for libc printf_type callback\n");
    return NULL;
}

// parse_type
#define GO(A)                                                          \
    static uintptr_t my_argp_parser_fct_##A = 0;                       \
    static int my_argp_parser_##A(int a, void* b, void* c)             \
    {                                                                  \
        return RunFunctionFmt(my_argp_parser_fct_##A, "ipp", a, b, c); \
    }
SUPER()
#undef GO
static void* find_argp_parser_Fct(void* fct)
{
    if (!fct) return NULL;
    void* p;
    if ((p = GetNativeFnc((uintptr_t)fct))) return p;
#define GO(A) \
    if (my_argp_parser_fct_##A == (uintptr_t)fct) return my_argp_parser_##A;
    SUPER()
#undef GO
#define GO(A)                                    \
    if (my_argp_parser_fct_##A == 0) {           \
        my_argp_parser_fct_##A = (uintptr_t)fct; \
        return my_argp_parser_##A;               \
    }
    SUPER()
#undef GO
    printf_log(LOG_NONE, "Warning, no more slot for libc argp_parser callback\n");
    return NULL;
}

// help_filter
#define GO(A)                                                          \
    static uintptr_t my_help_filter_fct_##A = 0;                       \
    static void* my_help_filter_##A(int a, void* b, void* c)             \
    {                                                                  \
        return (void*)(uintptr_t)RunFunctionFmt(my_help_filter_fct_##A, "ipp", a, b, c); \
    }
SUPER()
#undef GO
static void* find_help_filter_Fct(void* fct)
{
    if (!fct) return NULL;
    void* p;
    if ((p = GetNativeFnc((uintptr_t)fct))) return p;
#define GO(A) \
    if (my_help_filter_fct_##A == (uintptr_t)fct) return my_help_filter_##A;
    SUPER()
#undef GO
#define GO(A)                                    \
    if (my_help_filter_fct_##A == 0) {           \
        my_help_filter_fct_##A = (uintptr_t)fct; \
        return my_help_filter_##A;               \
    }
    SUPER()
#undef GO
    printf_log(LOG_NONE, "Warning, no more slot for libc help_filter callback\n");
    return NULL;
}

#undef SUPER

EXPORT int my_argp_parse(x64emu_t* emu, struct argp* argp, int argc, char** argv, int flags, int* index, void* input)
{
#if defined(HAVE_ARGP)
    if (!argp) {
        return argp_parse(argp, argc, argv, flags, index, input);
    }
    struct argp local = *argp;
    if (local.parser) {
        local.parser = find_argp_parser_Fct((void*)local.parser);
    }
    if (local.help_filter) {
        local.help_filter = find_help_filter_Fct((void*)local.help_filter);
    }
    return argp_parse(&local, argc, argv, flags, index, input);
#else
    (void)emu; (void)argp; (void)argc; (void)argv; (void)flags; (void)index; (void)input;
    printf_log(LOG_NONE, "Warning: unsupported argp_parse called, expecting failure\n");
    return -1;
#endif
}

// some my_XXX declare and defines
int32_t my___libc_start_main(x64emu_t* emu, int (*main) (int, char * *, char * *),
    int argc, char * * ubp_av, void (*init) (void), void (*fini) (void),
    void (*rtld_fini) (void), void (* stack_end)); // implemented in x64run_private.c
EXPORT void my___libc_init_first(x64emu_t* emu, int argc, char* arg0, char** b)
{
    // do nothing specific for now
    (void)emu; (void)argc; (void)arg0; (void)b;
    return;
}
uintptr_t my_syscall(x64emu_t *emu); // implemented in x64syscall.c
void EXPORT my___stack_chk_fail(x64emu_t* emu)
{
    char buff[200];
    #ifdef HAVE_TRACE
    sprintf(buff, "%p: Stack is corrupted, aborting (prev IP=%p)\n", (void*)emu->old_ip, (void*)emu->prev2_ip);
    #else
    sprintf(buff, "%p: Stack is corrupted, aborting\n", (void*)emu->old_ip);
    #endif
    if(BOX64ENV(rolling_log)) {
        print_rolling_log(LOG_INFO);
    }
    StopEmu(emu, buff, emu->segs[_CS]==0x23);
}
void EXPORT my___gmon_start__(x64emu_t *emu)
{
    (void)emu;
    printf_log(LOG_DEBUG, "__gmon_start__ called (dummy call)\n");
}

int EXPORT my___cxa_atexit(x64emu_t* emu, void* p, void* a, void* dso_handle)
{
    AddCleanup1Arg(emu, p, a, FindElfAddress(my_context, (uintptr_t)dso_handle));
    return 0;
}
int EXPORT my___cxa_at_quick_exit(x64emu_t* emu, void* p, void* dso_handle)
{
    (void)dso_handle; // glibc stores it but never uses it for quick_exit
    AddQuickCleanup(emu, p);
    return 0;
}
void EXPORT my___cxa_finalize(x64emu_t* emu, void* p)
{
    if(!p) {
        // p is null, call (and remove) all Cleanup functions
        CallAllCleanup(emu);
        return;
    }
    CallCleanup(emu, FindElfAddress(my_context, (uintptr_t)p));
}
int EXPORT my_atexit(x64emu_t* emu, void *p)
{
    AddCleanup(emu, p);
    return 0;
}
int EXPORT my_at_quick_exit(x64emu_t* emu, void *p)
{
    AddQuickCleanup(emu, p);
    return 0;
}
void EXPORT my_quick_exit(x64emu_t* emu, int status)
{
    CallQuickCleanup(emu, status);
    quick_exit(status);
}

int my_getcontext(x64emu_t* emu, void* ucp);
int my_setcontext(x64emu_t* emu, void* ucp);
int my_makecontext(x64emu_t* emu, void* ucp, void* fnc, int32_t argc, void* argv);
int my_swapcontext(x64emu_t* emu, void* ucp1, void* ucp2);

// All signal and context functions defined in signals.c

// All fts function defined in myfts.c

// getauxval implemented in auxval.c


// this one is defined in elfloader.c
int my_dl_iterate_phdr(x64emu_t *emu, void* F, void *data);

pid_t EXPORT my_fork(x64emu_t* emu)
{
    #if 1
    emu->quit = 1;
    emu->fork = 1;  // use regular fork...
    return 0;
    #else
    // execute atforks prepare functions, in reverse order
    for (int i=my_context->atfork_sz-1; i>=0; --i)
        if(my_context->atforks[i].prepare)
            RunFunctionWithEmu(emu, 0, my_context->atforks[i].prepare, 0);
    int type = emu->type;
    pid_t v;
    v = fork();
    if(type == EMUTYPE_MAIN)
        thread_set_emu(emu);
    if(v<0) {
        printf_log(LOG_NONE, "Warning, fork errored... (%d)\n", v);
        // error...
    } else if(v>0) {
        // execute atforks parent functions
        for (int i=0; i<my_context->atfork_sz; ++i)
            if(my_context->atforks[i].parent)
                RunFunctionWithEmu(emu, 0, my_context->atforks[i].parent, 0);

    } else /*if(v==0)*/ {
        // execute atforks child functions
        for (int i=0; i<my_context->atfork_sz; ++i)
            if(my_context->atforks[i].child)
                RunFunctionWithEmu(emu, 0, my_context->atforks[i].child, 0);
    }
    return v;
    #endif
}
pid_t EXPORT my___fork(x64emu_t* emu) __attribute__((alias("my_fork")));
pid_t EXPORT my_vfork(x64emu_t* emu)
{
    #if 1
    emu->quit = 1;
    emu->fork = 3;  // use regular fork...
    return 0;
    #else
    return 0;
    #endif
}

int EXPORT my_uname(struct utsname *buf)
{
    //TODO: check sizeof(struct utsname) == 390
    int ret = uname(buf);
    strcpy(buf->machine, "x86_64");
    return ret;
}

// X86_O_RDONLY 0x00
#define X86_O_WRONLY       0x01     // octal     01
#define X86_O_RDWR         0x02     // octal     02
#define X86_FMODE_EXEC     0x20
#define X86_O_CREAT        0x40     // octal     0100
#define X86_O_EXCL         0x80     // octal     0200
#define X86_O_NOCTTY       0x100    // octal     0400
#define X86_O_TRUNC        0x200    // octal    01000
#define X86_O_APPEND       0x400    // octal    02000
#define X86_O_NONBLOCK     0x800    // octal    04000
#define X86_O_SYNC         0x101000 // octal 04010000
#define X86_O_DSYNC        0x1000   // octal   010000
#define X86_O_RSYNC        X86_O_SYNC
#define X86_FASYNC         020000
#define X86_O_DIRECT       040000
#define X86_O_LARGEFILE    0100000
#define X86_O_DIRECTORY    0200000
#define X86_O_NOFOLLOW     0400000
#define X86_O_NOATIME      01000000
#define X86_O_CLOEXEC      02000000
#define X86_O_PATH         010000000
#define X86_O_TMPFILE      020200000
#define X86_FMODE_NONOTIFY 0x4000000

#ifndef O_TMPFILE
#define O_TMPFILE (020000000 | O_DIRECTORY)
#endif

#ifndef FMODE_EXEC
#define FMODE_EXEC 0x20
#endif
#ifndef FMODE_NONOTIFY
#define FMODE_NONOTIFY 0x4000000
#endif

#define SUPER()     \
    GO(O_WRONLY)    \
    GO(O_RDWR)      \
    GO(FMODE_EXEC)  \
    GO(O_CREAT)     \
    GO(O_EXCL)      \
    GO(O_NOCTTY)    \
    GO(O_TRUNC)     \
    GO(O_APPEND)    \
    GO(O_NONBLOCK)  \
    GO(O_SYNC)      \
    GO(O_DSYNC)     \
    GO(O_RSYNC)     \
    GO(FASYNC)      \
    GO(O_DIRECT)    \
    GO(O_LARGEFILE) \
    GO(O_TMPFILE)   \
    GO(O_DIRECTORY) \
    GO(O_NOFOLLOW)  \
    GO(O_NOATIME)   \
    GO(O_CLOEXEC)   \
    GO(O_PATH)      \
    GO(FMODE_NONOTIFY)

// x86->arm
int of_convert(int a)
{
    if(!a || a==-1) return a;
    int b=0;
    #define GO(A) if((a&(X86_##A))==(X86_##A)) {a&=~(X86_##A); b|=(A);}
    SUPER();
    #undef GO
    if(a) {
        printf_log(LOG_NONE, "Warning, of_convert(...) left over 0x%x, converted 0x%x\n", a, b);
    }
    return a|b;
}

// arm->x86
int of_unconvert(int a)
{
    if(!a || a==-1) return a;
    int b=0;
    #define GO(A) if((a&(A))==(A)) {a&=~(A); b|=(X86_##A);}
    SUPER();
    #undef GO
    int missing = 0;
    #ifdef ARM64
    if(!O_LARGEFILE) {
        if((a&(0400000))==(0400000)) {a&=~(0400000); b|=(X86_O_LARGEFILE);}
    }
    #elif defined(PPC64LE)
    if(!O_LARGEFILE) {
        if((a&(0200000))==(0200000)) {a&=~(0200000); b|=(X86_O_LARGEFILE);}
    }
    #else
    if(!O_LARGEFILE) missing |= X86_O_LARGEFILE;
    #endif
    if(a && (a&~missing)) {
        printf_log(LOG_NONE, "Warning, of_unconvert(...) left over 0x%x, converted 0x%x\n", a, b);
    }
    return a|b;
}
#undef SUPER


#ifdef PPC64LE
// ioctl number translation: x86_64 -> PPC64LE
// x86_64 _IOC encoding: dir(2 bits, 30-31) | size(14 bits, 16-29) | type(8 bits, 8-15) | nr(8 bits, 0-7)
// PPC64LE _IOC encoding: dir(3 bits, 29-31) | size(13 bits, 16-28) | type(8 bits, 8-15) | nr(8 bits, 0-7)
// Direction bits: x86: NONE=0, WRITE=1, READ=2  PPC: NONE=1, READ=2, WRITE=4

// x86_64 _IOC field extraction
#define X86_IOC_NRBITS    8
#define X86_IOC_TYPEBITS  8
#define X86_IOC_SIZEBITS  14
#define X86_IOC_DIRBITS   2
#define X86_IOC_NRSHIFT   0
#define X86_IOC_TYPESHIFT  (X86_IOC_NRSHIFT + X86_IOC_NRBITS)
#define X86_IOC_SIZESHIFT  (X86_IOC_TYPESHIFT + X86_IOC_TYPEBITS)
#define X86_IOC_DIRSHIFT   (X86_IOC_SIZESHIFT + X86_IOC_SIZEBITS)
#define X86_IOC_NRMASK    ((1 << X86_IOC_NRBITS) - 1)
#define X86_IOC_TYPEMASK  ((1 << X86_IOC_TYPEBITS) - 1)
#define X86_IOC_SIZEMASK  ((1 << X86_IOC_SIZEBITS) - 1)
#define X86_IOC_DIRMASK   ((1 << X86_IOC_DIRBITS) - 1)
#define X86_IOC_DIR(nr)   (((nr) >> X86_IOC_DIRSHIFT) & X86_IOC_DIRMASK)
#define X86_IOC_TYPE(nr)  (((nr) >> X86_IOC_TYPESHIFT) & X86_IOC_TYPEMASK)
#define X86_IOC_NR(nr)    (((nr) >> X86_IOC_NRSHIFT) & X86_IOC_NRMASK)
#define X86_IOC_SIZE(nr)  (((nr) >> X86_IOC_SIZESHIFT) & X86_IOC_SIZEMASK)

// x86_64 direction values
#define X86_IOC_NONE   0
#define X86_IOC_WRITE  1
#define X86_IOC_READ   2

// Old-style x86 terminal/file ioctl values (not _IOC-encoded)
#define X86_TCGETS           0x5401
#define X86_TCSETS           0x5402
#define X86_TCSETSW          0x5403
#define X86_TCSETSF          0x5404
#define X86_TCGETA           0x5405
#define X86_TCSETA           0x5406
#define X86_TCSETAW          0x5407
#define X86_TCSETAF          0x5408
#define X86_TCSBRK           0x5409
#define X86_TCXONC           0x540A
#define X86_TCFLSH           0x540B
#define X86_TIOCEXCL         0x540C
#define X86_TIOCNXCL         0x540D
#define X86_TIOCSCTTY        0x540E
#define X86_TIOCGPGRP        0x540F
#define X86_TIOCSPGRP        0x5410
#define X86_TIOCOUTQ         0x5411
#define X86_TIOCSTI          0x5412
#define X86_TIOCGWINSZ       0x5413
#define X86_TIOCSWINSZ       0x5414
#define X86_TIOCMGET         0x5415
#define X86_TIOCMBIS         0x5416
#define X86_TIOCMBIC         0x5417
#define X86_TIOCMSET         0x5418
#define X86_TIOCGSOFTCAR     0x5419
#define X86_TIOCSSOFTCAR     0x541A
#define X86_FIONREAD         0x541B
#define X86_TIOCLINUX        0x541C
#define X86_TIOCCONS         0x541D
#define X86_TIOCGSERIAL      0x541E
#define X86_TIOCSSERIAL      0x541F
#define X86_TIOCPKT          0x5420
#define X86_FIONBIO          0x5421
#define X86_TIOCNOTTY        0x5422
#define X86_TIOCSETD         0x5423
#define X86_TIOCGETD         0x5424
#define X86_TCSBRKP          0x5425
#define X86_TIOCGSID         0x5429
#define X86_FIONCLEX         0x5450
#define X86_FIOCLEX          0x5451
#define X86_FIOASYNC         0x5452
#define X86_FIOQSIZE         0x5460

#include <sys/ioctl.h>
#include <termios.h>

unsigned long ioctl_convert(unsigned long x86_req)
{
    // Lookup table for old-style x86 terminal/file ioctls
    switch(x86_req)
    {
        case X86_TCGETS:       return TCGETS;
        case X86_TCSETS:       return TCSETS;
        case X86_TCSETSW:      return TCSETSW;
        case X86_TCSETSF:      return TCSETSF;
        case X86_TCGETA:       return TCGETA;
        case X86_TCSETA:       return TCSETA;
        case X86_TCSETAW:      return TCSETAW;
        case X86_TCSETAF:      return TCSETAF;
        case X86_TCSBRK:       return TCSBRK;
        case X86_TCXONC:       return TCXONC;
        case X86_TCFLSH:       return TCFLSH;
        case X86_TIOCEXCL:     return TIOCEXCL;
        case X86_TIOCNXCL:     return TIOCNXCL;
        case X86_TIOCSCTTY:    return TIOCSCTTY;
        case X86_TIOCGPGRP:    return TIOCGPGRP;
        case X86_TIOCSPGRP:    return TIOCSPGRP;
        case X86_TIOCOUTQ:     return TIOCOUTQ;
        case X86_TIOCSTI:      return TIOCSTI;
        case X86_TIOCGWINSZ:   return TIOCGWINSZ;
        case X86_TIOCSWINSZ:   return TIOCSWINSZ;
        case X86_TIOCMGET:     return TIOCMGET;
        case X86_TIOCMBIS:     return TIOCMBIS;
        case X86_TIOCMBIC:     return TIOCMBIC;
        case X86_TIOCMSET:     return TIOCMSET;
        case X86_TIOCGSOFTCAR: return TIOCGSOFTCAR;
        case X86_TIOCSSOFTCAR: return TIOCSSOFTCAR;
        case X86_FIONREAD:     return FIONREAD;
        case X86_TIOCLINUX:    return TIOCLINUX;
        case X86_TIOCCONS:     return TIOCCONS;
        case X86_TIOCGSERIAL:  return TIOCGSERIAL;
        case X86_TIOCSSERIAL:  return TIOCSSERIAL;
        case X86_TIOCPKT:      return TIOCPKT;
        case X86_FIONBIO:      return FIONBIO;
        case X86_TIOCNOTTY:    return TIOCNOTTY;
        case X86_TIOCSETD:     return TIOCSETD;
        case X86_TIOCGETD:     return TIOCGETD;
        case X86_TCSBRKP:      return TCSBRKP;
        case X86_TIOCGSID:     return TIOCGSID;
        case X86_FIONCLEX:     return FIONCLEX;
        case X86_FIOCLEX:      return FIOCLEX;
        case X86_FIOASYNC:     return FIOASYNC;
        case X86_FIOQSIZE:     return FIOQSIZE;
    }

    // For _IOC-encoded ioctls, translate the encoding
    unsigned long x86_dir  = X86_IOC_DIR(x86_req);
    unsigned long x86_type = X86_IOC_TYPE(x86_req);
    unsigned long x86_nr   = X86_IOC_NR(x86_req);
    unsigned long x86_size = X86_IOC_SIZE(x86_req);

    // If it looks like a small raw number (not _IOC encoded), pass through
    if(x86_dir == 0 && x86_type == 0) {
        return x86_req;
    }

    // Translate x86 direction bits to PPC direction bits
    unsigned long ppc_dir = 0;
    if(x86_dir == X86_IOC_NONE) {
        // x86 NONE=0, but if type!=0 it's a real _IOC_NONE ioctl
        ppc_dir = 1; // PPC _IOC_NONE = 1
    } else {
        if(x86_dir & X86_IOC_READ)  ppc_dir |= 2; // PPC _IOC_READ = 2
        if(x86_dir & X86_IOC_WRITE) ppc_dir |= 4; // PPC _IOC_WRITE = 4
    }

    // PPC64LE: size is 13 bits (max 8191)
    if(x86_size > 8191) {
        printf_log(LOG_DEBUG, "Warning: ioctl size %lu exceeds PPC64LE 13-bit limit, clamping\n", x86_size);
        x86_size = 8191;
    }

    // Recompose as PPC64LE _IOC: dir(3 bits, 29-31) | size(13 bits, 16-28) | type(8, 8-15) | nr(8, 0-7)
    unsigned long ppc_req = (ppc_dir << 29) | (x86_size << 16) | (x86_type << 8) | x86_nr;

    printf_log(LOG_DEBUG, "ioctl_convert: x86=0x%lx -> ppc=0x%lx (dir %lu->%lu, type 0x%lx, nr 0x%lx, size %lu)\n",
               x86_req, ppc_req, X86_IOC_DIR(x86_req), ppc_dir, x86_type, x86_nr, x86_size);

    return ppc_req;
}

EXPORT int my_ioctl(x64emu_t* emu, int fd, unsigned long req, void* arg)
{
    (void)emu;
    unsigned long native_req = ioctl_convert(req);
    return ioctl(fd, native_req, arg);
}
#endif

EXPORT void* my__ZGTtnaX (size_t a) { (void)a; printf("warning _ZGTtnaX called\n"); return NULL; }
EXPORT void* my__ZGTtnam (size_t a) { (void)a; printf("warning _ZGTtnam called\n"); return NULL; }
EXPORT void my__ZGTtdlPv (void* a) { (void)a; printf("warning _ZGTtdlPv called\n"); }
EXPORT uint8_t my__ITM_RU1(const uint8_t * a) { (void)a; printf("warning _ITM_RU1 called\n"); return 0; }
EXPORT uint32_t my__ITM_RU4(const uint32_t * a) { (void)a; printf("warning _ITM_RU4 called\n"); return 0; }
EXPORT uint64_t my__ITM_RU8(const uint64_t * a) { (void)a; printf("warning _ITM_RU8 called\n"); return 0; }
EXPORT void my__ITM_memcpyRtWn(void * a, const void * b, size_t c) { (void)a; (void)b; (void)c; printf("warning _ITM_memcpyRtWn called\n"); }
EXPORT void my__ITM_memcpyRnWt(void * a, const void * b, size_t c) { (void)a; (void)b; (void)c; printf("warning _ITM_memcpyRnWt called\n"); }

EXPORT void my_longjmp(x64emu_t* emu, /*struct __jmp_buf_tag __env[1]*/void *p, int32_t __val);
EXPORT void my__longjmp(x64emu_t* emu, /*struct __jmp_buf_tag __env[1]*/void *p, int32_t __val) __attribute__((alias("my_longjmp")));
EXPORT void my_siglongjmp(x64emu_t* emu, /*struct __jmp_buf_tag __env[1]*/void *p, int32_t __val) __attribute__((alias("my_longjmp")));
EXPORT void my___longjmp_chk(x64emu_t* emu, /*struct __jmp_buf_tag __env[1]*/void *p, int32_t __val) __attribute__((alias("my_longjmp")));

//EXPORT int32_t my_setjmp(x64emu_t* emu, /*struct __jmp_buf_tag __env[1]*/void *p);
//EXPORT int32_t my__setjmp(x64emu_t* emu, /*struct __jmp_buf_tag __env[1]*/void *p) __attribute__((alias("my_setjmp")));
//EXPORT int32_t my___sigsetjmp(x64emu_t* emu, /*struct __jmp_buf_tag __env[1]*/void *p) __attribute__((alias("my_setjmp")));

EXPORT int my_printf(x64emu_t *emu, void* fmt, void* b) {
    myStackAlign(emu, (const char*)fmt, b, emu->scratch, R_EAX, 1);
    PREPARE_VALIST;
    return vprintf((const char*)fmt, VARARGS);
}
EXPORT int my___printf_chk(x64emu_t *emu, int chk, void* fmt, void* b)
{
    (void)chk;
    myStackAlign(emu, (const char*)fmt, b, emu->scratch, R_EAX, 2);
    PREPARE_VALIST;
    return vprintf((const char*)fmt, VARARGS);
}
extern int box64_stdout_no_w;
EXPORT int my_wprintf(x64emu_t *emu, void* fmt, void* b) {
    myStackAlignW(emu, (const char*)fmt, b, emu->scratch, R_EAX, 1);
    PREPARE_VALIST;
    if(box64_stdout_no_w) {
        wchar_t buff[2048];
        int ret = vswprintf(buff, 2047, fmt, VARARGS);
        printf("%S", buff);
        return ret;
    }
    return vwprintf((const wchar_t*)fmt, VARARGS);
}
EXPORT int my___wprintf_chk(x64emu_t *emu, int chk, void* fmt, void* b)
{
    (void)chk;
    myStackAlignW(emu, (const char*)fmt, b, emu->scratch, R_EAX, 2);
    PREPARE_VALIST;
    return vwprintf((const wchar_t*)fmt, VARARGS);
}

EXPORT int my_vprintf(x64emu_t *emu, void* fmt, x64_va_list_t b) {
    (void)emu;
    #ifdef CONVERT_VALIST
    CONVERT_VALIST(b);
    #else
    myStackAlignValist(emu, (const char*)fmt, emu->scratch, b);
    PREPARE_VALIST;
    #endif
    return vprintf(fmt, VARARGS);
}
EXPORT int my___vprintf_chk(x64emu_t *emu, void* fmt, x64_va_list_t b) __attribute__((alias("my_vprintf")));

EXPORT int my_vfprintf(x64emu_t *emu, void* F, void* fmt, x64_va_list_t b) {
    #ifdef CONVERT_VALIST
    CONVERT_VALIST(b);
    #else
    myStackAlignValist(emu, (const char*)fmt, emu->scratch, b);
    PREPARE_VALIST;
    #endif
    return vfprintf(F, fmt, VARARGS);
}
EXPORT int my___vfprintf_chk(x64emu_t *emu, void* F, void* fmt, x64_va_list_t b) __attribute__((alias("my_vfprintf")));
EXPORT int my__IO_vfprintf(x64emu_t *emu, void* F, void* fmt, x64_va_list_t b) __attribute__((alias("my_vfprintf")));

EXPORT int my_fprintf(x64emu_t *emu, void* F, void* fmt, void* b)  {
    myStackAlign(emu, (const char*)fmt, b, emu->scratch, R_EAX, 2);
    PREPARE_VALIST;
    return vfprintf(F, fmt, VARARGS);
}
EXPORT int my___fprintf_chk(x64emu_t *emu, void* F, int flag, void* fmt, void* b)  {
    (void)flag;
    myStackAlign(emu, (const char*)fmt, b, emu->scratch, R_EAX, 3);
    PREPARE_VALIST;
    return vfprintf(F, fmt, VARARGS);
}

EXPORT int my_vwprintf(x64emu_t *emu, void* fmt, x64_va_list_t b) {
    (void)emu;
    #ifdef CONVERT_VALIST
    CONVERT_VALIST(b);
    #else
    myStackAlignWValist(emu, (const char*)fmt, emu->scratch, b);
    PREPARE_VALIST;
    #endif
    int r = vwprintf(fmt, VARARGS);
    return r;
}

EXPORT int my_fwprintf(x64emu_t *emu, void* F, void* fmt, void* b)  {
    myStackAlignW(emu, (const char*)fmt, b, emu->scratch, R_EAX, 2);
    PREPARE_VALIST;
    return vfwprintf(F, fmt, VARARGS);
}

EXPORT int my___fwprintf_chk(x64emu_t *emu, void* F, int flag, void* fmt, void* b) {
    myStackAlignW(emu, (const char*)fmt, b, emu->scratch, R_EAX, 3);
    PREPARE_VALIST;
    return vfwprintf(F, fmt, VARARGS);
}

EXPORT int my_vfwprintf(x64emu_t *emu, void* F, void* fmt, x64_va_list_t  b) {
    #ifdef CONVERT_VALIST
    CONVERT_VALIST(b);
    #else
    myStackAlignWValist(emu, (const char*)fmt, emu->scratch, b);
    PREPARE_VALIST;
    #endif
    return vfwprintf(F, fmt, VARARGS);
}
EXPORT int my___vfwprintf_chk(x64emu_t *emu, void* F, int flag, void* fmt, x64_va_list_t b)  {
    #ifdef CONVERT_VALIST
    CONVERT_VALIST(b);
    #else
    myStackAlignWValist(emu, (const char*)fmt, emu->scratch, b);
    PREPARE_VALIST;
    #endif
    return vfwprintf(F, fmt, VARARGS);
}

EXPORT int my_dprintf(x64emu_t *emu, int d, void* fmt, void* b) {
    myStackAlign(emu, (const char*)fmt, b, emu->scratch, R_EAX, 2);
    PREPARE_VALIST;
    return vdprintf(d, fmt, VARARGS);
}

EXPORT int my___dprintf_chk(x64emu_t *emu, int d, int flag, void* fmt, void* b)  {
    (void)flag;
    myStackAlign(emu, (const char*)fmt, b, emu->scratch, R_EAX, 3);
    PREPARE_VALIST;
    return vdprintf(d, fmt, VARARGS);
}


EXPORT int my_vdprintf(x64emu_t *emu, int d, void* fmt, x64_va_list_t b) {
    #ifdef CONVERT_VALIST
    CONVERT_VALIST(b);
    #else
    myStackAlignValist(emu, (const char*)fmt, emu->scratch, b);
    PREPARE_VALIST;
    #endif
    return vdprintf(d, fmt, VARARGS);
}

EXPORT int my___vdprintf_chk(x64emu_t *emu, int d, int flag, void* fmt, x64_va_list_t b)  {
    (void)flag;
    #ifdef CONVERT_VALIST
    CONVERT_VALIST(b);
    #else
    myStackAlignValist(emu, (const char*)fmt, emu->scratch, b);
    PREPARE_VALIST;
    #endif
    return vdprintf(d, fmt, VARARGS);
}

#if 0
EXPORT void *my_div(void *result, int numerator, int denominator) {
    *(div_t *)result = div(numerator, denominator);
    return result;
}
#endif

EXPORT int my_snprintf(x64emu_t* emu, void* buff, size_t s, void * fmt, uint64_t * b) {
    #ifdef PREFER_CONVERT_VAARG
    CREATE_VALIST_FROM_VAARG(b, emu->scratch, 3);
    #else
    myStackAlign(emu, (const char*)fmt, b, emu->scratch, R_EAX, 3);
    PREPARE_VALIST;
    #endif
    int r = vsnprintf(buff, s, fmt, VARARGS);
    return r;
}
EXPORT int my___snprintf(x64emu_t* emu, void* buff, size_t s, void * fmt, uint64_t * b) __attribute__((alias("my_snprintf")));
EXPORT int my___snprintf_chk(x64emu_t* emu, void* buff, size_t s, int flags, size_t maxlen, void * fmt, uint64_t * b)
{
    (void)flags; (void)maxlen;
    #ifdef PREFER_CONVERT_VAARG
    CREATE_VALIST_FROM_VAARG(b, emu->scratch, 5);
    #else
    myStackAlign(emu, (const char*)fmt, b, emu->scratch, R_EAX, 5);
    PREPARE_VALIST;
    #endif
    int r = vsnprintf(buff, s, fmt, VARARGS);
    return r;
}

EXPORT int my_sprintf(x64emu_t* emu, void* buff, void * fmt, void * b) {
    #ifdef PREFER_CONVERT_VAARG
    CREATE_VALIST_FROM_VAARG(b, emu->scratch, 2);
    #else
    myStackAlign(emu, (const char*)fmt, b, emu->scratch, R_EAX, 2);
    PREPARE_VALIST;
    #endif
    return vsprintf(buff, (const char*)fmt, VARARGS);
}
EXPORT int my___sprintf_chk(x64emu_t* emu, void* buff, int flag, size_t l, void * fmt, void * b) {
    (void)flag; (void)l;
    #ifdef PREFER_CONVERT_VAARG
    CREATE_VALIST_FROM_VAARG(b, emu->scratch, 4);
    #else
    myStackAlign(emu, (const char*)fmt, b, emu->scratch, R_EAX, 4);
    PREPARE_VALIST;
    #endif
    return vsprintf(buff, (const char*)fmt, VARARGS);
}

EXPORT int my_asprintf(x64emu_t* emu, void** buff, void * fmt, uint64_t * b) {
    myStackAlign(emu, (const char*)fmt, b, emu->scratch, R_EAX, 2);
    PREPARE_VALIST;
    return vasprintf((char**)buff, (char*)fmt, VARARGS);
}
EXPORT int my___asprintf(x64emu_t* emu, void** buff, void * fmt, uint64_t * b) __attribute__((alias("my_asprintf")));

EXPORT int my_vasprintf(x64emu_t* emu, char** buff, void* fmt, x64_va_list_t b) {
    (void)emu;
    #ifdef CONVERT_VALIST
    CONVERT_VALIST(b);
    #else
    myStackAlignValist(emu, (const char*)fmt, emu->scratch, b);
    PREPARE_VALIST;
    #endif
    return vasprintf(buff, fmt, VARARGS);
}

EXPORT int my_vsprintf(x64emu_t* emu, void* buff,  void * fmt, x64_va_list_t b) {
    (void)emu;
    #ifdef CONVERT_VALIST
    CONVERT_VALIST(b);
    #else
    myStackAlignValist(emu, (const char*)fmt, emu->scratch, b);
    PREPARE_VALIST;
    #endif
    return vsprintf(buff, fmt, VARARGS);
}
EXPORT int my___vsprintf_chk(x64emu_t* emu, void* buff, void * fmt, x64_va_list_t b) __attribute__((alias("my_vsprintf")));

EXPORT int my_scanf(x64emu_t* emu, void* fmt, uint64_t* b)
{
    myStackAlignScanf(emu, (const char*)fmt, b, emu->scratch, 1);
    PREPARE_VALIST;

    return vscanf(fmt, VARARGS);
}

EXPORT int my_vfscanf(x64emu_t* emu, void* stream, void* fmt, x64_va_list_t b)
{
    (void)emu;
    #ifdef CONVERT_VALIST
    CONVERT_VALIST(b);
    #else
    myStackAlignScanfValist(emu, (const char*)fmt, emu->scratch, b);
    PREPARE_VALIST;
    #endif
    return vfscanf(stream, fmt, VARARGS);
}

EXPORT int my_vsscanf(x64emu_t* emu, void* stream, void* fmt, x64_va_list_t b)
{
    (void)emu;
    #ifdef CONVERT_VALIST
    CONVERT_VALIST(b);
    #else
    myStackAlignScanfValist(emu, (const char*)fmt, emu->scratch, b);
    PREPARE_VALIST;
    #endif
    return vsscanf(stream, fmt, VARARGS);
}

EXPORT int my___vsscanf(x64emu_t* emu, void* stream, void* fmt, void* b) __attribute__((alias("my_vsscanf")));

EXPORT int my_vfwscanf(x64emu_t* emu, void* F, void* fmt, x64_va_list_t b)
{
    (void)emu;
    #ifdef CONVERT_VALIST
    CONVERT_VALIST(b);
    #else
    myStackAlignScanfWValist(emu, (const char*)fmt, emu->scratch, b);
    PREPARE_VALIST;
    #endif
    return vfwscanf(F, fmt, VARARGS);
}

EXPORT int my_vwscanf(x64emu_t* emu, void* fmt, x64_va_list_t b)
{
    (void)emu;
    #ifdef CONVERT_VALIST
    CONVERT_VALIST(b);
    #else
    myStackAlignScanfWValist(emu, (const char*)fmt, emu->scratch, b);
    PREPARE_VALIST;
    #endif
    return vwscanf(fmt, VARARGS);
}

EXPORT int my_wscanf(x64emu_t* emu, void* fmt, uint64_t* b)
{
    myStackAlignScanfW(emu, (const char*)fmt, b, emu->scratch, 1);
    PREPARE_VALIST;

    return vwscanf(fmt, VARARGS);
}

EXPORT int my_vswscanf(x64emu_t* emu, void* stream, void* fmt, x64_va_list_t b)
{
    (void)emu;
    #ifdef CONVERT_VALIST
    CONVERT_VALIST(b);
    #else
    myStackAlignScanfWValist(emu, (const char*)fmt, emu->scratch, b);
    PREPARE_VALIST;
    #endif
    return vswscanf(stream, fmt, VARARGS);
}

EXPORT int my_sscanf(x64emu_t* emu, void* stream, void* fmt, uint64_t* b)
{
    myStackAlignScanf(emu, (const char*)fmt, b, emu->scratch, 2);
    PREPARE_VALIST;

    return vsscanf(stream, fmt, VARARGS);
}
EXPORT int my_vscanf(x64emu_t* emu, void* fmt, x64_va_list_t b)
{
    (void)emu;
    #ifdef CONVERT_VALIST
    CONVERT_VALIST(b);
    #else
    myStackAlignScanfValist(emu, (const char*)fmt, emu->scratch, b);
    PREPARE_VALIST;
    #endif
    return vscanf(fmt, VARARGS);
}

EXPORT int my__IO_vfscanf(x64emu_t* emu, void* stream, void* fmt, void* b) __attribute__((alias("my_vfscanf")));
EXPORT int my___isoc99_vsscanf(x64emu_t* emu, void* stream, void* fmt, void* b) __attribute__((alias("my_vsscanf")));
EXPORT int my___isoc99_vscanf(x64emu_t* emu, void* fmt, void* b) __attribute__((alias("my_vscanf")));
EXPORT int my___isoc99_vswscanf(x64emu_t* emu, void* stream, void* fmt, void* b) __attribute__((alias("my_vswscanf")));
EXPORT int my___isoc99_vfscanf(x64emu_t* emu, void* stream, void* fmt, void* b) __attribute__((alias("my_vfscanf")));

EXPORT int my___isoc99_fscanf(x64emu_t* emu, void* stream, void* fmt, uint64_t* b)
{
  myStackAlignScanf(emu, (const char*)fmt, b, emu->scratch, 2);
  PREPARE_VALIST;

  return vfscanf(stream, fmt, VARARGS);
}
EXPORT int my_fscanf(x64emu_t* emu, void* stream, void* fmt, uint64_t* b) __attribute__((alias("my___isoc99_fscanf")));

EXPORT int my___isoc99_scanf(x64emu_t* emu, void* fmt, uint64_t* b)
{
  myStackAlignScanf(emu, (const char*)fmt, b, emu->scratch, 1);
  PREPARE_VALIST;

  return vscanf(fmt, VARARGS);
}

EXPORT int my___isoc99_sscanf(x64emu_t* emu, void* stream, void* fmt, uint64_t* b)
{
  myStackAlignScanf(emu, (const char*)fmt, b, emu->scratch, 2);
  PREPARE_VALIST;

  return vsscanf(stream, fmt, VARARGS);
}

EXPORT int my___isoc99_swscanf(x64emu_t* emu, void* stream, void* fmt, uint64_t* b)
{
  myStackAlignScanfW(emu, (const char*)fmt, b, emu->scratch, 2);
  PREPARE_VALIST;

  return vswscanf(stream, fmt, VARARGS);
}

EXPORT int my___isoc23_swscanf(x64emu_t* emu, void* stream, void* fmt, uint64_t* b)
{
    myStackAlignScanfW(emu, (const char*)fmt, b, emu->scratch, 2);
    PREPARE_VALIST;

    return vswscanf(stream, fmt, VARARGS);
}

EXPORT int my_vsnprintf(x64emu_t* emu, void* buff, size_t s, void * fmt, x64_va_list_t b) {
    (void)emu;
    #ifdef CONVERT_VALIST
    CONVERT_VALIST(b);
    #else
    myStackAlignValist(emu, (const char*)fmt, emu->scratch, b);
    PREPARE_VALIST;
    #endif
    int r = vsnprintf(buff, s, fmt, VARARGS);
    return r;
}
EXPORT int my___vsnprintf(x64emu_t* emu, void* buff, size_t s, void * fmt, x64_va_list_t b) __attribute__((alias("my_vsnprintf")));
EXPORT int my___vsnprintf_chk(x64emu_t* emu, void* buff, size_t s, int flags, size_t slen, void * fmt, x64_va_list_t b) {
    (void)emu;
    #ifdef CONVERT_VALIST
    CONVERT_VALIST(b);
    #else
    myStackAlignValist(emu, (const char*)fmt, emu->scratch, b);
    PREPARE_VALIST;
    #endif
    int r = vsnprintf(buff, s, fmt, VARARGS);
    return r;
}
#if 0
EXPORT int my_vasprintf(x64emu_t* emu, void* strp, void* fmt, void* b, va_list V)
{
    #ifndef NOALIGN
    // need to align on arm
    myStackAlign((const char*)fmt, (uint32_t*)b, emu->scratch);
    PREPARE_VALIST;
    void* f = vasprintf;
    int r = ((iFppp_t)f)(strp, fmt, VARARGS);
    return r;
    #else
    void* f = vasprintf;
    int r = ((iFppp_t)f)(strp, fmt, (uint32_t*)b);
    return r;
    #endif
}
#endif
EXPORT int my___vasprintf_chk(x64emu_t* emu, void* buff, int flags, void* fmt, x64_va_list_t b)
{
    (void)emu; (void)flags;
    #ifdef CONVERT_VALIST
    CONVERT_VALIST(b);
    #else
    myStackAlignValist(emu, (const char*)fmt, emu->scratch, b);
    PREPARE_VALIST;
    #endif
    int r = vasprintf(buff, fmt, VARARGS);
    return r;
}
EXPORT int my___asprintf_chk(x64emu_t* emu, void* result_ptr, int flags, void* fmt, void* b)
{
    myStackAlign(emu, (const char*)fmt, b, emu->scratch, R_EAX, 3);
    PREPARE_VALIST;
    return vasprintf((char**)result_ptr, (char*)fmt, VARARGS);
}
EXPORT int my_vswprintf(x64emu_t* emu, void* buff, size_t s, void * fmt, x64_va_list_t b) {
    (void)emu;
    #ifdef CONVERT_VALIST
    CONVERT_VALIST(b);
    #else
    myStackAlignWValist(emu, (const char*)fmt, emu->scratch, b);
    PREPARE_VALIST;
    #endif
    int r = vswprintf(buff, s, fmt, VARARGS);
    return r;
}
EXPORT int my___vswprintf(x64emu_t* emu, void* buff, size_t s, void * fmt, x64_va_list_t b) __attribute__((alias("my_vswprintf")));
EXPORT int my___vswprintf_chk(x64emu_t* emu, void* buff, size_t s, void * fmt, x64_va_list_t b) __attribute__((alias("my_vswprintf")));

EXPORT int my_swscanf(x64emu_t* emu, void* stream, void* fmt, uint64_t* b)
{
    myStackAlignScanfW(emu, (const char*)fmt, b, emu->scratch, 2);
    PREPARE_VALIST;

    return vswscanf(stream, fmt, VARARGS);
}

EXPORT void my_argp_error(x64emu_t *emu, void* state, void* fmt, void* b) {
#if defined(HAVE_ARGP)
    if(!fmt) {
        argp_error(state, NULL);
        return;
    }
    myStackAlign(emu, (const char*)fmt, b, emu->scratch, R_EAX, 2);
    PREPARE_VALIST;
    argp_error(state, fmt, VARARGS);
#else
    (void)emu; (void)state; (void)fmt; (void)b;
#endif
}

EXPORT void my_error(x64emu_t *emu, int status, int errnum, void* fmt, void* b) {
    myStackAlign(emu, (const char*)fmt, b, emu->scratch, R_EAX, 3);
    PREPARE_VALIST;
    char buf[512];
    vsnprintf(buf, 512, (const char*)fmt, VARARGS);
    error(status, errnum, "%s", buf);
}
EXPORT void my_error_at_line(x64emu_t *emu, int status, int errnum, void* filename, uint32_t linenum, void* fmt, void* b) {
    myStackAlign(emu, (const char*)fmt, b, emu->scratch, R_EAX, 5);
    PREPARE_VALIST;
    char buf[512];
    vsnprintf(buf, 512, (const char*)fmt, VARARGS);
    error_at_line(status, errnum, filename, linenum, "%s", buf);
}

EXPORT void my_verr(x64emu_t* emu, int eval, void* fmt, x64_va_list_t b) {
    if (!fmt)
        return err(eval, NULL);
    #ifdef CONVERT_VALIST
    (void)emu;
    CONVERT_VALIST(b);
    #else
    myStackAlignValist(emu, (const char*)fmt, emu->scratch, b);
    PREPARE_VALIST;
    #endif
    return verr(eval, fmt, VARARGS);
}
EXPORT void my_verrx(x64emu_t* emu, int eval, void* fmt, x64_va_list_t b) {
    #ifdef CONVERT_VALIST
    (void)emu;
    CONVERT_VALIST(b);
    #else
    myStackAlignValist(emu, (const char*)fmt, emu->scratch, b);
    PREPARE_VALIST;
    #endif
    return verrx(eval, fmt, VARARGS);
}
EXPORT void my_err(x64emu_t *emu, int eval, void* fmt, void* b) {
    myStackAlign(emu, (const char*)fmt, b, emu->scratch, R_EAX, 2);
    PREPARE_VALIST;
    verr(eval, (const char*)fmt, VARARGS);
}
EXPORT void my_errx(x64emu_t *emu, int eval, void* fmt, void* b) {
    myStackAlign(emu, (const char*)fmt, b, emu->scratch, R_EAX, 2);
    PREPARE_VALIST;
    verrx(eval, (const char*)fmt, VARARGS);
}
EXPORT void my_vwarn(x64emu_t* emu, void* fmt, x64_va_list_t b) {
    if (!fmt)
        return warn(NULL);
    #ifdef CONVERT_VALIST
    (void)emu;
    CONVERT_VALIST(b);
    #else
    myStackAlignValist(emu, (const char*)fmt, emu->scratch, b);
    PREPARE_VALIST;
    #endif
    return vwarn(fmt, VARARGS);
}
EXPORT void my_vwarnx(x64emu_t* emu, void* fmt, x64_va_list_t b) {
    if (!fmt)
        return warnx(NULL);
    #ifdef CONVERT_VALIST
    (void)emu;
    CONVERT_VALIST(b);
    #else
    myStackAlignValist(emu, (const char*)fmt, emu->scratch, b);
    PREPARE_VALIST;
    #endif
    return vwarnx(fmt, VARARGS);
}

EXPORT void my_argp_failure(x64emu_t* emu, void* state, int status, int errnum, void* fmt, void* b) {
#if defined(HAVE_ARGP)
    if(!fmt) {
        argp_failure(state, status, errnum, NULL);
        return;
    }
    myStackAlign(emu, (const char*)fmt, b, emu->scratch, R_EAX, 4);
    PREPARE_VALIST;
    argp_failure(state, status, errnum, fmt, VARARGS);
#else
    (void)emu; (void)state; (void)status; (void)errnum; (void)fmt; (void)b;
#endif
}

EXPORT void my_warn(x64emu_t *emu, void* fmt, void* b) {
    myStackAlign(emu, (const char*)fmt, b, emu->scratch, R_EAX, 1);
    PREPARE_VALIST;
    vwarn((const char*)fmt, VARARGS);
}
EXPORT void my_warnx(x64emu_t *emu, void* fmt, void* b) {
    myStackAlign(emu, (const char*)fmt, b, emu->scratch, R_EAX, 1);
    PREPARE_VALIST;
    vwarnx((const char*)fmt, VARARGS);
}

EXPORT void my_syslog(x64emu_t* emu, int priority, const char* fmt, uint64_t* b)
{
    myStackAlign(emu, fmt, b, emu->scratch, R_EAX, 2);
    PREPARE_VALIST;
    return vsyslog(priority, fmt, VARARGS);
}
EXPORT void my___syslog_chk(x64emu_t* emu, int priority, int flags, const char* fmt, uint64_t* b)
{
    (void)flags;
    myStackAlign(emu, fmt, b, emu->scratch, R_EAX, 3);
    PREPARE_VALIST;
    return vsyslog(priority, fmt, VARARGS);
}
EXPORT void my_vsyslog(x64emu_t* emu, int priority, const char* fmt, x64_va_list_t b)
{
    (void)emu;
    #ifdef CONVERT_VALIST
    CONVERT_VALIST(b);
    #else
    myStackAlignValist(emu, (const char*)fmt, emu->scratch, b);
    PREPARE_VALIST;
    #endif
    return vsyslog(priority, fmt, VARARGS);
}
EXPORT void my___vsyslog_chk(x64emu_t* emu, int priority, int flag, const char* fmt, x64_va_list_t b)
{
    (void)emu;
    #ifdef CONVERT_VALIST
    CONVERT_VALIST(b);
    #else
    myStackAlignValist(emu, (const char*)fmt, emu->scratch, b);
    PREPARE_VALIST;
    #endif
    return vsyslog(priority, fmt, VARARGS);
}

EXPORT int my___swprintf_chk(x64emu_t* emu, void* s, size_t n, int32_t flag, size_t slen, void* fmt, uint64_t* b)
{
    (void)flag;
    (void)slen;
    myStackAlignW(emu, (const char*)fmt, b, emu->scratch, R_EAX, 5);
    PREPARE_VALIST;
    return vswprintf(s, n, (const wchar_t*)fmt, VARARGS);
}
EXPORT int my_swprintf(x64emu_t* emu, void* s, size_t n, void* fmt, uint64_t* b)
{
    myStackAlignW(emu, (const char*)fmt, b, emu->scratch, R_EAX, 3);
    PREPARE_VALIST;
    return vswprintf(s, n, (const wchar_t*)fmt, VARARGS);
}

EXPORT void my__ITM_addUserCommitAction(x64emu_t* emu, void* cb, uint32_t b, void* c)
{
    // disabled for now... Are all this _ITM_ stuff really mendatory?
    #if 0
    // quick and dirty... Should store the callback to be removed later....
    libc_my_t *my = (libc_my_t *)emu->context->libclib->w.p2;
    x64emu_t *cbemu = AddCallback(emu, (uintptr_t)cb, 1, c, NULL, NULL, NULL);
    my->_ITM_addUserCommitAction(libc1ArgCallback, b, cbemu);
    // should keep track of cbemu to remove at some point...
    #else
    (void)emu; (void)cb; (void)b; (void)c;
    printf("warning _ITM_addUserCommitAction called\n");
    #endif
}
EXPORT void my__ITM_registerTMCloneTable(x64emu_t* emu, void* p, uint32_t s) { (void)emu; (void)p; (void)s; }
EXPORT void my__ITM_deregisterTMCloneTable(x64emu_t* emu, void* p) { (void)emu; (void)p; }


EXPORT int my___fxstat(x64emu_t *emu, int vers, int fd, void* buf)
{
    (void)emu; (void)vers;
    struct stat64 st;
    int r = fstat64(fd, buf?&st:buf);
    if(buf && !r)
        UnalignStat64(&st, buf);
    return r;
}
EXPORT int my___fxstat64(x64emu_t *emu, int vers, int fd, void* buf) __attribute__((alias("my___fxstat")));

EXPORT int my_statx(x64emu_t* emu, int dirfd, void* path, int flags, uint32_t mask, void* buf)
{
    if(my->statx)
        return my->statx(dirfd, path, flags, mask, buf);
    #ifdef __NR_statx
    int ret = syscall(__NR_statx, dirfd, path, flags, mask, buf);
    if(ret<0) {
        errno = -ret;
        ret = -1;
    }
    return ret;
    #else
    errno = ENOSYS;
    return -1;
    #endif
}

EXPORT int my___xmknod(x64emu_t* emu, int v, char* path, uint32_t mode, dev_t* dev)
{
    (void)emu;
    (void)v;
    return mknod((const char*)path, mode, *dev);
}

EXPORT int my___xmknodat(x64emu_t* emu, int v, int dirfd, char* path, uint32_t mode, dev_t* dev)
{
    (void)emu;
    (void)v;
    return mknodat(dirfd, (const char*)path, mode, *dev);
}

EXPORT int my___xstat(x64emu_t* emu, int v, void* path, void* buf)
{
    (void)emu; (void)v;
    struct stat64 st;
    int r = stat64((const char*)path, buf?&st:buf);
    if(buf && !r)
        UnalignStat64(&st, buf);
    return r;
}
EXPORT int my___xstat64(x64emu_t* emu, int v, void* path, void* buf) __attribute__((alias("my___xstat")));

EXPORT int my___lxstat(x64emu_t* emu, int v, void* name, void* buf)
{
    (void)emu; (void)v;
    struct stat64 st;
    int r = lstat64((const char*)name, buf?&st:buf);
    if(buf && !r)
        UnalignStat64(&st, buf);
    return r;
}
EXPORT int my___lxstat64(x64emu_t* emu, int v, void* name, void* buf) __attribute__((alias("my___lxstat")));

EXPORT int my___fxstatat(x64emu_t* emu, int v, int d, void* path, void* buf, int flags)
{
    (void)emu; (void)v;
    struct  stat64 st;
    int r = fstatat64(d, path, &st, flags);
    if(!r)
        UnalignStat64(&st, buf);
    return r;
}
EXPORT int my___fxstatat64(x64emu_t* emu, int v, int d, void* path, void* buf, int flags) __attribute__((alias("my___fxstatat")));

EXPORT int my_stat(x64emu_t *emu, void* filename, void* buf)
{
    (void)emu;
    struct stat st;
    int r = stat(filename, &st);
    if(!r)
        UnalignStat64(&st, buf);
    return r;
}
EXPORT int my_stat64(x64emu_t *emu, void* filename, void* buf) __attribute__((alias("my_stat")));

EXPORT int my_lstat(x64emu_t *emu, void* filename, void* buf)
{
    (void)emu;
    struct stat st;
    int r = lstat(filename, &st);
    if(!r)
        UnalignStat64(&st, buf);
    return r;
}
EXPORT int my_lstat64(x64emu_t *emu, void* filename, void* buf) __attribute__((alias("my_lstat")));

EXPORT int my_fstat(x64emu_t *emu, int fd, void* buf)
{
    (void)emu;
    struct stat st;
    int r = fstat(fd, &st);
    if(!r)
        UnalignStat64(&st, buf);
    return r;
}
EXPORT int my_fstat64(x64emu_t* emu, int fd, void* buf) __attribute__((alias("my_fstat")));

EXPORT int my_fstatat(x64emu_t *emu, int fd, const char* path, void* buf, int flags)
{
    (void)emu;
    struct stat st;
    int r = fstatat(fd, path, &st, flags);
    if(!r)
        UnalignStat64(&st, buf);
    return r;
}
EXPORT int my_fstatat64(x64emu_t *emu, int fd, const char* path, void* buf, int flags) __attribute__((alias("my_fstatat")));

EXPORT int my__IO_file_stat(x64emu_t* emu, void* f, void* buf)
{
    struct stat64 st;
    int r = my->_IO_file_stat(f, &st);
    UnalignStat64(&st, buf);
    return r;
}

#if 0
EXPORT int my_fstatfs64(int fd, void* buf)
{
    struct statfs64 st;
    int r = fstatfs64(fd, &st);
    UnalignStatFS64(&st, buf);
    return r;
}

EXPORT int my_statfs64(const char* path, void* buf)
{
    struct statfs64 st;
    int r = statfs64(path, &st);
    UnalignStatFS64(&st, buf);
    return r;
}
#endif

#ifdef ANDROID
typedef int (*__compar_d_fn_t)(const void*, const void*, void*);

static size_t qsort_r_partition(void* base, size_t size, __compar_d_fn_t compar, void* arg, size_t lo, size_t hi)
{
    void* tmp = alloca(size);
    void* pivot = ((char*)base) + lo * size;
    size_t i = lo;
    for (size_t j = lo; j <= hi; j++)
    {
        void* base_i = ((char*)base) + i * size;
        void* base_j = ((char*)base) + j * size;
        if (compar(base_j, pivot, arg) < 0)
        {
            memcpy(tmp, base_i, size);
            memcpy(base_i, base_j, size);
            memcpy(base_j, tmp, size);
            i++;
        }
    }
    void* base_i = ((char *)base) + i * size;
    void* base_hi = ((char *)base) + hi * size;
    memcpy(tmp, base_i, size);
    memcpy(base_i, base_hi, size);
    memcpy(base_hi, tmp, size);
    return i;
}

static void qsort_r_helper(void* base, size_t size, __compar_d_fn_t compar, void* arg, ssize_t lo, ssize_t hi)
{
    if (lo < hi)
    {
        size_t p = qsort_r_partition(base, size, compar, arg, lo, hi);
        qsort_r_helper(base, size, compar, arg, lo, p - 1);
        qsort_r_helper(base, size, compar, arg, p + 1, hi);
    }
}

static void qsort_r(void* base, size_t nmemb, size_t size, __compar_d_fn_t compar, void* arg)
{
    return qsort_r_helper(base, size, compar, arg, 0, nmemb - 1);
}
#endif

typedef struct compare_r_s {
    x64emu_t* emu;
    uintptr_t f;
    void*     data;
    int       r;
} compare_r_t;

static int my_compare_r_cb(void* a, void* b, compare_r_t* arg)
{
    return (int)RunFunctionWithEmu(arg->emu, 0, arg->f, 2+arg->r, a, b, arg->data);
}
EXPORT void my_qsort(x64emu_t* emu, void* base, size_t nmemb, size_t size, void* fnc)
{
    compare_r_t args;
    args.emu = emu; args.f = (uintptr_t)fnc; args.r = 0; args.data = NULL;
    qsort_r(base, nmemb, size, (__compar_d_fn_t)my_compare_r_cb, &args);
}
EXPORT void my_qsort_r(x64emu_t* emu, void* base, size_t nmemb, size_t size, void* fnc, void* data)
{
    compare_r_t args;
    args.emu = emu; args.f = (uintptr_t)fnc; args.r = 1; args.data = data;
    qsort_r(base, nmemb, size, (__compar_d_fn_t)my_compare_r_cb, &args);
}
EXPORT void* my_bsearch(x64emu_t* emu, void* key, void* base, size_t nmemb, size_t size, void* fnc)
{
    (void)emu;
    return bsearch(key, base, nmemb, size, findcompareFct(fnc));
}

EXPORT void* my_lsearch(x64emu_t* emu, void* key, void* base, size_t* nmemb, size_t size, void* fnc)
{
    (void)emu;
    return lsearch(key, base, nmemb, size, findcompareFct(fnc));
}

EXPORT void* my_tsearch(x64emu_t* emu, void* key, void* root, void* fnc)
{
    (void)emu;
    return tsearch(key, root, findcompareFct(fnc));
}

EXPORT int my___sysctl(x64emu_t* emu, int* name, int nlen, void* oldval, size_t* oldlenp, void* newval, size_t newlen)
{
    return ENOSYS;
}

EXPORT int my_sysctl(x64emu_t* emu, int* name, int nlen, void* oldval, size_t* oldlenp, void* newval, size_t newlen)
{
    /* Glibc 2.32 Release note.
      The deprecated <sys/sysctl.h> header and the sysctl function have been
      removed.  To support old binaries, the sysctl function continues to
      exist as a compatibility symbol (on those architectures which had it),
      but always fails with ENOSYS.  This reflects the removal of the system
      call from all architectures, starting with Linux 5.5.
    */
    return ENOSYS;
}

EXPORT void my_tdestroy(x64emu_t* emu, void* root, void* fnc)
{
    (void)emu;
    tdestroy(root, findfreeFct(fnc));
}
EXPORT void* my_tdelete(x64emu_t* emu, void* key, void** root, void* fnc)
{
    (void)emu;
    return tdelete(key, root, findcompareFct(fnc));
}
EXPORT void* my_tfind(x64emu_t* emu, void* key, void** root, void* fnc)
{
    (void)emu;
    return tfind(key, root, findcompareFct(fnc));
}
EXPORT int my_tss_create(x64emu_t* emu, void* tss_id, void* destructor)
{
    return my->tss_create(tss_id, findDtorFct(destructor));
}
EXPORT void my_twalk(x64emu_t* emu, void* root, void* fnc)
{
    (void)emu;
    twalk(root, findactionFct(fnc));
}
EXPORT void* my_lfind(x64emu_t* emu, void* key, void* base, size_t* nmemb, size_t size, void* fnc)
{
    (void)emu;
    return lfind(key, base, nmemb, size, findcompareFct(fnc));
}

EXPORT void* my_fts_open(x64emu_t* emu, void* path, int options, void* c)
{
    (void)emu;
    return fts_open(path, options, findcompareFct(c));
}

EXPORT void* my_fts64_open(x64emu_t* emu, void* path, int options, void* c)
{
    (void)emu;
    return my->fts64_open(path, options, findcompareFct(c));
}

#if 0
struct i386_dirent {
    uint32_t d_ino;
    int32_t  d_off;
    uint16_t d_reclen;
    uint8_t  d_type;
    char     d_name[256];
};

EXPORT void* my_readdir(x64emu_t* emu, void* dirp)
{
    if (BOX64ENV(fix_64bit_inodes))
    {
        struct dirent64 *dp64 = readdir64((DIR *)dirp);
        if (!dp64) return NULL;
        uint32_t ino32 = dp64->d_ino ^ (dp64->d_ino >> 32);
        int32_t off32 = dp64->d_off;
        struct i386_dirent *dp32 = (struct i386_dirent *)&(dp64->d_off);
        dp32->d_ino = ino32;
        dp32->d_off = off32;
        dp32->d_reclen -= 8;
        return dp32;
    }
    else
    {
        static pFp_t f = NULL;
        if(!f) {
            library_t* lib = my_lib;
            if(!lib) return NULL;
            f = (pFp_t)dlsym(lib->w.lib, "readdir");
        }

        return f(dirp);
    }
}

EXPORT int32_t my_readdir_r(x64emu_t* emu, void* dirp, void* entry, void** result)
{
    struct dirent64 d64, *dp64;
    if (BOX64ENV(fix_64bit_inodes) && (sizeof(d64.d_name) > 1))
    {
        static iFppp_t f = NULL;
        if(!f) {
            library_t* lib = my_lib;
            if(!lib)
            {
                *result = NULL;
                return 0;
            }
            f = (iFppp_t)dlsym(lib->w.lib, "readdir64_r");
        }

        int r = f(dirp, &d64, &dp64);
        if (r || !dp64 || !entry)
        {
            *result = NULL;
            return r;
        }

        struct i386_dirent *dp32 = (struct i386_dirent *)entry;
        int namelen = dp64->d_reclen - offsetof(struct dirent64, d_name);
        if (namelen > sizeof(dp32->d_name))
        {
            *result = NULL;
            return ENAMETOOLONG;
        }

        dp32->d_ino = dp64->d_ino ^ (dp64->d_ino >> 32);
        dp32->d_off = dp64->d_off;
        dp32->d_reclen = namelen + offsetof(struct i386_dirent, d_name);
        dp32->d_type = dp64->d_type;
        memcpy(dp32->d_name, dp64->d_name, namelen);
        *result = dp32;
        return 0;
    }
    else
    {
        static iFppp_t f = NULL;
        if(!f) {
            library_t* lib = my_lib;
            if(!lib)
            {
                *result = NULL;
                return 0;
            }
            f = (iFppp_t)dlsym(lib->w.lib, "readdir_r");
        }

        return f(dirp, entry, result);
    }
}
#endif

static int isProcAny(const char *path, const char* w)
{
    if(strncmp(path, "/proc/", 6)==0) {
        int pid;
        char p[4096] ={0};
        if(sscanf(path, "/proc/%d/%s", &pid, p)==2)
            if(strcmp(p, w) == 0)
                return pid;
    }
    return -1;
}

static int isProcSelf(const char *path, const char* w)
{
    if(strncmp(path, "/proc/", 6)==0) {
        char tmp[64];
        // check if self ....
        sprintf(tmp, "/proc/self/%s", w);
        if(strcmp((const char*)path, tmp)==0)
            return 1;
        // check if self PID ....
        pid_t pid = getpid();
        sprintf(tmp, "/proc/%d/%s", pid, w);
        if(strcmp((const char*)path, tmp)==0)
            return 1;
    }
    return 0;
}

static int isSysCpuCache(const char *path, const char* w, int* _cpu, int* _index)
{
    char tmp[128] = {0};
    int cpu=0, index=0;
    if(sscanf(path, "/sys/devices/system/cpu/cpu%d/cache/index%d/%s", &cpu, &index, tmp)!=3)
        return 0;
    if(strcmp(tmp, w))
        return 0;
    if(_cpu) * _cpu = cpu;
    if(_index) *_index = index;
    return 1;
}

static long isProcMem(const char* path)
{
    long pid;
    if(sscanf(path, "/proc/%ld/mem", &pid)==1)
        return pid;
    return 0;
}

// buf may not end with NULL and prefix must end with NULL.
static int start_with(char *buf, ssize_t buflen, char *prefix)
{
    size_t pre_len = strlen(prefix);
    if ((size_t)buflen < pre_len)
        return 0;
    return memcmp(buf, prefix, pre_len) == 0;
}

EXPORT ssize_t my_readlink(x64emu_t* emu, void* path, void* buf, size_t sz)
{
    if(isProcSelf((const char*)path, "exe")) {
        // special case for self...
        size_t srclen = strlen(emu->context->fullpath);
        size_t cplen = (srclen >= sz) ? sz : srclen;
        memcpy(buf, emu->context->fullpath, cplen);
        return (ssize_t)cplen;
    }
    ssize_t ret = readlink((const char*)path, (char*)buf, sz);
    int pid = (ret>0)?isProcAny(path, "exe"):0;
    if(ret>0 && (pid!=-1) && start_with(buf, ret, my_context->box64path)) {
        size_t box64path_len = strlen(my_context->box64path);
        int ok = !memcmp(buf, my_context->box64path, box64path_len);
        if(!ok) {
            char _deleted[box64path_len+strlen(" (deleted)")+1];
            strcpy(_deleted, my_context->box64path);
            strcat(_deleted, " (deleted)");
            ok = !memcmp(buf, _deleted, strlen(_deleted));
        }
        if(ok) {
            // this is a process run with box64, try to grab the cmdline of the process to try gather the real binary launched
            // which might not be possible if the cmdlin as been to much changed, like with a wine process...
            char cmdline_name[4096] = {0};
            sprintf(cmdline_name, "/proc/%d/cmdline", pid);
            FILE* cmdline = fopen(cmdline_name, "r");
            if(cmdline) {
                char filename[4096] = {0};  // first arg should be the program name
                size_t sz_cmd = fread(filename, 1, 4095, cmdline); // keep last char to end the string
                fclose(cmdline);
                if(filename[0]=='/') {
                    size_t guestpath_len = strnlen(filename, sz_cmd);
                    sz = guestpath_len > sz ? sz : guestpath_len;
                    // absolute path, easy...
                    memcpy(buf, filename, sz);
                    return sz;
                } else if(filename[0] != 0) {
                    // relative path, need to grap cwd and cannonicalise the path
                    // box64 ./test_app or box64 test_app
                    char cwd_name[36] = {0};
                    snprintf(cwd_name, sizeof(cwd_name), "/proc/%d/cwd", pid);
                    char cwd[MAX_PATH] = {0};
                    if(readlink(cwd_name, cwd, MAX_PATH-1)>0 && strlen(cwd)+strlen(filename)+1<MAX_PATH) {
                        strcat(cwd, "/");
                        strcat(cwd, filename);
                        char* real = box_realpath(cwd, NULL);
                        // No exit, so failure.
                        if (!real)
                            return ret;
                        size_t sz_real = strlen(real);
                        sz = sz_real > sz ? sz : sz_real;
                        memcpy(buf, real, sz);
                        box_free(real);
                        return sz;
                    }
                    // overflow... so falure
                }
            }
        }
    }
    return ret;
}

EXPORT ssize_t my___readlink_chk(x64emu_t* emu, void* path, void* buf, size_t sz, size_t buflen)
{
    return my_readlink(emu, path, buf, sz);
}

#ifndef NOALIGN
void CreateCPUInfoFile(int fd)
{
    size_t dummy;
    char buff[600];
    int n = box64_sysinfo.box64_ncpu;
    // generate fake CPUINFO
    #define P \
    dummy = write(fd, buff, strlen(buff))
    for (int i=0; i<n; ++i) {
        sprintf(buff, "processor\t: %d\n", i);
        P;
        sprintf(buff, "vendor_id\t: GenuineIntel\n");
        P;
        sprintf(buff, "cpu family\t: 6\n");
        P;
        sprintf(buff, "model\t\t: 1\n");
        P;
        sprintf(buff, "model name\t: %s\n", box64_sysinfo.cpuname);
        P;
        sprintf(buff, "stepping\t: 1\nmicrocode\t: 0x10\n");
        P;
        sprintf(buff, "cpu MHz\t\t: %g\n", box64_sysinfo.frequency / 1000000.);
        P;
        sprintf(buff, "cache size\t: %d\n", 4096);
        P;
        sprintf(buff, "physical id\t: %d\nsiblings\t: %d\n", 0, n);
        P;
        sprintf(buff, "core id\t\t: %d\ncpu cores\t: %d\n", i, n);
        P;
        sprintf(buff, "bogomips\t: %g\n", box64_sysinfo.bogomips / 1000000.);
        P;
        sprintf(buff, "flags\t\t: fpu cx8 sep ht cmov clflush mmx sse sse2 syscall tsc lahf_lm ssse3 ht tm lm fxsr cpuid"\
                      "%s cx16%s movbe pni "\
                      "sse4_1%s%s%s lzcnt popcnt%s%s%s%s%s%s%s%s%s\n",
                      BOX64ENV(pclmulqdq)?" pclmulqdq":"",
                      BOX64ENV(aes)?" aes":"",
                      BOX64ENV(sse42)?" sse4_2":"", BOX64ENV(avx)?" avx":"", BOX64ENV(shaext)?" sha_ni":"",
                      BOX64ENV(avx)?" bmi1":"", (BOX64ENV(avx) == 2)?" avx2":"", BOX64ENV(avx)?" bmi2":"",
                      ((BOX64ENV(avx) == 2)&&BOX64ENV(aes))?" vaes":"", (BOX64ENV(avx) == 2)?" fma":"",
                      BOX64ENV(avx)?" xsave":"", BOX64ENV(avx)?" f16c":"", (BOX64ENV(avx) == 2)?" randr":"",
                      (BOX64ENV(avx) == 2)?" adx":""
                      );
        P;
        sprintf(buff, "address sizes\t: 48 bits physical, 48 bits virtual\n");
        P;
        sprintf(buff, "\n");
        P;
    }
    (void)dummy;
    #undef P
}
void CreateCPUPresentFile(int fd)
{
    size_t dummy;
    char buff[600];
    int n = box64_sysinfo.box64_ncpu;
    // generate fake CPUINFO
    sprintf(buff, "0-%d\n", n-1);
    dummy = write(fd, buff, strlen(buff));
    (void)dummy;
}
void CreateClocksourceFile(int fd)
{
    size_t dummy;
    dummy = write(fd, "tsc\n", strlen("tsc\n"));
    (void)dummy;
}
void CreateCpuCacheAssoc(int fd, int cpu, int index)
{
    size_t dummy;
    char tmp[64];
    sprintf(tmp, "%d\n", (index>=3)?16:8);  // Random be coherent values...
    dummy = write(fd, tmp, strlen(tmp));
    (void)dummy;
}
void CreateCpuCacheCoher(int fd, int cpu, int index)
{
    size_t dummy;
    char tmp[64];
    sprintf(tmp, "%d\n", 64);  // Random be coherent values...
    dummy = write(fd, tmp, strlen(tmp));
    (void)dummy;
}
void CreateCpuCacheSize(int fd, int cpu, int index)
{
    size_t dummy;
    char tmp[64];
    int cachesize = 12288;
    switch(index) {
        case 0: cachesize = 32; break;
        case 1: cachesize = 32; break;
        case 2: cachesize = 256; break;
    }
    sprintf(tmp, "%dK\n", cachesize);  // Random be coherent values...
    dummy = write(fd, tmp, strlen(tmp));
    (void)dummy;
}

#ifdef ANDROID
static int shm_open(const char *name, int oflag, mode_t mode) {
    return -1;
}
static int shm_unlink(const char *name) {
    return -1;
}
#endif

#define TMP_CPUINFO "box64_tmpcpuinfo"
#define TMP_CPUTOPO "box64_tmpcputopo%d"
#define TMP_CLOCKSOURCE "box64_tmpclocksource"
#endif
#define TMP_MEMMAP  "box64_tmpmemmap"
#define TMP_CMDLINE "box64_tmpcmdline"
#define TMP_CPUPRESENT "box64_cpupresent"
#define TMP_CPUCACHE_ASSOC "box64_cpucacheassoc"
#define TMP_CPUCACHE_COHER "box64_cpucachecoher"
#define TMP_CPUCACHE_SIZE "box64_cpucachesize"
EXPORT int32_t my_open(x64emu_t* emu, void* pathname, int32_t flags, uint32_t mode)
{
    if(isProcSelf((const char*) pathname, "cmdline")) {
        // special case for self command line...
        #if 0
        char tmpcmdline[200] = {0};
        char tmpbuff[100] = {0};
        sprintf(tmpbuff, "%s/cmdlineXXXXXX", getenv("TMP")?getenv("TMP"):".");
        int tmp = mkstemp(tmpbuff);
        int dummy;
        if(tmp<0) return open(pathname, flags, mode);
        dummy = write(tmp, emu->context->fullpath, strlen(emu->context->fullpath)+1);
        for (int i=1; i<emu->context->argc; ++i)
            dummy = write(tmp, emu->context->argv[i], strlen(emu->context->argv[i])+1);
        lseek(tmp, 0, SEEK_SET);
        #else
        int tmp = shm_open(TMP_CMDLINE, O_RDWR | O_CREAT, S_IRWXU);
        if(tmp<0) return open(pathname, flags, mode);
        shm_unlink(TMP_CMDLINE);    // remove the shm file, but it will still exist because it's currently in use
        int dummy = write(tmp, emu->context->fullpath, strlen(emu->context->fullpath)+1);
        (void)dummy;
        for (int i=1; i<emu->context->argc; ++i)
            if(emu->context->argv[i])
                dummy = write(tmp, emu->context->argv[i], strlen(emu->context->argv[i])+1);
        lseek(tmp, 0, SEEK_SET);
        #endif
        return tmp;
    }
    if(isProcSelf((const char*)pathname, "exe")) {
        return open(emu->context->fullpath, flags, mode);
    }
    #ifndef NOALIGN
    if(strcmp((const char*)pathname, "/proc/cpuinfo")==0) {
        // special case for cpuinfo
        int tmp = shm_open(TMP_CPUINFO, O_RDWR | O_CREAT, S_IRWXU);
        if(tmp<0) return open(pathname, flags, mode); // error fallback
        shm_unlink(TMP_CPUINFO);    // remove the shm file, but it will still exist because it's currently in use
        CreateCPUInfoFile(tmp);
        lseek(tmp, 0, SEEK_SET);
        return tmp;
    }
    if(!strcmp((const char*)pathname, "/sys/bus/clocksource/devices/clocksource0/current_clocksource")) {
        // special case to say tsc as current clocksource
        int tmp = shm_open(TMP_CLOCKSOURCE, O_RDWR | O_CREAT, S_IRWXU);
        if(tmp<0) return open(pathname, flags, mode); // error fallback
        shm_unlink(TMP_CLOCKSOURCE);    // remove the shm file, but it will still exist because it's currently in use
        CreateClocksourceFile(tmp);
        lseek(tmp, 0, SEEK_SET);
        return tmp;
    }
    int cpu, index;
    if(isSysCpuCache(pathname, "ways_of_associativity", &cpu, &index) && !FileExist(pathname, IS_FILE)) {
        // Create a dummy one
        int tmp = shm_open(TMP_CPUCACHE_ASSOC, O_RDWR | O_CREAT, S_IRWXU);
        if(tmp<0) return open(pathname, flags, mode); // error fallback
        shm_unlink(TMP_CPUCACHE_ASSOC);    // remove the shm file, but it will still exist because it's currently in use
        CreateCpuCacheAssoc(tmp, cpu, index);
        lseek(tmp, 0, SEEK_SET);
        return tmp;
    }
    if(isSysCpuCache(pathname, "coherency_line_size", &cpu, &index) && !FileExist(pathname, IS_FILE)) {
        // Create a dummy one
        int tmp = shm_open(TMP_CPUCACHE_COHER, O_RDWR | O_CREAT, S_IRWXU);
        if(tmp<0) return open(pathname, flags, mode); // error fallback
        shm_unlink(TMP_CPUCACHE_COHER);    // remove the shm file, but it will still exist because it's currently in use
        CreateCpuCacheCoher(tmp, cpu, index);
        lseek(tmp, 0, SEEK_SET);
        return tmp;
    }
    if(isSysCpuCache(pathname, "size", &cpu, &index) && !FileExist(pathname, IS_FILE)) {
        // Create a dummy one
        int tmp = shm_open(TMP_CPUCACHE_SIZE, O_RDWR | O_CREAT, S_IRWXU);
        if(tmp<0) return open(pathname, flags, mode); // error fallback
        shm_unlink(TMP_CPUCACHE_SIZE);    // remove the shm file, but it will still exist because it's currently in use
        CreateCpuCacheAssoc(tmp, cpu, index);
        lseek(tmp, 0, SEEK_SET);
        return tmp;
    }
    if(box64_wine && isProcMem(pathname) && (mode&O_WRONLY)) {
        // deny using proc/XX/mem as it messes up with dynarec memory protection & tracking
        return -1;
    }
    #endif

    if (!strcmp((const char*)pathname, "box64-custom-bashrc-file")) {
        int tmp = shm_open("box64-custom-bashrc-file", O_RDWR | O_CREAT, S_IRWXU);
        if (tmp < 0) return open(pathname, flags, mode); // error fallback
        shm_unlink("box64-custom-bashrc-file");
        const char* content = "if [ -f ~/.bashrc ]\nthen\n. ~/.bashrc\nfi\nexport PS1=\"(box64) \"$PS1\nexport BOX64_NOBANNER=1\nexport BOX64_LOG=0\n";
        size_t dummy;
        dummy = write(tmp, content, strlen(content));
        (void)dummy;
        lseek(tmp, 0, SEEK_SET);
        return tmp;
    }

    if(!strcmp((const char*)pathname, "/etc/os-release")) {
        char* pv = getenv("BOX64_PRESSURE_VESSEL_FILES");
        if(pv) {
            char tmp[MAX_PATH] = {0};
            snprintf(tmp, sizeof(tmp)-1, "%s/lib/os-release", pv);
            if(FileExist(tmp, IS_FILE)) {
                return open(tmp, flags, mode);
            }
        }
    }

    int ret = open(pathname, flags, mode);
    return ret;
}
EXPORT int32_t my___open(x64emu_t* emu, void* pathname, int32_t flags, uint32_t mode) __attribute__((alias("my_open")));

//#ifdef DYNAREC
//static int hasDBFromAddress(uintptr_t addr)
//{
//    int idx = (addr>>DYNAMAP_SHIFT);
//    return getDB(idx)?1:0;
//}
//#endif

//EXPORT int32_t my_read(int fd, void* buf, uint32_t count)
//{
//    int ret = read(fd, buf, count);
//#ifdef DYNAREC
//    if(ret!=count && ret>0) {
//        // continue reading...
//        void* p = buf+ret;
//        if(hasDBFromAddress((uintptr_t)p)) {
//            // allow writing the whole block (this happens with HalfLife, libMiles load code directly from .mix and other file like that)
//            unprotectDB((uintptr_t)p, count-ret, 1);
//            int l;
//            do {
//                l = read(fd, p, count-ret);
//                if(l>0) {
//                    p+=l; ret+=l;
//                }
//            } while(l>0);
//        }
//    }
//#endif
//    return ret;
//}

EXPORT int32_t my_open64(x64emu_t* emu, void* pathname, int32_t flags, uint32_t mode)
{
    if(isProcSelf((const char*)pathname, "cmdline")) {
        // special case for self command line...
        #if 0
        char tmpcmdline[200] = {0};
        char tmpbuff[100] = {0};
        sprintf(tmpbuff, "%s/cmdlineXXXXXX", getenv("TMP")?getenv("TMP"):".");
        int tmp = mkstemp64(tmpbuff);
        int dummy;
        if(tmp<0) return open64(pathname, flags, mode);
        dummy = write(tmp, emu->context->fullpath, strlen(emu->context->fullpath)+1);
        for (int i=1; i<emu->context->argc; ++i)
            dummy = write(tmp, emu->context->argv[i], strlen(emu->context->argv[i])+1);
        lseek64(tmp, 0, SEEK_SET);
        #else
        int tmp = shm_open(TMP_CMDLINE, O_RDWR | O_CREAT, S_IRWXU);
        if(tmp<0) return open64(pathname, flags, mode);
        shm_unlink(TMP_CMDLINE);    // remove the shm file, but it will still exist because it's currently in use
        int dummy = write(tmp, emu->context->fullpath, strlen(emu->context->fullpath)+1);
        (void)dummy;
        for (int i=1; i<emu->context->argc; ++i)
            if(emu->context->argv[i])
                dummy = write(tmp, emu->context->argv[i], strlen(emu->context->argv[i])+1);
        lseek(tmp, 0, SEEK_SET);
        #endif
        return tmp;
    }
    if(isProcSelf((const char*)pathname, "exe")) {
        return open64(emu->context->fullpath, flags, mode);
    }
    #ifndef NOALIGN
    if(strcmp((const char*)pathname, "/proc/cpuinfo")==0) {
        // special case for cpuinfo
        int tmp = shm_open(TMP_CPUINFO, O_RDWR | O_CREAT, S_IRWXU);
        if(tmp<0) return open64(pathname, flags, mode); // error fallback
        shm_unlink(TMP_CPUINFO);    // remove the shm file, but it will still exist because it's currently in use
        CreateCPUInfoFile(tmp);
        lseek(tmp, 0, SEEK_SET);
        return tmp;
    }
    if (BOX64ENV(maxcpu) && (!strcmp(pathname, "/sys/devices/system/cpu/present") || !strcmp(pathname, "/sys/devices/system/cpu/online")) && (box64_sysinfo.ncpu >= BOX64ENV(maxcpu))) {
        // special case for cpu present (to limit to 64 cores)
        int tmp = shm_open(TMP_CPUPRESENT, O_RDWR | O_CREAT, S_IRWXU);
        if(tmp<0) return open64(pathname, mode); // error fallback
        shm_unlink(TMP_CPUPRESENT);    // remove the shm file, but it will still exist because it's currently in use
        CreateCPUPresentFile(tmp);
        lseek(tmp, 0, SEEK_SET);
        return tmp;
    }
    if(!strcmp((const char*)pathname, "/sys/bus/clocksource/devices/clocksource0/current_clocksource")) {
        // special case to say tsc as current clocksource
        int tmp = shm_open(TMP_CLOCKSOURCE, O_RDWR | O_CREAT, S_IRWXU);
        if(tmp<0) return open64(pathname, flags, mode); // error fallback
        shm_unlink(TMP_CLOCKSOURCE);    // remove the shm file, but it will still exist because it's currently in use
        CreateClocksourceFile(tmp);
        lseek(tmp, 0, SEEK_SET);
        return tmp;
    }
    int cpu, index;
    if(isSysCpuCache(pathname, "ways_of_associativity", &cpu, &index) && !FileExist(pathname, IS_FILE)) {
        // Create a dummy one
        int tmp = shm_open(TMP_CPUCACHE_ASSOC, O_RDWR | O_CREAT, S_IRWXU);
        if(tmp<0) return open(pathname, flags, mode); // error fallback
        shm_unlink(TMP_CPUCACHE_ASSOC);    // remove the shm file, but it will still exist because it's currently in use
        CreateCpuCacheAssoc(tmp, cpu, index);
        lseek(tmp, 0, SEEK_SET);
        return tmp;
    }
    if(isSysCpuCache(pathname, "coherency_line_size", &cpu, &index) && !FileExist(pathname, IS_FILE)) {
        // Create a dummy one
        int tmp = shm_open(TMP_CPUCACHE_COHER, O_RDWR | O_CREAT, S_IRWXU);
        if(tmp<0) return open(pathname, flags, mode); // error fallback
        shm_unlink(TMP_CPUCACHE_COHER);    // remove the shm file, but it will still exist because it's currently in use
        CreateCpuCacheCoher(tmp, cpu, index);
        lseek(tmp, 0, SEEK_SET);
        return tmp;
    }
    if(isSysCpuCache(pathname, "size", &cpu, &index) && !FileExist(pathname, IS_FILE)) {
        // Create a dummy one
        int tmp = shm_open(TMP_CPUCACHE_SIZE, O_RDWR | O_CREAT, S_IRWXU);
        if(tmp<0) return open(pathname, flags, mode); // error fallback
        shm_unlink(TMP_CPUCACHE_SIZE);    // remove the shm file, but it will still exist because it's currently in use
        CreateCpuCacheAssoc(tmp, cpu, index);
        lseek(tmp, 0, SEEK_SET);
        return tmp;
    }
    #endif
    return open64(pathname, flags, mode);
}

EXPORT FILE* my_fopen64(x64emu_t* emu, const char* path, const char* mode)
{
    if(isProcSelf((const char*)path, "cmdline")) {
        int tmp = shm_open(TMP_CMDLINE, O_RDWR | O_CREAT, S_IRWXU);
        if(tmp<0) return fopen64(path, mode);
        shm_unlink(TMP_CMDLINE);    // remove the shm file, but it will still exist because it's currently in use
        int dummy = write(tmp, emu->context->fullpath, strlen(emu->context->fullpath)+1);
        (void)dummy;
        for (int i=1; i<emu->context->argc; ++i)
            if(emu->context->argv[i])
                dummy = write(tmp, emu->context->argv[i], strlen(emu->context->argv[i])+1);
        lseek(tmp, 0, SEEK_SET);
        return fdopen(tmp, mode);
    }
    if(isProcSelf((const char*)path, "exe")) {
        return fopen64(emu->context->fullpath, mode);
    }
    if(isProcSelf(path, "maps")) {
        // special case for self memory map
        int tmp = shm_open(TMP_MEMMAP, O_RDWR | O_CREAT, S_IRWXU);
        if(tmp<0) return fopen64(path, mode); // error fallback
        shm_unlink(TMP_MEMMAP);    // remove the shm file, but it will still exist because it's currently in use
        CreateMemorymapFile(emu->context, tmp);
        lseek(tmp, 0, SEEK_SET);
        return fdopen(tmp, mode);
    }
    #ifndef NOALIGN
    if(strcmp(path, "/proc/cpuinfo")==0) {
        // special case for cpuinfo
        int tmp = shm_open(TMP_CPUINFO, O_RDWR | O_CREAT, S_IRWXU);
        if(tmp<0) return fopen64(path, mode); // error fallback
        shm_unlink(TMP_CPUINFO);    // remove the shm file, but it will still exist because it's currently in use
        CreateCPUInfoFile(tmp);
        lseek(tmp, 0, SEEK_SET);
        return fdopen(tmp, mode);
    }
    if (BOX64ENV(maxcpu) && (!strcmp(path, "/sys/devices/system/cpu/present") || !strcmp(path, "/sys/devices/system/cpu/online")) && (box64_sysinfo.ncpu >= BOX64ENV(maxcpu))) {
        // special case for cpu present (to limit to 64 cores)
        int tmp = shm_open(TMP_CPUPRESENT, O_RDWR | O_CREAT, S_IRWXU);
        if(tmp<0) return fopen64(path, mode); // error fallback
        shm_unlink(TMP_CPUPRESENT);    // remove the shm file, but it will still exist because it's currently in use
        CreateCPUPresentFile(tmp);
        lseek(tmp, 0, SEEK_SET);
        return fdopen(tmp, mode);
    }
    if(strcmp(path, "/sys/bus/clocksource/devices/clocksource0/current_clocksource")==0) {
        // special case to say tsc as current clocksource
        int tmp = shm_open(TMP_CLOCKSOURCE, O_RDWR | O_CREAT, S_IRWXU);
        if(tmp<0) return fopen64(path, mode); // error fallback
        shm_unlink(TMP_CLOCKSOURCE);    // remove the shm file, but it will still exist because it's currently in use
        CreateClocksourceFile(tmp);
        lseek(tmp, 0, SEEK_SET);
        return fdopen(tmp, mode);
    }
    int cpu=0, index=0;
    if(isSysCpuCache(path, "ways_of_associativity", &cpu, &index) && !FileExist(path, IS_FILE)) {
        // Create a dummy one
        int tmp = shm_open(TMP_CPUCACHE_ASSOC, O_RDWR | O_CREAT, S_IRWXU);
        if(tmp<0) return fopen64(path, mode); // error fallback
        shm_unlink(TMP_CPUCACHE_ASSOC);    // remove the shm file, but it will still exist because it's currently in use
        CreateCpuCacheAssoc(tmp, cpu, index);
        lseek(tmp, 0, SEEK_SET);
        return fdopen(tmp, mode);
    }
    if(isSysCpuCache(path, "coherency_line_size", &cpu, &index) && !FileExist(path, IS_FILE)) {
        // Create a dummy one
        int tmp = shm_open(TMP_CPUCACHE_COHER, O_RDWR | O_CREAT, S_IRWXU);
        if(tmp<0) return fopen64(path, mode); // error fallback
        shm_unlink(TMP_CPUCACHE_COHER);    // remove the shm file, but it will still exist because it's currently in use
        CreateCpuCacheCoher(tmp, cpu, index);
        lseek(tmp, 0, SEEK_SET);
        return fdopen(tmp, mode);
    }
    if(isSysCpuCache(path, "size", &cpu, &index) && !FileExist(path, IS_FILE)) {
        // Create a dummy one
        int tmp = shm_open(TMP_CPUCACHE_SIZE, O_RDWR | O_CREAT, S_IRWXU);
        if(tmp<0) return fopen64(path, mode); // error fallback
        shm_unlink(TMP_CPUCACHE_SIZE);    // remove the shm file, but it will still exist because it's currently in use
        CreateCpuCacheAssoc(tmp, cpu, index);
        lseek(tmp, 0, SEEK_SET);
        return fdopen(tmp, mode);
    }
    #endif
    if(isProcSelf(path, "exe")) {
        return fopen64(emu->context->fullpath, mode);
    }
    return fopen64(path, mode);
}
EXPORT FILE* my_fopen(x64emu_t* emu, const char* path, const char* mode) __attribute__((alias("my_fopen64")));

#if 0
EXPORT int32_t my_ftw(x64emu_t* emu, void* pathname, void* B, int32_t nopenfd)
{
    static iFppi_t f = NULL;
    if(!f) {
        library_t* lib = my_lib;
        if(!lib) return 0;
        f = (iFppi_t)dlsym(lib->w.lib, "ftw");
    }

    return f(pathname, findftwFct(B), nopenfd);
}

#endif

#ifndef NOALIGN
EXPORT int32_t my_epoll_ctl(x64emu_t* emu, int32_t epfd, int32_t op, int32_t fd, void* event)
{
    struct epoll_event _event[1] = {0};
    if(event && (op!=EPOLL_CTL_DEL))
        AlignEpollEvent(_event, event, 1);
    return epoll_ctl(epfd, op, fd, event?_event:NULL);
}
EXPORT int32_t my_epoll_wait(x64emu_t* emu, int32_t epfd, void* events, int32_t maxevents, int32_t timeout)
{
    struct epoll_event _events[maxevents];
    //AlignEpollEvent(_events, events, maxevents);
    int32_t ret = epoll_wait(epfd, _events, maxevents, timeout);
    if((ret > 0) && events)
        UnalignEpollEvent(events, _events, ret);
    return ret;
}
EXPORT int32_t my_epoll_pwait(x64emu_t* emu, int32_t epfd, void* events, int32_t maxevents, int32_t timeout, const sigset_t *sigmask)
{
    struct epoll_event _events[maxevents];
    //AlignEpollEvent(_events, events, maxevents);
    int32_t ret = epoll_pwait(epfd, _events, maxevents, timeout, sigmask);
    if((ret > 0) && events)
        UnalignEpollEvent(events, _events, ret);
    return ret;
}
EXPORT int32_t my_epoll_pwait2(x64emu_t* emu, int epfd, void* events, int maxevents, struct timespec *timeout, sigset_t * sigmask)
{
    struct epoll_event _events[maxevents];
    //AlignEpollEvent(_events, events, maxevents);
    int ret = 0;
    if(!my->epoll_pwait2) {
        // epoll_pwait2 doesn't exist, to tranforming timeout to int, and from nanosecods to milliseconds...
        int tout = -1;
        if(timeout) {
            int64_t tmp = (timeout->tv_nsec + timeout->tv_sec*1000000000LL)/1000000LL;
            if(tmp>1<<31) tmp = 1<<31;
            tout = tmp;
        }
        ret = epoll_pwait(epfd, _events, maxevents, tout, sigmask);
    } else
        ret = my->epoll_pwait2(epfd, _events, maxevents, timeout, sigmask);
    if((ret > 0) && events)
        UnalignEpollEvent(events, _events, ret);
    return ret;
}
#endif

#ifndef ANDROID
EXPORT int32_t my_glob64(x64emu_t *emu, void* pat, int32_t flags, void* errfnc, void* pglob)
{
    (void)emu;
    return glob64(pat, flags, findgloberrFct(errfnc), pglob);
}
EXPORT int32_t my_glob(x64emu_t *emu, void* pat, int32_t flags, void* errfnc, void* pglob) __attribute__((alias("my_glob64")));
#endif

EXPORT int my_scandir64(x64emu_t *emu, void* dir, void* namelist, void* sel, void* comp)
{
    (void)emu;
    return scandir64(dir, namelist, findfilter64Fct(sel), findcompare64Fct(comp));
}
EXPORT int my_scandir(x64emu_t *emu, void* dir, void* namelist, void* sel, void* comp) __attribute__((alias("my_scandir64")));

EXPORT int my_scandirat(x64emu_t *emu, int dirfd, void* dirp, void* namelist, void* sel, void* comp)
{
    (void)emu;
    return scandirat(dirfd, dirp, namelist, findfilter64Fct(sel), findcompare64Fct(comp));
}

EXPORT int my_scandirat64(x64emu_t *emu, int dirfd, void* dirp, void* namelist, void* sel, void* comp)
{
    (void)emu;
    return scandirat64(dirfd, dirp, namelist, findfilter64Fct(sel), findcompare64Fct(comp));
}

EXPORT int my_ftw64(x64emu_t* emu, void* filename, void* func, int descriptors)
{
    (void)emu;
    return ftw64(filename, findftw64Fct(func), descriptors);
}
EXPORT int my_ftw(x64emu_t* emu, void* filename, void* func, int descriptors) __attribute__((alias("my_ftw64")));

EXPORT int32_t my_nftw64(x64emu_t* emu, void* pathname, void* B, int32_t nopenfd, int32_t flags)
{
    (void)emu;
    return nftw64(pathname, findnftw64Fct(B), nopenfd, flags);
}
EXPORT int my_nftw(x64emu_t* emu, void* pathname, void* B, int32_t nopenfd, int32_t flags) __attribute__((alias("my_nftw64")));

EXPORT char** my_environ = NULL;
EXPORT char** my__environ = NULL;
EXPORT char** my___environ = NULL;  // all aliases

EXPORT int32_t my_execv(x64emu_t* emu, const char* path, char* const argv[])
{
    int ret;
    int n = 0;
    int self = isProcSelf(path, "exe");
    int x64 = FileIsX64ELF(path);
    int x86 = my_context->box86path?FileIsX86ELF(path):0;
    int script = (my_context->bashpath && FileIsShell(path))?1:0;
    int python = (my_context->pythonpath && FileIsPython(path))?1:0;
    printf_log(LOG_DEBUG, "execv(\"%s\", %p) is x64=%d x86=%d script=%d python=%d self=%d\n", path, argv, x64, x86, script, python, self);
    while (argv[n])
        ++n;
    if (BOX64ENV(steam_vulkan) && n == 3 && !strcmp(argv[0], "sh") && !strcmp(argv[1], "-c") && strstr(argv[2], "steamwebhelper.sh")) {
        char** newargv = (char**)box_calloc(n + 1, sizeof(char*));
        for (int i = 0; i <= n; ++i)
            newargv[i] = argv[i];
        // For some reason, Steam UI on RISC-V/LoongArch does not have hardware accel.
        // To workaround this, we insert `--enable-features=Vulkan` to the exec of steamwebhelper to force Vulkan.
        // For cases where there is an existing `--enable-features=` string:
        static const char* vulkanstr1 = "Vulkan,";
        static const char* searchstr1 = "--enable-features=";
        // For cases where there is no existing `--enable-features=` string:
        static const char* vulkanstr2 = "--enable-features=Vulkan ";
        static const char* searchstr2 = "--disable-features=";

        size_t bufsize = strlen(newargv[2]) + strlen(vulkanstr1);
        char* pos = strstr(newargv[2], searchstr1);
        if (!pos) {
            size_t bufsize = strlen(newargv[2]) + strlen(vulkanstr2);
            pos = strstr(newargv[2], searchstr2);
            if (!pos) goto do_exec;

            char* newstr = (char*)box_calloc(bufsize + 1, 1);
            size_t insertat = pos - newargv[2];
            strncpy(newstr, newargv[2], insertat);
            newstr[insertat] = '\0';
            strcat(newstr, vulkanstr2);
            strcat(newstr, newargv[2] + insertat);
            newargv[2] = newstr;
            goto do_exec;
        }
        char* newstr = (char*)box_calloc(bufsize + 1, 1);
        size_t insertat = pos - newargv[2] + strlen(searchstr1);
        strncpy(newstr, newargv[2], insertat);
        newstr[insertat] = '\0';
        strcat(newstr, vulkanstr1);
        strcat(newstr, newargv[2] + insertat);
        newargv[2] = newstr;
    do_exec:
        ret = execv(path, (void*)newargv);
        box_free(newargv);
        return ret;
    }
#if 1
    if (x64 || x86 || script || python || self) {
        int skip_first = 0;
        if(strlen(path)>=strlen("wine64-preloader") && strcmp(path+strlen(path)-strlen("wine64-preloader"), "wine64-preloader")==0)
            skip_first++;
        // count argv...
        int n=skip_first;
        while(argv[n]) ++n;
        int toadd = (script || python)?2:1;
        const char** newargv = (const char**)box_calloc(n+toadd+2, sizeof(char*));
        newargv[0] = x86?emu->context->box86path:emu->context->box64path;
        if(script) newargv[1] = emu->context->bashpath; // script needs to be launched with bash
        if(python) newargv[1] = emu->context->pythonpath; // python scripts need box64-python
        memcpy(newargv+toadd, argv+skip_first, sizeof(char*)*(n+1-skip_first));
        if(self)
            newargv[1] = emu->context->fullpath;
        else {
            // TODO check if envp is not environ and add the value on a copy
            if(strcmp(newargv[toadd], skip_first?argv[skip_first]:path))
                setenv(x86?"BOX86_ARG0":"BOX64_ARG0", newargv[toadd], 1);
            newargv[toadd] = skip_first?argv[skip_first]:path;
        }
        printf_log(LOG_DEBUG, " => execv(\"%s\", %p [\"%s\", \"%s\", \"%s\"...:%d])\n", newargv[0], newargv, newargv[0], n?newargv[1]:"", (n>1)?newargv[2]:"",n);
        char** envv = NULL;
        if(my_environ!=my_context->envv) envv = my_environ;
        if(my__environ!=my_context->envv) envv = my__environ;
        if(my___environ!=my_context->envv) envv = my___environ;

        int ret;
        if(envv)
            ret = execve(newargv[0], (char* const*)newargv, envv);
        else
            ret = execv(newargv[0], (char* const*)newargv);
        box_free(newargv);
        return ret;
    }
#endif
    return execv(path, argv);
}

EXPORT int32_t my_execve(x64emu_t* emu, const char* path, char* const argv[], char* const envp[])
{
    int self = isProcSelf(path, "exe");
    int x64 = FileIsX64ELF(path);
    int x86 = my_context->box86path?FileIsX86ELF(path):0;
    int script = (my_context->bashpath && FileIsShell(path))?1:0;
    int python = (my_context->pythonpath && FileIsPython(path))?1:0;
    if(box64env.log>=LOG_DEBUG) {
        printf_log(LOG_DEBUG, "execve(\"%s\", %p[\"%s\"", path, argv, argv[0]);
        for(int i=1; argv[i]; ++i)
            printf_log_prefix(0, LOG_DEBUG, ", \"%s\"", argv[i]);
        printf_log_prefix(0, LOG_DEBUG, "], %p) is x64=%d x86=%d script=%d python=%d (my_context->envv=%p, environ=%p\n", envp, x64, x86, script, python, my_context->envv, environ);
    }
    // hack to update the environ var if needed
    if(envp == my_context->envv && environ) {
        envp = environ;
    } else if(box64env.log>=LOG_DEBUG) {
        printf_log(LOG_DEBUG, "envv=[\"%s\'", envp[0]);
        for(int i=1; envp[i]; ++i)
            printf_log_prefix(0, LOG_DEBUG, ", \"%s\"", envp[i]);
        printf_log_prefix(0, LOG_DEBUG, "]\n");
    }
    if (x64 || x86 || self || script || python) {
        int skip_first = 0;
        if(strlen(path)>=strlen("wine64-preloader") && strcmp(path+strlen(path)-strlen("wine64-preloader"), "wine64-preloader")==0)
            skip_first++;
        // count argv...
        int n=skip_first;
        while(argv[n]) ++n;
        int toadd = (script || python)?2:1;
        const char** newargv = (const char**)alloca((n+1+toadd-skip_first)*sizeof(char*));
        memset(newargv, 0, (n+1+toadd)*sizeof(char*));
        newargv[0] = x86?emu->context->box86path:emu->context->box64path;
        if(script) newargv[1] = emu->context->bashpath; // script needs to be launched with bash
        if(python) newargv[1] = emu->context->pythonpath; // script needs to be launched with bash
        memcpy(newargv+toadd, argv+skip_first, sizeof(char*)*(n+1-skip_first));
        if(self) newargv[toadd] = emu->context->fullpath;
        else {
            // TODO check if envp is not environ and add the value on a copy
            if(strcmp(newargv[toadd], path))
                setenv(x86?"BOX86_ARG0":"BOX64_ARG0", newargv[toadd], 1);
            newargv[toadd] = path;
        }
        printf_log(LOG_DEBUG, " => execve(\"%s\", %p [\"%s\", \"%s\", \"%s\"...:%d], %p)\n", newargv[0], newargv, newargv[0], (n+toadd-skip_first)?newargv[1]:"", ((n+toadd-skip_first)>1)?newargv[2]:"",n, envp);
        int ret = execve(newargv[0], (char* const*)newargv, envp);
        return ret;
    }
    if(!strcmp(path + strlen(path) - strlen("/uname"), "/uname")
     && argv[1] && (!strcmp(argv[1], "-m") || !strcmp(argv[1], "-p") || !strcmp(argv[1], "-i"))
     && !argv[2]) {
        // uname -m is redirected to box64 -m
        path = my_context->box64path;
        char *argv2[3] = { my_context->box64path, argv[1], NULL };
        return execve(path, argv2, envp);
    }
    #ifndef NOALIGN
    if(!strcmp(path + strlen(path) - strlen("/grep"), "/grep")
    && argv[1] && argv[2] && (!strcmp(argv[2], "/proc/cpuinfo") || (argv[1][1]=='-' && argv[3] && !strcmp(argv[3], "/proc/cpuinfo")))) {
        // special case of a bash script shell running grep on cpuinfo to extract capacities...
        int cpuinfo = strcmp(argv[2], "/proc/cpuinfo")?3:2;
        int n=0;
        while(argv[n]) ++n;
        const char** newargv = (const char**)alloca((n+1)*sizeof(char*));
        memcpy(newargv, argv, sizeof(char*)*(n+1));
        // create a dummy cpuinfo in temp (that will stay there, sorry)
        const char* tmpdir = GetTmpDir();
        char template[100] = {0};
        sprintf(template, "%s/box64cpuinfoXXXXXX", tmpdir);
        int fd = mkstemp(template);
        CreateCPUInfoFile(fd);
        // get back the name
        char cpuinfo_file[100] = {0};
        sprintf(template, "/proc/self/fd/%d", fd);
        int rl = readlink(template, cpuinfo_file, sizeof(cpuinfo_file));
        close(fd);
        chmod(cpuinfo_file, 0666);
        newargv[cpuinfo] = cpuinfo_file;
        printf_log(LOG_DEBUG, " => execve(\"%s\", %p [\"%s\", \"%s\", \"%s\"...:%d], %p)\n", path, newargv, newargv[0], newargv[1], newargv[2],n, envp);
        int ret = execve(path, (char* const*)newargv, envp);
        return ret;
    }
    if(!strcmp(path + strlen(path) - strlen("/cat"), "/cat")
    && argv[1] && !strcmp(argv[1], "/proc/cpuinfo")) {
        // special case of a bash script shell running grep on cpuinfo to extract capacities...
        int cpuinfo = 1;
        int n=0;
        while(argv[n]) ++n;
        const char** newargv = (const char**)alloca((n+1)*sizeof(char*));
        memcpy(newargv, argv, sizeof(char*)*(n+1));
        // create a dummy cpuinfo in temp (that will stay there, sorry)
        const char* tmpdir = GetTmpDir();
        char template[100] = {0};
        sprintf(template, "%s/box64cpuinfoXXXXXX", tmpdir);
        int fd = mkstemp(template);
        CreateCPUInfoFile(fd);
        // get back the name
        char cpuinfo_file[100] = {0};
        sprintf(template, "/proc/self/fd/%d", fd);
        int rl = readlink(template, cpuinfo_file, sizeof(cpuinfo_file));
        close(fd);
        chmod(cpuinfo_file, 0666);
        newargv[cpuinfo] = cpuinfo_file;
        printf_log(LOG_DEBUG, " => execve(\"%s\", %p [\"%s\", \"%s\", \"%s\"...:%d], %p)\n", path, newargv, newargv[0], newargv[1], newargv[2],n, envp);
        int ret = execve(path, (char* const*)newargv, envp);
        return ret;
    }
    /*if(!strcmp(path + strlen(path) - strlen("/bwrap"), "/bwrap")) {
        printf_log(LOG_NONE, "\n\n*********\n\nCalling bwrap!\n\n**********\n\n");
    }*/
    #endif

    return execve(path, argv, envp);
}

// execvp should use PATH to search for the program first
EXPORT int32_t my_execvp(x64emu_t* emu, const char* path, char* const argv[])
{
    // need to use BOX64_PATH / PATH here...
    char* fullpath = ResolveFileSoft(path, &my_context->box64_path);
    // use fullpath...
    int self = isProcSelf(fullpath, "exe");
    int x64 = FileIsX64ELF(fullpath);
    int x86 = my_context->box86path?FileIsX86ELF(fullpath):0;
    int script = (my_context->bashpath && FileIsShell(fullpath))?1:0;
    int python = (my_context->pythonpath && FileIsPython(fullpath))?1:0;
    printf_log(LOG_DEBUG, "execvp(\"%s\", %p), IsX86=%d / fullpath=\"%s\"\n", path, argv, x64, fullpath);
    if (x64 || x86 || script || python || self) {
        // count argv...
        int i=0;
        while(argv[i]) ++i;
        int toadd = (script || python)?2:1;
        char** newargv = (char**)alloca((i+toadd+1)*sizeof(char*));
        memset(newargv, 0, (i+toadd+1)*sizeof(char*));
        newargv[0] = x86?emu->context->box86path:emu->context->box64path;
        if(script) newargv[1] = emu->context->bashpath; // script needs to be launched with bash
        if(python) newargv[1] = emu->context->pythonpath; // script needs to be launched with bash
        for (int j=0; j<i; ++j)
            newargv[j+toadd] = argv[j];
        if(self) newargv[1] = emu->context->fullpath;
        //else if(script) newargv[2] = fullpath;
        else {
            // TODO check if envp is not environ and add the value on a copy
            if(strcmp(newargv[toadd], path))
                setenv(x86?"BOX86_ARG0":"BOX64_ARG0", newargv[toadd], 1);
            newargv[toadd] = fullpath;
        }

        printf_log(LOG_DEBUG, " => execvp(\"%s\", %p [\"%s\", \"%s\"...:%d])\n", newargv[0], newargv, newargv[1], i?newargv[2]:"", i);
        char** envv = NULL;
        if(my_environ!=my_context->envv) envv = my_environ;
        if(my__environ!=my_context->envv) envv = my__environ;
        if(my___environ!=my_context->envv) envv = my___environ;
        int ret;
        if(envv)
            ret = execvpe(newargv[0], newargv, envv);
        else
            ret = execvp(newargv[0], newargv);
        box_free(fullpath);
        return ret;
    }
    if((!strcmp(path + strlen(path) - strlen("/uname"), "/uname") || !strcmp(path, "uname"))
     && argv[1] && (!strcmp(argv[1], "-m") || !strcmp(argv[1], "-p") || !strcmp(argv[1], "-i"))
     && !argv[2]) {
        // uname -m is redirected to box64 -m
        path = my_context->box64path;
        char *argv2[3] = { my_context->box64path, argv[1], NULL };

        return execvp(path, argv2);
    }

    // fullpath is gone, so the search will only be on PATH, not on BOX64_PATH (is that an issue?)
    return execvp(path, argv);
}
// execvp should use PATH to search for the program first
EXPORT int32_t my_execvpe(x64emu_t* emu, const char* path, char* argv[], char* const envp[])
{
    // need to use BOX64_PATH / PATH here...
    char* fullpath = ResolveFileSoft(path, &my_context->box64_path);
    // use fullpath...
    int self = isProcSelf(fullpath, "exe");
    int x64 = FileIsX64ELF(fullpath);
    int x86 = my_context->box86path?FileIsX86ELF(fullpath):0;
    int script = (my_context->bashpath && FileIsShell(fullpath))?1:0;
    int python = (my_context->pythonpath && FileIsPython(fullpath))?1:0;
    printf_log(LOG_DEBUG, "execvpe(\"%s\", %p[%s,%s,%s], %p), IsX86=%d IsScript=%d IsPython=%d / fullpath=\"%s\"\n", path, argv, (argv && argv[0])?argv[0]:"(nil)", (argv && argv[0] && argv[1])?argv[1]:"(nil)", (argv && argv[0] && argv[1] && argv[2])?argv[2]:"(nil)", envp, x64, script, python, fullpath);
    char buffsrlc[MAX_PATH*3] = {0};
    #define SRLC "/usr/bin/steam-runtime-launch-client"
    if(!self && !x64 && !x86 && !script && !strcmp(path, SRLC) && !FileExist(SRLC, IS_FILE)) {
        // adjust parameter
        char* runtime = getenv("BOX64_PRESSURE_ENV_PATH");
        if(runtime) {
            strncpy(buffsrlc, runtime, sizeof(buffsrlc)-1);
            strncat(buffsrlc, "/steam-runtime-launch-client", sizeof(buffsrlc)-1);
            path = buffsrlc;
            if(!strcmp(argv[0], SRLC))
                argv[0] = buffsrlc;
            printf_log(LOG_DEBUG, "Changed path of %s\n", SRLC);
            // resolve again
            fullpath = ResolveFileSoft(path, &my_context->box64_path);
            x64 = FileIsX64ELF(fullpath);
            x86 = my_context->box86path?FileIsX86ELF(fullpath):0;
            script = (my_context->bashpath && FileIsShell(fullpath))?1:0;
            python = (my_context->pythonpath && FileIsPython(fullpath))?1:0;
        } else
            printf_log(LOG_INFO, "Warning, trying to launch " SRLC " without BOX64_PRESSURE_ENV_PATH set\n");

    }
    // hack to update the environ var if needed
    if(!x64 && !x86 && !script && !self && !strcmp(path, "/bin/sh") && argv) {
        if(argv[0] && argv[1] && !strcmp(argv[1], "-c") && argv[2] && !argv[3]) {
            // it's a "/bin/sh -c XXX" type of command line, with XXXX being the whole command line...
            // because XXXX can contains things like "VAR=something" type of definition, getting the program to be launched can be tricky
            char* prog = NULL;
            char buff[MAX_PATH*3] = {0};
            char* multiarg2 = NULL;
            int n = 0;
            while(argv[n]) ++n;
            if(FileExist(argv[2], IS_FILE))
                prog = argv[2];
            else {
                if(strstr(argv[2], SRLC) && !FileExist(SRLC, IS_FILE)) {
                    char* runtime = getenv("BOX64_PRESSURE_ENV_PATH");
                    char* srlc = strstr(argv[2], SRLC);
                    if(runtime && ((srlc==argv[2]) || (*(srlc-1)==' '))) {
                        strncpy(buffsrlc, argv[2], sizeof(buffsrlc)-1);
                        *strstr(buffsrlc, SRLC) = '\0';
                        strncat(buffsrlc, runtime, sizeof(buffsrlc)-1);
                        strncat(buffsrlc, "/steam-runtime-launch-client", sizeof(buffsrlc)-1);
                        strncat(buffsrlc, srlc+strlen(SRLC), sizeof(buffsrlc)-1);
                        argv[2] = buffsrlc;
                        printf_log(LOG_DEBUG, "Changed path of %s\n", SRLC);
                    } else
                        printf_log(LOG_INFO, "Warning, trying to launch " SRLC " without BOX64_PRESSURE_ENV_PATH set\n");
                }
                strncpy(buff, argv[2], sizeof(buff)-1);
                char* p = GetSpaceSeparator(buff);
                char* prev = buff;
                while(p && !prog) {
                    char* next = p;
                    while(*next==' ') ++next;
                    char* p2 = strchr(prev, '=');
                    if(!p2 || (p2>p)) {
                        // found it
                        multiarg2 = next;
                        *p = '\0';
                        prog = prev;
                        prev = next;
                        if(!strlen(prog) || !strcmp(prog, "mangohud")) {
                            //nope, nothing found, rollback
                            prog = NULL;
                            multiarg2 = NULL;
                            *p = ' ';
                        }
                    } else {
                        // nope, there is a = in the middle (will not work if equal is escaped tho)
                        prev = next;
                    }
                    p = GetSpaceSeparator(next);
                }
            }
            // should check if start with '/' and resolve else
            if(prog && FileExist(prog, IS_FILE)) {
                x64 = FileIsX64ELF(prog);
                x86 = my_context->box86path?FileIsX86ELF(prog):0;
                script = (my_context->bashpath && FileIsShell(prog))?1:0;
                python = (my_context->pythonpath && FileIsPython(prog))?1:0;
            }
            if(x64 || x86 || script) {
                char buff2[MAX_PATH*4] = {0};
                // rebuild argv[2]
                if(multiarg2) {
                    // get the front stuffs first
                    strncpy(buff2, argv[2], sizeof(buff2)-1);
                    *strstr(buff2, prog) = '\0';
                    strncat(buff2, " ", sizeof(buff2)-1);
                    strncat(buff2, x86?emu->context->box86path:emu->context->box64path, sizeof(buff2)-1);
                    strncat(buff2, " ", sizeof(buff2)-1);
                    strncat(buff2, prog, sizeof(buff2)-1);
                    strncat(buff2, " ", sizeof(buff2)-1);
                    strncat(buff2, multiarg2, sizeof(buff2)-1);
                } else {
                    strcpy(buff2, x86?emu->context->box86path:emu->context->box64path);
                    strncat(buff2, " ", sizeof(buff2)-1);
                    strncat(buff2, argv[2], sizeof(buff2)-1);
                }
                argv[2] = buff2;
                printf_log(LOG_DEBUG, "Will launch %s instead\n", argv[2]);
                return execvpe(path, argv, envp);
            }
        }
    }
    #undef SRLC
    if(envp == my_context->envv && environ) {
        envp = environ;
    }
    if (x64 || x86 || script || python || self) {
        // count argv...
        int i=0;
        while(argv[i]) ++i;
        int toadd = (script || python)?2:1;
        char** newargv = (char**)alloca((i+toadd+1)*sizeof(char*));
        memset(newargv, 0, (i+toadd+1)*sizeof(char*));
        newargv[0] = x86?emu->context->box86path:emu->context->box64path;
        if(script) newargv[1] = emu->context->bashpath; // script needs to be launched with bash
        if(python) newargv[1] = emu->context->pythonpath; // python scripts need box64-python
        for (int j=0; j<i; ++j)
            newargv[j+toadd] = argv[j];
        if(self) newargv[1] = emu->context->fullpath;
        //else if(script) newargv[2] = fullpath;
        else {
            // TODO check if envp is not environ and add the value on a copy
            if(strcmp(newargv[toadd], path))
                setenv(x86?"BOX86_ARG0":"BOX64_ARG0", newargv[toadd], 1);
            newargv[toadd] = fullpath;
        }

        printf_log(LOG_DEBUG, " => execvpe(\"%s\", %p [\"%s\", \"%s\"...:%d])\n", newargv[0], newargv, newargv[1], i?newargv[2]:"", i);
        int ret;
        ret = execvpe(newargv[0], newargv, envp);
        box_free(fullpath);
        return ret;
    }
    if((!strcmp(path + strlen(path) - strlen("/uname"), "/uname") || !strcmp(path, "uname"))
     && argv[1] && (!strcmp(argv[1], "-m") || !strcmp(argv[1], "-p") || !strcmp(argv[1], "-i"))
     && !argv[2]) {
        // uname -m is redirected to box64 -m
        path = my_context->box64path;
        char *argv2[3] = { my_context->box64path, argv[1], NULL };

        return execvpe(path, argv2, envp);
    }

    // fullpath is gone, so the search will only be on PATH, not on BOX64_PATH (is that an issue?)
    return execvpe(path, argv, envp);
}

EXPORT int32_t my_execl(x64emu_t* emu, const char* path)
{
    // count argv...
    int cnt=0;
    while(getVargN(emu, cnt+1)) ++cnt;
    // create the arg list and use execv
    char* argv[cnt+1];
    memset(argv, 0, sizeof(argv));
    for(int i=0; i<cnt; ++i)
        argv[i] = getVargN(emu, i+1);
    return my_execv(emu, path, argv);
}

EXPORT int32_t my_execle(x64emu_t* emu, const char* path)
{
    // count argv...
    int cnt=0;
    while(getVargN(emu, cnt+1)) ++cnt;
    // get envp
    char** envp = getVargN(emu, cnt+2);
    // create the arg list and use execve
    char* argv[cnt+1];
    memset(argv, 0, sizeof(argv));
    for(int i=0; i<cnt; ++i)
        argv[i] = getVargN(emu, i+1);
    return my_execve(emu, path, argv, envp);
}

EXPORT int32_t my_execlp(x64emu_t* emu, const char* path)
{
    // count argv...
    int cnt=0;
    while(getVargN(emu, cnt+1)) ++cnt;
    // create the arg list and use execv
    char* argv[cnt+1];
    memset(argv, 0, sizeof(argv));
    for(int i=0; i<cnt; ++i)
        argv[i] = getVargN(emu, i+1);
    return my_execvp(emu, path, argv);
}

static char** spawn_env_with_arg0(char* const envp[], const char* name, const char* arg0, char** added)
{
    size_t n = 0;
    size_t replace = (size_t)-1;
    size_t name_len = strlen(name);

    while (envp && envp[n]) {
        if (!strncmp(envp[n], name, name_len) && envp[n][name_len] == '=')
            replace = n;
        ++n;
    }

    char** ret = (char**)box_calloc(n + (replace == (size_t)-1 ? 2 : 1), sizeof(char*));
    if (n)
        memcpy(ret, envp, n * sizeof(char*));
    *added = (char*)box_malloc(name_len + strlen(arg0) + 2);
    sprintf(*added, "%s=%s", name, arg0);
    if (replace == (size_t)-1)
        ret[n] = *added;
    else
        ret[replace] = *added;
    return ret;
}

EXPORT int32_t my_posix_spawn(x64emu_t* emu, pid_t* pid, const char* fullpath,
    const posix_spawn_file_actions_t *actions, const posix_spawnattr_t* attrp,  char* const argv[], char* const envp[])
{
    int self = isProcSelf(fullpath, "exe");
    int x64 = FileIsX64ELF(fullpath);
    int x86 = my_context->box86path?FileIsX86ELF(fullpath):0;
    int script = (my_context->bashpath && FileIsShell(fullpath))?1:0;
    int python = (my_context->pythonpath && FileIsPython(fullpath))?1:0;
    int ret;
    printf_log(/*LOG_DEBUG*/LOG_INFO, "posix_spawn(%p, \"%s\", %p, %p, %p[\"%s\", \"%s\", ...], %p), IsX64=%d, IsX86=%d IsScript=%d IsPython=%d %s\n", pid, fullpath, actions, attrp, argv, argv[0], argv[1]?argv[1]:"", envp, x64, x86, script, python, (envp==my_context->envv)?"envp is context->envv":"");
    // hack to update the environ var if needed
    if(envp == my_context->envv && environ) {
        envp = environ;
    }
    if (x64 || x86 || script || python || self) {
        int n=1;
        while(argv[n]) ++n;
        int toadd = (script || python)?2:1;
        const char** newargv = (const char**)alloca((n+1+toadd)*sizeof(char*));
        memset(newargv, 0, (n+1+toadd)*sizeof(char*));
        newargv[0] = x86?emu->context->box86path:emu->context->box64path;
        if(script) newargv[1] = emu->context->bashpath; // script needs to be launched with bash
        if(python) newargv[1] = emu->context->pythonpath; // python scripts need box64-python
        memcpy(newargv+toadd, argv, (n+1)*sizeof(char*));
        char** spawn_envp = (char**)envp;
        char* arg0_env = NULL;
        if(self) newargv[toadd] = emu->context->fullpath;
        else {
            if(strcmp(newargv[toadd], fullpath))
                spawn_envp = spawn_env_with_arg0(envp, x86?"BOX86_ARG0":"BOX64_ARG0", newargv[toadd], &arg0_env);
            newargv[toadd] = fullpath;
        }
        printf_log(/*LOG_DEBUG*/LOG_INFO, " => posix_spawn(%p, \"%s\", %p, %p, %p [\"%s\", \"%s\", \"%s\"...:%d], %p)\n", pid, newargv[0], actions, attrp, newargv, newargv[0], newargv[1], newargv[2]?newargv[2]:"", n, envp);
        ret = posix_spawn(pid, newargv[0], actions, attrp, (char* const*)newargv, spawn_envp);
        if (spawn_envp != envp) {
            box_free(arg0_env);
            box_free(spawn_envp);
        }
        printf_log(/*LOG_DEBUG*/LOG_INFO, "posix_spawn returned %d\n", ret);
        //box_free(newargv);
    } else
        ret = posix_spawn(pid, fullpath, actions, attrp, argv, envp);
    return ret;
}

// execvp should use PATH to search for the program first
EXPORT int32_t my_posix_spawnp(x64emu_t* emu, pid_t* pid, const char* path,
    const posix_spawn_file_actions_t *actions, const posix_spawnattr_t* attrp,  char* const argv[], char* const envp[])
{
    // need to use BOX64_PATH / PATH here...
    char* fullpath = ResolveFileSoft(path, &my_context->box64_path);
    // use fullpath...
    int self = isProcSelf(fullpath, "exe");
    int x64 = FileIsX64ELF(fullpath);
    int x86 = my_context->box86path?FileIsX86ELF(path):0;
    int script = (my_context->bashpath && FileIsShell(fullpath))?1:0;
    int python = (my_context->pythonpath && FileIsPython(fullpath))?1:0;
    int ret;
    printf_log(/*LOG_DEBUG*/LOG_INFO, "posix_spawnp(%p, \"%s\", %p, %p, %p, %p), IsX86=%d IsScript=%d IsPython=%d / fullpath=\"%s\"\n", pid, path, actions, attrp, argv, envp, x64, script, python, fullpath);
    // hack to update the environ var if needed
    if(envp == my_context->envv && environ) {
        envp = environ;
    }
    if (x64 || x86 || script || python || self) {
        int n=1;
        while(argv[n]) ++n;
        int toadd = (script || python)?2:1;
        const char** newargv = (const char**)alloca((n+1+toadd)*sizeof(char*));
        memset(newargv, 0, (n+1+toadd)*sizeof(char*));
        newargv[0] = x86?emu->context->box86path:emu->context->box64path;
        if(script) newargv[1] = emu->context->bashpath; // script needs to be launched with bash
        if(python) newargv[1] = emu->context->pythonpath; // python scripts need box64-python
        memcpy(newargv+toadd, argv, (n+1)*sizeof(char*));
        char** spawn_envp = (char**)envp;
        char* arg0_env = NULL;
        if(self) newargv[toadd] = emu->context->fullpath;
        else {
            if(strcmp(newargv[toadd], fullpath))
                spawn_envp = spawn_env_with_arg0(envp, x86?"BOX86_ARG0":"BOX64_ARG0", newargv[toadd], &arg0_env);
            newargv[toadd] = fullpath;
        }
        printf_log(/*LOG_DEBUG*/LOG_INFO, " => posix_spawn(%p, \"%s\", %p, %p, %p [\"%s\", \"%s\", \"%s\"...:%d], %p)\n", pid, newargv[0], actions, attrp, newargv, newargv[0], newargv[1], newargv[2]?newargv[2]:"", n, envp);
        ret = posix_spawn(pid, newargv[0], actions, attrp, (char* const*)newargv, spawn_envp);
        if (spawn_envp != envp) {
            box_free(arg0_env);
            box_free(spawn_envp);
        }
        printf_log(/*LOG_DEBUG*/LOG_INFO, "posix_spawn returned %d\n", ret);
        //box_free(newargv);
    } else
        ret = posix_spawnp(pid, path, actions, attrp, argv, envp);
    box_free(fullpath);
    return ret;
}

EXPORT void my__Jv_RegisterClasses() {}

EXPORT int32_t my___cxa_thread_atexit_impl(x64emu_t* emu, void* dtor, void* obj, void* dso)
{
    (void)emu;
    //printf_log(LOG_INFO, "Warning, call to __cxa_thread_atexit_impl(%p, %p, %p) ignored\n", dtor, obj, dso);
    AddCleanup1Arg(emu, dtor, obj, FindElfAddress(my_context, (uintptr_t)dso));
    return 0;
}

EXPORT int32_t my_delete_module(x64emu_t* emu, const char* name, unsigned int flags)
{
    (void)emu;
    return syscall(__NR_delete_module, name, flags);
}

EXPORT int32_t my_init_module(x64emu_t* emu, void* module_image, unsigned long size, const char* param_values)
{
    (void)emu;
    return syscall(__NR_init_module, module_image, size, param_values);
}

EXPORT int32_t my___register_atfork(x64emu_t *emu, void* prepare, void* parent, void* child, void* handle)
{
    (void)emu;
    // this is partly incorrect, because the emulated funcionts should be executed by actual fork and not by my_atfork...
    if(my_context->atfork_sz==my_context->atfork_cap) {
        my_context->atfork_cap += 4;
        my_context->atforks = (atfork_fnc_t*)box_realloc(my_context->atforks, my_context->atfork_cap*sizeof(atfork_fnc_t));
    }
    int i = my_context->atfork_sz++;
    my_context->atforks[i].prepare = (uintptr_t)prepare;
    my_context->atforks[i].parent = (uintptr_t)parent;
    my_context->atforks[i].child = (uintptr_t)child;
    my_context->atforks[i].handle = FindElfAddress(my_context, (uintptr_t)handle);
    return 0;
}

#if 0
EXPORT uint64_t my___umoddi3(uint64_t a, uint64_t b)
{
    return a%b;
}
EXPORT uint64_t my___udivdi3(uint64_t a, uint64_t b)
{
    return a/b;
}
EXPORT int64_t my___divdi3(int64_t a, int64_t b)
{
    return a/b;
}

EXPORT int32_t my___poll_chk(void* a, uint32_t b, int c, int l)
{
    return poll(a, b, c);   // no check...
}
#endif

EXPORT int32_t my_fcntl64(x64emu_t* emu, int32_t a, int32_t b, void* c)
{
    (void)emu;
    if(b==F_SETFL)
        c = (void*)(uintptr_t)of_convert((intptr_t)c);
    #if 0
    if(b==F_GETLK64 || b==F_SETLK64 || b==F_SETLKW64)
    {
        my_flock64_t fl;
        AlignFlock64(&fl, c);
        int ret = fcntl(a, b, &fl);
        UnalignFlock64(c, &fl);
        return ret;
    }
    #endif
    int ret = fcntl(a, b, c);
    if(b==F_GETFL && ret!=-1)
        ret = of_unconvert(ret);

    return ret;
}

EXPORT int32_t my_fcntl(x64emu_t* emu, int32_t a, int32_t b, void* c)
{
    (void)emu;
    if(b==F_SETFL && (intptr_t)c==0xFFFFF7FF) {
        // special case for ~O_NONBLOCK...
        int flags = fcntl(a, F_GETFL);
        if(flags&O_NONBLOCK) {
            flags &= ~O_NONBLOCK;
            return fcntl(a, b, flags);
        }
        return 0;
    }
    if(b==F_SETFL)
        c = (void*)(uintptr_t)of_convert((intptr_t)c);
    #if 0
    if(b==F_GETLK64 || b==F_SETLK64 || b==F_SETLKW64)
    {
        my_flock64_t fl;
        AlignFlock64(&fl, c);
        int ret = fcntl(a, b, &fl);
        UnalignFlock64(c, &fl);
        return ret;
    }
    #endif
    int ret = fcntl(a, b, c);
    if(b==F_GETFL && ret!=-1)
        ret = of_unconvert(ret);

    return ret;
}
EXPORT int32_t my___fcntl(x64emu_t* emu, int32_t a, int32_t b, void* c) __attribute__((alias("my_fcntl")));

#if 0
EXPORT int32_t my_preadv64(x64emu_t* emu, int32_t fd, void* v, int32_t c, int64_t o)
{
    library_t* lib = my_lib;
    if(!lib) return 0;
    void* f = dlsym(lib->w.lib, "preadv64");
    if(f)
        return ((iFipiI_t)f)(fd, v, c, o);
    return syscall(__NR_preadv, fd, v, c,(uint32_t)(o&0xffffffff), (uint32_t)((o>>32)&0xffffffff));
}

EXPORT int32_t my_pwritev64(x64emu_t* emu, int32_t fd, void* v, int32_t c, int64_t o)
{
    library_t* lib = my_lib;
    if(!lib) return 0;
    void* f = dlsym(lib->w.lib, "pwritev64");
    if(f)
        return ((iFipiI_t)f)(fd, v, c, o);
    #ifdef __arm__
    return syscall(__NR_pwritev, fd, v, c, 0, (uint32_t)(o&0xffffffff), (uint32_t)((o>>32)&0xffffffff));
    // on arm, 64bits args needs to be on even/odd register, so need to put a 0 for aligment
    #else
    return syscall(__NR_pwritev, fd, v, c,(uint32_t)(o&0xffffffff), (uint32_t)((o>>32)&0xffffffff));
    #endif
}

EXPORT int32_t my_accept4(x64emu_t* emu, int32_t fd, void* a, void* l, int32_t flags)
{
    library_t* lib = my_lib;
    if(!lib) return 0;
    void* f = dlsym(lib->w.lib, "accept4");
    if(f)
        return ((iFippi_t)f)(fd, a, l, flags);
    if(!flags)
        return accept(fd, a, l);
    return syscall(__NR_accept4, fd, a, l, flags);
}

EXPORT  int32_t my_fallocate64(int fd, int mode, int64_t offs, int64_t len)
{
    iFiiII_t f = NULL;
    static int done = 0;
    if(!done) {
        library_t* lib = my_lib;
        f = (iFiiII_t)dlsym(lib->w.lib, "fallocate64");
        done = 1;
    }
    if(f)
        return f(fd, mode, offs, len);
    else
        return syscall(__NR_fallocate, fd, mode, (uint32_t)(offs&0xffffffff), (uint32_t)((offs>>32)&0xffffffff), (uint32_t)(len&0xffffffff), (uint32_t)((len>>32)&0xffffffff));
        //return posix_fallocate64(fd, offs, len);
}

EXPORT struct __processor_model
{
  unsigned int __cpu_vendor;
  unsigned int __cpu_type;
  unsigned int __cpu_subtype;
  unsigned int __cpu_features[1];
} my___cpu_model;

#include "cpu_info.h"
void InitCpuModel()
{
    // some pseudo random cpu info...
    my___cpu_model.__cpu_vendor = VENDOR_INTEL;
    my___cpu_model.__cpu_type = INTEL_PENTIUM_M;
    my___cpu_model.__cpu_subtype = 0; // N/A
    my___cpu_model.__cpu_features[0] = (1<<FEATURE_CMOV)
                                     | (1<<FEATURE_MMX)
                                     | (1<<FEATURE_SSE)
                                     | (1<<FEATURE_SSE2)
                                     | (1<<FEATURE_SSE3)
                                     | (1<<FEATURE_SSSE3)
                                     | (1<<FEATURE_MOVBE)
                                     | (1<<FEATURE_ADX);
}
#endif

#ifdef ANDROID
void ctSetup()
{
}
#else
EXPORT const unsigned short int *my___ctype_b;
EXPORT const int32_t *my___ctype_tolower;
EXPORT const int32_t *my___ctype_toupper;

void ctSetup()
{
    my___ctype_b = *(__ctype_b_loc());
    my___ctype_toupper = *(__ctype_toupper_loc());
    my___ctype_tolower = *(__ctype_tolower_loc());
}
#endif

EXPORT void my___register_frame_info(void* a, void* b)
{
    // nothing
    (void)a; (void)b;
}
EXPORT void* my___deregister_frame_info(void* a)
{
    (void)a;
    return NULL;
}

EXPORT void* my____brk_addr = NULL;

void EXPORT my_longjmp(x64emu_t* emu, /*struct __jmp_buf_tag __env[1]*/void *p, int32_t __val)
{
    jump_buff_x64_t *jpbuff = &((__jmp_buf_tag_t*)p)->__jmpbuf;
    //restore  regs
    R_RBX = jpbuff->save_rbx;
    R_RBP = jpbuff->save_rbp;
    R_R12 = jpbuff->save_r12;
    R_R13 = jpbuff->save_r13;
    R_R14 = jpbuff->save_r14;
    R_R15 = jpbuff->save_r15;
    R_RSP = jpbuff->save_rsp;
    // jmp to saved location, plus restore val to rax
    R_RAX = __val?__val:1;
    R_RIP = jpbuff->save_rip;
    if(((__jmp_buf_tag_t*)p)->__mask_was_saved) {
        sigprocmask(SIG_SETMASK, &((__jmp_buf_tag_t*)p)->__saved_mask, NULL);
    }
    if(emu->flags.quitonlongjmp) {
        emu->flags.longjmp = 1;
        emu->quit = 1;
    }
}

EXPORT int32_t my___sigsetjmp(x64emu_t* emu, /*struct __jmp_buf_tag __env[1]*/void *p, int savesigs)
{
    jump_buff_x64_t *jpbuff = &((__jmp_buf_tag_t*)p)->__jmpbuf;
    // save the buffer
    jpbuff->save_rbx = R_RBX;
    jpbuff->save_rbp = R_RBP;
    jpbuff->save_r12 = R_R12;
    jpbuff->save_r13 = R_R13;
    jpbuff->save_r14 = R_R14;
    jpbuff->save_r15 = R_R15;
    jpbuff->save_rsp = R_RSP+sizeof(uintptr_t); // include "return address"
    jpbuff->save_rip = *(uintptr_t*)(R_RSP);
    if(savesigs) {
        if(sigprocmask(SIG_SETMASK, NULL, &((__jmp_buf_tag_t*)p)->__saved_mask))
            ((__jmp_buf_tag_t*)p)->__mask_was_saved = 0;
        else
            ((__jmp_buf_tag_t*)p)->__mask_was_saved = 1;
    } else
        ((__jmp_buf_tag_t*)p)->__mask_was_saved = 0;
    // quit emulation loop and create a new jumpbuf if needed
    if(!emu->flags.jmpbuf_ready) {
        emu->flags.need_jmpbuf = 1;
        emu->quit = 1;
    }
    return 0;
}
EXPORT int32_t my_sigsetjmp(x64emu_t* emu, /*struct __jmp_buf_tag __env[1]*/void *p, int savesigs)
{
    return my___sigsetjmp(emu, p, savesigs);
}
EXPORT int32_t my__setjmp(x64emu_t* emu, /*struct __jmp_buf_tag __env[1]*/void *p)
{
    return  my___sigsetjmp(emu, p, 0);
}
EXPORT int32_t my_setjmp(x64emu_t* emu, /*struct __jmp_buf_tag __env[1]*/void *p)
{
    return  my___sigsetjmp(emu, p, 1);
}

EXPORT void my___explicit_bzero_chk(x64emu_t* emu, void* dst, uint32_t len, uint32_t dstlen)
{
    (void)emu; (void)dstlen;
    memset(dst, 0, len);
}

EXPORT void* my_realpath(x64emu_t* emu, void* path, void* resolved_path)
{
    if(path && isProcSelf(path, "exe")) {
        return realpath(emu->context->fullpath, resolved_path);
    }
    return realpath(path, resolved_path);
}

EXPORT int my_readlinkat(x64emu_t* emu, int fd, void* path, void* buf, size_t bufsize)
{
    if((fd==AT_FDCWD) && isProcSelf(path, "exe")) {
        strncpy(buf, my_context->fullpath, bufsize);
        size_t l = strlen(my_context->fullpath);
        return (l>bufsize)?bufsize:(l+1);
    }
    return readlinkat(fd, path, buf, bufsize);
}

EXPORT ssize_t my___readlinkat_chk(x64emu_t* emu, int dirfd, void* path, void* buf, size_t sz, size_t buflen)
{
    return my_readlinkat(emu, dirfd, path, buf, sz);
}

extern int have48bits;
void* last_mmap_addr[2] = {0};
size_t last_mmap_len[2] = {0};
int last_mmap_idx = 0;
#ifdef DYNAREC
void* last_mmap_0_addr = NULL;
size_t last_mmap_0_len = 0;
#endif

static int is_pe_file(int fd)
{
    unsigned char magic[4];
    unsigned char offset[4];
    if (pread(fd, magic, sizeof(magic), 0) != (ssize_t)sizeof(magic)) return 0;
    if (magic[0] != 'M' || magic[1] != 'Z') return 0;
    if (pread(fd, offset, sizeof(offset), 0x3c) != (ssize_t)sizeof(offset)) return 0;
    uint32_t pe_offset = (uint32_t)offset[0] | (uint32_t)offset[1]<<8 | (uint32_t)offset[2]<<16 | (uint32_t)offset[3]<<24;
    if (pread(fd, magic, sizeof(magic), pe_offset) != (ssize_t)sizeof(magic)) return 0;
    return !memcmp(magic, "PE\0\0", sizeof(magic));
}

static int is_elf_or_pe(int fd)
{
    unsigned char magic[4];
    if (pread(fd, magic, sizeof(magic), 0) != (ssize_t)sizeof(magic)) return 0;
    return !memcmp(magic, "\x7f" "ELF", sizeof(magic)) || is_pe_file(fd);
}

static int is_writable_mapping(uintptr_t start, uintptr_t end)
{
    for(uintptr_t page = start; page < end; page += box64_pagesize)
        if(!memExist(page) || !(getProtection(page) & PROT_WRITE)) return 0;
    return 1;
}

static int can_copy_pe_mmap(int fd, size_t size, ssize_t offset)
{
    struct stat st;
    if(offset < 0 || fstat(fd, &st) || !S_ISREG(st.st_mode) || st.st_size < offset) return 0;
    return size <= (uint64_t)(st.st_size - offset) && is_pe_file(fd);
}

static int pread_mmap(int fd, void* addr, size_t size, ssize_t offset)
{
    size_t done = 0;
    while(done < size) {
        ssize_t ret = pread(fd, (char*)addr + done, size - done, offset + (off_t)done);
        if(ret > 0) {
            done += ret;
            continue;
        }
        if(ret < 0 && errno == EINTR) continue;
        if(!ret) errno = EIO;
        return -1;
    }
    return 0;
}

EXPORT void* my_mmap64(x64emu_t* emu, void *addr, size_t length, int prot, int flags, int fd, ssize_t offset)
{
    (void)emu;
    if(BOX64ENV(dynarec_log)>=LOG_DEBUG) {printf_log(LOG_NONE, "mmap64(%p, 0x%zx, 0x%x, 0x%x, %d, %zd) ", addr, length, prot, flags, fd, offset);}

    uintptr_t start = (uintptr_t)addr;
    uintptr_t end = start + length;
    uintptr_t mapped_end = (end + X86_PAGE_SIZE - 1) & ~(X86_PAGE_SIZE - 1);

    void* ret;
    int e;
    uintptr_t host_start = start & ~(box64_pagesize - 1);
    uintptr_t host_end = (mapped_end + box64_pagesize - 1) & ~(box64_pagesize - 1);
    int emulated_first_edge = 0;
    int emulated_last_edge = 0;
    uintptr_t first_edge_page = 0;
    uintptr_t last_edge_page = 0;
    uint32_t first_edge_prot = 0;
    uint32_t last_edge_prot = 0;

    // For Wine: a guest MAP_FIXED mapping may only partially cover its first or last host page.
    // replace all complete host pages normally, but preserve unrelated guest pages at the edges.
    // the edge host pages retain the union of their old and new protections because the kernel cannot protect
    // their 4k guest pages independently
    if(box64_pagesize > X86_PAGE_SIZE && addr && length && end > start && mapped_end >= end &&
       !(start & (X86_PAGE_SIZE - 1)) && (flags & MAP_FIXED) && (flags & MAP_ANONYMOUS) &&
       (flags & MAP_PRIVATE) && !(flags & MAP_SHARED) && !offset && host_end &&
       (start != host_start || mapped_end != host_end) &&
       (start == host_start || (memExist(host_start) && memExist(host_start + box64_pagesize - 1))) &&
       (mapped_end == host_end || (memExist(host_end - box64_pagesize) && memExist(host_end - 1)))) {
        uintptr_t full_start = start == host_start ? start : host_start + box64_pagesize;
        uintptr_t full_end = mapped_end & ~(box64_pagesize - 1);
        int failed = 0;
        int saved_errno = 0;

        if(start != host_start) {
            uint32_t old_prot = getProtection(host_start);
            first_edge_page = host_start;
            first_edge_prot = prot | old_prot;
            int host_prot = first_edge_prot & ~PROT_CUSTOM;
            if(host_prot & PROT_WRITE) host_prot |= PROT_READ;
            if(mprotect((void*)host_start, box64_pagesize, host_prot | PROT_READ | PROT_WRITE)) {
                failed = 1;
                saved_errno = errno;
            } else
                emulated_first_edge = 1;
        }

        last_edge_page = host_end - box64_pagesize;
        if(!failed && mapped_end != host_end && (!emulated_first_edge || last_edge_page != first_edge_page)) {
            uint32_t old_prot = getProtection(last_edge_page);
            last_edge_prot = prot | old_prot;
            int host_prot = last_edge_prot & ~PROT_CUSTOM;
            if(host_prot & PROT_WRITE) host_prot |= PROT_READ;
            if(mprotect((void*)last_edge_page, box64_pagesize, host_prot | PROT_READ | PROT_WRITE)) {
                failed = 1;
                saved_errno = errno;
            } else
                emulated_last_edge = 1;
        }

        if(!failed && full_start < full_end &&
           box_mmap((void*)full_start, full_end - full_start, prot, flags, fd, offset) == MAP_FAILED) {
            failed = 1;
            saved_errno = errno;
        }

        // Wine restores reserved ranges with PROT_NONE | MAP_NORESERVE.
        // Do not zero the edge: it may still be MAP_SHARED, and memset would modify its backing object.
        if(!failed && (!box64_wine || prot || !(flags & MAP_NORESERVE))) {
            if(emulated_first_edge) {
                uintptr_t edge_end = mapped_end < host_start + box64_pagesize ? mapped_end : host_start + box64_pagesize;
                memset((void*)start, 0, edge_end - start);
            }
            if(mapped_end != host_end && (!emulated_first_edge || last_edge_page != first_edge_page)) {
                uintptr_t edge_start = start > last_edge_page ? start : last_edge_page;
                memset((void*)edge_start, 0, mapped_end - edge_start);
            }
        }

        if(emulated_last_edge) {
            int host_prot = last_edge_prot & ~PROT_CUSTOM;
            if(host_prot & PROT_WRITE) host_prot |= PROT_READ;
            if(mprotect((void*)last_edge_page, box64_pagesize, host_prot) && !failed) {
                failed = 1;
                saved_errno = errno;
            }
        }
        if(emulated_first_edge) {
            int host_prot = first_edge_prot & ~PROT_CUSTOM;
            if(host_prot & PROT_WRITE) host_prot |= PROT_READ;
            if(mprotect((void*)first_edge_page, box64_pagesize, host_prot) && !failed) {
                failed = 1;
                saved_errno = errno;
            }
        }

        if(failed) {
            ret = MAP_FAILED;
            errno = saved_errno;
        } else
            ret = addr;
        e = errno;
    // Also For Wine: Wine loads PE sections into memory it has already reserved.
    // On hosts with pages larger than 4K, a guest-aligned address or file offset may be
    // invalid for mmap, and rounding the end may overwrite adjacent reserved (BSS) memory.
    // Zero the guest pages and copy the requested file bytes, matching Wine's fallback for
    // PE sections with sector-aligned rather than page-aligned file offsets.
    } else if(box64_wine && box64_pagesize > X86_PAGE_SIZE && addr && length && end > start &&
              mapped_end >= end && host_end && !(start & (X86_PAGE_SIZE - 1)) &&
              (flags & MAP_FIXED) && (flags & MAP_PRIVATE) && !(flags & (MAP_SHARED | MAP_ANONYMOUS)) &&
              fd >= 0 && offset >= 0 && (prot & PROT_WRITE) &&
              ((start | mapped_end | (uintptr_t)offset) & (box64_pagesize - 1)) &&
              is_writable_mapping(host_start, host_end) &&
              can_copy_pe_mmap(fd, length, offset)) {
        memset(addr, 0, mapped_end - start);
        if(pread_mmap(fd, addr, length, offset))
            ret = MAP_FAILED;
        else
            ret = addr;
        e = errno;
    } else {
        ret = box_mmap(addr, length, prot, flags, fd, offset);
        e = errno;
    }
    if(emu && box64_is32bits && ret!=MAP_FAILED && ((ret>(void*)0xc0000000) || (ret+length>(void*)0xc0000000))) {
        // do not allow allocating memory that high for 32bits process
        box_munmap(ret, length);
        ret = MAP_FAILED;
        e = EEXIST;
    }
    if((ret==MAP_FAILED && (emu || box64_is32bits)) && (BOX64ENV(log)>=LOG_DEBUG || BOX64ENV(dynarec_log)>=LOG_DEBUG)) {printf_log_prefix(0, LOG_NONE, "%s (%d)\n", strerror(errno), errno);}
    if(((ret!=MAP_FAILED) && (emu || box64_is32bits)) && (BOX64ENV(log)>=LOG_DEBUG || BOX64ENV(dynarec_log)>=LOG_DEBUG)) {printf_log_prefix(0, LOG_NONE, "%p\n", ret);}
    #ifdef DYNAREC
    if(BOX64ENV(dynarec) && ret!=MAP_FAILED) {
        /*if(flags&0x100000 && addr!=ret)
        {
            // program used MAP_FIXED_NOREPLACE but the host linux didn't support it
            // and responded with a different address, so ignore it
        } else*/ {
            if(prot& PROT_EXEC)
                addDBFromAddressRange((uintptr_t)ret, length);
            else
                cleanDBFromAddressRange((uintptr_t)ret, length, prot?0:1);
        }
    }
    #endif
    if(ret!=MAP_FAILED) {
        if (emu && !(flags & MAP_ANONYMOUS) && (fd > 0)) {
            // the last_mmap will allow mmap created by wine, even those that have hole, to be fully tracked as one single mmap
            // check if the file is actually a PE file to be safe.
            int in_last_mmap_0 = (ret >= last_mmap_addr[0]) && ret + length < (last_mmap_addr[0] + last_mmap_len[0]);
            int in_last_mmap_1 = (ret >= last_mmap_addr[1]) && ret + length < (last_mmap_addr[1] + last_mmap_len[1]);
            int is_executable = (in_last_mmap_0 || in_last_mmap_1) && is_elf_or_pe(fd);
            if (is_executable && in_last_mmap_0)
                RecordEnvMappings((uintptr_t)last_mmap_addr[0], last_mmap_len[0], fd);
            else if (is_executable && in_last_mmap_1)
                RecordEnvMappings((uintptr_t)last_mmap_addr[1], last_mmap_len[1], fd);
            else
                RecordEnvMappings((uintptr_t)ret, length, fd);
        }
        if((flags&MAP_SHARED) && (fd>0)) {
            uint32_t flags = fcntl(fd, F_GETFL);
            if((flags&O_ACCMODE)==O_RDWR) {
                if((BOX64ENV(log)>=LOG_DEBUG || BOX64ENV(dynarec_log)>=LOG_DEBUG)) {printf_log(LOG_NONE, "Note: Marking the region (%p-%p prot=%x) as NEVERCLEAN because fd have O_RDWR attribute\n", ret, ret+length, prot);}
                prot |= PROT_NEVERCLEAN;
            }
        }
        // hack to capture full size of the mmap done by wine
#if defined(ANDROID) || defined(WINLATOR_GLIBC)
        if(emu && (fd==-1) && (flags&(MAP_PRIVATE|MAP_ANON))==(MAP_PRIVATE|MAP_ANON) && !(flags&MAP_NORESERVE)) {
#else
        if(emu && (fd==-1) && (flags&(MAP_PRIVATE|MAP_ANON))==(MAP_PRIVATE|MAP_ANON)) {
#endif
            last_mmap_addr[last_mmap_idx] = ret;
            last_mmap_len[last_mmap_idx] = length;
        } else {
            last_mmap_addr[last_mmap_idx] = NULL;
            last_mmap_len[last_mmap_idx] = 0;
        }
        last_mmap_idx = 1-last_mmap_idx;
        #ifdef DYNAREC
        if(!prot) {
            last_mmap_0_addr = ret;
            last_mmap_0_len = length;
        } else {
            last_mmap_0_addr = NULL;
            last_mmap_0_len = 0;
        }
        #endif
        if(emu) {
            setProtection_mmap((uintptr_t)ret, length, prot);
            setGuestProtection((uintptr_t)ret, length, prot, 1);
        } else
            setProtection_box((uintptr_t)ret, length, prot);
        if(emulated_first_edge) {
            if(emu)
                setProtection_mmap(first_edge_page, box64_pagesize, first_edge_prot);
            else
                setProtection_box(first_edge_page, box64_pagesize, first_edge_prot);
        }
        if(emulated_last_edge) {
            if(emu)
                setProtection_mmap(last_edge_page, box64_pagesize, last_edge_prot);
            else
                setProtection_box(last_edge_page, box64_pagesize, last_edge_prot);
        }
        if(addr && ret!=addr)
            e = EEXIST;
    }
    errno = e;  // preserve errno
    return ret;
}
EXPORT void* my_mmap(x64emu_t* emu, void *addr, size_t length, int prot, int flags, int fd, ssize_t offset) __attribute__((alias("my_mmap64")));

EXPORT void* my_mremap(x64emu_t* emu, void* old_addr, size_t old_size, size_t new_size, int flags, void* new_addr)
{
    #ifdef DYNAREC
    last_mmap_0_addr = NULL;
    last_mmap_0_len = 0;
    #endif
    (void)emu;
    if((emu || box64_is32bits) && (BOX64ENV(log)>=LOG_DEBUG || BOX64ENV(dynarec_log)>=LOG_DEBUG)) {printf_log(LOG_NONE, "mremap(%p, %lu, %lu, %d, %p)=>", old_addr, old_size, new_size, flags, new_addr);}
    void* ret = mremap(old_addr, old_size, new_size, flags, new_addr);
    if((emu || box64_is32bits) && (BOX64ENV(log)>=LOG_DEBUG || BOX64ENV(dynarec_log)>=LOG_DEBUG)) {printf_log(LOG_NONE, "%p\n", ret);}
    if(ret!=(void*)-1) {
        uint32_t prot = getProtection((uintptr_t)old_addr)&~PROT_CUSTOM;
        if(ret==old_addr) {
            if(old_size && old_size<new_size) {
                setProtection_mmap((uintptr_t)ret+old_size, new_size-old_size, prot);
                setGuestProtection((uintptr_t)ret+old_size, new_size-old_size, prot, 1);
                #ifdef DYNAREC
                if(BOX64ENV(dynarec))
                    addDBFromAddressRange((uintptr_t)ret+old_size, new_size-old_size);
                #endif
            } else if(old_size && new_size<old_size) {
                freeProtection((uintptr_t)ret+new_size, old_size-new_size);
                freeGuestProtection((uintptr_t)ret+new_size, old_size-new_size);
                #ifdef DYNAREC
                if(BOX64ENV(dynarec))
                    cleanDBFromAddressRange((uintptr_t)ret+new_size, old_size-new_size, 1);
                #endif
            } else if(!old_size) {
                setProtection_mmap((uintptr_t)ret, new_size, prot);
                setGuestProtection((uintptr_t)ret, new_size, prot, 1);
                #ifdef DYNAREC
                if(BOX64ENV(dynarec))
                    addDBFromAddressRange((uintptr_t)ret, new_size);
                #endif
            }
        } else {
            if(old_size
            #ifdef MREMAP_DONTUNMAP
            && ((flags&MREMAP_DONTUNMAP)==0)
            #endif
            ) {
                freeProtection((uintptr_t)old_addr, old_size);
                freeGuestProtection((uintptr_t)old_addr, old_size);
                #ifdef DYNAREC
                if(BOX64ENV(dynarec))
                    cleanDBFromAddressRange((uintptr_t)old_addr, old_size, 1);
                #endif
            }
            setProtection_mmap((uintptr_t)ret, new_size, prot); // should copy the protection from old block
            setGuestProtection((uintptr_t)ret, new_size, prot, 1);
            #ifdef DYNAREC
            if(BOX64ENV(dynarec))
                addDBFromAddressRange((uintptr_t)ret, new_size);
            #endif
        }
    }
    return ret;
}

EXPORT int my_munmap(x64emu_t* emu, void* addr, size_t length)
{
    (void)emu;
    if((emu || box64_is32bits) && (BOX64ENV(log)>=LOG_DEBUG || BOX64ENV(dynarec_log)>=LOG_DEBUG)) {printf_log(LOG_NONE, "munmap(%p, 0x%lx)\n", addr, length);}
    uintptr_t start = (uintptr_t)addr;
    uintptr_t unmap_start = start;
    size_t unmap_length = length;
    int partial_host_pages = 0;

    if(box64_pagesize > X86_PAGE_SIZE) {
        if((start & (X86_PAGE_SIZE - 1)) || !length) {
            errno = EINVAL;
            return -1;
        }

        // HACK: we have to leave partial edge pages mapped.
        uintptr_t guest_end = (start + length + X86_PAGE_SIZE - 1) & ~(X86_PAGE_SIZE - 1);
        if((start & (box64_pagesize - 1)) || (guest_end & (box64_pagesize - 1))) {
            uintptr_t host_start = (start + box64_pagesize - 1) & ~(box64_pagesize - 1);
            uintptr_t host_end = guest_end & ~(box64_pagesize - 1);
            unmap_start = host_start;
            unmap_length = host_start < host_end ? host_end - host_start : 0;
            partial_host_pages = 1;
        }
    }

    int ret = unmap_length ? box_munmap((void*)unmap_start, unmap_length) : 0;
    int e = errno;
    #ifdef DYNAREC
    if(!ret) {
        WillRemoveMapping((uintptr_t)addr, length);
    }
    if(!ret && BOX64ENV(dynarec) && length) {
        if(last_mmap_0_len && last_mmap_0_addr==addr && last_mmap_0_len==length)
        {} else // ignore this one
            cleanDBFromAddressRange((uintptr_t)addr, length, 1);
    }
    last_mmap_0_addr = NULL;
    last_mmap_0_len = 0;
    #endif
    if(!ret) {
        last_mmap_addr[1-last_mmap_idx] = NULL;
        last_mmap_len[1-last_mmap_idx] = 0;
        if(!partial_host_pages)
            freeProtection(start, length);
        else if(unmap_length)
            freeProtection(unmap_start, unmap_length);
        if(partial_host_pages) {
            setGuestProtection(start, length, 0, 0);
            if(unmap_length)
                freeGuestProtection(unmap_start, unmap_length);
        } else
            freeGuestProtection(start, length);
        RemoveMapping((uintptr_t)addr, length);
    }
    errno = e;  // preseve errno
    return ret;
}

EXPORT int my_mprotect(x64emu_t* emu, void *addr, unsigned long len, int prot)
{
    #ifdef DYNAREC
    last_mmap_0_addr = NULL;
    last_mmap_0_len = 0;
    #endif
    (void)emu;
    if(emu && (BOX64ENV(log)>=LOG_DEBUG || BOX64ENV(dynarec_log)>=LOG_DEBUG)) {printf_log(LOG_NONE, "mprotect(%p, 0x%lx, 0x%x)\n", addr, len, prot);}
    if(prot&PROT_WRITE)
        prot|=PROT_READ;    // PROT_READ is implicit with PROT_WRITE on x86_64
    uintptr_t start = (uintptr_t)addr;
    if(box64_pagesize == X86_PAGE_SIZE) {
        int ret = mprotect(addr, len, prot);
        if(!ret && len) {
            updateProtection(start, len, prot);
            setGuestProtection(start, len, prot, 0);
        }
        return ret;
    }
    if(start & (X86_PAGE_SIZE - 1)) {
        errno = EINVAL;
        return -1;
    }
    if(!len) return 0;
    uintptr_t end = (start + len + X86_PAGE_SIZE - 1) & ~(X86_PAGE_SIZE - 1);
    uintptr_t host_start = start & ~(box64_pagesize - 1);
    uintptr_t host_end = (end + box64_pagesize - 1) & ~(box64_pagesize - 1);

    if(host_end - host_start == box64_pagesize && (start != host_start || end != host_end)) {
        int host_prot = prot | (getProtection(host_start) & ~PROT_CUSTOM);
        int ret = mprotect((void*)host_start, box64_pagesize, host_prot);
        if(!ret) {
            updateProtection(host_start, box64_pagesize, host_prot);
            setGuestProtection(start, end - start, prot, 0);
        }
        return ret;
    }

    if(start != host_start) {
        int host_prot = prot | (getProtection(host_start) & ~PROT_CUSTOM);
        int ret = mprotect((void*)host_start, box64_pagesize, host_prot);
        if(ret) return -1;
        updateProtection(host_start, box64_pagesize, host_prot);
        host_start += box64_pagesize;
    }
    if(end != host_end) {
        host_end -= box64_pagesize;
        int host_prot = prot | (getProtection(host_end) & ~PROT_CUSTOM);
        int ret = mprotect((void*)host_end, box64_pagesize, host_prot);
        if(ret) return -1;
        updateProtection(host_end, box64_pagesize, host_prot);
    }
    if(host_start == host_end) {
        setGuestProtection(start, end - start, prot, 0);
        return 0;
    }
    int ret = mprotect((void*)host_start, host_end - host_start, prot);
    if(!ret) {
        updateProtection(host_start, host_end - host_start, prot);
        setGuestProtection(start, end - start, prot, 0);
    }
    return ret;
}

EXPORT int my_madvise(x64emu_t* emu, void* addr, size_t length, int advice)
{
    (void)emu;
    uintptr_t start = (uintptr_t)addr;
    if(start & (X86_PAGE_SIZE - 1)) {
        errno = EINVAL;
        return -1;
    }
    if(!length) return 0;
    uintptr_t end = (start + length + X86_PAGE_SIZE - 1) & ~(X86_PAGE_SIZE - 1);

    if(advice != MADV_DONTNEED) {
        uintptr_t host_start = start & ~(box64_pagesize - 1);
        uintptr_t host_end = (end + box64_pagesize - 1) & ~(box64_pagesize - 1);
        return madvise((void*)host_start, host_end - host_start, advice);
    }

    if(box64_pagesize == X86_PAGE_SIZE) return madvise(addr, end - start, advice);

    if(!(start & (box64_pagesize - 1)) && !(end & (box64_pagesize - 1)))
        return madvise(addr, end - start, advice);

    if(!memExist(start) || !memExist(end - 1)) {
        errno = ENOMEM;
        return -1;
    }

    if(IsAddrElfOrFileMapped(start) || IsAddrElfOrFileMapped(end - 1)) return 0;

    if(!(getProtection(start) & PROT_WRITE) || !(getProtection(end - 1) & PROT_WRITE)) return 0;

    memset((void*)start, 0, end - start);
    return 0;
}

EXPORT int my___madvise(x64emu_t* emu, void* addr, size_t length, int advice) __attribute__((alias("my_madvise")));

EXPORT int my_posix_madvise(x64emu_t* emu, void* addr, size_t length, int advice)
{
    (void)emu;
    if(advice == POSIX_MADV_DONTNEED) return 0;
    return posix_madvise(addr, length, advice);
}

typedef struct mallinfo (*mallinfo_fnc)(void);
EXPORT void* my_mallinfo(x64emu_t* emu, void* p)
{
    static mallinfo_fnc f = NULL;
    static int inited = 0;
    if(!inited) {
        inited = 1;
        f = (mallinfo_fnc)dlsym(my_lib->w.lib, "mallinfo");
    }
    if(f)
        *(struct mallinfo*)p=f();
    else
        memset(p, 0, sizeof(struct mallinfo));
    return p;
}

struct my_mallinfo2_s {
    size_t arena;
    size_t ordblks;
    size_t smblks;
    size_t hblks;
    size_t hblkhd;
    size_t usmblks;
    size_t fsmblks;
    size_t uordblks;
    size_t fordblks;
    size_t keepcost;
};

typedef struct my_mallinfo2_s (*mallinfo2_fnc)(void);
EXPORT void* my_mallinfo2(x64emu_t* emu, void* p)
{
    static mallinfo2_fnc f = NULL;
    static int inited = 0;
    if(!inited) {
        inited = 1;
        f = (mallinfo2_fnc)dlsym(my_lib->w.lib, "mallinfo2");
    }
    if(f)
        *(struct my_mallinfo2_s*)p = f();
    else
        memset(p, 0, sizeof(struct my_mallinfo2_s));
    return p;
}

#ifdef STATICBUILD
void my_updateGlobalOpt() {}
void my_checkGlobalOpt() {}
#endif

EXPORT int my_getopt(int argc, char* const argv[], const char *optstring)
{
    my_updateGlobalOpt();
    int ret = getopt(argc, argv, optstring);
    my_checkGlobalOpt();
    return ret;
}

EXPORT int my_getopt_long(int argc, char* const argv[], const char* optstring, const struct option *longopts, int *longindex)
{
    my_updateGlobalOpt();
    int ret = getopt_long(argc, argv, optstring, longopts, longindex);
    my_checkGlobalOpt();
    return ret;
}

EXPORT int my_getopt_long_only(int argc, char* const argv[], const char* optstring, const struct option *longopts, int *longindex)
{
    my_updateGlobalOpt();
    int ret = getopt_long_only(argc, argv, optstring, longopts, longindex);
    my_checkGlobalOpt();
    return ret;
}

#ifndef ANDROID
typedef struct {
   void  *read;
   void *write;
   void  *seek;
   void *close;
} my_cookie_io_functions_t;

typedef struct my_cookie_s {
    uintptr_t r, w, s, c;
    void* cookie;
} my_cookie_t;

static ssize_t my_cookie_read(void *p, char *buf, size_t size)
{
    my_cookie_t* cookie = (my_cookie_t*)p;
    return (ssize_t)RunFunctionFmt(cookie->r, "ppL", cookie->cookie, buf, size)       ;
}
static ssize_t my_cookie_write(void *p, const char *buf, size_t size)
{
    my_cookie_t* cookie = (my_cookie_t*)p;
    return (ssize_t)RunFunctionFmt(cookie->w, "ppL", cookie->cookie, buf, size)       ;
}
static int my_cookie_seek(void *p, off64_t *offset, int whence)
{
    my_cookie_t* cookie = (my_cookie_t*)p;
    return RunFunctionFmt(cookie->s, "ppi", cookie->cookie, offset, whence)       ;
}
static int my_cookie_close(void *p)
{
    my_cookie_t* cookie = (my_cookie_t*)p;
    int ret = 0;
    if(cookie->c)
        ret = RunFunctionFmt(cookie->c, "p", cookie->cookie)      ;
    box_free(cookie);
    return ret;
}
EXPORT void* my_fopencookie(x64emu_t* emu, void* cookie, void* mode, my_cookie_io_functions_t *s)
{
    cookie_io_functions_t io_funcs = {s->read?my_cookie_read:NULL, s->write?my_cookie_write:NULL, s->seek?my_cookie_seek:NULL, my_cookie_close};
    my_cookie_t *cb = (my_cookie_t*)box_calloc(1, sizeof(my_cookie_t));
    cb->r = (uintptr_t)s->read;
    cb->w = (uintptr_t)s->write;
    cb->s = (uintptr_t)s->seek;
    cb->c = (uintptr_t)s->close;
    cb->cookie = cookie;
    return fopencookie(cb, mode, io_funcs);
}
#endif

#if 0

EXPORT long my_prlimit64(void* pid, uint32_t res, void* new_rlim, void* old_rlim)
{
    return syscall(__NR_prlimit64, pid, res, new_rlim, old_rlim);
}

EXPORT void* my_reallocarray(void* ptr, size_t nmemb, size_t size)
{
    return realloc(ptr, nmemb*size);
}

#ifndef __OPEN_NEEDS_MODE
# define __OPEN_NEEDS_MODE(oflag) \
  (((oflag) & O_CREAT) != 0)
// || ((oflag) & __O_TMPFILE) == __O_TMPFILE)
#endif
EXPORT int my___open_nocancel(x64emu_t* emu, void* file, int oflag, int* b)
{
    int mode = 0;
    if (__OPEN_NEEDS_MODE (oflag))
        mode = b[0];
    return openat(AT_FDCWD, file, oflag, mode);
}

EXPORT int my___libc_alloca_cutoff(x64emu_t* emu, size_t size)
{
    // not always implemented on old linux version...
    library_t* lib = my_lib;
    if(!lib) return 0;
    void* f = dlsym(lib->w.lib, "__libc_alloca_cutoff");
    if(f)
        return ((iFL_t)f)(size);
    // approximate version but it's better than nothing....
    return (size<=(65536*4));
}

EXPORT int my_nanosleep(const struct timespec *req, struct timespec *rem)
{
    if(!req)
        return 0;   // workaround for some strange calls
    return nanosleep(req, rem);
}
#endif

// DL functions from wrappedlibdl.c
void* my_dlopen(x64emu_t* emu, void *filename, int flag);
int my_dlclose(x64emu_t* emu, void *handle);
void* my_dlsym(x64emu_t* emu, void *handle, void *symbol);
EXPORT int my___libc_dlclose(x64emu_t* emu, void* handle)
{
    return my_dlclose(emu, handle);
}
EXPORT void* my___libc_dlopen_mode(x64emu_t* emu, void* name, int mode)
{
    return my_dlopen(emu, name, mode);
}
EXPORT void* my___libc_dlsym(x64emu_t* emu, void* handle, void* name)
{
    return my_dlsym(emu, handle, name);
}

#ifdef ANDROID
void obstackSetup() {
}
#else
// all obstack function defined in obstack.c file
void obstackSetup();
#endif

EXPORT void* my_malloc(unsigned long size)
{
    return calloc(1, size);
}

static int check_getrlimit_buffer(void* rlim, size_t size)
{
    if(isGuestRangeFakelyProtected((uintptr_t)rlim, size, PROT_WRITE)) return 1;
    errno = EFAULT;
    return 0;
}

EXPORT int my___getrlimit(x64emu_t* emu, int resource, struct rlimit* rlim)
{
    (void)emu;
    return check_getrlimit_buffer(rlim, sizeof(*rlim)) ? getrlimit(resource, rlim) : -1;
}

EXPORT int my_getrlimit(x64emu_t* emu, uint32_t resource, struct rlimit* rlim)
{
    return my___getrlimit(emu, resource, rlim);
}

EXPORT int my_getrlimit64(x64emu_t* emu, uint32_t resource, struct rlimit64* rlim)
{
    (void)emu;
    return check_getrlimit_buffer(rlim, sizeof(*rlim)) ? getrlimit64(resource, rlim) : -1;
}

EXPORT int my_setrlimit(x64emu_t* emu, int ressource, const struct rlimit *rlim)
{
    int ret = (ressource==RLIMIT_AS)?0:setrlimit(ressource, rlim);
    if(ressource==RLIMIT_AS) printf_log(LOG_DEBUG, " (ignored) RLIMIT_AS, cur=0x%lx, max=0x%lx ", rlim->rlim_cur, rlim->rlim_max);
    return ret;
}

#if 0
#ifdef PANDORA
#define RENAME_NOREPLACE    (1 << 0)
#define RENAME_EXCHANGE     (1 << 1)
#define RENAME_WHITEOUT     (1 << 2)
EXPORT int my_renameat2(int olddirfd, void* oldpath, int newdirfd, void* newpath, uint32_t flags)
{
    // simulate that function, but
    if(flags&RENAME_NOREPLACE) {
        if(FileExist(newpath, -1)) {
            errno = EEXIST;
            return -1;
        }
        flags &= ~RENAME_NOREPLACE;
    }
    if(!flags) return renameat(olddirfd, oldpath, newdirfd, newpath);
    if(flags&RENAME_WHITEOUT) {
        errno = EINVAL;
        return -1;  // not handling that
    }
    if((flags&RENAME_EXCHANGE) && (olddirfd==-1) && (newdirfd==-1)) {
        // cannot do atomically...
        char* tmp = (char*)box_malloc(strlen(oldpath)+10); // create a temp intermediary
        tmp = strcat(oldpath, ".tmp");
        int ret = renameat(-1, oldpath, -1, tmp);
        if(ret==-1) return -1;
        ret = renameat(-1, newpath, -1, oldpath);
        if(ret==-1) return -1;
        ret = renameat(-1, tmp, -1, newpath);
        box_free(tmp);
        return ret;
    }
    return -1; // unknown flags
}
#endif

#ifndef __NR_memfd_create
#define MFD_CLOEXEC         0x0001U
#define MFD_ALLOW_SEALING   0x0002U
EXPORT int my_memfd_create(x64emu_t* emu, void* name, uint32_t flags)
{
    // try to simulate that function
    uint32_t fl = O_RDWR | O_CREAT;
    if(flags&MFD_CLOEXEC)
        fl |= O_CLOEXEC;
    int tmp = shm_open(name, fl, S_IRWXU);
    if(tmp<0) return -1;
    shm_unlink(name);    // remove the shm file, but it will still exist because it's currently in use
    return tmp;
}
#endif

#ifndef GRND_RANDOM
#define GRND_RANDOM 0x0002
#endif
EXPORT int my_getentropy(x64emu_t* emu, void* buffer, size_t length)
{
    library_t* lib = my_lib;
    if(!lib) return 0;
    void* f = dlsym(lib->w.lib, "getentropy");
    if(f)
        return ((iFpL_t)f)(buffer, length);
    // custom implementation
    if(length>256) {
        errno = EIO;
        return -1;
    }
    int ret = my_getrandom(emu, buffer, length, GRND_RANDOM);
    if(ret!=length) {
        errno = EIO;
        return -1;
    }
    return 0;
}

EXPORT void my_mcount(void* frompc, void* selfpc)
{
    // stub doing nothing...
    return;
}
#endif

#ifndef ANDROID
union semun {
  int              val;    /* Value for SETVAL */
  struct semid_ds *buf;    /* Buffer for IPC_STAT, IPC_SET */
  unsigned short  *array;  /* Array for GETALL, SETALL */
  struct seminfo  *__buf;  /* Buffer for IPC_INFO
                              (Linux-specific) */
};
#endif
#ifndef SEM_STAT_ANY
#define SEM_STAT_ANY 20
#endif

EXPORT int my_semctl(int semid, int semnum, int cmd, union semun b)
{
    struct semid_ds semidds;
    void *backup = NULL;
    if ((cmd == IPC_STAT) || (cmd == IPC_SET) || (cmd == SEM_STAT) || (cmd == SEM_STAT_ANY)) {
        backup = b.buf;
        b.buf = &semidds;
        if (cmd == IPC_SET) {
            AlignSemidDs(&semidds, backup);
        }
    }
    int ret = semctl(semid, semnum, cmd, b);
    if ((cmd == IPC_STAT) || (cmd == IPC_SET) || (cmd == SEM_STAT) || (cmd == SEM_STAT_ANY)) {
        b.buf = backup;
        if (cmd == IPC_STAT) {
            UnalignSemidDs(backup, &semidds);
        }
    }
    return ret;
}

EXPORT int64_t userdata_sign = 0x1234598765ABCEF0;
EXPORT uint32_t userdata[1024];

int isProcessBox64(pid_t pid)
{
    if(ptrace(PTRACE_PEEKDATA, pid, &userdata_sign, NULL)==userdata_sign)
        return 1;
    return 0;
}

EXPORT long my_ptrace(x64emu_t* emu, int request, pid_t pid, void* addr, uint32_t* data)
{
    if(request == PTRACE_POKEUSER) {
        if(isProcessBox64(pid)  && (uintptr_t)addr < sizeof(my_x64_user_t)) {
        //printf_log_prefix(2, LOG_INFO, "Using ptrace POKE at %p for 0x%x (userdata 0x%x)\n", addr, pid, data);
            long ret = ptrace(PTRACE_POKEDATA, pid, addr+(uintptr_t)userdata, data);
            return ret;
        }
        //printf_log_prefix(2, LOG_INFO, "Using ptrace POKE at %p for 0x%x (faked 0x%x)\n", addr, pid, data);
        // fallback to a generic local faking
        if((uintptr_t)addr < sizeof(userdata)) {
            *(uintptr_t*)(addr+(uintptr_t)userdata) = (uintptr_t)data;
        // lets just ignore this for now!
            errno = 0;
            return 0;
        }
        errno = EINVAL;
        return -1;
    }
    if(request == PTRACE_PEEKUSER) {
        if(isProcessBox64(pid)  && (uintptr_t)addr < sizeof(my_x64_user_t)) {
            long ret = ptrace(PTRACE_PEEKDATA, pid, addr+(uintptr_t)userdata, data);
            if((uintptr_t)addr==offsetof(my_x64_user_t, u_debugreg[6])) {
                // clean up DR6...
                ret |= 0b111111110000ULL;
                ret &= 0xffffefffULL;
                ret |= 0xffff0000ULL;
            }
            if((uintptr_t)addr==offsetof(my_x64_user_t, u_debugreg[7])) {
                // clean up DR7...
                ret |= 1ULL<<10;
                ret &= (0xffff3fffLL);
            }
            //printf_log_prefix(2, LOG_INFO, "Using ptrace PEEK at %p for 0x%x (userdata) => 0x%x\n", addr, pid, ret);
            return ret;
        }
        // fallback to a generic local faking
        if((uintptr_t)addr < sizeof(userdata)) {
            errno = 0;
            //printf_log_prefix(2, LOG_INFO, "Using ptrace PEEK at %p for 0x%x (faked) => 0x%x\n", addr, pid, *(uintptr_t*)(addr+(uintptr_t)userdata));
            return *(uintptr_t*)(addr+(uintptr_t)userdata);
        }
        //printf_log_prefix(2, LOG_INFO, "Using ptrace PEEK at %p for 0x%x (error) => -1)\n", addr, pid);
        errno = EINVAL;
        return -1;
    }
    long ret = ptrace(request, pid, addr, data);
    return ret;
}

static void copy_from_iovec(void* dst, size_t len, const struct iovec* iov, unsigned long cnt, size_t skip)
{
    char* out = (char*)dst;
    size_t done = 0;
    for (unsigned long i = 0; i < cnt && done < len; ++i) {
        size_t ilen = iov[i].iov_len;
        if (skip >= ilen) {
            skip -= ilen;
            continue;
        }
        size_t chunk = ilen - skip;
        if (chunk > len - done) chunk = len - done;
        memcpy(out + done, (const char*)iov[i].iov_base + skip, chunk);
        done += chunk;
        skip = 0;
    }
}

static ssize_t ptrace_poke_range(pid_t pid, const char* src, uintptr_t dst, size_t size)
{
    size_t written = 0;
    while (size > 0) {
        uintptr_t align = dst & ~(sizeof(long) - 1);
        unsigned offset = dst & (sizeof(long) - 1);
        unsigned chunk = sizeof(long) - offset;
        if (chunk > size) chunk = (unsigned)size;

        errno = 0;
        long old = ptrace(PTRACE_PEEKDATA, pid, (void*)align, NULL);
        if (old == -1 && errno) return -1;

        long value = old;
        memcpy((char*)&value + offset, src + written, chunk);

        errno = 0;
        if (ptrace(PTRACE_POKEDATA, pid, (void*)align, (void*)value) == -1 && errno) return -1;

        written += chunk;
        dst += chunk;
        size -= chunk;
    }
    return written;
}

EXPORT ssize_t my_process_vm_writev(x64emu_t* emu, pid_t pid, struct iovec* local_iov, unsigned long liovcnt,
    struct iovec* remote_iov, unsigned long riovcnt, unsigned long flags)
{
    (void)emu;
    size_t total = 0;
    for (unsigned long i = 0; i < riovcnt; ++i)
        total += remote_iov[i].iov_len;
    ssize_t ret = syscall(__NR_process_vm_writev, pid, local_iov, liovcnt, remote_iov, riovcnt, flags);

    if (ret >= 0 && (size_t)ret == total) return ret;
    if (ret < 0 && errno != EFAULT) return ret;

    // this is for Syringe.exe:
    // Wine use process_vm_writev to implement WriteProcessMemory:
    // the request is forwarded to wineserver, and wineserver use process_vm_writev to do the write.
    // but the memory it's trying to write might be protected by DynaRec, and a normal process_vm_writev will fail with EFAULT,
    // so try to write with ptrace instead...

    // printf_log(LOG_INFO, "process_vm_writev fallback for pid=%d total=%zu ret=%zd errno=%d\n", pid, total, ret, errno);
    size_t done = (ret > 0) ? (size_t)ret : 0; // partial write
    size_t remain = total - done;
    if (!remain) return ret;

    // try to attach if the target is not already stopped / traced.
    int attached = 0;
    errno = 0;
    long test = ptrace(PTRACE_PEEKDATA, pid, (void*)0, NULL);
    if (test == -1 && errno == ESRCH) {
        if (ptrace(PTRACE_ATTACH, pid, NULL, NULL) == -1) return ret;
        attached = 1;
        int status;
        if (waitpid(pid, &status, __WALL) == -1) {
            ptrace(PTRACE_DETACH, pid, NULL, NULL);
            return ret;
        }
    }

    char* buf = (char*)malloc(remain);
    if (!buf) {
        if (attached) ptrace(PTRACE_DETACH, pid, NULL, 0);
        return ret;
    }
    copy_from_iovec(buf, remain, local_iov, liovcnt, done);

    // write the remaining bytes...
    size_t ptrace_written = 0;
    size_t skip = done;
    for (unsigned long i = 0; i < riovcnt && ptrace_written < remain; ++i) {
        size_t len = remote_iov[i].iov_len;
        if (skip >= len) {
            skip -= len;
            continue;
        }
        uintptr_t dst = (uintptr_t)remote_iov[i].iov_base + skip;
        size_t chunk = len - skip;
        if (chunk > remain - ptrace_written) chunk = remain - ptrace_written;
        ssize_t w = ptrace_poke_range(pid, buf + ptrace_written, dst, chunk);
        if (w == (size_t)-1) break;
        ptrace_written += w;
        skip = 0;
    }

    free(buf);
    if (attached) ptrace(PTRACE_DETACH, pid, NULL, 0);

    // printf_log(LOG_INFO, "process_vm_writev fallback done=%zu ptrace_written=%zu\n", done, ptrace_written);
    if (ptrace_written) return (ssize_t)(done + ptrace_written);
    return ret;
}

// Backtrace stuff

#include "elfs/elfdwarf_private.h"
EXPORT int my_backtrace(x64emu_t* emu, void** buffer, int size)
{
    if (!size) return 0;
    dwarf_unwind_t *unwind = init_dwarf_unwind_registers(emu);
    int idx = 0;
    char success = 0;
    if(!(getProtection_fast(R_RSP)&PROT_READ))
        return 0;
    if(!(getProtection_fast((uintptr_t)buffer)&PROT_READ))
        return 0;
    uintptr_t addr = *(uintptr_t*)R_RSP;
    buffer[0] = (void*)addr;
    while (++idx < size) {
        uintptr_t ret_addr = get_parent_registers(unwind, FindElfAddress(my_context, addr), addr, &success);
        if (ret_addr == my_context->exit_bridge) {
            // TODO: do something to be able to get the function name
            buffer[idx] = (void*)ret_addr;
            success = 2;
            // See elfdwarf_private.c for the register mapping
            unwind->regs[7] = unwind->regs[6]; // mov rsp, rbp
            if(!(getProtection_fast(unwind->regs[7])&PROT_READ))
                return idx-1;
            unwind->regs[6] = *(uint64_t*)unwind->regs[7]; // pop rbp
            unwind->regs[7] += 8;
            ret_addr = *(uint64_t*)unwind->regs[7]; // ret
            unwind->regs[7] += 8;
            if (++idx < size) buffer[idx] = (void*)ret_addr;
        } else if (!success) break;
        else buffer[idx] = (void*)ret_addr;
        addr = ret_addr;
    }
    free_dwarf_unwind_registers(&unwind);
    return idx;
}

// special version, called in signal with SHOWBT
EXPORT int my_backtrace_ip(x64emu_t* emu, void** buffer, int size)
{
    if (!size) return 0;
    dwarf_unwind_t *unwind = init_dwarf_unwind_registers(emu);
    int idx = 0;
    char success = 1;
    uintptr_t addr = R_RIP;
    buffer[0] = (void*)addr;
    while ((++idx < size) && success) {
        uintptr_t ret_addr = get_parent_registers(unwind, FindElfAddress(my_context, addr), addr, &success);
        if (ret_addr == my_context->exit_bridge) {
            // TODO: do something to be able to get the function name
            buffer[idx] = (void*)ret_addr;
            success = 2;
            // See elfdwarf_private.c for the register mapping
            unwind->regs[7] = unwind->regs[6]; // mov rsp, rbp
            unwind->regs[6] = *(uint64_t*)unwind->regs[7]; // pop rbp
            unwind->regs[7] += 8;
            ret_addr = *(uint64_t*)unwind->regs[7]; // ret
            unwind->regs[7] += 8;
            if (++idx < size) buffer[idx] = (void*)ret_addr;
        } else if (!success) {
            if(getProtection((uintptr_t)addr)&(PROT_READ)) {
                if (getProtection((uintptr_t)addr - 19) && *(uint8_t*)(addr - 19) == 0xCC && IsBridgeSignature(*(uint8_t*)(addr - 19 + 1), *(uint8_t*)(addr - 19 + 2))) {
                    buffer[idx-1] = (void*)(addr-19);
                    success = 2;
                    if(idx==1)
                        unwind->regs[7] -= 8;
                    ret_addr = *(uint64_t*)unwind->regs[7]; // ret
                    unwind->regs[7] += 8;
                    buffer[idx] = (void*)ret_addr;
                } else {
                    // try a simple end of function epilog
                    unwind->regs[7] = unwind->regs[6]; // mov rsp, rbp
                    if(getProtection(unwind->regs[7])&(PROT_READ)) {
                        unwind->regs[6] = *(uint64_t*)unwind->regs[7]; // pop rbp
                        unwind->regs[7] += 8;
                        ret_addr = *(uint64_t*)unwind->regs[7]; // ret
                        unwind->regs[7] += 8;
                        buffer[idx] = (void*)ret_addr;
                        success = 2;
                    } else
                        break;
                }
            } else
                break;
        } else buffer[idx] = (void*)ret_addr;
        addr = ret_addr;
    }
    free_dwarf_unwind_registers(&unwind);
    return idx;
}

EXPORT char** my_backtrace_symbols(x64emu_t* emu, uintptr_t* buffer, int size)
{
    (void)emu;
    char** ret = (char**)calloc(1, size*sizeof(char*) + size*200);  // capping each strings to 200 chars, not using box_calloc (program space)
    char* s = (char*)(ret+size);
    for (int i=0; i<size; ++i) {
        uintptr_t start = 0;
        uint64_t sz = 0;
        elfheader_t *hdr = FindElfAddress(my_context, buffer[i]);
        const char* symbname = FindNearestSymbolName(hdr, (void*)buffer[i], &start, &sz);
        if(!sz) sz=0x100;   // arbitrary value...
        if (symbname && buffer[i]>=start && (buffer[i]<(start+sz) || !sz)) {
            snprintf(s, 200, "%s(%s+%lx) [%p]", ElfName(hdr), symbname, buffer[i] - start, (void*)buffer[i]);
        } else if (hdr) {
            snprintf(s, 200, "%s+%lx [%p]", ElfName(hdr), buffer[i] - (uintptr_t)GetBaseAddress(hdr), (void*)buffer[i]);
        } else {
            snprintf(s, 200, "??? [%p]", (void*)buffer[i]);
        }
        ret[i] = s;
        s += 200;
    }
    return ret;
}

EXPORT void my_backtrace_symbols_fd(x64emu_t* emu, uintptr_t* buffer, int size, int fd)
{
    (void)emu;
    char s[200];
    for (int i=0; i<size; ++i) {
        uintptr_t start = 0;
        uint64_t sz = 0;
        const char* symbname = FindNearestSymbolName(FindElfAddress(my_context, buffer[i]), (void*)buffer[i], &start, &sz);
        if(!sz) sz=0x100;   // arbitrary value...
        if(symbname && buffer[i]>=start && (buffer[i]<(start+sz) || !sz))
            snprintf(s, 200, "%s+%ld [%p]\n", symbname, buffer[i] - start, (void*)buffer[i]);
        else
            snprintf(s, 200, "??? [%p]\n", (void*)buffer[i]);
        int dummy = write(fd, s, strlen(s));
        (void)dummy;
    }
}

EXPORT int my_iopl(x64emu_t* emu, int level)
{
    // Set I/O permission (so access IN/OUT opcodes) Default is 0. Can Set to 0..3
    // set permission for all 65536 ports addresses
    // note ioperm can set individual permission
    /*static iFi_t real_iopl = NULL;
    static int searched = 0;
    if(!searched) {
        searched = 1;
        real_iopl = (iFi_t)dlsym(my_lib, "iopl");
    }
    if(real_iopl)
        return real_iopl(level);*/
    // For now, lets just return "unsupported"
    errno = ENOSYS;
    return -1;
}

EXPORT int my_stime(x64emu_t* emu, const time_t *t)
{
    // TODO?
    errno = EPERM;
    return -1;
}

int GetTID();
#ifdef ANDROID
void updateGlibcTidCache() {}
#else
struct glibc_pthread {
#if defined(NO_ALIGN)
    char header[704];
#else
    void* header[24];
#endif
  void* list[2];
  pid_t tid;
};
pid_t getGlibcCachedTid() {
  pthread_mutex_t lock = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
  pthread_mutex_lock(&lock);
  pid_t tid = lock.__data.__owner;
  pthread_mutex_unlock(&lock);
  pthread_mutex_destroy(&lock);
  return tid;
}
void updateGlibcTidCache() {
  pid_t real_tid = GetTID();
  pid_t cached_tid = getGlibcCachedTid();
  if (cached_tid != real_tid) {
    pid_t* cached_tid_location =
        &((struct glibc_pthread*)(pthread_self()))->tid;
    *cached_tid_location = real_tid;
  }
}
#endif
typedef struct clone_arg_s {
 uintptr_t stack;
 x64emu_t *emu;
 uintptr_t fnc;
 void* args;
 int stack_clone_used;
 int flags;
 void* tls;
} clone_arg_t;
void init_mutexes(box64context_t* context);
static int clone_fn(void* p)
{
    clone_arg_t* arg = (clone_arg_t*)p;
    updateGlibcTidCache();  // update cache tid if needed
    x64emu_t *emu = arg->emu;
    R_RSP = arg->stack;
    emu->flags.quitonexit = 1;
    if(arg->flags & CLONE_SETTLS) SetFSBaseEmu(emu, arg->tls);
    thread_forget_emu();    //TODO: not all will flags needs this, probably just CLONE_VM?
    thread_set_emu(emu);
    if(arg->flags&CLONE_NEWUSER) {
        init_mutexes(my_context);
    }
    int ret = RunFunctionWithEmu(emu, 0, arg->fnc, 1, arg->args);
    int exited = (emu->flags.quitonexit==2);
    thread_set_emu(NULL); // emu is gone...
    if(arg->stack_clone_used)
        my_context->stack_clone_used = 0;
    box_free(arg);
    /*if(exited)
        exit(ret);*/
    return ret;
}

EXPORT int my_clone(x64emu_t* emu, void* fn, void* stack, int flags, void* args, void* parent, void* tls, void* child)
{
    printf_log(LOG_DEBUG, "my_clone(fn:%p(%s), stack:%p, 0x%x, args:%p, %p, %p, %p)", fn, getAddrFunctionName((uintptr_t)fn), stack, flags, args, parent, tls, child);
    void* mystack = NULL;
    clone_arg_t* arg = (clone_arg_t*)box_calloc(1, sizeof(clone_arg_t));
    x64emu_t * newemu = NewX64Emu(emu->context, R_RIP, (uintptr_t)stack, 0, 0);
    SetupX64Emu(newemu, emu);
    //CloneEmu(newemu, emu);
    if(my_context->stack_clone_used) {
        printf_log(LOG_DEBUG, " no free stack_clone ");
        mystack = box_malloc(1024*1024);  // stack for own process... memory leak, but no practical way to remove it
    } else {
        if(!my_context->stack_clone)
            my_context->stack_clone = box_malloc(1024*1024);
        mystack = my_context->stack_clone;
        printf_log(LOG_DEBUG, " using stack_clone ");
        my_context->stack_clone_used = 1;
        arg->stack_clone_used = 1;
    }
    arg->stack = (uintptr_t)stack &~7LL;
    arg->args = args;
    arg->fnc = (uintptr_t)fn;
    arg->tls = tls;
    arg->emu = newemu;
    arg->flags = flags;
    // the emulated callback can use wrapped libc before exec, so it cannot safely
    // share the host allocator and address space with a suspended parent.
    if((flags & (CLONE_VM|CLONE_VFORK)) == (CLONE_VM|CLONE_VFORK))
        flags &=~ (CLONE_VM|CLONE_VFORK);
    flags &=~ CLONE_SETTLS;   // guest TLS is applied to the emulated FS base in clone_fn
    int64_t ret = clone(clone_fn, (void*)((uintptr_t)mystack+1024*1024), flags, arg, parent, NULL, child);
    return (uintptr_t)ret;
}

EXPORT void my___cxa_pure_virtual(x64emu_t* emu)
{
    printf_log(LOG_NONE, "Pure virtual function called\n");
    emu->quit = 1;
    abort();
}

EXPORT size_t my_strlcpy(x64emu_t* emu, void* dst, void* src, size_t l)
{
    if (l > 0) {
        strncpy(dst, src, l-1);
        ((char*)dst)[l-1] = '\0';
    }
    return strlen(src);
}
EXPORT size_t my_strlcat(x64emu_t* emu, void* dst, void* src, size_t l)
{
    if (l == 0)
        return strlen(src);
    size_t s = strlen(dst);
    if(s>=l)
        return s + strlen(src);
    strncat(dst, src, l-s-1);
    ((char*)dst)[l-1] = '\0';
    return s+strlen(src);
}

EXPORT int my_register_printf_specifier(x64emu_t* emu, int c, void* f1, void* f2)
{
    //TODO: defining a new sepcifier for printf, it should also be registered on myStackAlign/myStackAlignW, using f2 to get the type of arg
    return my->register_printf_specifier(c, findprintf_outputFct(f1), findprintf_arginfoFct(f2));
}

EXPORT int my_register_printf_type(x64emu_t* emu, void* f)
{
    //TODO: defining a new type, probably needs to also register that for myStackAlign stuffs
    return my->register_printf_type(findprintf_typeFct(f));
}

EXPORT __uint128_t my___udivti3(__uint128_t a, __uint128_t b)
{
    return a/b;
}

extern int box64_quit;
extern int box64_exit_code;
void endBox64();
static void* timed_exit_thread(void* a)
{
    // this is a workaround for some NVidia drivers on ARM64 that may freeze at exit
    // waiting on a pthread_cond_destroy
    usleep(5000000); // wait 5 seconds
    printf_log(LOG_DEBUG, "Too late, forced exit...\n");
    _exit(box64_exit_code); // force exit, something is wrong
}

void startTimedExit()
{
    static int started = 0;
    if(started)
        return;
    started = 1;
    pthread_t exit_thread;
    pthread_create(&exit_thread, NULL, timed_exit_thread, NULL);
}

EXPORT void my_exit(x64emu_t* emu, int code)
{
    if(emu->flags.quitonexit) {
        emu->quit = 1;
        R_EAX = code;
        emu->flags.quitonexit = 2;
        return;
    }
    emu->quit = 1;
    box64_exit_code = code;
    endBox64();
    startTimedExit();
    exit(code);
}

EXPORT void my__exit(x64emu_t* emu, int code)
{
    if(emu->flags.quitonexit || emu->quit) {
        _exit(code);
    }
    dynarec_log(LOG_INFO, "Fast _exit called\n");
    emu->quit = 1;
    box64_exit_code = code;
    SerializeAllMapping();   // just to be safe
    // then call all the fini

    _exit(code);
}

EXPORT int my_prctl(x64emu_t* emu, int option, unsigned long arg2, unsigned long arg3, unsigned long arg4, unsigned long arg5)
{
    if(option==PR_SET_NAME) {
        printf_log(LOG_DEBUG, "set process name to \"%s\"\n", (char*)arg2);
        ApplyEnvFileEntry((char*)arg2);
        size_t l = strlen((char*)arg2);
        if(l>4 && !strcasecmp((char*)arg2+l-4, ".exe")) {
            printf_log(LOG_DEBUG, "hacking orig command line to \"%s\"\n", (char*)arg2);
            strcpy(my_context->orig_argv[0], (char*)arg2);
        }
    }
    if(option==PR_SET_SECCOMP) {
        printf_log(LOG_DEBUG, "Ignoring prctl(PR_SET_SECCOMP, ...)\n");
        return 0;
    }
    if (option == PR_SET_SYSCALL_USER_DISPATCH) {
        long ret = my_syscall_user_dispatch_prctl(emu, arg2, arg3, arg4, (void*)arg5);
        if(ret < 0) {
            errno = -ret;
            return -1;
        }
        return 0;
    }
    return prctl(option, arg2, arg3, arg4, arg5);
}

EXPORT int my_pidfd_open(x64emu_t* emu, int pid, unsigned int flags)
{
    (void)emu;
#if defined(SYS_pidfd_open)
    return syscall(SYS_pidfd_open, pid, flags);
#elif defined(__NR_pidfd_open)
    return syscall(__NR_pidfd_open, pid, flags);
#else
    (void)pid;
    (void)flags;
    errno = ENOSYS;
    return -1;
#endif
}

EXPORT int my_pidfd_send_signal(x64emu_t* emu, int pidfd, int sig, siginfo_t* info, unsigned int flags)
{
    (void)emu;
    int hsig = signal_from_x64(sig);
    siginfo_t hinfo;
    siginfo_t* hptr = NULL;
    if(info) {
        memcpy(&hinfo, info, sizeof(hinfo));
        hinfo.si_signo = hsig;
        hptr = &hinfo;
    }
#if defined(SYS_pidfd_send_signal)
    return syscall(SYS_pidfd_send_signal, pidfd, hsig, hptr, flags);
#elif defined(__NR_pidfd_send_signal)
    return syscall(__NR_pidfd_send_signal, pidfd, hsig, hptr, flags);
#else
    errno = ENOSYS;
    return -1;
#endif
}

EXPORT int my_pidfd_getfd(x64emu_t* emu, int pidfd, int targetfd, unsigned int flags)
{
    (void)emu;
#if defined(SYS_pidfd_getfd)
    return syscall(SYS_pidfd_getfd, pidfd, targetfd, flags);
#elif defined(__NR_pidfd_getfd)
    return syscall(__NR_pidfd_getfd, pidfd, targetfd, flags);
#else
    (void)pidfd;
    (void)targetfd;
    (void)flags;
    errno = ENOSYS;
    return -1;
#endif
}


EXPORT int my_fsopen(x64emu_t* emu, const char* fs_name, unsigned int flags)
{
    (void)emu;
#if defined(SYS_fsopen)
    return syscall(SYS_fsopen, fs_name, flags);
#elif defined(__NR_fsopen)
    return syscall(__NR_fsopen, fs_name, flags);
#else
    (void)fs_name;
    (void)flags;
    errno = ENOSYS;
    return -1;
#endif
}

EXPORT int my_fsconfig(x64emu_t* emu, int fs_fd, unsigned int cmd, const char* key, const void* value, int aux)
{
    (void)emu;
#if defined(SYS_fsconfig)
    return syscall(SYS_fsconfig, fs_fd, cmd, key, value, aux);
#elif defined(__NR_fsconfig)
    return syscall(__NR_fsconfig, fs_fd, cmd, key, value, aux);
#else
    (void)fs_fd;
    (void)cmd;
    (void)key;
    (void)value;
    (void)aux;
    errno = ENOSYS;
    return -1;
#endif
}

EXPORT int my_fsmount(x64emu_t* emu, int fs_fd, unsigned int flags, unsigned int attr_flags)
{
    (void)emu;
#if defined(SYS_fsmount)
    return syscall(SYS_fsmount, fs_fd, flags, attr_flags);
#elif defined(__NR_fsmount)
    return syscall(__NR_fsmount, fs_fd, flags, attr_flags);
#else
    (void)fs_fd;
    (void)flags;
    (void)attr_flags;
    errno = ENOSYS;
    return -1;
#endif
}

EXPORT int my_fspick(x64emu_t* emu, int dfd, const char* path, unsigned int flags)
{
    (void)emu;
#if defined(SYS_fspick)
    return syscall(SYS_fspick, dfd, path, flags);
#elif defined(__NR_fspick)
    return syscall(__NR_fspick, dfd, path, flags);
#else
    (void)dfd;
    (void)path;
    (void)flags;
    errno = ENOSYS;
    return -1;
#endif
}

EXPORT int my_move_mount(x64emu_t* emu, int from_dfd, const char* from_path, int to_dfd, const char* to_path, unsigned int flags)
{
    (void)emu;
#if defined(SYS_move_mount)
    return syscall(SYS_move_mount, from_dfd, from_path, to_dfd, to_path, flags);
#elif defined(__NR_move_mount)
    return syscall(__NR_move_mount, from_dfd, from_path, to_dfd, to_path, flags);
#else
    (void)from_dfd;
    (void)from_path;
    (void)to_dfd;
    (void)to_path;
    (void)flags;
    errno = ENOSYS;
    return -1;
#endif
}

EXPORT int my_mount_setattr(x64emu_t* emu, int dfd, const char* path, unsigned int flags, void* attr, size_t size)
{
    (void)emu;
#if defined(SYS_mount_setattr)
    return syscall(SYS_mount_setattr, dfd, path, flags, attr, size);
#elif defined(__NR_mount_setattr)
    return syscall(__NR_mount_setattr, dfd, path, flags, attr, size);
#else
    (void)dfd;
    (void)path;
    (void)flags;
    (void)attr;
    (void)size;
    errno = ENOSYS;
    return -1;
#endif
}

size_t __attribute__((weak)) strlcpy(char* dest, const char* src, size_t len)
{
    size_t l = strlen(src);
    if(len) {
        strncpy(dest, src, len-1);
        dest[len-1]=0;
    }
    return l;
}
size_t __attribute__((weak)) __strlcpy_chk(char* dest, const char* src, size_t len, size_t chk)
{
    // in case it's not defined... create a weak version with no actual chk
    return strlcpy(dest, src, len);
}

__attribute__((weak)) uint32_t arc4random(void)
{
    return get_random32();
}

__attribute__((weak)) const char* strerrorname_np(int errnum)
{
    (void)errnum;
    return NULL;
}

__attribute__((weak)) int open_tree(int dfd, const char* path, unsigned int flags)
{
#ifdef SYS_open_tree
    return syscall(SYS_open_tree, dfd, path, flags);
#else
    errno = ENOSYS;
    return -1;
#endif
}

__attribute__((weak)) int dn_skipname(const unsigned char* ptr, const unsigned char* eom)
{
    (void)ptr;
    (void)eom;
    errno = ENOSYS;
    return -1;
}

#ifndef _SC_NPROCESSORS_ONLN
#define _SC_NPROCESSORS_ONLN    84
#endif
#ifndef _SC_NPROCESSORS_CONF
#define _SC_NPROCESSORS_CONF    83
#endif
EXPORT int my_getpagesize(x64emu_t* emu) {
    (void)emu;
    return X86_PAGE_SIZE;
}
EXPORT int my___getpagesize(x64emu_t* emu) __attribute__((alias("my_getpagesize")));

EXPORT long my_sysconf(x64emu_t* emu, int what) {
    if(what==_SC_PAGESIZE) {
        return X86_PAGE_SIZE;
    }
    if(what==_SC_NPROCESSORS_ONLN) {
        return box64_sysinfo.box64_ncpu;
    }
    if(what==_SC_NPROCESSORS_CONF) {
        return box64_sysinfo.box64_ncpu;
    }
    return sysconf(what);
}
EXPORT long my___sysconf(x64emu_t* emu, int what) __attribute__((alias("my_sysconf")));

EXPORT int my_sched_getaffinity(x64emu_t* emu, pid_t pid, size_t sz, cpu_set_t* mask)
{
    uint32_t skipcpu=0, maxcpu=0;
    if(pid && pid!=getpid()) {
        // another process, so check if it's a box64 compatible one
        int attached = 0;
        errno = 0;
        long test = ptrace(PTRACE_PEEKDATA, pid, (void*)0, NULL);
        if (test == -1 && errno == ESRCH) {
            if (ptrace(PTRACE_ATTACH, pid, NULL, NULL) != -1) {
                int status;
                if (waitpid(pid, &status, __WALL) != -1) {
                    if(isProcessBox64(pid)) {
                        // gather other process skip & maxcpu
                        skipcpu = ptrace(PTRACE_PEEKDATA, pid, &BOX64ENV(skipcpu), NULL);
                        maxcpu = ptrace(PTRACE_PEEKDATA, pid, &BOX64ENV(maxcpu), NULL);
                    }
                }
                ptrace(PTRACE_DETACH, pid, NULL, NULL);
            }
        } else if(errno==0) {
            if(isProcessBox64(pid)) {
                // gather other process skip & maxcpu
                skipcpu = ptrace(PTRACE_PEEKDATA, pid, &BOX64ENV(skipcpu), NULL);
                maxcpu = ptrace(PTRACE_PEEKDATA, pid, &BOX64ENV(maxcpu), NULL);
            }
        }
    } else {
        skipcpu = BOX64ENV(skipcpu);
        maxcpu = BOX64ENV(maxcpu);
    }
    if(!skipcpu)
        return sched_getaffinity(pid, sz, mask);
    uint8_t mask_[sz];
    int ret = sched_getaffinity(pid, sz, (cpu_set_t*)mask_);
    if(ret>=0) {
        cpumask_shiftright(mask_, sz, skipcpu);
        cpumask_maxcpu(mask, sz, maxcpu);
        memcpy(mask, mask_, sz);
    }
    return ret;
}
EXPORT int my_sched_getcpu(x64emu_t* emu)
{
    int ret = sched_getcpu();
    if(ret>=0 && BOX64ENV(skipcpu)) {
        ret -= BOX64ENV(skipcpu);
        if(ret<0) ret = 0;
    }
    return ret;
}
EXPORT int my_sched_setaffinity(x64emu_t* emu, pid_t pid, size_t sz, cpu_set_t* mask)
{
    uint32_t skipcpu=0, maxcpu=0;
    if(pid && pid!=getpid()) {
        // another process, so check if it's a box64 compatible one
        int attached = 0;
        errno = 0;
        long test = ptrace(PTRACE_PEEKDATA, pid, (void*)0, NULL);
        if (test == -1 && errno == ESRCH) {
            if (ptrace(PTRACE_ATTACH, pid, NULL, NULL) != -1) {
                int status;
                if (waitpid(pid, &status, __WALL) != -1) {
                    if(isProcessBox64(pid)) {
                        // gather other process skip & maxcpu
                        skipcpu = ptrace(PTRACE_PEEKDATA, pid, &BOX64ENV(skipcpu), NULL);
                        maxcpu = ptrace(PTRACE_PEEKDATA, pid, &BOX64ENV(maxcpu), NULL);
                    }
                }
                ptrace(PTRACE_DETACH, pid, NULL, NULL);
            }
        } else if(errno==0) {
            if(isProcessBox64(pid)) {
                // gather other process skip & maxcpu
                skipcpu = ptrace(PTRACE_PEEKDATA, pid, &BOX64ENV(skipcpu), NULL);
                maxcpu = ptrace(PTRACE_PEEKDATA, pid, &BOX64ENV(maxcpu), NULL);
            }
        }
    } else {
        skipcpu = BOX64ENV(skipcpu);
        maxcpu = BOX64ENV(maxcpu);
    }
    if(!skipcpu)
        return sched_setaffinity(pid, sz, mask);
    uint8_t mask_[sz];
    memcpy(mask_, mask, sz);
    cpumask_maxcpu(mask, sz, maxcpu);
    cpumask_shiftleft(mask_, sz, skipcpu);
    return sched_setaffinity(pid, sz, (cpu_set_t*)mask_);
}

EXPORT char* my___progname = NULL;
EXPORT char* my___progname_full = NULL;
EXPORT char* my_program_invocation_name = NULL;
EXPORT char* my_program_invocation_short_name = NULL;

// ignoring this for now
EXPORT char my___libc_single_threaded = 0;

EXPORT char* secure_getenv(const char* name)
{
    // ignoring the "secure" part for now
    //TODO: better handling of user and process ID
    return getenv(name);
}

#ifdef STATICBUILD
#include "libtools/static_libc.h"
#endif

#ifndef STATICBUILD
#define PRE_INIT\
    if(1)                                                      \
        lib->w.lib = dlopen(NULL, RTLD_LAZY | RTLD_GLOBAL);    \
    else
#endif

#if defined(ANDROID)
#ifdef STATICBUILD
#define NEEDED_LIBS_DEF   3,\
    "libpthread.so",        \
    "libdl.so" ,            \
    "libm.so"
#define NEEDED_LIBS_234 3,  \
    "libpthread.so",        \
    "libdl.so" ,            \
    "libm.so"
#else
#define NEEDED_LIBS_DEF   4,\
    "libpthread.so",        \
    "libdl.so" ,            \
    "libm.so",              \
    "libbsd.so"
#define NEEDED_LIBS_234 4,  \
    "libpthread.so",        \
    "libdl.so" ,            \
    "libm.so",              \
    "libbsd.so"
#endif
#else
#ifdef STATICBUILD
#define NEEDED_LIBS_DEF   5,\
    "ld-linux-x86-64.so.2", \
    "libpthread.so.0",      \
    "libdl.so.2",           \
    "libutil.so.1",         \
    "librt.so.1"
#define NEEDED_LIBS_234 6,  \
    "ld-linux-x86-64.so.2", \
    "libpthread.so.0",      \
    "libdl.so.2",           \
    "libutil.so.1",         \
    "libresolv.so.2",       \
    "librt.so.1"
#else
#define NEEDED_LIBS_DEF   6,\
    "ld-linux-x86-64.so.2", \
    "libpthread.so.0",      \
    "libdl.so.2",           \
    "libutil.so.1",         \
    "librt.so.1",           \
    "libbsd.so.0"
#define NEEDED_LIBS_234 7,  \
    "ld-linux-x86-64.so.2", \
    "libpthread.so.0",      \
    "libdl.so.2",           \
    "libutil.so.1",         \
    "libresolv.so.2",       \
    "librt.so.1",           \
    "libbsd.so.0"
#endif
#endif

#undef HAS_MY

#define CUSTOM_INIT         \
    box64->libclib = lib;   \
    /*InitCpuModel();*/         \
    ctSetup();              \
    obstackSetup();         \
    my_environ = my__environ = my___environ = box64->envv;                      \
    my___progname_full = my_program_invocation_name = box64->argv[0];           \
    my___progname = my_program_invocation_short_name =                          \
        (strrchr(box64->argv[0], '/') ? strrchr(box64->argv[0], '/') + 1 : box64->argv[0]); \
    getMy(lib);                                                                 \
    if(box64_isglibc234)                                                        \
        setNeededLibs(lib, NEEDED_LIBS_234);                                    \
    else                                                                        \
        setNeededLibs(lib, NEEDED_LIBS_DEF);

#define CUSTOM_FINI \
    freeMy();       \
    return;     // do not unload...

#include "wrappedlib_init.h"
