mirror of
https://github.com/ptitSeb/box86.git
synced 2026-11-12 18:57:06 -10:00
1614 lines
58 KiB
C
Executable File
1614 lines
58 KiB
C
Executable File
#ifndef __USE_FILE_OFFSET64
|
|
#define __USE_FILE_OFFSET64
|
|
#endif
|
|
#ifndef __USE_LARGEFILE64
|
|
#define __USE_LARGEFILE64
|
|
#endif
|
|
#ifndef _LARGEFILE64_SOURCE
|
|
#define _LARGEFILE64_SOURCE
|
|
#endif
|
|
#ifndef _FILE_OFFSET_BIT
|
|
#define _FILE_OFFSET_BIT 64
|
|
#endif
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <time.h>
|
|
#include <limits.h>
|
|
#include <errno.h>
|
|
#include <unistd.h>
|
|
#include <dirent.h>
|
|
#include <signal.h>
|
|
#include <sys/syscall.h>
|
|
#include <sys/prctl.h>
|
|
#ifdef DYNAREC
|
|
#ifdef ARM
|
|
#include <linux/auxvec.h>
|
|
#include <asm/hwcap.h>
|
|
#endif
|
|
#endif
|
|
|
|
#include "build_info.h"
|
|
#include "debug.h"
|
|
#include "box86context.h"
|
|
#include "fileutils.h"
|
|
#include "elfloader.h"
|
|
#include "box86stack.h"
|
|
#include "x86emu.h"
|
|
#include "x86run.h"
|
|
#include "x86trace.h"
|
|
#include "librarian.h"
|
|
#include "library.h"
|
|
#include "auxval.h"
|
|
#include "wine_tools.h"
|
|
|
|
box86context_t *my_context = NULL;
|
|
int box86_log = LOG_NONE;
|
|
int box86_dump = 0;
|
|
int box86_nobanner = 0;
|
|
int box86_dynarec_log = LOG_NONE;
|
|
int box86_pagesize;
|
|
uintptr_t box86_load_addr = 0;
|
|
int box86_backtrace = 0;
|
|
#ifdef DYNAREC
|
|
int box86_dynarec = 1;
|
|
int box86_dynarec_dump = 0;
|
|
int box86_dynarec_forced = 0;
|
|
int box86_dynarec_largest = 0;
|
|
int box86_dynarec_bigblock = 1;
|
|
int box86_dynarec_strongmem = 0;
|
|
int box86_dynarec_x87double = 0;
|
|
int box86_dynarec_fastnan = 1;
|
|
int box86_dynarec_safeflags = 1;
|
|
uintptr_t box86_nodynarec_start = 0;
|
|
uintptr_t box86_nodynarec_end = 0;
|
|
#ifdef ARM
|
|
int arm_vfp = 0; // vfp version (3 or 4), with 32 registers is mendatory
|
|
int arm_swap = 0;
|
|
int arm_div = 0;
|
|
int arm_v8 = 0;
|
|
#endif
|
|
#else //DYNAREC
|
|
int box86_dynarec = 0;
|
|
#endif
|
|
int dlsym_error = 0;
|
|
int cycle_log = 0;
|
|
int trace_xmm = 0;
|
|
int trace_emm = 0;
|
|
#ifdef HAVE_TRACE
|
|
uint64_t start_cnt = 0;
|
|
#ifdef DYNAREC
|
|
int box86_dynarec_trace = 0;
|
|
#endif
|
|
#endif
|
|
#ifdef PANDORA
|
|
int x11color16 = 0;
|
|
#endif
|
|
#if defined(RPI) || defined(RK3399) || defined(RK3288) || defined(GOA_CLONE) || defined(PYRA) || defined(PANDORA)
|
|
int box86_tokitori2 = 0;
|
|
#endif
|
|
int box86_sc3u = 0;
|
|
int box86_mapclean = 0;
|
|
int box86_zoom = 0;
|
|
int x11threads = 0;
|
|
int x11glx = 1;
|
|
int allow_missing_libs = 0;
|
|
int allow_missing_symbols = 0;
|
|
int fix_64bit_inodes = 0;
|
|
int box86_prefer_wrapped = 0;
|
|
int box86_prefer_emulated = 0;
|
|
int box86_steam = 0;
|
|
int box86_wine = 0;
|
|
int box86_nopulse = 0;
|
|
int box86_nogtk = 0;
|
|
int box86_novulkan = 0;
|
|
int box86_showsegv = 0;
|
|
char* libGL = NULL;
|
|
uintptr_t trace_start = 0, trace_end = 0;
|
|
char* trace_func = NULL;
|
|
uint32_t default_fs = 0;
|
|
int jit_gdb = 0;
|
|
int box86_tcmalloc_minimal = 0;
|
|
|
|
FILE* ftrace = NULL;
|
|
int ftrace_has_pid = 0;
|
|
|
|
void openFTrace()
|
|
{
|
|
char* t = getenv("BOX86_TRACE_FILE");
|
|
char tmp[500];
|
|
char* p = t;
|
|
if(p && strstr(t, "%pid")) {
|
|
int next = 0;
|
|
do {
|
|
strcpy(tmp, p);
|
|
char* c = strstr(tmp, "%pid");
|
|
*c = 0; // cut
|
|
char pid[16];
|
|
if(next)
|
|
sprintf(pid, "%d-%d", getpid(), next);
|
|
else
|
|
sprintf(pid, "%d", getpid());
|
|
strcat(tmp, pid);
|
|
c = strstr(p, "%pid") + strlen("%pid");
|
|
strcat(tmp, c);
|
|
++next;
|
|
} while (FileExist(tmp, IS_FILE));
|
|
p = tmp;
|
|
ftrace_has_pid = 1;
|
|
}
|
|
if(p) {
|
|
if(!strcmp(p, "stderr"))
|
|
ftrace = stderr;
|
|
else {
|
|
ftrace = fopen64(p, "w");
|
|
if(!ftrace) {
|
|
ftrace = stdout;
|
|
printf_log(LOG_INFO, "Cannot open trace file \"%s\" for writing (error=%s)\n", p, strerror(errno));
|
|
} else {
|
|
if(!box86_nobanner)
|
|
printf("BOX86 Trace redirected to \"%s\"\n", p);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void my_child_fork()
|
|
{
|
|
if(ftrace_has_pid) {
|
|
// open a new ftrace...
|
|
fclose(ftrace);
|
|
openFTrace();
|
|
}
|
|
}
|
|
|
|
#ifdef DYNAREC
|
|
int getNCpu();
|
|
void GatherDynarecExtensions()
|
|
{
|
|
if(box86_dynarec==0) // no need to check if no dynarec
|
|
return;
|
|
#ifdef ARM
|
|
unsigned long hwcap = real_getauxval(AT_HWCAP);
|
|
if(!hwcap) // no HWCap: provide a default...
|
|
hwcap = HWCAP_HALF|HWCAP_FAST_MULT|HWCAP_EDSP|HWCAP_NEON|HWCAP_VFPv3;
|
|
// first, check all needed extensions, lif half, edsp and fastmult
|
|
if((hwcap&HWCAP_HALF) == 0) {
|
|
printf_log(LOG_INFO, "Missing HALF cpu support, disabling Dynarec\n");
|
|
box86_dynarec=0;
|
|
return;
|
|
}
|
|
if((hwcap&HWCAP_FAST_MULT) == 0) {
|
|
printf_log(LOG_INFO, "Missing FAST_MULT cpu support, disabling Dynarec\n");
|
|
box86_dynarec=0;
|
|
return;
|
|
}
|
|
if((hwcap&HWCAP_EDSP) == 0) {
|
|
printf_log(LOG_INFO, "Missing EDSP cpu support, disabling Dynarec\n");
|
|
box86_dynarec=0;
|
|
return;
|
|
}
|
|
if((hwcap&HWCAP_NEON) == 0) {
|
|
printf_log(LOG_INFO, "Missing NEON support, disabling Dynarec\n");
|
|
box86_dynarec=0;
|
|
return;
|
|
}
|
|
if(hwcap&HWCAP_VFPv3D16) {
|
|
printf_log(LOG_INFO, "VFPU only have 16 registers, disabling Dynarec\n");
|
|
box86_dynarec=0;
|
|
return;
|
|
}
|
|
if(hwcap&HWCAP_VFPv3)
|
|
arm_vfp = 3;
|
|
if(hwcap&HWCAP_VFPv4)
|
|
arm_vfp = 4;
|
|
if(!arm_vfp) {
|
|
printf_log(LOG_INFO, "VFPUv3+ not detected, disabling Dynarec\n");
|
|
box86_dynarec=0;
|
|
return;
|
|
}
|
|
if(hwcap&HWCAP_SWP)
|
|
arm_swap = 1;
|
|
if(hwcap&HWCAP_IDIVA)
|
|
arm_div = 1;
|
|
#ifdef AT_HWCAP2
|
|
unsigned long hwcap2 = real_getauxval(AT_HWCAP2);
|
|
if((hwcap2&HWCAP2_AES) || (hwcap2&HWCAP2_CRC32))
|
|
arm_v8 = 1;
|
|
#endif
|
|
printf_log(LOG_INFO, "Dynarec for ARMv%d, with extension: HALF FAST_MULT EDSP NEON VFPv%d", 7+arm_v8, arm_vfp);
|
|
if(arm_swap)
|
|
printf_log(LOG_INFO, " SWP");
|
|
if(arm_div)
|
|
printf_log(LOG_INFO, " IDIVA");
|
|
|
|
printf_log(LOG_INFO, " PageSize:%d", box86_pagesize);
|
|
int ncpu = getNCpu();
|
|
printf_log(LOG_INFO, " Cores:%d\n", ncpu);
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
EXPORTDYN
|
|
void LoadLogEnv()
|
|
{
|
|
ftrace = stdout;
|
|
box86_nobanner = isatty(fileno(stdout))?0:1;
|
|
const char *p = getenv("BOX86_NOBANNER");
|
|
if(p) {
|
|
if(strlen(p)==1) {
|
|
if(p[0]>='0' && p[0]<='1')
|
|
box86_nobanner = p[0]-'0';
|
|
}
|
|
}
|
|
// grab BOX86_TRACE_FILE envvar, and change %pid to actual pid is present in the name
|
|
openFTrace();
|
|
//box86_log = isatty(fileno(ftrace))?LOG_INFO:LOG_NONE; //default LOG value different if stdout is redirected or not
|
|
p = getenv("BOX86_LOG");
|
|
if(p) {
|
|
if(strlen(p)==1) {
|
|
if(p[0]>='0'+LOG_NONE && p[0]<='0'+LOG_NEVER) {
|
|
box86_log = p[0]-'0';
|
|
if(box86_log == LOG_NEVER) {
|
|
--box86_log;
|
|
box86_dump = 1;
|
|
}
|
|
}
|
|
} else {
|
|
if(!strcasecmp(p, "NONE"))
|
|
box86_log = LOG_NONE;
|
|
else if(!strcasecmp(p, "INFO"))
|
|
box86_log = LOG_INFO;
|
|
else if(!strcasecmp(p, "DEBUG"))
|
|
box86_log = LOG_DEBUG;
|
|
else if(!strcasecmp(p, "DUMP")) {
|
|
box86_log = LOG_DEBUG;
|
|
box86_dump = 1;
|
|
}
|
|
}
|
|
if(!box86_nobanner)
|
|
printf_log(LOG_INFO, "Debug level is %d\n", box86_log);
|
|
}
|
|
p = getenv("BOX86_ROLLING_LOG");
|
|
if(p) {
|
|
int cycle = 0;
|
|
if(sscanf(p, "%d", &cycle)==1)
|
|
cycle_log = cycle;
|
|
if(cycle_log==1)
|
|
cycle_log = 16;
|
|
if(cycle_log<0)
|
|
cycle_log = 0;
|
|
if(cycle_log && box86_log>LOG_INFO) {
|
|
cycle_log = 0;
|
|
printf_log(LOG_NONE, "Incompatible Rolling log and Debug Log, disabling Rolling log\n");
|
|
}
|
|
}
|
|
if(!box86_nobanner && cycle_log)
|
|
printf_log(LOG_INFO, "Rolling log, showing last %d function call on signals\n", cycle_log);
|
|
p = getenv("BOX86_DUMP");
|
|
if(p) {
|
|
if(strlen(p)==1) {
|
|
if(p[0]>='0' && p[0]<='1')
|
|
box86_dump = p[0]-'0';
|
|
}
|
|
}
|
|
if(!box86_nobanner && box86_dump)
|
|
printf_log(LOG_INFO, "Elf Dump if ON\n");
|
|
#ifdef DYNAREC
|
|
p = getenv("BOX86_DYNAREC_DUMP");
|
|
if(p) {
|
|
if(strlen(p)==1) {
|
|
if(p[0]>='0' && p[0]<='2')
|
|
box86_dynarec_dump = p[0]-'0';
|
|
}
|
|
if (box86_dynarec_dump) printf_log(LOG_INFO, "Dynarec blocks are dumped%s\n", (box86_dynarec_dump>1)?" in color":"");
|
|
}
|
|
p = getenv("BOX86_DYNAREC_LOG");
|
|
if(p) {
|
|
if(strlen(p)==1) {
|
|
if((p[0]>='0'+LOG_NONE) && (p[0]<='0'+LOG_VERBOSE))
|
|
box86_dynarec_log = p[0]-'0';
|
|
} else {
|
|
if(!strcasecmp(p, "NONE"))
|
|
box86_dynarec_log = LOG_NONE;
|
|
else if(!strcasecmp(p, "INFO"))
|
|
box86_dynarec_log = LOG_INFO;
|
|
else if(!strcasecmp(p, "DEBUG"))
|
|
box86_dynarec_log = LOG_DEBUG;
|
|
else if(!strcasecmp(p, "VERBOSE"))
|
|
box86_dynarec_log = LOG_VERBOSE;
|
|
}
|
|
printf_log(LOG_INFO, "Dynarec log level is %d\n", box86_dynarec_log);
|
|
}
|
|
p = getenv("BOX86_DYNAREC");
|
|
if(p) {
|
|
if(strlen(p)==1) {
|
|
if(p[0]>='0' && p[0]<='1')
|
|
box86_dynarec = p[0]-'0';
|
|
}
|
|
printf_log(LOG_INFO, "Dynarec is %s\n", box86_dynarec?"on":"off");
|
|
}
|
|
p = getenv("BOX86_DYNAREC_FORCED");
|
|
if(p) {
|
|
if(strlen(p)==1) {
|
|
if(p[0]>='0' && p[0]<='1')
|
|
box86_dynarec_forced = p[0]-'0';
|
|
}
|
|
if(box86_dynarec_forced)
|
|
printf_log(LOG_INFO, "Dynarec is forced on all addresses\n");
|
|
}
|
|
p = getenv("BOX86_DYNAREC_BIGBLOCK");
|
|
if(p) {
|
|
if(strlen(p)==1) {
|
|
if(p[0]>='0' && p[0]<='2')
|
|
box86_dynarec_bigblock = p[0]-'0';
|
|
}
|
|
if(!box86_dynarec_bigblock)
|
|
printf_log(LOG_INFO, "Dynarec will not try to make big blocks\n");
|
|
else if (box86_dynarec_bigblock>1)
|
|
printf_log(LOG_INFO, "Dynarec will try to make bigger blocks\n");
|
|
}
|
|
p = getenv("BOX86_DYNAREC_STRONGMEM");
|
|
if(p) {
|
|
if(strlen(p)==1) {
|
|
if(p[0]>='0' && p[0]<='2')
|
|
box86_dynarec_strongmem = p[0]-'0';
|
|
}
|
|
if(box86_dynarec_strongmem)
|
|
printf_log(LOG_INFO, "Dynarec will try to emulate a strong memory model%s\n", (box86_dynarec_strongmem==1)?" with limited performace loss":"");
|
|
}
|
|
p = getenv("BOX86_DYNAREC_X87DOUBLE");
|
|
if(p) {
|
|
if(strlen(p)==1) {
|
|
if(p[0]>='0' && p[0]<='1')
|
|
box86_dynarec_x87double = p[0]-'0';
|
|
}
|
|
if(box86_dynarec_x87double)
|
|
printf_log(LOG_INFO, "Dynarec will use only double for x87 emulation\n");
|
|
}
|
|
p = getenv("BOX86_DYNAREC_FASTNAN");
|
|
if(p) {
|
|
if(strlen(p)==1) {
|
|
if(p[0]>='0' && p[0]<='1')
|
|
box86_dynarec_fastnan = p[0]-'0';
|
|
}
|
|
if(!box86_dynarec_fastnan)
|
|
printf_log(LOG_INFO, "Dynarec will try to normalize generated NAN\n");
|
|
}
|
|
p = getenv("BOX86_DYNAREC_SAFEFLAGS");
|
|
if(p) {
|
|
if(strlen(p)==1) {
|
|
if(p[0]>='0' && p[0]<='2')
|
|
box86_dynarec_safeflags = p[0]-'0';
|
|
}
|
|
if(!box86_dynarec_safeflags)
|
|
printf_log(LOG_INFO, "Dynarec will not play it safe with x86 flags\n");
|
|
else
|
|
printf_log(LOG_INFO, "Dynarec will play %s safe with x86 flags\n", (box86_dynarec_safeflags==1)?"moderatly":"it");
|
|
}
|
|
p = getenv("BOX86_NODYNAREC");
|
|
if(p) {
|
|
if (strchr(p,'-')) {
|
|
if(sscanf(p, "%d-%d", &box86_nodynarec_start, &box86_nodynarec_end)!=2) {
|
|
if(sscanf(p, "0x%X-0x%X", &box86_nodynarec_start, &box86_nodynarec_end)!=2)
|
|
sscanf(p, "%x-%x", &box86_nodynarec_start, &box86_nodynarec_end);
|
|
}
|
|
printf_log(LOG_INFO, "No Dynablock creation that start in %p - %p range\n", (void*)box86_nodynarec_start, (void*)box86_nodynarec_end);
|
|
}
|
|
}
|
|
|
|
#endif
|
|
#ifdef HAVE_TRACE
|
|
p = getenv("BOX86_TRACE_XMM");
|
|
if(p) {
|
|
if(strlen(p)==1) {
|
|
if(p[0]>='0' && p[0]<='0'+1)
|
|
trace_xmm = p[0]-'0';
|
|
}
|
|
}
|
|
p = getenv("BOX86_TRACE_EMM");
|
|
if(p) {
|
|
if(strlen(p)==1) {
|
|
if(p[0]>='0' && p[0]<='0'+1)
|
|
trace_emm = p[0]-'0';
|
|
}
|
|
}
|
|
p = getenv("BOX86_TRACE_START");
|
|
if(p) {
|
|
char* p2;
|
|
start_cnt = strtoll(p, &p2, 10);
|
|
printf_log(LOG_INFO, "Will start trace only after %llu instructions\n", start_cnt);
|
|
}
|
|
#ifdef DYNAREC
|
|
p = getenv("BOX86_DYNAREC_TRACE");
|
|
if(p) {
|
|
if(strlen(p)==1) {
|
|
if(p[0]>='0' && p[0]<='0'+1)
|
|
box86_dynarec_trace = p[0]-'0';
|
|
if(box86_dynarec_trace)
|
|
printf_log(LOG_INFO, "Dynarec generated code will also print a trace\n");
|
|
}
|
|
}
|
|
#endif
|
|
#endif
|
|
// Other BOX86 env. var.
|
|
p = getenv("BOX86_LOAD_ADDR");
|
|
if(p) {
|
|
if(sscanf(p, "0x%zx", &box86_load_addr)!=1)
|
|
box86_load_addr = 0;
|
|
if(box86_load_addr)
|
|
printf_log(LOG_INFO, "Use a starting load address of %p\n", (void*)box86_load_addr);
|
|
}
|
|
p = getenv("BOX86_DLSYM_ERROR");
|
|
if(p) {
|
|
if(strlen(p)==1) {
|
|
if(p[0]>='0' && p[0]<='0'+1)
|
|
dlsym_error = p[0]-'0';
|
|
}
|
|
printf_log(LOG_INFO, "Shows details of dlopen / dlsym /dlclose : %s\n", dlsym_error?"Yes":"No");
|
|
}
|
|
#ifdef PANDORA
|
|
p = getenv("BOX86_X11COLOR16");
|
|
if(p) {
|
|
if(strlen(p)==1) {
|
|
if(p[0]>='0' && p[0]<='0'+1)
|
|
x11color16 = p[0]-'0';
|
|
}
|
|
printf_log(LOG_INFO, "Try to adjust X11 Color (32->16bits) : %s\n", x11color16?"Yes":"No");
|
|
}
|
|
#endif
|
|
p = getenv("BOX86_X11THREADS");
|
|
if(p) {
|
|
if(strlen(p)==1) {
|
|
if(p[0]>='0' && p[0]<='0'+1)
|
|
x11threads = p[0]-'0';
|
|
}
|
|
if(x11threads)
|
|
printf_log(LOG_INFO, "Try to Call XInitThreads if libX11 is loaded\n");
|
|
}
|
|
p = getenv("BOX86_X11GLX");
|
|
if(p) {
|
|
if(strlen(p)==1) {
|
|
if(p[0]>='0' && p[0]<='0'+1)
|
|
x11glx = p[0]-'0';
|
|
}
|
|
if(x11glx)
|
|
printf_log(LOG_INFO, "Hack to force libX11 GLX extension present\n");
|
|
else
|
|
printf_log(LOG_INFO, "Disabled Hack to force libX11 GLX extension present\n");
|
|
}
|
|
p = getenv("BOX86_LIBGL");
|
|
if(p)
|
|
libGL = box_strdup(p);
|
|
if(!libGL) {
|
|
p = getenv("SDL_VIDEO_GL_DRIVER");
|
|
if(p)
|
|
libGL = box_strdup(p);
|
|
}
|
|
if(libGL) {
|
|
printf_log(LOG_INFO, "BOX86 using \"%s\" as libGL.so.1\n", p);
|
|
}
|
|
p = getenv("BOX86_ALLOWMISSING_LIBS");
|
|
if(!p)
|
|
p = getenv("BOX86_ALLOWMISSINGLIBS");
|
|
if(p) {
|
|
if(strlen(p)==1) {
|
|
if(p[0]>='0' && p[0]<='0'+1)
|
|
allow_missing_libs = p[0]-'0';
|
|
}
|
|
if(allow_missing_libs)
|
|
printf_log(LOG_INFO, "Allow missing needed libs\n");
|
|
}
|
|
p = getenv("BOX86_ALLOWMISSING_SYMBOLS");
|
|
if(p) {
|
|
if(strlen(p)==1) {
|
|
if(p[0]>='0' && p[0]<='0'+1)
|
|
allow_missing_symbols = p[0]-'0';
|
|
}
|
|
if(allow_missing_symbols)
|
|
printf_log(LOG_INFO, "Allow missing needed symbols\n");
|
|
}
|
|
p = getenv("BOX86_NOPULSE");
|
|
if(p) {
|
|
if(strlen(p)==1) {
|
|
if(p[0]>='0' && p[0]<='0'+1)
|
|
box86_nopulse = p[0]-'0';
|
|
}
|
|
if(box86_nopulse)
|
|
printf_log(LOG_INFO, "Disable the use of pulseaudio libs\n");
|
|
}
|
|
p = getenv("BOX86_NOGTK");
|
|
if(p) {
|
|
if(strlen(p)==1) {
|
|
if(p[0]>='0' && p[0]<='0'+1)
|
|
box86_nogtk = p[0]-'0';
|
|
}
|
|
if(box86_nogtk)
|
|
printf_log(LOG_INFO, "Disable the use of wrapped gtk libs\n");
|
|
}
|
|
p = getenv("BOX86_NOVULKAN");
|
|
if(p) {
|
|
if(strlen(p)==1) {
|
|
if(p[0]>='0' && p[0]<='0'+1)
|
|
box86_novulkan = p[0]-'0';
|
|
}
|
|
if(box86_novulkan)
|
|
printf_log(LOG_INFO, "Disable the use of wrapped vulkan libs\n");
|
|
}
|
|
p = getenv("BOX86_FIX_64BIT_INODES");
|
|
if(p) {
|
|
if(strlen(p)==1) {
|
|
if(p[0]>='0' && p[0]<='0'+1)
|
|
fix_64bit_inodes = p[0]-'0';
|
|
}
|
|
if(fix_64bit_inodes)
|
|
printf_log(LOG_INFO, "Fix 64bit inodes\n");
|
|
}
|
|
p = getenv("BOX86_JITGDB");
|
|
if(p) {
|
|
if(strlen(p)==1) {
|
|
if(p[0]>='0' && p[0]<='0'+2)
|
|
jit_gdb = p[0]-'0';
|
|
}
|
|
if(jit_gdb)
|
|
printf_log(LOG_INFO, "Launch %s on segfault\n", (jit_gdb==2)?"gdbserver":"gdb");
|
|
}
|
|
p = getenv("BOX86_SHOWSEGV");
|
|
if(p) {
|
|
if(strlen(p)==1) {
|
|
if(p[0]>='0' && p[0]<='0'+1)
|
|
box86_showsegv = p[0]-'0';
|
|
}
|
|
if(box86_showsegv)
|
|
printf_log(LOG_INFO, "Show Segfault signal even if a signal handler is present\n");
|
|
}
|
|
p = getenv("BOX86_BACKTRACE");
|
|
if(p) {
|
|
if(strlen(p)==1) {
|
|
if(p[0]>='0' && p[0]<='0'+1)
|
|
box86_backtrace = p[0]-'0';
|
|
}
|
|
if(box86_backtrace)
|
|
printf_log(LOG_INFO, "Show Backtrace for signals\n");
|
|
}
|
|
box86_pagesize = sysconf(_SC_PAGESIZE);
|
|
if(!box86_pagesize)
|
|
box86_pagesize = 4096;
|
|
#ifdef DYNAREC
|
|
GatherDynarecExtensions();
|
|
#endif
|
|
}
|
|
|
|
EXPORTDYN
|
|
void LoadEnvPath(path_collection_t *col, const char* defpath, const char* env)
|
|
{
|
|
const char* p = getenv(env);
|
|
if(p) {
|
|
printf_log(LOG_INFO, "%s: ", env);
|
|
ParseList(p, col, 1);
|
|
} else {
|
|
printf_log(LOG_INFO, "Using default %s: ", env);
|
|
ParseList(defpath, col, 1);
|
|
}
|
|
if(LOG_INFO<=box86_log) {
|
|
for(int i=0; i<col->size; i++)
|
|
printf_log(LOG_INFO, "%s%s", col->paths[i], (i==col->size-1)?"\n":":");
|
|
}
|
|
}
|
|
|
|
EXPORTDYN
|
|
int CountEnv(char** env)
|
|
{
|
|
// count, but remove all BOX86_* environnement
|
|
// also remove PATH and LD_LIBRARY_PATH
|
|
// but add 2 for default BOX86_PATH and BOX86_LD_LIBRARY_PATH
|
|
char** p = env;
|
|
int c = 0;
|
|
while(*p) {
|
|
if(strncmp(*p, "BOX86_", 6)!=0)
|
|
//if(!(strncmp(*p, "PATH=", 5)==0 || strncmp(*p, "LD_LIBRARY_PATH=", 16)==0))
|
|
++c;
|
|
++p;
|
|
}
|
|
return c+2;
|
|
}
|
|
EXPORTDYN
|
|
int GatherEnv(char*** dest, char** env, const char* prog)
|
|
{
|
|
// Add all but BOX86_* environnement
|
|
// but add 2 for default BOX86_PATH and BOX86_LD_LIBRARY_PATH
|
|
char** p = env;
|
|
int idx = 0;
|
|
int box86_path = 0;
|
|
int box86_ld_path = 0;
|
|
char* path = NULL;
|
|
char* ld_path = NULL;
|
|
while(*p) {
|
|
if(strncmp(*p, "BOX86_PATH=", 11)==0) {
|
|
(*dest)[idx++] = box_strdup(*p);
|
|
box86_path = 1;
|
|
} else if(strncmp(*p, "BOX86_LD_LIBRARY_PATH=", 22)==0) {
|
|
(*dest)[idx++] = box_strdup(*p);
|
|
box86_ld_path = 1;
|
|
} else if(strncmp(*p, "_=", 2)==0) {
|
|
/*int l = strlen(prog);
|
|
char tmp[l+3];
|
|
strcpy(tmp, "_=");
|
|
strcat(tmp, prog);
|
|
(*dest)[idx++] = box_strdup(tmp);*/
|
|
} else if(strncmp(*p, "BOX86_", 6)!=0) {
|
|
(*dest)[idx++] = box_strdup(*p);
|
|
/*if(!(strncmp(*p, "PATH=", 5)==0 || strncmp(*p, "LD_LIBRARY_PATH=", 16)==0)) {
|
|
}*/
|
|
}
|
|
++p;
|
|
}
|
|
// update the calloc of envv when adding new variables here
|
|
if(!box86_path) {
|
|
(*dest)[idx++] = box_strdup("BOX86_PATH=.:bin");
|
|
}
|
|
if(!box86_ld_path) {
|
|
(*dest)[idx++] = box_strdup("BOX86_LD_LIBRARY_PATH=.:lib");
|
|
}
|
|
// add "_=prog" at the end...
|
|
if(prog) {
|
|
int l = strlen(prog);
|
|
char tmp[l+3];
|
|
strcpy(tmp, "_=");
|
|
strcat(tmp, prog);
|
|
(*dest)[idx++] = box_strdup(tmp);
|
|
}
|
|
// and a final NULL
|
|
(*dest)[idx++] = 0;
|
|
return 0;
|
|
}
|
|
|
|
|
|
void PrintHelp() {
|
|
printf("\n\nThis is Box86, the Linux x86 emulator with a twist\n");
|
|
printf("\nUsage is box86 [options] path/to/software [args]\n");
|
|
printf("to launch x86 software\n");
|
|
printf(" options can be :\n");
|
|
printf(" '-v'|'--version' to print box86 version and quit\n");
|
|
printf(" '-h'|'--help' to print box86 help and quit\n");
|
|
printf("You can also set some environment variables:\n");
|
|
printf(" BOX86_PATH is the box86 version of PATH (default is '.:bin')\n");
|
|
printf(" BOX86_LD_LIBRARY_PATH is the box86 version LD_LIBRARY_PATH (default is '.:lib')\n");
|
|
printf(" BOX86_LOG with 0/1/2/3 or NONE/INFO/DEBUG/DUMP to set the printed debug info (level 3 is level 2 + BOX86_DUMP)\n");
|
|
printf(" BOX86_ROLLING_LOG 0/1 use a rolling level 2 log of 16 entries\n");
|
|
printf(" BOX86_DUMP with 0/1 to dump elf infos\n");
|
|
printf(" BOX86_NOBANNER with 0/1 to enable/disable the printing of box86 version and build at start\n");
|
|
#ifdef DYNAREC
|
|
printf(" BOX86_DYNAREC_LOG with 0/1/2/3 or NONE/INFO/DEBUG/DUMP to set the printed dynarec info\n");
|
|
printf(" BOX86_DYNAREC with 0/1 to disable or enable Dynarec (On by default)\n");
|
|
printf(" BOX86_NODYNAREC with address interval (0x1234-0x4567) to forbid dynablock creation in the interval specified\n");
|
|
printf(" BOX86_DYNAREC_BIGBLOCK 0/1/2 to control Dynarec building BigBlock or not (default: 1)\n");
|
|
printf(" BOX86_DYNAREC_STRONGMEM 0/1/2 to control Dynarec emulation attempt of Stong memory model (default: 0)\n");
|
|
printf(" BOX86_DYNAREC_X87DOUBLE 0/1 to force Dynarec to use Double for x87 emulation (default: 0)\n");
|
|
printf(" BOX86_DYNAREC_FASTNAN 0/1 to control if NaN are x86 accurate or not (default: 1)\n");
|
|
printf(" BOX86_DYNAREC_SAFEFLAGS 0/1/2 to control flags generation on RET/CALL opcodes (default: 1)\n");
|
|
#endif
|
|
#ifdef HAVE_TRACE
|
|
printf(" BOX86_TRACE with 1 to enable x86 execution trace\n");
|
|
printf(" or with XXXXXX-YYYYYY to enable x86 execution trace only between address\n");
|
|
printf(" or with FunctionName to enable x86 execution trace only in one specific function\n");
|
|
printf(" use BOX86_TRACE_INIT instead of BOX_TRACE to start trace before init of Libs and main program\n\t (function name will probably not work then)\n");
|
|
printf(" BOX86_TRACE_XMM with 1 to enable dump of SSE/SSE2 register along with regular registers\n");
|
|
printf(" BOX86_TRACE_START with N to enable trace after N instructions\n");
|
|
#ifdef DYNAREC
|
|
printf(" BOX86_DYNAREC_TRACE with 0/1 to disable or enable Trace on generated code too\n");
|
|
#endif
|
|
#endif
|
|
printf(" BOX86_TRACE_FILE with FileName to redirect logs in a file (or stderr to use stderr instead of stdout)");
|
|
printf(" BOX86_DLSYM_ERROR with 1 to log dlsym errors\n");
|
|
printf(" BOX86_LOAD_ADDR=0xXXXXXX try to load at 0xXXXXXX main binary (if binary is a PIE)\n");
|
|
printf(" BOX86_NOSIGSEGV=1 to disable handling of SigSEGV\n");
|
|
printf(" BOX86_NOSIGILL=1 to disable handling of SigILL\n");
|
|
printf(" BOX86_SHOWSEGV=1 to show Segfault signal even if a signal handler is present\n");
|
|
#ifdef PANDORA
|
|
printf(" BOX86_X11COLOR16=1 to try convert X11 color from 32 bits to 16 bits (to avoid light green on light cyan windows\n");
|
|
#endif
|
|
printf(" BOX86_X11THREADS=1 to call XInitThreads when loading X11 (for old Loki games with Loki_Compat lib)");
|
|
printf(" BOX86_LIBGL=libXXXX set the name (and optionnaly full path) for libGL.so.1\n");
|
|
printf(" BOX86_LD_PRELOAD=XXXX[:YYYYY] force loading XXXX (and YYYY...) libraries with the binary\n");
|
|
printf(" BOX86_ALLOWMISSINGLIBS with 1 to allow to continue even if a lib is missing (unadvised, will probably crash later)\n");
|
|
printf(" BOX64_PREFER_EMULATED=1 to prefer emulated libs first (execpt for glibc, alsa, pulse, GL, vulkan and X11\n");
|
|
printf(" BOX64_PREFER_WRAPPED if box86 will use wrapped libs even if the lib is specified with absolute path\n");
|
|
printf(" BOX86_NOPULSE=1 to disable the loading of pulseaudio libs\n");
|
|
printf(" BOX86_NOGTK=1 to disable the loading of wrapped gtk libs\n");
|
|
printf(" BOX86_NOVULKAN=1 to disable the loading of wrapped vulkan libs\n");
|
|
printf(" BOX86_ENV='XXX=yyyy' will add XXX=yyyy env. var.\n");
|
|
printf(" BOX86_ENV1='XXX=yyyy' will add XXX=yyyy env. var. and continue with BOX86_ENV2 ... until var doesn't exist\n");
|
|
printf(" BOX86_JITGDB with 1 to launch \"gdb\" when a segfault is trapped, attached to the offending process\n");
|
|
}
|
|
|
|
void addNewEnvVar(const char* s)
|
|
{
|
|
if(!s)
|
|
return;
|
|
char* p = box_strdup(s);
|
|
char* e = strchr(p, '=');
|
|
if(!e) {
|
|
printf_log(LOG_INFO, "Invalid speicifc env. var. '%s'\n", s);
|
|
box_free(p);
|
|
return;
|
|
}
|
|
*e='\0';
|
|
++e;
|
|
setenv(p, e, 1);
|
|
box_free(p);
|
|
}
|
|
|
|
EXPORTDYN
|
|
void LoadEnvVars(box86context_t *context)
|
|
{
|
|
// Check custom env. var. and add them if needed
|
|
{
|
|
char* p = getenv("BOX86_ENV");
|
|
if(p)
|
|
addNewEnvVar(p);
|
|
int i = 1;
|
|
char box86_env[50];
|
|
do {
|
|
sprintf(box86_env, "BOX86_ENV%d", i);
|
|
p = getenv(box86_env);
|
|
if(p) {
|
|
addNewEnvVar(p);
|
|
++i;
|
|
}
|
|
} while(p);
|
|
}
|
|
// check BOX86_LD_LIBRARY_PATH and load it
|
|
LoadEnvPath(&context->box86_ld_lib, ".:lib:lib32:x86:i686", "BOX86_LD_LIBRARY_PATH");
|
|
#ifdef PANDORA
|
|
if(FileExist("/mnt/utmp/codeblocks/usr/lib/i386-linux-gnu", 0))
|
|
AddPath("/mnt/utmp/codeblocks/usr/lib/i386-linux-gnu", &context->box86_ld_lib, 1);
|
|
if(FileExist("/mnt/utmp/box86/lib/i386-linux-gnu", 0))
|
|
AddPath("/mnt/utmp/box86/lib/i386-linux-gnu", &context->box86_ld_lib, 1);
|
|
//TODO: add relative path to box86 location
|
|
#endif
|
|
if(FileExist("/lib/box86", 0))
|
|
AddPath("/lib/box86", &context->box86_ld_lib, 1);
|
|
if(FileExist("/usr/lib/box86", 0))
|
|
AddPath("/usr/lib/box86", &context->box86_ld_lib, 1);
|
|
if(FileExist("/lib/i386-linux-gnu", 0))
|
|
AddPath("/lib/i386-linux-gnu", &context->box86_ld_lib, 1);
|
|
if(FileExist("/usr/lib/i386-linux-gnu", 0))
|
|
AddPath("/usr/lib/i386-linux-gnu", &context->box86_ld_lib, 1);
|
|
if(FileExist("/lib/i686-pc-linux-gnu", 0))
|
|
AddPath("/lib/i686-pc-linux-gnu", &context->box86_ld_lib, 1);
|
|
if(FileExist("/usr/lib/i686-pc-linux-gnu", 0))
|
|
AddPath("/usr/lib/i686-pc-linux-gnu", &context->box86_ld_lib, 1);
|
|
if(FileExist("/usr/lib32", 0))
|
|
AddPath("/usr/lib32", &context->box86_ld_lib, 1);
|
|
if(getenv("LD_LIBRARY_PATH"))
|
|
PrependList(&context->box86_ld_lib, getenv("LD_LIBRARY_PATH"), 1); // in case some of the path are for x86 world
|
|
if(getenv("BOX86_EMULATED_LIBS")) {
|
|
char* p = getenv("BOX86_EMULATED_LIBS");
|
|
ParseList(p, &context->box86_emulated_libs, 0);
|
|
if (my_context->box86_emulated_libs.size && box86_log) {
|
|
printf_log(LOG_INFO, "BOX86 will force the used of emulated libs for ");
|
|
for (int i=0; i<context->box86_emulated_libs.size; ++i)
|
|
printf_log(LOG_INFO, "%s ", context->box86_emulated_libs.paths[i]);
|
|
printf_log(LOG_INFO, "\n");
|
|
}
|
|
}
|
|
// add libssl and libcrypto, prefer emulated version because of multiple version exist
|
|
AddPath("libssl.so.1", &context->box86_emulated_libs, 0);
|
|
AddPath("libssl.so.1.0.0", &context->box86_emulated_libs, 0);
|
|
AddPath("libcrypto.so.1", &context->box86_emulated_libs, 0);
|
|
AddPath("libcrypto.so.1.0.0", &context->box86_emulated_libs, 0);
|
|
|
|
if(getenv("BOX86_PREFER_WRAPPED")) {
|
|
if (strcmp(getenv("BOX86_PREFER_WRAPPED"), "1")==0) {
|
|
box86_prefer_wrapped = 1;
|
|
printf_log(LOG_INFO, "BOX86: Prefer Wrapped libs\n");
|
|
}
|
|
}
|
|
if(getenv("BOX86_PREFER_EMULATED")) {
|
|
if (strcmp(getenv("BOX86_PREFER_EMULATED"), "1")==0) {
|
|
box86_prefer_emulated = 1;
|
|
printf_log(LOG_INFO, "BOX86: Prefer Emulated libs\n");
|
|
}
|
|
}
|
|
if(getenv("BOX86_NOSIGSEGV")) {
|
|
if (strcmp(getenv("BOX86_NOSIGSEGV"), "1")==0) {
|
|
context->no_sigsegv = 1;
|
|
printf_log(LOG_INFO, "BOX86: Disabling handling of SigSEGV\n");
|
|
}
|
|
}
|
|
if(getenv("BOX86_NOSIGILL")) {
|
|
if (strcmp(getenv("BOX86_NOSIGILL"), "1")==0) {
|
|
context->no_sigill = 1;
|
|
printf_log(LOG_INFO, "BOX86: Disabling handling of SigILL\n");
|
|
}
|
|
}
|
|
// check BOX86_PATH and load it
|
|
LoadEnvPath(&context->box86_path, ".:bin", "BOX86_PATH");
|
|
if(getenv("PATH"))
|
|
AppendList(&context->box86_path, getenv("PATH"), 1); // in case some of the path are for x86 world
|
|
#ifdef HAVE_TRACE
|
|
char* p = getenv("BOX86_TRACE");
|
|
if(p) {
|
|
if (strcmp(p, "0"))
|
|
context->x86trace = 1;
|
|
}
|
|
p = getenv("BOX86_TRACE_INIT");
|
|
if(p) {
|
|
if (strcmp(p, "0"))
|
|
context->x86trace = 1;
|
|
}
|
|
if(my_context->x86trace) {
|
|
printf_log(LOG_INFO, "Initializing Zydis lib\n");
|
|
if(InitX86Trace(my_context)) {
|
|
printf_log(LOG_INFO, "Zydis init failed, no x86 trace activated\n");
|
|
context->x86trace = 0;
|
|
}
|
|
}
|
|
#endif
|
|
#ifdef BUILD_LIB
|
|
context->argc = 1; // need 1
|
|
context->argv = (char**)box_malloc(sizeof(char*));
|
|
context->argv[0] = box_strdup("dummy");
|
|
#endif
|
|
}
|
|
|
|
EXPORTDYN
|
|
void setupTraceInit(box86context_t* context)
|
|
{
|
|
#ifdef HAVE_TRACE
|
|
char* p = getenv("BOX86_TRACE_INIT");
|
|
if(p) {
|
|
setbuf(stdout, NULL);
|
|
uintptr_t trace_start=0, trace_end=0;
|
|
if (strcmp(p, "1")==0)
|
|
SetTraceEmu(0, 0);
|
|
else if (strchr(p,'-')) {
|
|
if(sscanf(p, "%d-%d", &trace_start, &trace_end)!=2) {
|
|
if(sscanf(p, "0x%X-0x%X", &trace_start, &trace_end)!=2)
|
|
sscanf(p, "%x-%x", &trace_start, &trace_end);
|
|
}
|
|
if(trace_start || trace_end)
|
|
SetTraceEmu(trace_start, trace_end);
|
|
} else {
|
|
if (GetGlobalSymbolStartEnd(my_context->maplib, p, &trace_start, &trace_end, NULL, -1, NULL)) {
|
|
SetTraceEmu(trace_start, trace_end);
|
|
printf_log(LOG_INFO, "TRACE on %s only (%p-%p)\n", p, (void*)trace_start, (void*)trace_end);
|
|
} else {
|
|
printf_log(LOG_NONE, "Warning, symbol to Traced (\"%s\") not found, disabling trace\n", p);
|
|
SetTraceEmu(0, 100); // disabling trace, mostly
|
|
}
|
|
}
|
|
} else {
|
|
p = getenv("BOX86_TRACE");
|
|
if(p)
|
|
if (strcmp(p, "0"))
|
|
SetTraceEmu(0, 1);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
EXPORTDYN
|
|
void setupTrace(box86context_t* context)
|
|
{
|
|
#ifdef HAVE_TRACE
|
|
char* p = getenv("BOX86_TRACE");
|
|
if(p) {
|
|
setbuf(stdout, NULL);
|
|
uintptr_t trace_start=0, trace_end=0;
|
|
if (strcmp(p, "1")==0)
|
|
SetTraceEmu(0, 0);
|
|
else if (strchr(p,'-')) {
|
|
if(sscanf(p, "%d-%d", &trace_start, &trace_end)!=2) {
|
|
if(sscanf(p, "0x%X-0x%X", &trace_start, &trace_end)!=2)
|
|
sscanf(p, "%x-%x", &trace_start, &trace_end);
|
|
}
|
|
if(trace_start || trace_end) {
|
|
SetTraceEmu(trace_start, trace_end);
|
|
if(!trace_start && trace_end==1) {
|
|
printf_log(LOG_INFO, "TRACE enabled but inactive\n");
|
|
} else {
|
|
printf_log(LOG_INFO, "TRACE on %s only (%p-%p)\n", p, (void*)trace_start, (void*)trace_end);
|
|
}
|
|
}
|
|
} else {
|
|
if (GetGlobalSymbolStartEnd(my_context->maplib, p, &trace_start, &trace_end, NULL, -1, NULL)) {
|
|
SetTraceEmu(trace_start, trace_end);
|
|
printf_log(LOG_INFO, "TRACE on %s only (%p-%p)\n", p, (void*)trace_start, (void*)trace_end);
|
|
} else if(GetLocalSymbolStartEnd(my_context->maplib, p, &trace_start, &trace_end, NULL, -1, NULL)) {
|
|
SetTraceEmu(trace_start, trace_end);
|
|
printf_log(LOG_INFO, "TRACE on %s only (%p-%p)\n", p, (void*)trace_start, (void*)trace_end);
|
|
} else {
|
|
printf_log(LOG_NONE, "Warning, symbol to Traced (\"%s\") not found, trying to set trace later\n", p);
|
|
SetTraceEmu(0, 1); // disabling trace, mostly
|
|
trace_func = box_strdup(p);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void endBox86()
|
|
{
|
|
if(!my_context)
|
|
return;
|
|
x86emu_t* emu = thread_get_emu();
|
|
//atexit first
|
|
printf_log(LOG_DEBUG, "Calling atexit registered functions\n");
|
|
CallAllCleanup(emu);
|
|
// then call all the fini
|
|
printf_log(LOG_DEBUG, "Calling fini for all loaded elfs and unload native libs\n");
|
|
RunElfFini(my_context->elfs[0], emu);
|
|
FreeLibrarian(&my_context->local_maplib, emu); // unload all libs
|
|
FreeLibrarian(&my_context->maplib, emu); // unload all libs
|
|
// waiting for all thread except this one to finish
|
|
int this_thread = GetTID();
|
|
int pid = getpid();
|
|
int running = 1;
|
|
int attempt = 0;
|
|
printf_log(LOG_DEBUG, "Waiting for all threads to finish before unloading box86context\n");
|
|
while(running) {
|
|
DIR *proc_dir;
|
|
char dirname[100];
|
|
snprintf(dirname, sizeof dirname, "/proc/self/task");
|
|
proc_dir = opendir(dirname);
|
|
running = 0;
|
|
if (proc_dir)
|
|
{
|
|
struct dirent *entry;
|
|
while ((entry = readdir(proc_dir)) != NULL && !running)
|
|
{
|
|
if(entry->d_name[0] == '.')
|
|
continue;
|
|
|
|
int tid = atoi(entry->d_name);
|
|
// tid != pthread_t, so no pthread functions are available here
|
|
if(tid!=this_thread) {
|
|
if(attempt>4000) {
|
|
printf_log(LOG_INFO, "Stop waiting for remaining thread %04d\n", tid);
|
|
// enough wait, kill all thread!
|
|
//syscall(__NR_tgkill, pid, tid, SIGABRT);
|
|
running = 0;
|
|
} else {
|
|
running = 1;
|
|
++attempt;
|
|
sched_yield();
|
|
}
|
|
}
|
|
}
|
|
closedir(proc_dir);
|
|
}
|
|
}
|
|
// all done, free context
|
|
FreeBox86Context(&my_context);
|
|
if(libGL) {
|
|
box_free(libGL);
|
|
libGL = NULL;
|
|
}
|
|
}
|
|
|
|
#ifdef BUILD_LIB
|
|
#ifdef BUILD_DYNAMIC
|
|
EXPORTDYN
|
|
box86context_t* GetBox86Context()
|
|
{
|
|
return my_context;
|
|
}
|
|
|
|
EXPORTDYN
|
|
x86emu_t* GetX86Emu()
|
|
{
|
|
return thread_get_emu();
|
|
}
|
|
|
|
__attribute__((constructor))
|
|
void init_library()
|
|
{
|
|
// init random seed
|
|
srandom(time(NULL));
|
|
|
|
// check BOX86_LOG debug level
|
|
LoadLogEnv();
|
|
|
|
printf_log(LOG_INFO, "Initializing libbox86\n");
|
|
// create a global box86 context
|
|
my_context = NewBox86Context(0);
|
|
LoadEnvVars(my_context);
|
|
CalcStackSize(my_context); // with no elf, so default size of 8M
|
|
// init x86 emu
|
|
x86emu_t *emu = NewX86Emu(my_context, my_context->ep, (uintptr_t)my_context->stack, my_context->stacksz, 0);
|
|
// stack setup is much more complicated then just that!
|
|
SetupInitialStack(emu);
|
|
SetupX86Emu(emu);
|
|
thread_set_emu(emu);
|
|
}
|
|
#endif
|
|
|
|
#else
|
|
|
|
static void free_contextargv()
|
|
{
|
|
for(int i=0; i<my_context->argc; ++i)
|
|
box_free(my_context->argv[i]);
|
|
}
|
|
|
|
extern char **environ;
|
|
|
|
int main(int argc, const char **argv, char **env)
|
|
{
|
|
init_malloc_hook();
|
|
init_auxval(argc, argv, environ?environ:env);
|
|
// trying to open and load 1st arg
|
|
if(argc==1) {
|
|
PrintBox86Version();
|
|
PrintHelp();
|
|
return 1;
|
|
}
|
|
if(argc>1 && !strcmp(argv[1], "/usr/bin/gdb"))
|
|
exit(0);
|
|
// uname -m is redirected to box86 -m
|
|
if(argc==2 && (!strcmp(argv[1], "-m") || !strcmp(argv[1], "-p") || !strcmp(argv[1], "-i")))
|
|
{
|
|
// try box64 first
|
|
execlp("box64", "box64", argv[1], (char*)NULL);
|
|
// fallthru if box64 is not installed
|
|
printf("i686\n");
|
|
exit(0);
|
|
}
|
|
// init random seed
|
|
srandom(time(NULL));
|
|
|
|
// check BOX86_LOG debug level
|
|
LoadLogEnv();
|
|
char* bashpath = NULL;
|
|
{
|
|
char* p = getenv("BOX86_BASH");
|
|
if(p) {
|
|
if(FileIsX86ELF(p) || FileIsX64ELF(p)) {
|
|
bashpath = p;
|
|
} else {
|
|
printf_log(LOG_INFO, "the x86 bash \"%s\" is not an x86 binary\n", p);
|
|
}
|
|
}
|
|
if(!bashpath && (p=getenv("BOX64_BASH"))) {
|
|
if(FileIsX86ELF(p) || FileIsX64ELF(p)) {
|
|
bashpath = p;
|
|
} else {
|
|
printf_log(LOG_INFO, "the x86_64 bash \"%s\" is not an x86_64 binary\n", p);
|
|
}
|
|
}
|
|
}
|
|
if(bashpath)
|
|
printf_log(LOG_INFO, "Using bash \"%s\"\n", bashpath);
|
|
|
|
const char* prog = argv[1];
|
|
int nextarg = 1;
|
|
// check if some options are passed
|
|
while(prog && prog[0]=='-') {
|
|
if(!strcmp(prog, "-v") || !strcmp(prog, "--version")) {
|
|
PrintBox86Version();
|
|
exit(0);
|
|
}
|
|
if(!strcmp(prog, "-h") || !strcmp(prog, "--help")) {
|
|
PrintHelp();
|
|
exit(0);
|
|
}
|
|
// other options?
|
|
if(!strcmp(prog, "--")) {
|
|
prog = argv[++nextarg];
|
|
break;
|
|
}
|
|
printf("Warning, unrecognized option '%s'\n", prog);
|
|
prog = argv[++nextarg];
|
|
}
|
|
if(!prog || nextarg==argc) {
|
|
printf("Box86: nothing to run\n");
|
|
exit(0);
|
|
}
|
|
if(!box86_nobanner)
|
|
PrintBox86Version();
|
|
// precheck, for win-preload
|
|
if(strstr(prog, "wine-preloader")==(prog+strlen(prog)-strlen("wine-preloader"))
|
|
|| strstr(prog, "wine64-preloader")==(prog+strlen(prog)-strlen("wine64-preloader"))) {
|
|
// wine-preloader detecter, skipping it if next arg exist and is an x86 binary
|
|
int x86 = (nextarg<argc)?FileIsX86ELF(argv[nextarg]):0;
|
|
if(x86) {
|
|
prog = argv[++nextarg];
|
|
printf_log(LOG_INFO, "BOX86: Wine preloader detected, loading \"%s\" directly\n", prog);
|
|
//wine_preloaded = 1;
|
|
}
|
|
box86_wine = 1;
|
|
}
|
|
// check if this is wine
|
|
if(!strcmp(prog, "wine") || (strlen(prog)>5 && !strcmp(prog+strlen(prog)-strlen("/wine"), "/wine"))) {
|
|
const char* prereserve = getenv("WINEPRELOADRESERVE");
|
|
printf_log(LOG_INFO, "BOX86: Wine detected, WINEPRELOADRESERVE=\"%s\"\n", prereserve?prereserve:"");
|
|
if(wine_preloaded)
|
|
wine_prereserve(prereserve);
|
|
// special case for winedbg, doesn't work anyway
|
|
if(argv[nextarg+1] && strstr(argv[nextarg+1], "winedbg")==argv[nextarg+1]) {
|
|
printf_log(LOG_NONE, "winedbg detected, not launching it!\n");
|
|
exit(0); // exiting, it doesn't work anyway
|
|
}
|
|
box86_wine = 1;
|
|
}
|
|
// check if this is wineserver
|
|
if(!strcmp(prog, "wineserver") || (strlen(prog)>9 && !strcmp(prog+strlen(prog)-strlen("/wineserver"), "/wineserver"))) {
|
|
box86_wine = 1;
|
|
}
|
|
if(box86_wine) {
|
|
// disabling the use of futex_waitv for now
|
|
setenv("WINEFSYNC", "0", 1);
|
|
}
|
|
// Create a new context
|
|
my_context = NewBox86Context(argc - nextarg);
|
|
|
|
// check BOX86_LD_LIBRARY_PATH and load it
|
|
LoadEnvVars(my_context);
|
|
|
|
my_context->box86path = ResolveFile(argv[0], &my_context->box86_path);
|
|
// check if box64 is present
|
|
{
|
|
my_context->box64path = getenv("BOX86_BOX64");
|
|
if(!my_context->box64path) {
|
|
my_context->box64path = box_strdup(my_context->box86path);
|
|
char* p = strrchr(my_context->box64path, '8'); // get the 8 of box86
|
|
p[0] = '6'; p[1] = '4'; // change 86 to 64
|
|
}
|
|
if(!FileExist(my_context->box64path, IS_FILE)) {
|
|
box_free(my_context->box64path);
|
|
my_context->box64path = ResolveFile("box64", &my_context->box86_path);
|
|
}
|
|
if(!FileExist(my_context->box64path, IS_FILE)) {
|
|
box_free(my_context->box64path);
|
|
my_context->box64path = NULL;
|
|
if(bashpath && FileIsX64ELF(bashpath)) {
|
|
printf_log(LOG_INFO, "The bash binary given is an x86_64 version, but box64 is not found\n");
|
|
bashpath = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
// prepare all other env. var
|
|
my_context->envc = CountEnv(environ?environ:env);
|
|
printf_log(LOG_INFO, "Counted %d Env var\n", my_context->envc);
|
|
// allocate extra space for new environment variables such as BOX86_PATH
|
|
my_context->envv = (char**)box_calloc(my_context->envc+4, sizeof(char*));
|
|
GatherEnv(&my_context->envv, environ?environ:env, my_context->box86path);
|
|
if(box86_dump) {
|
|
for (int i=0; i<my_context->envc; ++i)
|
|
printf_dump(LOG_NEVER, " Env[%02d]: %s\n", i, my_context->envv[i]);
|
|
}
|
|
|
|
path_collection_t ld_preload = {0};
|
|
if(getenv("BOX86_LD_PRELOAD")) {
|
|
char* p = getenv("BOX86_LD_PRELOAD");
|
|
ParseList(p, &ld_preload, 0);
|
|
if (ld_preload.size && box86_log) {
|
|
printf_log(LOG_INFO, "BOX86 try to Preload ");
|
|
for (int i=0; i<ld_preload.size; ++i)
|
|
printf_log(LOG_INFO, "%s ", ld_preload.paths[i]);
|
|
printf_log(LOG_INFO, "\n");
|
|
}
|
|
} else {
|
|
if(getenv("LD_PRELOAD")) {
|
|
char* p = getenv("LD_PRELOAD");
|
|
if(strstr(p, "libtcmalloc_minimal.so.4"))
|
|
box86_tcmalloc_minimal = 1;
|
|
if(strstr(p, "libtcmalloc_minimal.so.0"))
|
|
box86_tcmalloc_minimal = 1;
|
|
if(strstr(p, "libtcmalloc_minimal.so"))
|
|
box86_tcmalloc_minimal = 1;
|
|
if(strstr(p, "libtcmalloc_minimal_debug.so.4"))
|
|
box86_tcmalloc_minimal = 1;
|
|
if(strstr(p, "libasan.so"))
|
|
box86_tcmalloc_minimal = 1; // it seems Address Sanitizer doesn't handle dlsym'd malloc very well
|
|
ParseList(p, &ld_preload, 0);
|
|
if (ld_preload.size && box86_log) {
|
|
printf_log(LOG_INFO, "BOX86 try to Preload ");
|
|
for (int i=0; i<ld_preload.size; ++i)
|
|
printf_log(LOG_INFO, "%s ", ld_preload.paths[i]);
|
|
printf_log(LOG_INFO, "\n");
|
|
}
|
|
}
|
|
}
|
|
// lets build argc/argv stuff
|
|
printf_log(LOG_INFO, "Looking for %s\n", prog);
|
|
if(strchr(prog, '/'))
|
|
my_context->argv[0] = box_strdup(prog);
|
|
else
|
|
my_context->argv[0] = ResolveFile(prog, &my_context->box86_path);
|
|
|
|
const char* prgname = strrchr(prog, '/');
|
|
if(!prgname)
|
|
prgname = prog;
|
|
else
|
|
++prgname;
|
|
if(box86_wine) {
|
|
AddPath("libdl.so.2", &ld_preload, 0);
|
|
}
|
|
// special case for LittleInferno that use an old libvorbis
|
|
if(strstr(prgname, "LittleInferno.bin.x86")==prgname) {
|
|
printf_log(LOG_INFO, "LittleInferno detected, forcing emulated libvorbis\n");
|
|
AddPath("libvorbis.so.0", &my_context->box86_emulated_libs, 0);
|
|
}
|
|
// special case for dontstarve that use an old SDL2
|
|
if(strstr(prgname, "dontstarve")) {
|
|
printf_log(LOG_INFO, "Dontstarve* detected, forcing emulated SDL2\n");
|
|
AddPath("libSDL2-2.0.so.0", &my_context->box86_emulated_libs, 0);
|
|
}
|
|
// special case for A Golden Wake / GeminiRue that use an old SDL2 and vorbis libs
|
|
if(strstr(prgname, "A_Golden_Wake.bin.x86")==prgname || strstr(prgname, "GeminiRue.bin.x86")==prgname) {
|
|
printf_log(LOG_INFO, "A Golden Wake / Gemini Rue detected, prefer emulated libs\n");
|
|
box86_prefer_emulated = 1;
|
|
}
|
|
// special case for steam that somehow seems to alter libudev opaque pointer (udev_monitor)
|
|
if(strstr(prgname, "steam")==prgname) {
|
|
printf_log(LOG_INFO, "steam detected, forcing emulated libudev and gtk\n");
|
|
AddPath("libudev.so.0", &my_context->box86_emulated_libs, 0);
|
|
box86_steam = 1;
|
|
box86_nogtk = 1;
|
|
AddPath("libSDL2-2.0.so.0", &my_context->box86_emulated_libs, 0);
|
|
unsetenv("BOX86_NOGTK");
|
|
#ifdef DYNAREC
|
|
box86_dynarec_strongmem = 2;
|
|
#endif
|
|
#ifdef PANDORA
|
|
x11color16 = 1;
|
|
#endif
|
|
}
|
|
// special case for steam-runtime-check-requirements to fake 64bits suport
|
|
if(strstr(prgname, "steam-runtime-check-requirements")==prgname) {
|
|
printf_log(LOG_INFO, "steam-runtime-check-requirements detected, faking All is good!\n");
|
|
exit(0); // exiting, not testing anything
|
|
}
|
|
// special case for streaming_client to use emulated libSDL2
|
|
if(strstr(prgname, "streaming_client")==prgname) {
|
|
printf_log(LOG_INFO, "streaming_client detected, using emulated libSDL2!\n");
|
|
AddPath("libSDL2-2.0.so.0", &my_context->box86_emulated_libs, 0);
|
|
AddPath("libSDL2_ttf-2.0.so.0", &my_context->box86_emulated_libs, 0);
|
|
}
|
|
// special case for UnrealLinux.bin, it doesn't like "full path resolution"
|
|
if(!strcmp(prog, "UnrealLinux.bin") && my_context->argv[0]) {
|
|
box_free(my_context->argv[0]);
|
|
my_context->argv[0] = box_strdup("./UnrealLinux.bin");
|
|
}
|
|
#if defined(RPI) || defined(RK3399) || defined(RK3288) || defined(GOA_CLONE) || defined(PYRA) || defined(PANDORA)
|
|
// special case for TokiTori 2+, that check if texture max size is > = 8192
|
|
if(strstr(prgname, "TokiTori2.bin.x86")==prgname) {
|
|
printf_log(LOG_INFO, "TokiTori 2+ detected, runtime patch to fix GPU non-power-of-two faillure\n");
|
|
box86_tokitori2 = 1;
|
|
}
|
|
#endif
|
|
// special case for SimCity3k Demo, that have a bug were it do feof after fclose on the same fd
|
|
if(strstr(prgname, "sc3u_demo.x86")==prgname) {
|
|
printf_log(LOG_INFO, "SimCity3000 Demo detected, runtime patch to fix buf feof after fclose\n");
|
|
box86_sc3u = 1;
|
|
}
|
|
if(strstr(prgname, "sc3u")==prgname) {
|
|
printf_log(LOG_INFO, "SimCity3000 detected, runtime patch to fix buf feof after fclose\n");
|
|
box86_sc3u = 1;
|
|
}
|
|
// special case for zoom
|
|
if(strstr(prgname, "zoom")==prgname) {
|
|
printf_log(LOG_INFO, "Zoom detected, trying to use system libturbojpeg if possible\n");
|
|
box86_zoom = 1;
|
|
}
|
|
// special case for X3Reunion to use emulated libjpeg62
|
|
if(strstr(prgname, "X3R_main")) {
|
|
printf_log(LOG_INFO, "X3Reunion detected, forcing emulated libjpeg\n");
|
|
AddPath("libjpeg.so.62", &my_context->box86_emulated_libs, 0);
|
|
}
|
|
// special case for bash (add BOX86_NOBANNER=1 if not there)
|
|
if(!strcmp(prgname, "bash")) {
|
|
printf_log(LOG_INFO, "bash detected, disabling banner\n");
|
|
if (!box86_nobanner) {
|
|
setenv("BOX86_NOBANNER", "1", 0);
|
|
setenv("BOX64_NOBANNER", "1", 0);
|
|
}
|
|
if (!bashpath) {
|
|
bashpath = (char*)prog;
|
|
setenv("BOX86_BASH", prog, 1);
|
|
}
|
|
}
|
|
if(bashpath)
|
|
my_context->bashpath = box_strdup(bashpath);
|
|
|
|
/*if(strstr(prgname, "awesomium_process")==prgname) {
|
|
printf_log(LOG_INFO, "awesomium_process detected, forcing emulated libpng12\n");
|
|
AddPath("libpng12.so.0", &my_context->box86_emulated_libs, 0);
|
|
}*/
|
|
/*if(!strcmp(prgname, "gdb")) {
|
|
exit(-1);
|
|
}*/
|
|
|
|
for(int i=1; i<my_context->argc; ++i) {
|
|
my_context->argv[i] = box_strdup(argv[i+nextarg]);
|
|
printf_log(LOG_INFO, "argv[%i]=\"%s\"\n", i, my_context->argv[i]);
|
|
}
|
|
|
|
// check if file exist
|
|
if(!my_context->argv[0] || !FileExist(my_context->argv[0], IS_FILE)) {
|
|
printf_log(LOG_NONE, "Error: file is not found (check BOX86_PATH)\n");
|
|
free_contextargv();
|
|
FreeBox86Context(&my_context);
|
|
FreeCollection(&ld_preload);
|
|
return -1;
|
|
}
|
|
if(!FileExist(my_context->argv[0], IS_FILE|IS_EXECUTABLE)) {
|
|
printf_log(LOG_NONE, "Error: %s is not an executable file\n", my_context->argv[0]);
|
|
free_contextargv();
|
|
FreeBox86Context(&my_context);
|
|
FreeCollection(&ld_preload);
|
|
return -1;
|
|
}
|
|
if(!(my_context->fullpath = box_realpath(my_context->argv[0], NULL)))
|
|
my_context->fullpath = box_strdup(my_context->argv[0]);
|
|
if(getenv("BOX86_ARG0"))
|
|
my_context->argv[0] = box_strdup(getenv("BOX86_ARG0"));
|
|
FILE *f = fopen64(my_context->fullpath, "rb");
|
|
if(!f) {
|
|
printf_log(LOG_NONE, "Error: Cannot open %s\n", my_context->fullpath);
|
|
free_contextargv();
|
|
FreeBox86Context(&my_context);
|
|
FreeCollection(&ld_preload);
|
|
return -1;
|
|
}
|
|
elfheader_t *elf_header = LoadAndCheckElfHeader(f, my_context->fullpath, 1);
|
|
if(!elf_header) {
|
|
int x64 = my_context->box64path?FileIsX64ELF(my_context->fullpath):0;
|
|
int script = my_context->bashpath?FileIsShell(my_context->fullpath):0;
|
|
printf_log(LOG_NONE, "Error: reading elf header of %s, try to launch %s instead\n", my_context->fullpath, x64?"using box64":(script?"using bash":"natively"));
|
|
fclose(f);
|
|
FreeCollection(&ld_preload);
|
|
int ret;
|
|
if(x64) {
|
|
// duplicate the array and insert 1st arg as box86
|
|
const char** newargv = (const char**)box_calloc(my_context->argc+2, sizeof(char*));
|
|
newargv[0] = my_context->box64path;
|
|
for(int i=0; i<my_context->argc; ++i)
|
|
newargv[i+1] = my_context->argv[i];
|
|
FreeBox86Context(&my_context);
|
|
ret = execvp(newargv[0], (char * const*)newargv);
|
|
} else if (script) {
|
|
// duplicate the array and insert 1st arg as box64, 2nd is bash
|
|
const char** newargv = (const char**)box_calloc(my_context->argc+3, sizeof(char*));
|
|
newargv[0] = my_context->box86path;
|
|
newargv[1] = my_context->bashpath;
|
|
for(int i=0; i<my_context->argc; ++i)
|
|
newargv[i+2] = my_context->argv[i];
|
|
ret = execvp(newargv[0], (char * const*)newargv);
|
|
} else {
|
|
const char** newargv = (const char**)box_calloc(my_context->argc+1, sizeof(char*));
|
|
for(int i=0; i<my_context->argc; ++i)
|
|
newargv[i] = my_context->argv[i];
|
|
ret = execvp(newargv[0], (char * const*)newargv);
|
|
}
|
|
free_contextargv();
|
|
FreeBox86Context(&my_context);
|
|
return ret;
|
|
}
|
|
AddElfHeader(my_context, elf_header);
|
|
|
|
if(CalcLoadAddr(elf_header)) {
|
|
printf_log(LOG_NONE, "Error: reading elf header of %s\n", my_context->fullpath);
|
|
fclose(f);
|
|
free_contextargv();
|
|
FreeBox86Context(&my_context);
|
|
FreeCollection(&ld_preload);
|
|
return -1;
|
|
}
|
|
// allocate memory
|
|
if(AllocElfMemory(my_context, elf_header, 1)) {
|
|
printf_log(LOG_NONE, "Error: allocating memory for elf %s\n", my_context->fullpath);
|
|
fclose(f);
|
|
free_contextargv();
|
|
FreeBox86Context(&my_context);
|
|
FreeCollection(&ld_preload);
|
|
return -1;
|
|
}
|
|
// Load elf into memory
|
|
if(LoadElfMemory(f, my_context, elf_header)) {
|
|
printf_log(LOG_NONE, "Error: loading in memory elf %s\n", my_context->fullpath);
|
|
fclose(f);
|
|
free_contextargv();
|
|
FreeBox86Context(&my_context);
|
|
FreeCollection(&ld_preload);
|
|
return -1;
|
|
}
|
|
// can close the file now
|
|
fclose(f);
|
|
my_context->brk = ElfGetBrk(elf_header);
|
|
if(ElfCheckIfUseTCMallocMinimal(elf_header)) {
|
|
if(!box86_tcmalloc_minimal) {
|
|
// need to reload with tcmalloc_minimal as a LD_PRELOAD!
|
|
printf_log(LOG_INFO, "BOX86: tcmalloc_minimal.so.4 used, reloading box86 with the lib preladed\n");
|
|
// need to get a new envv variable. so first count it and check if LD_PRELOAD is there
|
|
int preload=(getenv("LD_PRELOAD"))?1:0;
|
|
int nenv = 0;
|
|
while(env[nenv]) nenv++;
|
|
// alloc + "LD_PRELOAD" if needd + last NULL ending
|
|
char** newenv = (char**)box_calloc(nenv+1+((preload)?0:1), sizeof(char*));
|
|
// copy strings
|
|
for (int i=0; i<nenv; ++i)
|
|
newenv[i] = box_strdup(env[i]);
|
|
// add ld_preload
|
|
if(preload) {
|
|
// find the line
|
|
int l = 0;
|
|
while(l<nenv) {
|
|
if(strstr(newenv[l], "LD_PRELOAD=")==newenv[l]) {
|
|
// found it!
|
|
char *old = newenv[l];
|
|
newenv[l] = (char*)box_calloc(strlen(old)+strlen("libtcmalloc_minimal.so.4:")+1, sizeof(char));
|
|
strcpy(newenv[l], "LD_PRELOAD=libtcmalloc_minimal.so.4:");
|
|
strcat(newenv[l], old + strlen("LD_PRELOAD="));
|
|
box_free(old);
|
|
// done, end loop
|
|
l = nenv;
|
|
} else ++l;
|
|
}
|
|
} else {
|
|
//move last one
|
|
newenv[nenv] = box_strdup(newenv[nenv-1]);
|
|
box_free(newenv[nenv-1]);
|
|
newenv[nenv-1] = box_strdup("LD_PRELOAD=libtcmalloc_minimal.so.4");
|
|
}
|
|
// duplicate argv too
|
|
char** newargv = box_calloc(argc+1, sizeof(char*));
|
|
int narg = 0;
|
|
while(argv[narg]) {newargv[narg] = box_strdup(argv[narg]); narg++;}
|
|
// launch with new env...
|
|
if(execve(newargv[0], newargv, newenv)<0)
|
|
printf_log(LOG_NONE, "Failed to relaunch, error is %d/%s\n", errno, strerror(errno));
|
|
} else {
|
|
printf_log(LOG_INFO, "BOX86: Using tcmalloc_minimal.so.4, and it's in the LD_PRELOAD command\n");
|
|
}
|
|
}
|
|
#if defined(RPI) || defined(RK3399) || defined(GOA_CLONE)
|
|
// before launching emulation, let's check if this is a mojosetup from GOG
|
|
if (((strstr(prog, "bin/linux/x86/mojosetup") && getenv("MOJOSETUP_BASE")) || strstr(prog, ".mojosetup/mojosetup"))
|
|
&& getenv("GTK2_RC_FILES")) {
|
|
sanitize_mojosetup_gtk_background();
|
|
}
|
|
#endif
|
|
// change process name
|
|
{
|
|
char* p = strrchr(my_context->fullpath, '/');
|
|
if(p)
|
|
++p;
|
|
else
|
|
p = my_context->fullpath;
|
|
if(prctl(PR_SET_NAME, p)==-1)
|
|
printf_log(LOG_NONE, "Error setting process name (%s)\n", strerror(errno));
|
|
else
|
|
printf_log(LOG_INFO, "Rename process to \"%s\"\n", p);
|
|
// and now all change the argv (so libs libs mesa find the correct program names)
|
|
char* endp = (char*)argv[argc-1];
|
|
while(*endp)
|
|
++endp; // find last argv[] address
|
|
uintptr_t diff = prog - argv[0]; // this is the difference we need to compensate
|
|
for(p=(char*)prog; p<=endp; ++p)
|
|
*(p - diff) = *p; // copy all element at argv[nextarg] to argv[0]
|
|
memset(endp - diff, 0, diff); // fill reminder with NULL
|
|
for(int i=nextarg; i<argc; ++i)
|
|
argv[i] -= diff; // adjust strings
|
|
}
|
|
// get and alloc stack size and align
|
|
if(CalcStackSize(my_context)) {
|
|
printf_log(LOG_NONE, "Error: allocating stack\n");
|
|
free_contextargv();
|
|
FreeBox86Context(&my_context);
|
|
FreeCollection(&ld_preload);
|
|
return -1;
|
|
}
|
|
#if defined(RPI) || defined(RK3399) || defined(RK3288) || defined(GOA_CLONE) || defined(PYRA) || defined(PANDORA)
|
|
if(box86_tokitori2) {
|
|
uint32_t *patch = (uint32_t*)0x85897f4;
|
|
if(*patch==0x2000) {
|
|
*patch = 0x1000;
|
|
printf_log(LOG_NONE, "Runtime patching the game\n");
|
|
} else
|
|
printf_log(LOG_NONE, "Cannot patch the game\n");
|
|
}
|
|
#endif
|
|
if(box86_sc3u) {
|
|
uint32_t *patch1 = (uint32_t*)0x8400684;
|
|
uint32_t *patch2 = (uint32_t*)0x8406644;
|
|
if(*patch1==0x57f4c483) {
|
|
*patch1=0x57f407eb;
|
|
printf_log(LOG_NONE, "Runtime patching the game\n");
|
|
} else if(*patch2==0x57f4c483) {
|
|
*patch2=0x57f407eb;
|
|
printf_log(LOG_NONE, "Runtime patching the game\n");
|
|
} else
|
|
|
|
printf_log(LOG_NONE, "Cannot patch the game\n");
|
|
}
|
|
// init x86 emu
|
|
x86emu_t *emu = NewX86Emu(my_context, my_context->ep, (uintptr_t)my_context->stack, my_context->stacksz, 0);
|
|
// stack setup is much more complicated then just that!
|
|
SetupInitialStack(emu); // starting here, the argv[] don't need free anymore
|
|
SetupX86Emu(emu);
|
|
SetEAX(emu, my_context->argc);
|
|
SetEBX(emu, (uint32_t)my_context->argv);
|
|
|
|
// child fork to handle traces
|
|
pthread_atfork(NULL, NULL, my_child_fork);
|
|
|
|
thread_set_emu(emu);
|
|
|
|
setupTraceInit(my_context);
|
|
// export symbols
|
|
AddSymbols(my_context->maplib, GetMapSymbols(elf_header), GetWeakSymbols(elf_header), GetLocalSymbols(elf_header), elf_header);
|
|
if(wine_preloaded) {
|
|
uintptr_t wineinfo = FindSymbol(GetMapSymbols(elf_header), "wine_main_preload_info", -1, NULL, 1, NULL);
|
|
if(!wineinfo) wineinfo = FindSymbol(GetWeakSymbols(elf_header), "wine_main_preload_info", -1, NULL, 1, NULL);
|
|
if(!wineinfo) wineinfo = FindSymbol(GetLocalSymbols(elf_header), "wine_main_preload_info", -1, NULL, 1, NULL);
|
|
if(!wineinfo) {printf_log(LOG_NONE, "Warning, Symbol wine_main_preload_info not found\n");}
|
|
else {
|
|
*(void**)wineinfo = get_wine_prereserve();
|
|
printf_log(LOG_DEBUG, "WINE wine_main_preload_info found and updated\n");
|
|
}
|
|
#ifdef DYNAREC
|
|
dynarec_wine_prereserve();
|
|
#endif
|
|
}
|
|
AddMainElfToLinkmap(elf_header);
|
|
// pre-load lib if needed
|
|
if(ld_preload.size) {
|
|
for(int i=0; i<ld_preload.size; ++i)
|
|
if(AddNeededLib(my_context->maplib, &my_context->neededlibs, NULL, 0, 0, (const char**)&ld_preload.paths[i], 1, my_context, emu)) {
|
|
printf_log(LOG_INFO, "Warning, cannot pre-load the lib (%s)\n", ld_preload.paths[i]);
|
|
}
|
|
}
|
|
FreeCollection(&ld_preload);
|
|
// Call librarian to load all dependant elf
|
|
if(LoadNeededLibs(elf_header, my_context->maplib, &my_context->neededlibs, NULL, 0, 0, my_context, emu)) {
|
|
printf_log(LOG_NONE, "Error: loading needed libs in elf %s\n", my_context->fullpath);
|
|
FreeBox86Context(&my_context);
|
|
return -1;
|
|
}
|
|
// reloc...
|
|
printf_log(LOG_DEBUG, "And now export symbols / relocation for %s...\n", ElfName(elf_header));
|
|
if(RelocateElf(my_context->maplib, NULL, 0, elf_header)) {
|
|
printf_log(LOG_NONE, "Error: relocating symbols in elf %s\n", my_context->fullpath);
|
|
FreeBox86Context(&my_context);
|
|
return -1;
|
|
}
|
|
// and handle PLT
|
|
RelocateElfPlt(my_context->maplib, NULL, 0, elf_header);
|
|
// defered init
|
|
RunDeferedElfInit(emu);
|
|
// update TLS of main elf
|
|
RefreshElfTLS(elf_header);
|
|
// do some special case check, _IO_2_1_stderr_ and friends, that are setup by libc, but it's already done here, so need to do a copy
|
|
ResetSpecialCaseMainElf(elf_header);
|
|
// init...
|
|
setupTrace(my_context);
|
|
// get entrypoint
|
|
my_context->ep = GetEntryPoint(my_context->maplib, elf_header);
|
|
|
|
atexit(endBox86);
|
|
|
|
// emulate!
|
|
printf_log(LOG_DEBUG, "Start x86emu on Main\n");
|
|
// Stack is ready, with stacked: NULL env NULL argv argc
|
|
SetEIP(emu, my_context->ep);
|
|
ResetFlags(emu);
|
|
PushExit(emu); // push to pop it just after
|
|
SetEDX(emu, Pop32(emu)); // EDX is exit function
|
|
DynaRun(emu);
|
|
// Get EAX
|
|
int ret = GetEAX(emu);
|
|
printf_log(LOG_DEBUG, "Emulation finished, EAX=%d\n", ret);
|
|
|
|
if(trace_func) {
|
|
box_free(trace_func);
|
|
trace_func = NULL;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
#endif //BUILD_LIB
|