mirror of
https://github.com/ptitSeb/box64.git
synced 2026-11-07 09:26:11 -10:00
1626 lines
59 KiB
C
1626 lines
59 KiB
C
// SPDX-License-Identifier: MIT
|
|
#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 <pwd.h>
|
|
#include <signal.h>
|
|
#include <sys/syscall.h>
|
|
#include <sys/mman.h>
|
|
#include <pthread.h>
|
|
#include <sys/prctl.h>
|
|
#include <sys/stat.h>
|
|
#include <stdarg.h>
|
|
#include <ctype.h>
|
|
#ifdef BOX32
|
|
#include <sys/personality.h>
|
|
#endif
|
|
#ifdef DYNAREC
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
#endif
|
|
|
|
#include "os.h"
|
|
#include "build_info.h"
|
|
#include "debug.h"
|
|
#include "fileutils.h"
|
|
#include "box64context.h"
|
|
#include "box64cpu.h"
|
|
#include "box64cpu_util.h"
|
|
#include "wine_tools.h"
|
|
#include "elfloader.h"
|
|
#include "custommem.h"
|
|
#include "box64stack.h"
|
|
#include "auxval.h"
|
|
#include "threads.h"
|
|
#include "x64trace.h"
|
|
#include "librarian.h"
|
|
#include "symbols.h"
|
|
#include "emu/x64run_private.h"
|
|
#include "elfs/elfloader_private.h"
|
|
#include "x64emu.h"
|
|
#include "library.h"
|
|
#include "core.h"
|
|
#include "env.h"
|
|
#include "dynacache.h"
|
|
#include "cleanup.h"
|
|
#include "freq.h"
|
|
#include "hostext.h"
|
|
#include "sysinfo.h"
|
|
|
|
box64context_t *my_context = NULL;
|
|
extern box64env_t box64env;
|
|
|
|
int box64_quit = 0;
|
|
int box64_exit_code = 0;
|
|
int box64_stdout_no_w = 0;
|
|
uintptr_t box64_pagesize;
|
|
path_collection_t box64_addlibs = {0};
|
|
int box64_is32bits = 0;
|
|
int box64_isAddressSpace32 = 0;
|
|
int box64_rdtsc = 0;
|
|
uint8_t box64_rdtsc_shift = 0;
|
|
int box64_mapclean = 0;
|
|
int box64_zoom = 0;
|
|
int box64_steam = 0;
|
|
int box64_steamcmd = 0;
|
|
int box64_srt_logger = 0;
|
|
int box64_musl = 0;
|
|
int box64_nolibs = 0;
|
|
int box64_hasinterp = 0;
|
|
char* box64_custom_gstreamer = NULL;
|
|
int box64_tcmalloc_minimal = 0;
|
|
uintptr_t fmod_smc_start = 0;
|
|
uintptr_t fmod_smc_end = 0;
|
|
uint32_t default_gs = 0x53;
|
|
uint32_t default_fs = 0;
|
|
int box64_isglibc234 = 0;
|
|
int box64_unittest_mode = 0;
|
|
sysinfo_t box64_sysinfo = { 0 };
|
|
|
|
#ifdef DYNAREC
|
|
cpu_ext_t cpuext = {0};
|
|
#endif
|
|
|
|
int box64_wine = 0;
|
|
const char* box64_guest_name = NULL;
|
|
const char* box64_wine_guest_name = NULL;
|
|
|
|
#ifdef HAVE_TRACE
|
|
uintptr_t trace_start = 0, trace_end = 0;
|
|
uintptr_t* trace_addrs = NULL;
|
|
int trace_addrs_count = 0;
|
|
char* trace_func = NULL;
|
|
#endif
|
|
|
|
FILE* ftrace = NULL;
|
|
char* ftrace_name = NULL;
|
|
int ftrace_opened = 0;
|
|
|
|
|
|
static void openFTrace(void)
|
|
{
|
|
const char* p = BOX64ENV(trace_file);
|
|
#ifndef MAX_PATH
|
|
#define MAX_PATH 4096
|
|
#endif
|
|
char tmp[MAX_PATH];
|
|
char tmp2[MAX_PATH];
|
|
int append = 0;
|
|
|
|
if (ftrace_name) box_free(ftrace_name);
|
|
ftrace_name = NULL;
|
|
|
|
if (p && strlen(p) && p[strlen(p) - 1] == '+') {
|
|
strncpy(tmp2, p, sizeof(tmp2));
|
|
tmp2[strlen(p)-1]='\0';
|
|
p = tmp2;
|
|
append = 1;
|
|
}
|
|
|
|
if (!p || ftrace_opened) return;
|
|
ftrace_opened = 1;
|
|
|
|
if (strstr(p, "\%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", GetTID(), next);
|
|
else
|
|
sprintf(pid, "%d", GetTID());
|
|
strcat(tmp, pid);
|
|
c = strstr(p, "\%pid") + strlen("\%pid");
|
|
strcat(tmp, c);
|
|
++next;
|
|
} while (FileExist(tmp, IS_FILE) && !append);
|
|
p = tmp;
|
|
}
|
|
|
|
if (!strcmp(p, "stdout"))
|
|
ftrace = stdout;
|
|
else if (!strcmp(p, "stderr"))
|
|
ftrace = stderr;
|
|
else {
|
|
if (append)
|
|
ftrace = fopen(p, "a");
|
|
else
|
|
ftrace = fopen(p, "w");
|
|
if (!ftrace) {
|
|
ftrace = stderr;
|
|
printf_log(LOG_INFO, "Cannot open trace file \"%s\" for writing (error=%s), fallback to stderr\n", p, strerror(errno));
|
|
} else {
|
|
ftrace_name = box_strdup(p);
|
|
if (!BOX64ENV(nobanner)) {
|
|
fprintf(stderr, "[BOX64] Trace %s to \"%s\" (set BOX64_NOBANNER=1 to suppress this log)\n", append ? "appended" : "redirected", p);
|
|
}
|
|
PrintBox64Version(0);
|
|
}
|
|
}
|
|
}
|
|
|
|
void computeRDTSC()
|
|
{
|
|
int hardware = 0;
|
|
box64_rdtsc_shift = 0;
|
|
#if defined(ARM64) || defined(RV64) || defined(LA64)
|
|
hardware = 1;
|
|
box64_rdtsc = 0; // allow hardware counter
|
|
#else
|
|
box64_rdtsc = 1;
|
|
printf_log(LOG_INFO, "Will use time-based emulation for RDTSC, even if hardware counters are available\n");
|
|
#endif
|
|
uint64_t freq = ReadTSCFrequency(NULL);
|
|
if(freq<((BOX64ENV(rdtsc_1ghz))?1000000000LL:1000000)) {
|
|
box64_rdtsc = 1;
|
|
if(hardware) printf_log(LOG_INFO, "Hardware counter is too slow (%d kHz), not using it\n", freq/1000);
|
|
hardware = 0;
|
|
freq = ReadTSCFrequency(NULL);
|
|
}
|
|
uint64_t efreq = freq;
|
|
while(efreq<2000000000 && box64_rdtsc_shift<31) { // minimum 2GHz, but not too much shift
|
|
++box64_rdtsc_shift;
|
|
efreq = freq<<box64_rdtsc_shift;
|
|
}
|
|
printf_log(LOG_INFO, "Will use %s counter measured at ", box64_rdtsc?"software":"hardware");
|
|
int ghz = freq>=1000000000LL;
|
|
if(ghz) freq/=100000000LL; else freq/=100000;
|
|
if (ghz) printf_log_prefix(0, LOG_INFO, "%d.%d GHz", freq / 10, freq % 10);
|
|
if (!ghz && (freq >= 1000)) printf_log_prefix(0, LOG_INFO, "%d MHz", freq / 10);
|
|
if (!ghz && (freq < 1000)) printf_log_prefix(0, LOG_INFO, "%d.%d MHz", freq / 10, freq % 10);
|
|
if(box64_rdtsc_shift) {
|
|
printf_log_prefix(0, LOG_INFO, " emulating ");
|
|
ghz = efreq>=1000000000LL;
|
|
if(ghz) efreq/=100000000LL; else efreq/=100000;
|
|
if (ghz) printf_log_prefix(0, LOG_INFO, "%d.%d GHz", efreq / 10, efreq % 10);
|
|
if (!ghz && (efreq >= 1000)) printf_log_prefix(0, LOG_INFO, "%d MHz", efreq / 10);
|
|
if (!ghz && (efreq < 1000)) printf_log_prefix(0, LOG_INFO, "%d.%d MHz", efreq / 10, efreq % 10);
|
|
}
|
|
printf_log_prefix(0, LOG_INFO, "\n");
|
|
}
|
|
|
|
static void displayMiscInfo(void)
|
|
{
|
|
openFTrace();
|
|
|
|
if ((BOX64ENV(nobanner) || BOX64ENV(log)) && ftrace == stdout)
|
|
box64_stdout_no_w = 1;
|
|
|
|
#if !defined(DYNAREC) && (defined(ARM64) || defined(RV64) || defined(LA64))
|
|
printf_log(LOG_INFO, "Warning: DynaRec is available on this host architecture, an interpreter-only build is probably not intended.\n");
|
|
#endif
|
|
|
|
char* p;
|
|
|
|
#ifdef DYNAREC
|
|
if (DetectHostCpuFeatures())
|
|
PrintHostCpuFeatures();
|
|
else {
|
|
printf_log(LOG_INFO, "Minimum CPU requirements not met, disabling DynaRec\n");
|
|
SET_BOX64ENV(dynarec, 0);
|
|
}
|
|
|
|
#if defined(LA64)
|
|
if (box64env.avx && !cpuext.lasx) {
|
|
box64env.avx = 0;
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
printf_log(LOG_INFO, "Running on %s with %d core%s, pagesize: %zd", box64_sysinfo.cpuname, box64_sysinfo.ncpu, box64_sysinfo.ncpu > 1 ? "s" : "", box64_pagesize);
|
|
if (BOX64ENV(maxcpu))
|
|
printf_log_prefix(0, LOG_INFO, ", emulating %d core%s", BOX64ENV(maxcpu), BOX64ENV(maxcpu) > 1 ? "s" : "");
|
|
if (BOX64ENV(skipcpu))
|
|
printf_log_prefix(0, LOG_INFO, " skipping %d core%s\n", BOX64ENV(skipcpu), BOX64ENV(skipcpu)>1? "s":"");
|
|
else
|
|
printf_log_prefix(0, LOG_INFO, "\n");
|
|
computeRDTSC();
|
|
}
|
|
|
|
static void hookMangoHud()
|
|
{
|
|
const char* config = getenv("MANGOHUD_CONFIG");
|
|
const char* configfile = getenv("MANGOHUD_CONFIGFILE");
|
|
if (config || configfile) return;
|
|
if (FileExist("/etc/MangoHud.conf", IS_FILE)) return;
|
|
const char* configdir = getenv("XDG_CONFIG_HOME");
|
|
const char* homedir = getenv("HOME");
|
|
#ifndef ANDROID
|
|
homedir = homedir ? homedir : getpwuid(getuid())->pw_dir;
|
|
#endif
|
|
if (!homedir) return;
|
|
|
|
static char config_base[256];
|
|
snprintf(config_base, sizeof(config_base), "%s%s",
|
|
configdir ? configdir : homedir,
|
|
configdir ? "" : "/.config");
|
|
|
|
const char* files[] = { "MangoHud.conf", "box64.conf" };
|
|
for (int i = 0; i < 2; i++) {
|
|
static char path[512];
|
|
snprintf(path, sizeof(path), "%s/MangoHud/%s", config_base, files[i]);
|
|
if (FileExist(path, IS_FILE)) return;
|
|
}
|
|
setenv("MANGOHUD_CONFIG", "legacy_layout=0,custom_text_center=" BOX64_BUILD_INFO_STRING ",gpu_stats=1,cpu_stats=1,fps=1,frame_timing=1", 0);
|
|
}
|
|
|
|
static void loadPath(path_collection_t *col, const char* defpath, const char* path)
|
|
{
|
|
if(path) {
|
|
ParseList(path, col, 1);
|
|
} else {
|
|
ParseList(defpath, col, 1);
|
|
}
|
|
}
|
|
|
|
void PrintCollection(path_collection_t* col, const char* env)
|
|
{
|
|
if (LOG_INFO<=BOX64ENV(log)) {
|
|
printf_log(LOG_INFO, "%s: ", env);
|
|
for(int i=0; i<col->size; i++)
|
|
printf_log_prefix(0, LOG_INFO, "%s%s", col->paths[i], (i==col->size-1)?"":":");
|
|
printf_log_prefix(0, LOG_INFO, "\n");
|
|
}
|
|
}
|
|
|
|
EXPORTDYN
|
|
int CountEnv(char** env)
|
|
{
|
|
char** p = env;
|
|
int c = 0;
|
|
while(*p) {
|
|
++c;
|
|
++p;
|
|
}
|
|
return c;
|
|
}
|
|
EXPORTDYN
|
|
int GatherEnv(char*** dest, char** env, char* prog)
|
|
{
|
|
char** p = env;
|
|
int idx = 0;
|
|
int _prog = 0;
|
|
while(*p) {
|
|
if(strncmp(*p, "_=", 2)==0) {
|
|
_prog = 1;
|
|
} else {
|
|
(*dest)[idx++] = box_strdup(*p);
|
|
}
|
|
++p;
|
|
}
|
|
// add "_=prog" at the end...
|
|
if(_prog && 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++] = NULL;
|
|
return 0;
|
|
}
|
|
|
|
void AddNewLibs(const char* list)
|
|
{
|
|
AppendList(&box64_addlibs, list, 0);
|
|
printf_log(LOG_INFO, "Adding %s to the libs\n", list);
|
|
}
|
|
|
|
void PrintHelp() {
|
|
PrintfFtrace(0, "%s\n", BOX64_BUILD_INFO_STRING);
|
|
PrintfFtrace(0, "Linux userspace x86-64 emulator with a twist.\n");
|
|
PrintfFtrace(0, "There are many environment variables to control Box64's behaviour, checkout the documentation here: https://github.com/ptitSeb/box64/blob/main/docs/USAGE.md\n\n");
|
|
PrintfFtrace(0, "USAGE:\n");
|
|
PrintfFtrace(0, "\tbox64 [options] path/to/x86_64/executable [args]\n");
|
|
PrintfFtrace(0, "\tbox64-bash\n");
|
|
PrintfFtrace(0, "OPTIONS:\n");
|
|
PrintfFtrace(0, "\t-v, --version print box64 version and quit\n");
|
|
PrintfFtrace(0, "\t-h, --help print this and quit\n");
|
|
PrintfFtrace(0, "\t-k, --kill-all kill all box64 instances\n");
|
|
PrintfFtrace(0, "\t-t, --test run a unit test\n");
|
|
PrintfFtrace(0, "\t--dynacache-list list of DynaCache file and their validity\n");
|
|
PrintfFtrace(0, "\t--dynacache-clean remove invalid DynaCache files\n");
|
|
}
|
|
|
|
void KillAllInstances()
|
|
{
|
|
|
|
struct dirent* entry;
|
|
ssize_t len;
|
|
char proc_path[PATH_MAX];
|
|
char exe_path[PATH_MAX+8];
|
|
char exe_target[PATH_MAX];
|
|
char self_name[PATH_MAX];
|
|
char self_name_with_deleted[PATH_MAX+16];
|
|
|
|
DIR* proc_dir = opendir("/proc");
|
|
if (proc_dir == NULL) {
|
|
perror("opendir(/proc)");
|
|
return;
|
|
}
|
|
|
|
ssize_t self_len = readlink("/proc/self/exe", exe_path, sizeof(exe_path) - 1);
|
|
if (self_len == -1) {
|
|
perror("readlink(/proc/self/exe)");
|
|
closedir(proc_dir);
|
|
return;
|
|
}
|
|
exe_path[self_len] = '\0';
|
|
|
|
char* base_name_self = strrchr(exe_path, '/');
|
|
base_name_self = base_name_self ? base_name_self + 1 : exe_path;
|
|
strncpy(self_name, base_name_self, sizeof(self_name));
|
|
self_name[sizeof(self_name) - 1] = '\0';
|
|
|
|
snprintf(self_name_with_deleted, sizeof(self_name_with_deleted), "%s (deleted)", self_name);
|
|
|
|
const pid_t self = getpid();
|
|
while ((entry = readdir(proc_dir))) {
|
|
const pid_t pid = atoi(entry->d_name);
|
|
if (!pid || pid == self) continue;
|
|
|
|
snprintf(proc_path, sizeof(proc_path), "/proc/%s", entry->d_name);
|
|
struct stat statbuf;
|
|
if (stat(proc_path, &statbuf) == -1 || !S_ISDIR(statbuf.st_mode)) continue;
|
|
snprintf(exe_path, sizeof(exe_path), "%s/exe", proc_path);
|
|
ssize_t len = readlink(exe_path, exe_target, sizeof(exe_target) - 1);
|
|
if (len == -1) continue;
|
|
exe_target[len] = '\0';
|
|
char* base_name = strrchr(exe_target, '/');
|
|
base_name = base_name ? base_name + 1 : exe_target;
|
|
if (strcmp(base_name, self_name) != 0 && strcmp(base_name, self_name_with_deleted) != 0) continue;
|
|
|
|
if (!kill(pid, SIGKILL)) {
|
|
printf_log(LOG_INFO, "Killed box64 process %d\n", pid);
|
|
} else {
|
|
printf_log(LOG_INFO, "Failed to kill box64 process %d: %s\n", pid, strerror(errno));
|
|
}
|
|
}
|
|
closedir(proc_dir);
|
|
}
|
|
|
|
static void addLibPaths(box64context_t* context)
|
|
{
|
|
if(BOX64ENV(emulated_libs)) {
|
|
char* p = BOX64ENV(emulated_libs);
|
|
ParseList(p, &context->box64_emulated_libs, 0);
|
|
if (my_context->box64_emulated_libs.size && BOX64ENV(log)) {
|
|
printf_log(LOG_INFO, "BOX64 will force the used of emulated libs for ");
|
|
for (int i=0; i<context->box64_emulated_libs.size; ++i)
|
|
printf_log_prefix(0, LOG_INFO, "%s ", context->box64_emulated_libs.paths[i]);
|
|
printf_log_prefix(0, LOG_INFO, "\n");
|
|
}
|
|
}
|
|
|
|
// Add libssl and libcrypto (and a few others) to prefer the emulated version because multiple versions exist
|
|
#define GO(A) AddPath(A, &context->box64_emulated_libs, 0);
|
|
GO("libssl.so.1");
|
|
GO("libssl.so.1.0.0");
|
|
GO("libcrypto.so.1");
|
|
GO("libcrypto.so.1.0.0");
|
|
GO("libunwind.so.8");
|
|
GO("libpng12.so.0");
|
|
GO("libpng16.so.16");
|
|
GO("libcurl.so.4");
|
|
GO("libtbbmalloc.so.2");
|
|
GO("libtbbmalloc_proxy.so.2");
|
|
GO("libicuuc.so.64");
|
|
GO("libicui18n.so.64");
|
|
GO("libicuuc.so.66");
|
|
GO("libicui18n.so.66");
|
|
GO("libicuuc.so.67");
|
|
GO("libicui18n.so.67");
|
|
GO("libicuuc.so.68");
|
|
GO("libicui18n.so.68");
|
|
GO("libicuuc.so.72");
|
|
GO("libicui18n.so.72");
|
|
GO("libicuuc.so.73");
|
|
GO("libicui18n.so.73");
|
|
GO("libicuuc.so.74");
|
|
GO("libicui18n.so.74");
|
|
GO("libicuuc.so.75");
|
|
GO("libicui18n.so.75");
|
|
GO("libicuuc.so.76");
|
|
GO("libicui18n.so.76");
|
|
GO("libSDL3.so.0");
|
|
#undef GO
|
|
|
|
if(BOX64ENV(nosigsegv)) {
|
|
context->no_sigsegv = 1;
|
|
}
|
|
if(BOX64ENV(nosigill)) {
|
|
context->no_sigill = 1;
|
|
}
|
|
if(BOX64ENV(addlibs)) {
|
|
AddNewLibs(BOX64ENV(addlibs));
|
|
}
|
|
loadPath(&context->box64_path, ".:bin", BOX64ENV(path));
|
|
if(getenv("PATH"))
|
|
AppendList(&context->box64_path, getenv("PATH"), 1); // in case some of the path are for x86 world
|
|
}
|
|
|
|
static void setupZydis(box64context_t* context)
|
|
{
|
|
#ifdef HAVE_TRACE
|
|
if ((BOX64ENV(trace_init) && strcmp(BOX64ENV(trace_init), "0")) || (BOX64ENV(trace) && strcmp(BOX64ENV(trace), "0"))) {
|
|
context->x64trace = 1;
|
|
}
|
|
if (context->x64trace || BOX64ENV(dynarec_disasm)) {
|
|
printf_log(LOG_INFO, "Initializing Zydis lib\n");
|
|
if (InitX64Trace(context)) {
|
|
printf_log(LOG_INFO, "Zydis init failed. No x86 trace activated\n");
|
|
context->x64trace = 0;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
EXPORTDYN
|
|
void LoadLDPath(box64context_t *context)
|
|
{
|
|
#ifdef BOX32
|
|
if(box64_is32bits)
|
|
loadPath(&context->box64_ld_lib, ".:lib:i386:bin:libs", BOX64ENV(ld_library_path));
|
|
else
|
|
#endif
|
|
loadPath(&context->box64_ld_lib, ".:lib:lib64:x86_64:bin64:libs64", BOX64ENV(ld_library_path));
|
|
#ifndef TERMUX
|
|
if(box64_is32bits) {
|
|
#ifdef BOX32
|
|
if(FileExist("/lib/i386-linux-gnu", 0))
|
|
AddPath("/lib/i386-linux-gnu", &context->box64_ld_lib, 1);
|
|
if(FileExist("/usr/lib/i386-linux-gnu", 0))
|
|
AddPath("/usr/lib/i386-linux-gnu", &context->box64_ld_lib, 1);
|
|
if(FileExist("/usr/i386-linux-gnu/lib", 0))
|
|
AddPath("/usr/i386-linux-gnu/lib", &context->box64_ld_lib, 1);
|
|
if(FileExist("/usr/i686-linux-gnu/lib", 0))
|
|
AddPath("/usr/i686-linux-gnu/lib", &context->box64_ld_lib, 1);
|
|
if(FileExist("/usr/lib/box64-i386-linux-gnu", 0))
|
|
AddPath("/usr/lib/box64-i386-linux-gnu", &context->box64_ld_lib, 1);
|
|
if(FileExist("/opt/box64/lib32", 0))
|
|
AddPath("/opt/box64/lib32", &context->box64_ld_lib, 1);
|
|
if(FileExist("/data/data/com.termux/files/usr/glibc/lib/i386-linux-gnu", 0))
|
|
AddPath("/data/data/com.termux/files/usr/glibc/lib/i386-linux-gnu", &context->box64_ld_lib, 1);
|
|
if(FileExist("/data/data/com.termux/files/usr/glibc/lib/box64-i386-linux-gnu", 0))
|
|
AddPath("/data/data/com.termux/files/usr/glibc/lib/box64-i386-linux-gnu", &context->box64_ld_lib, 1);
|
|
if(FileExist("/data/data/com.termux/files/usr/lib/box64-i386-linux-gnu", 0))
|
|
AddPath("/data/data/com.termux/files/usr/lib/box64-i386-linux-gnu", &context->box64_ld_lib, 1);
|
|
#endif
|
|
} else {
|
|
if(FileExist("/lib/x86_64-linux-gnu", 0))
|
|
AddPath("/lib/x86_64-linux-gnu", &context->box64_ld_lib, 1);
|
|
if(FileExist("/usr/lib/x86_64-linux-gnu", 0))
|
|
AddPath("/usr/lib/x86_64-linux-gnu", &context->box64_ld_lib, 1);
|
|
if(FileExist("/usr/x86_64-linux-gnu/lib", 0))
|
|
AddPath("/usr/x86_64-linux-gnu/lib", &context->box64_ld_lib, 1);
|
|
if(FileExist("/usr/lib/box64-x86_64-linux-gnu", 0))
|
|
AddPath("/usr/lib/box64-x86_64-linux-gnu", &context->box64_ld_lib, 1);
|
|
if(FileExist("/opt/box64/lib64", 0))
|
|
AddPath("/opt/box64/lib64", &context->box64_ld_lib, 1);
|
|
if(FileExist("/data/data/com.termux/files/usr/glibc/lib/x86_64-linux-gnu", 0))
|
|
AddPath("/data/data/com.termux/files/usr/glibc/lib/x86_64-linux-gnu", &context->box64_ld_lib, 1);
|
|
if(FileExist("/data/data/com.termux/files/usr/glibc/lib/box64-x86_64-linux-gnu", 0))
|
|
AddPath("/data/data/com.termux/files/usr/glibc/lib/box64-x86_64-linux-gnu", &context->box64_ld_lib, 1);
|
|
if(FileExist("/data/data/com.termux/files/usr/lib/box64-x86_64-linux-gnu", 0))
|
|
AddPath("/data/data/com.termux/files/usr/lib/box64-x86_64-linux-gnu", &context->box64_ld_lib, 1);
|
|
}
|
|
#else
|
|
//TODO: Add Termux Library Path - Lily
|
|
if(box64_is32bits) {
|
|
#ifdef BOX32
|
|
if(FileExist("/data/data/com.termux/files/usr/lib/i386-linux-gnu", 0))
|
|
AddPath("/data/data/com.termux/files/usr/lib/i386-linux-gnu", &context->box64_ld_lib, 1);
|
|
#endif
|
|
} else {
|
|
if(FileExist("/data/data/com.termux/files/usr/lib/x86_64-linux-gnu", 0))
|
|
AddPath("/data/data/com.termux/files/usr/lib/x86_64-linux-gnu", &context->box64_ld_lib, 1);
|
|
}
|
|
#endif
|
|
if(getenv("LD_LIBRARY_PATH"))
|
|
PrependList(&context->box64_ld_lib, getenv("LD_LIBRARY_PATH"), 1); // in case some of the path are for x86 world
|
|
}
|
|
|
|
#ifdef HAVE_TRACE
|
|
static void setupTraceAddrList(char *p)
|
|
{
|
|
int cap = 16;
|
|
int count = 0;
|
|
char *saveptr = NULL;
|
|
|
|
uintptr_t* addrs = (uintptr_t*)box_malloc(cap * sizeof(uintptr_t));
|
|
char* tmp = box_strdup(p);
|
|
char* tok = strtok_r(tmp, ":", &saveptr);
|
|
|
|
while (tok) {
|
|
if (count >= cap) {
|
|
cap *= 2;
|
|
addrs = (uintptr_t*)box_realloc(addrs, cap * sizeof(uintptr_t));
|
|
}
|
|
|
|
char* endptr;
|
|
uintptr_t addr = (uintptr_t)strtoul(tok, &endptr, 0);
|
|
if (endptr != tok)
|
|
addrs[count++] = addr;
|
|
|
|
tok = strtok_r(NULL, ":", &saveptr);
|
|
}
|
|
|
|
if (count > 0)
|
|
SetTraceAddrs(addrs, count);
|
|
|
|
box_free(tmp);
|
|
box_free(addrs);
|
|
}
|
|
#endif
|
|
|
|
EXPORTDYN
|
|
void setupTraceInit()
|
|
{
|
|
#ifdef HAVE_TRACE
|
|
char* p = BOX64ENV(trace_init);
|
|
if(p) {
|
|
setbuf(stdout, NULL);
|
|
uintptr_t s_trace_start=0, s_trace_end=0;
|
|
if (strcmp(p, "1")==0)
|
|
SetTraceEmu(0, 0);
|
|
else if (strcmp(p, "0")==0)
|
|
;
|
|
else if (strchr(p,'-')) {
|
|
if(sscanf(p, "%ld-%ld", &s_trace_start, &s_trace_end)!=2) {
|
|
if(sscanf(p, "0x%lX-0x%lX", &s_trace_start, &s_trace_end)!=2)
|
|
sscanf(p, "%lx-%lx", &s_trace_start, &s_trace_end);
|
|
}
|
|
if(s_trace_start || s_trace_end)
|
|
SetTraceEmu(s_trace_start, s_trace_end);
|
|
} else if (strchr(p,':')) {
|
|
// Parse BOX64_TRACE=0x1000:0x2000:0x3000 format.
|
|
setupTraceAddrList(p);
|
|
} else {
|
|
int veropt = 1;
|
|
int ver = 0;
|
|
const char* vername = NULL;
|
|
int search = 0;
|
|
for(int i=0; i<my_context->elfsize && !search; ++i) {
|
|
search = ElfGetSymbolStartEnd(my_context->elfs[i], &s_trace_start, &s_trace_end, p, &ver, &vername, 1, &veropt)?1:0;
|
|
if(!search)
|
|
search = ElfGetSymTabStartEnd(my_context->elfs[i], &s_trace_start, &s_trace_end, p);
|
|
}
|
|
if(search) {
|
|
SetTraceEmu(s_trace_start, s_trace_end);
|
|
printf_log(LOG_INFO, "TRACE on %s only (%p-%p)\n", p, (void*)s_trace_start, (void*)s_trace_end);
|
|
} else {
|
|
printf_log(LOG_NONE, "Warning, Symbol to trace (\"%s\") not found, Disabling trace\n", p);
|
|
SetTraceEmu(0, 100); // disabling trace, mostly
|
|
}
|
|
}
|
|
} else {
|
|
if(BOX64ENV(trace) && strcmp(BOX64ENV(trace), "0"))
|
|
SetTraceEmu(0, 1);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void setupTraceMapLib(lib_t* maplib)
|
|
{
|
|
#ifdef HAVE_TRACE
|
|
if(!trace_func)
|
|
return;
|
|
char* p = trace_func;
|
|
uintptr_t s_trace_start=0, s_trace_end=0;
|
|
void* search = NULL;
|
|
if(GetAnySymbolStartEnd(maplib, p, &s_trace_start, &s_trace_end, 0, NULL, 1)) {
|
|
SetTraceEmu(s_trace_start, s_trace_end);
|
|
printf_log(LOG_INFO, "TRACE on %s only (%p-%p)\n", p, (void*)s_trace_start, (void*)s_trace_end);
|
|
box_free(trace_func);
|
|
trace_func = NULL;
|
|
return;
|
|
} else if(GetSymTabStartEnd(maplib, p, &s_trace_start, &s_trace_end)) {
|
|
SetTraceEmu(s_trace_start, s_trace_end);
|
|
printf_log(LOG_INFO, "TRACE on %s only (%p-%p)\n", p, (void*)s_trace_start, (void*)s_trace_end);
|
|
box_free(trace_func);
|
|
trace_func = NULL;
|
|
return;
|
|
} else {
|
|
printf_log(LOG_NONE, "Warning, Symbol to trace (\"%s\") not found. Trying to set trace later\n", p);
|
|
SetTraceEmu(0, 1); // disabling trace, mostly
|
|
}
|
|
#endif
|
|
}
|
|
|
|
EXPORTDYN
|
|
void setupTrace()
|
|
{
|
|
#ifdef HAVE_TRACE
|
|
char* p = BOX64ENV(trace);
|
|
if(p) {
|
|
setbuf(stdout, NULL);
|
|
uintptr_t s_trace_start=0, s_trace_end=0;
|
|
if (strcmp(p, "1")==0)
|
|
SetTraceEmu(0, 0);
|
|
else if (strcmp(p, "0")==0)
|
|
;
|
|
else if (strchr(p,'-')) {
|
|
if(sscanf(p, "%ld-%ld", &s_trace_start, &s_trace_end)!=2) {
|
|
if(sscanf(p, "0x%lX-0x%lX", &s_trace_start, &s_trace_end)!=2)
|
|
sscanf(p, "%lx-%lx", &s_trace_start, &s_trace_end);
|
|
}
|
|
if(s_trace_start || s_trace_end) {
|
|
SetTraceEmu(s_trace_start, s_trace_end);
|
|
if(!s_trace_start && s_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*)s_trace_start, (void*)s_trace_end);
|
|
}
|
|
}
|
|
} else if (strchr(p,':')) {
|
|
// Parse BOX64_TRACE=0x1000:0x2000:0x3000 format.
|
|
setupTraceAddrList(p);
|
|
} else {
|
|
int search = 0;
|
|
if (my_context->elfs) {
|
|
int veropt = 1;
|
|
int ver = 0;
|
|
const char* vername = NULL;
|
|
for(int i=0; i<my_context->elfsize && !search; ++i) {
|
|
search = ElfGetSymbolStartEnd(my_context->elfs[i], &s_trace_start, &s_trace_end, p, &ver, &vername, 1, &veropt)?1:0;
|
|
if(!search)
|
|
search = ElfGetSymTabStartEnd(my_context->elfs[i], &s_trace_start, &s_trace_end, p);
|
|
}
|
|
}
|
|
if(search) {
|
|
SetTraceEmu(s_trace_start, s_trace_end);
|
|
printf_log(LOG_INFO, "TRACE on %s only (%p-%p)\n", p, (void*)s_trace_start, (void*)s_trace_end);
|
|
} else {
|
|
printf_log(LOG_NONE, "Warning, Symbol to trace (\"%s\") not found. Trying to set trace later\n", p);
|
|
SetTraceEmu(0, 1); // disabling trace, mostly
|
|
if(trace_func)
|
|
box_free(trace_func);
|
|
trace_func = box_strdup(p);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
#ifndef STATICBUILD
|
|
void endMallocHook();
|
|
#endif
|
|
|
|
void finiPendingDLOpenedNoUnload(x64emu_t* emu);
|
|
|
|
void endBox64()
|
|
{
|
|
if(!my_context || box64_quit)
|
|
return;
|
|
|
|
SerializeAllMapping(); // just to be safe
|
|
// then call all the fini
|
|
dynarec_log(LOG_DEBUG, "endBox64() called\n");
|
|
box64_quit = 1;
|
|
x64emu_t* emu = thread_get_emu();
|
|
void startTimedExit();
|
|
startTimedExit();
|
|
// atexit first
|
|
printf_log(LOG_DEBUG, "Calling atexit registered functions (exiting box64)\n");
|
|
CallAllCleanup(emu);
|
|
printf_log(LOG_DEBUG, "Calling fini for all loaded elfs and unload native libs\n");
|
|
//void closeAllDLOpened();
|
|
//closeAllDLOpened(); // close residual dlopened libs. Disabled, seems like a bad idea, better to unload with proper dependancies
|
|
RunElfFini(my_context->elfs[0], emu);
|
|
printf_log(LOG_DEBUG, "Finished calling fini for dlopened libs\n");
|
|
finiPendingDLOpenedNoUnload(emu); // call fini for dlopened libs, but don't unload them, as they might be needed by the main elf fini
|
|
// unload needed libs
|
|
needed_libs_t* needed = my_context->elfs[0]->needed;
|
|
printf_log(LOG_DEBUG, "Unloaded main elf: Will Dec RefCount of %d libs\n", needed?needed->size:0);
|
|
int workers = get_active_emu_workers();
|
|
if (workers > 0) {
|
|
const int sleep_us = 10000; // 10ms
|
|
const int max_wait_ms = 2000; // 2s
|
|
int waited_ms = 0;
|
|
|
|
printf_log(LOG_DEBUG, "endBox64: waiting emu workers to exit (n=%d)\n", workers);
|
|
while ((workers = get_active_emu_workers()) > 0 && waited_ms < max_wait_ms) {
|
|
usleep(sleep_us);
|
|
waited_ms += sleep_us / 1000;
|
|
}
|
|
|
|
if (workers > 0) {
|
|
printf_log(LOG_DEBUG, "endBox64: %d emu workers still alive after %dms, skip unload/free to avoid UAF crash\n", workers, waited_ms);
|
|
return;
|
|
}
|
|
}
|
|
if(needed)
|
|
for(int i=0; i<needed->size; ++i)
|
|
DecRefCount(&needed->libs[i], emu);
|
|
// all done, free context
|
|
#ifndef STATICBUILD
|
|
endMallocHook();
|
|
#endif
|
|
SerializeAllMapping(); // to be safe
|
|
FreeBox64Context(&my_context);
|
|
#ifdef DYNAREC
|
|
// disable dynarec now
|
|
SET_BOX64ENV(dynarec, 0);
|
|
#endif
|
|
if(BOX64ENV(libgl)) {
|
|
box_free(BOX64ENV(libgl));
|
|
SET_BOX64ENV(libgl, NULL);
|
|
}
|
|
if(box64_custom_gstreamer) {
|
|
box_free(box64_custom_gstreamer);
|
|
box64_custom_gstreamer = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
static void free_contextargv()
|
|
{
|
|
for(int i=0; i<my_context->argc; ++i)
|
|
box_free(my_context->argv[i]);
|
|
}
|
|
|
|
static void add_argv(const char* what) {
|
|
int there = 0;
|
|
for(int i=1; i<my_context->argc && !there; ++i)
|
|
if(!strcmp(my_context->argv[i], what))
|
|
there = 1;
|
|
if(!there) {
|
|
// try to prepend the arg, not appending
|
|
static int where = 0;
|
|
if(!where)
|
|
where = (box64_wine)?2:1;
|
|
printf_log(LOG_INFO, "Inserting \"%s\" to the argument %d\n", what, where);
|
|
my_context->argv = (char**)box_realloc(my_context->argv, (my_context->argc+1+1)*sizeof(char*));
|
|
memmove(my_context->argv+where+1, my_context->argv+where, (my_context->argc-where)*sizeof(char*));
|
|
my_context->argv[where] = box_strdup(what);
|
|
my_context->argc++;
|
|
where++;
|
|
}
|
|
}
|
|
|
|
#ifndef STATICBUILD
|
|
void pressure_vessel(int argc, const char** argv, int nextarg, const char* prog);
|
|
#endif
|
|
extern char** environ;
|
|
|
|
int initialize(int argc, const char **argv, char** env, x64emu_t** emulator, elfheader_t** elfheader, int exec)
|
|
{
|
|
#ifndef STATICBUILD
|
|
init_malloc_hook();
|
|
#endif
|
|
init_auxval(argc, argv, environ?environ:env);
|
|
ftrace = stderr; // init early: BOX64_VERSION path below uses PrintfFtrace
|
|
// analogue to QEMU_VERSION in qemu-user-mode emulation
|
|
if(getenv("BOX64_VERSION")) {
|
|
PrintBox64Version(0);
|
|
exit(0);
|
|
}
|
|
// trying to open and load 1st arg
|
|
if(argc==1) {
|
|
/*PrintBox64Version(1);
|
|
PrintHelp();
|
|
return 1;*/
|
|
printf("[BOX64] Missing operand after 'box64'\n");
|
|
printf("See 'box64 --help' for more information.\n");
|
|
exit(0);
|
|
}
|
|
if(argc>1 && !strcmp(argv[1], "/usr/bin/gdb") && BOX64ENV(trace_file))
|
|
exit(0);
|
|
// uname -m is redirected to box64 -m
|
|
if (argc == 2 && (!strcmp(argv[1], "-m") || !strcmp(argv[1], "-p") || !strcmp(argv[1], "-i"))) {
|
|
printf("x86_64\n");
|
|
exit(0);
|
|
}
|
|
|
|
if (argc >= 3 && (!strcmp(argv[1], "--test") || !strcmp(argv[1], "-t"))) {
|
|
box64_unittest_mode = 1;
|
|
exit(unittest(argc, argv));
|
|
}
|
|
|
|
ftrace = stderr;
|
|
|
|
// grab pagesize
|
|
box64_pagesize = sysconf(_SC_PAGESIZE);
|
|
if(!box64_pagesize)
|
|
box64_pagesize = 4096;
|
|
|
|
LoadEnvVariables();
|
|
InitializeEnvFiles();
|
|
|
|
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")) {
|
|
PrintBox64Version(0);
|
|
exit(0);
|
|
}
|
|
if(!strcmp(prog, "-h") || !strcmp(prog, "--help")) {
|
|
PrintHelp();
|
|
exit(0);
|
|
}
|
|
if (!strcmp(prog, "-k") || !strcmp(prog, "--kill-all")) {
|
|
KillAllInstances();
|
|
#ifdef DYNAREC
|
|
DetectHostCpuFeatures();
|
|
#endif
|
|
DynaCacheClean();
|
|
exit(0);
|
|
}
|
|
if(!strcmp(prog, "-dl") || !strcmp(prog, "--dynacache-list")) {
|
|
#ifdef DYNAREC
|
|
DetectHostCpuFeatures();
|
|
#endif
|
|
DynaCacheList(argv[nextarg+1]);
|
|
exit(0);
|
|
}
|
|
if(!strcmp(prog, "-dc") || !strcmp(prog, "--dynacache-clean")) {
|
|
#ifdef DYNAREC
|
|
DetectHostCpuFeatures();
|
|
#endif
|
|
DynaCacheClean();
|
|
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("[BOX64] Nothing to run\n");
|
|
exit(0);
|
|
}
|
|
|
|
if (!BOX64ENV(nobanner)) PrintBox64Version(1);
|
|
|
|
InitializeSystemInfo();
|
|
displayMiscInfo();
|
|
|
|
hookMangoHud();
|
|
|
|
char* bashpath = NULL;
|
|
{
|
|
char* p = BOX64ENV(bash);
|
|
if(p) {
|
|
if(FileIsX64ELF(p)) {
|
|
bashpath = p;
|
|
printf_log(LOG_INFO, "Using bash \"%s\"\n", bashpath);
|
|
} else {
|
|
printf_log(LOG_INFO, "The x86_64 bash \"%s\" is not an x86_64 binary.\n", p);
|
|
}
|
|
}
|
|
}
|
|
char* pythonpath = NULL;
|
|
{
|
|
char* p = BOX64ENV(python3);
|
|
if(p) {
|
|
if(FileExist(p, IS_FILE)) {
|
|
pythonpath = p;
|
|
printf_log(LOG_INFO, "Using python helper \"%s\"\n", pythonpath);
|
|
} else {
|
|
printf_log(LOG_INFO, "The python helper \"%s\" was not found.\n", p);
|
|
}
|
|
}
|
|
}
|
|
|
|
// precheck, for win-preload
|
|
const char* prog_ = strrchr(prog, '/');
|
|
if(!prog_) prog_ = prog; else ++prog_;
|
|
if(!strcmp(prog_, "wine-preloader") || !strcmp(prog_, "wine64-preloader")) {
|
|
// wine-preloader detecter, skipping it if next arg exist and is an x86 binary
|
|
int x64 = (nextarg<argc)?FileIsX64ELF(argv[nextarg]):0;
|
|
#ifdef BOX32
|
|
int x86 = (nextarg<argc)?FileIsX86ELF(argv[nextarg]):0;
|
|
#else
|
|
int x86 = 0;
|
|
#endif
|
|
if(x64 || x86) {
|
|
prog = argv[++nextarg];
|
|
printf_log(LOG_INFO, "Wine preloader detected, loading \"%s\" directly\n", prog);
|
|
wine_preloaded = 1;
|
|
prog_ = strrchr(prog, '/');
|
|
if(!prog_) prog_ = prog; else ++prog_;
|
|
}
|
|
}
|
|
int ld_libs_args = -1;
|
|
int is_custom_gstreamer = 0;
|
|
// check if this is wine
|
|
if(!strcmp(prog_, "wine64")
|
|
|| !strcmp(prog_, "wine64-development")
|
|
|| !strcmp(prog_, "wine")) {
|
|
const char* prereserve = getenv("WINEPRELOADRESERVE");
|
|
printf_log(LOG_INFO, "Wine64 detected, WINEPRELOADRESERVE=\"%s\"\n", prereserve?prereserve:"");
|
|
if(wine_preloaded || 1) {
|
|
wine_prereserve(prereserve);
|
|
}
|
|
// special case for winedbg, doesn't work anyway
|
|
if(argv[nextarg+1] && strstr(argv[nextarg+1], "winedbg")==argv[nextarg+1]) {
|
|
if(getenv("BOX64_WINEDBG")) {
|
|
SET_BOX64ENV(nobanner, 1);
|
|
BOX64ENV(log) = 0;
|
|
} else {
|
|
printf_log(LOG_NONE, "winedbg detected, not launching it!\n");
|
|
exit(0); // exiting, it doesn't work anyway
|
|
}
|
|
}
|
|
box64_wine = 1;
|
|
// Try to get the name of the exe being run, to ApplyEnvFileEntry laters
|
|
if(argv[nextarg+1] && argv[nextarg+1][0]!='-' && strlen(argv[nextarg+1])>4 /*&& !strcasecmp(argv[nextarg+1]+strlen(argv[nextarg+1])-4, ".exe")*/) {
|
|
const char* pp = strrchr(argv[nextarg+1], '/');
|
|
if(pp)
|
|
box64_wine_guest_name = pp + 1;
|
|
else {
|
|
pp = strrchr(argv[nextarg+1], '\\');
|
|
if(pp)
|
|
box64_wine_guest_name = pp + 1;
|
|
else
|
|
box64_wine_guest_name = argv[nextarg + 1];
|
|
}
|
|
}
|
|
if (box64_wine_guest_name) printf_log(LOG_INFO, "Detected running wine with \"%s\"\n", box64_wine_guest_name);
|
|
} else if(strstr(prog, "ld-musl-x86_64.so.1")) {
|
|
// check if ld-musl-x86_64.so.1 is used
|
|
printf_log(LOG_INFO, "ld-musl detected. Trying to workaround and use system ld-linux\n");
|
|
box64_musl = 1;
|
|
// skip ld-musl and go through args unti "--" is found, handling "--library-path" to add some libs to BOX64_LD_LIBRARY
|
|
++nextarg;
|
|
while(strcmp(argv[nextarg], "--")) {
|
|
if(!strcmp(argv[nextarg], "--library-path")) {
|
|
++nextarg;
|
|
ld_libs_args = nextarg;
|
|
}
|
|
++nextarg;
|
|
}
|
|
++nextarg;
|
|
prog = argv[nextarg];
|
|
} else if(!strcmp(prog_, "steam") ) {
|
|
printf_log(LOG_INFO, "steam detected\n");
|
|
box64_steam = 1;
|
|
} else if(!strcmp(prog_, "steamcmd")) {
|
|
printf_log(LOG_INFO, "steamcmd detected\n");
|
|
box64_steamcmd = 1;
|
|
} else if(!strcmp(prog_, "srt-logger")) {
|
|
box64_srt_logger = 1;
|
|
} else if(!strcmp(prog_, "wineserver")) {
|
|
// check if this is wineserver
|
|
box64_wine = 1;
|
|
}
|
|
// Create a new context
|
|
#ifdef BOX32
|
|
box64_is32bits = FileIsX86ELF(prog);
|
|
#endif
|
|
my_context = NewBox64Context(argc - nextarg);
|
|
|
|
addLibPaths(my_context);
|
|
if(box64_wine) {
|
|
// check if it's proton, with it's custom gstreamer build, to disable gtk3 loading
|
|
char* tmp_ = ResolveFile(prog_, &my_context->box64_path);
|
|
if(tmp_) {
|
|
char tmp[strlen(tmp_)+100];
|
|
strcpy(tmp, tmp_);
|
|
box_free(tmp_);
|
|
char* pp = strrchr(tmp, '/');
|
|
if(pp) {
|
|
*pp = '\0'; // remove the wine binary call
|
|
strcat(tmp, "/../lib64/gstreamer-1.0");
|
|
// check if it exist
|
|
if(FileExist(tmp, 0)) {
|
|
box64_custom_gstreamer = box_strdup(tmp);
|
|
} else {
|
|
*pp = '\0';
|
|
strcat(tmp, "/../lib/x86_64-linux-gnu/gstreamer-1.0");
|
|
if(FileExist(tmp, 0)) {
|
|
box64_custom_gstreamer = box_strdup(tmp);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Append ld_list if it exist
|
|
if(ld_libs_args!=-1)
|
|
PrependList(&my_context->box64_ld_lib, argv[ld_libs_args], 1);
|
|
if(is_custom_gstreamer) //TODO: is this still needed?
|
|
AddPath("libwayland-client.so.0", &my_context->box64_emulated_libs, 0);
|
|
|
|
my_context->box64path = ResolveFile(argv[0], &my_context->box64_path);
|
|
// 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 BOX64_PATH
|
|
my_context->envv = (char**)box_calloc(my_context->envc+1, sizeof(char*));
|
|
|
|
#ifndef STATICBUILD
|
|
// pre-check for pressure-vessel-wrap
|
|
if(!strcmp(prog_, "pressure-vessel-wrap")) {
|
|
printf_log(LOG_INFO, "pressure-vessel-wrap detected, bashpath=%s\n", my_context->bashpath?my_context->bashpath:"(nil)");
|
|
unsetenv("BOX64_ARG0");
|
|
if(!my_context->bashpath)
|
|
my_context->bashpath = ResolveFile("box64-bash", &my_context->box64_path);
|
|
if(!my_context->pythonpath)
|
|
my_context->pythonpath = ResolveFile("box64-python", &my_context->box64_path);
|
|
pressure_vessel(argc, argv, nextarg+1, prog);
|
|
}
|
|
#endif
|
|
|
|
path_collection_t ld_preload = {0};
|
|
if(getenv("BOX64_LD_PRELOAD")) {
|
|
char* p = getenv("BOX64_LD_PRELOAD");
|
|
ParseList(p, &ld_preload, 0);
|
|
if (ld_preload.size && BOX64ENV(log)) {
|
|
printf_log(LOG_INFO, "BOX64 trying to Preload ");
|
|
for (int i=0; i<ld_preload.size; ++i)
|
|
printf_log_prefix(0, LOG_INFO, "%s ", ld_preload.paths[i]);
|
|
printf_log_prefix(0, LOG_INFO, "\n");
|
|
}
|
|
}
|
|
if(getenv("LD_PRELOAD")) {
|
|
char* p = getenv("LD_PRELOAD");
|
|
if(strstr(p, "libtcmalloc_minimal.so.0"))
|
|
box64_tcmalloc_minimal = 1;
|
|
if(strstr(p, "libtcmalloc_minimal.so.4"))
|
|
box64_tcmalloc_minimal = 1;
|
|
if(strstr(p, "libtcmalloc_minimal_debug.so.4"))
|
|
box64_tcmalloc_minimal = 1;
|
|
if(strstr(p, "libasan.so"))
|
|
box64_tcmalloc_minimal = 1; // it seems Address Sanitizer doesn't handle dlsym'd malloc very well
|
|
AppendList(&ld_preload, p, 0);
|
|
if (ld_preload.size && BOX64ENV(log)) {
|
|
printf_log(LOG_INFO, "BOX64 trying to Preload ");
|
|
for (int i=0; i<ld_preload.size; ++i)
|
|
printf_log_prefix(0, LOG_INFO, "%s ", ld_preload.paths[i]);
|
|
printf_log_prefix(0, LOG_INFO, "\n");
|
|
}
|
|
}
|
|
// print PATH and LD_LIB used
|
|
PrintCollection(&my_context->box64_ld_lib, "Library search path");
|
|
PrintCollection(&my_context->box64_path, "Binary search path");
|
|
// 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] = ResolveFileSoft(prog, &my_context->box64_path);
|
|
//
|
|
GatherEnv(&my_context->envv, environ?environ:env, my_context->argv[0]);
|
|
if (BOX64ENV(dump) || BOX64ENV(log)<=LOG_DEBUG) {
|
|
for (int i=0; i<my_context->envc; ++i)
|
|
printf_dump(LOG_DEBUG, " Env[%02d]: %s\n", i, my_context->envv[i]);
|
|
}
|
|
// check if box86 is present
|
|
{
|
|
my_context->box86path = box_strdup(my_context->box64path);
|
|
#ifndef BOX32
|
|
if(strstr(my_context->box86path, "box64")) {
|
|
char* p = strrchr(my_context->box86path, '6'); // get the 6 of box64
|
|
p[0] = '8'; p[1] = '6'; // change 64 to 86
|
|
} else {
|
|
box_free(my_context->box86path);
|
|
my_context->box86path = ResolveFileSoft("box86", &my_context->box64_path);
|
|
}
|
|
if(my_context->box86path && !FileExist(my_context->box86path, IS_FILE)) {
|
|
box_free(my_context->box86path);
|
|
my_context->box86path = NULL;
|
|
}
|
|
#endif
|
|
}
|
|
box64_guest_name = strrchr(prog, '/');
|
|
if (!box64_guest_name)
|
|
box64_guest_name = prog;
|
|
else
|
|
++box64_guest_name;
|
|
if(box64_wine) {
|
|
#ifdef ANDROID
|
|
AddPath("libdl.so", &ld_preload, 0);
|
|
#else
|
|
AddPath("libdl.so.2", &ld_preload, 0);
|
|
#endif
|
|
}
|
|
// special case for zoom
|
|
if (strstr(box64_guest_name, "zoom") == box64_guest_name) {
|
|
printf_log(LOG_INFO, "Zoom detected, Trying to use system libturbojpeg if possible\n");
|
|
box64_zoom = 1;
|
|
}
|
|
// special case for bash
|
|
int setup_bash_rcfile = 0;
|
|
if (!strcmp(box64_guest_name, "bash") || !strcmp(box64_guest_name, "box64-bash")) {
|
|
printf_log(LOG_INFO, "Bash detected, disabling banner\n");
|
|
if (!BOX64ENV(nobanner)) {
|
|
setenv("BOX86_NOBANNER", "1", 0);
|
|
setenv("BOX64_NOBANNER", "1", 0);
|
|
}
|
|
if (!bashpath) {
|
|
bashpath = (char*)prog;
|
|
SET_BOX64ENV(bash, (char*)prog);
|
|
setenv("BOX64_BASH", prog, 1);
|
|
}
|
|
setup_bash_rcfile = 1;
|
|
}
|
|
if(!bashpath)
|
|
bashpath = ResolveFile("box64-bash", &my_context->box64_path);
|
|
if(bashpath)
|
|
my_context->bashpath = box_strdup(bashpath);
|
|
if(!pythonpath)
|
|
pythonpath = ResolveFile("box64-python", &my_context->box64_path);
|
|
if(pythonpath)
|
|
my_context->pythonpath = box_strdup(pythonpath);
|
|
|
|
int applied = ApplyEnvFileEntry(box64_guest_name);
|
|
if (box64_wine && box64_wine_guest_name) {
|
|
applied |= ApplyEnvFileEntry(box64_wine_guest_name);
|
|
box64_wine_guest_name = NULL;
|
|
}
|
|
if (applied) {
|
|
printf_log(LOG_INFO, "Applied settings from rcfile\n");
|
|
displayMiscInfo();
|
|
}
|
|
PrintEnvVariables(&box64env, LOG_INFO);
|
|
setupZydis(my_context);
|
|
|
|
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]);
|
|
}
|
|
|
|
// Setup custom bash rcfile iff no args are present
|
|
if (setup_bash_rcfile && my_context->argc == 1) {
|
|
add_argv("--rcfile");
|
|
add_argv("box64-custom-bashrc-file"); // handled by my_open
|
|
}
|
|
|
|
if(BOX64ENV(nosandbox))
|
|
{
|
|
add_argv("--no-sandbox");
|
|
}
|
|
if(BOX64ENV(inprocessgpu))
|
|
{
|
|
add_argv("--in-process-gpu");
|
|
}
|
|
// add new args only if there is no args already
|
|
if(BOX64ENV(args)) {
|
|
char tmp[256];
|
|
char* p = BOX64ENV(args);
|
|
int state = 0;
|
|
char* p2 = p;
|
|
if(my_context->argc==1 || (my_context->argc==2 && box64_wine))
|
|
while(state>=0) {
|
|
switch(*p2) {
|
|
case 0: // end of flux
|
|
if(state && (p2!=p)) add_argv(p);
|
|
state = -1;
|
|
break;
|
|
case '"': // start/end of quotes
|
|
if(state<2) {if(!state) p=p2; state=2;} else state=1;
|
|
break;
|
|
case ' ':
|
|
if(state==1) {strncpy(tmp, p, p2-p); tmp[p2-p]='\0'; add_argv(tmp); state=0;}
|
|
break;
|
|
default:
|
|
if(state==0) {state=1; p=p2;}
|
|
break;
|
|
}
|
|
++p2;
|
|
}
|
|
}
|
|
if(BOX64ENV(insert_args)) {
|
|
char tmp[256];
|
|
char* p = BOX64ENV(insert_args);
|
|
int state = 0;
|
|
char* p2 = p;
|
|
while(state>=0) {
|
|
switch(*p2) {
|
|
case 0: // end of flux
|
|
if(state && (p2!=p)) add_argv(p);
|
|
state = -1;
|
|
break;
|
|
case '"': // start/end of quotes
|
|
if(state<2) {if(!state) p=p2; state=2;} else state=1;
|
|
break;
|
|
case ' ':
|
|
if(state==1) {strncpy(tmp, p, p2-p); tmp[p2-p]='\0'; add_argv(tmp); state=0;}
|
|
break;
|
|
default:
|
|
if(state==0) {state=1; p=p2;}
|
|
break;
|
|
}
|
|
++p2;
|
|
}
|
|
}
|
|
// 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. (%s)\n", my_context->argv[0]);
|
|
free_contextargv();
|
|
FreeBox64Context(&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();
|
|
FreeBox64Context(&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("BOX64_ARG0")) {
|
|
my_context->argv[0] = box_strdup(getenv("BOX64_ARG0"));
|
|
unsetenv("BOX64_ARG0");
|
|
}
|
|
FILE *f = fopen(my_context->fullpath, "rb");
|
|
if(!f) {
|
|
printf_log(LOG_NONE, "Error: Cannot open %s\n", my_context->fullpath);
|
|
free_contextargv();
|
|
FreeBox64Context(&my_context);
|
|
FreeCollection(&ld_preload);
|
|
return -1;
|
|
}
|
|
#ifdef BOX32
|
|
box64_is32bits = FileIsX86ELF(my_context->fullpath);
|
|
// try to switch personality, but only if not already tried
|
|
if(box64_is32bits && !box64env.nopersona32bits) {
|
|
int tried = getenv("BOX32_PERSONA32BITS")?1:0;
|
|
if(tried) {
|
|
unsetenv("BOX32_PERSONA32BITS");
|
|
int p = personality(0xffffffff);
|
|
if(p==ADDR_LIMIT_32BIT) {
|
|
box64_isAddressSpace32 = 1;
|
|
printf_log(LOG_INFO, "Personality set to 32bits\n");
|
|
}
|
|
} else {
|
|
if(personality(ADDR_LIMIT_32BIT)!=-1) {
|
|
int nenv = 0;
|
|
while(env[nenv]) nenv++;
|
|
// alloc + "LD_PRELOAD" if needd + last NULL ending
|
|
char** newenv = (char**)box_calloc(nenv+1+1, sizeof(char*));
|
|
// copy strings
|
|
for (int i=0; i<nenv; ++i)
|
|
newenv[i] = box_strdup(env[i]);
|
|
newenv[nenv] = "BOX32_PERSONA32BITS=1";
|
|
// re-launch...
|
|
if(execve(my_context->box64path, (void*)argv, newenv)<0)
|
|
printf_log(LOG_NONE, "Failed to relaunch. Error is %d/%s (argv[0]=\"%s\")\n", errno, strerror(errno), argv[0]);
|
|
}
|
|
}
|
|
}
|
|
if(box64_is32bits) {
|
|
printf_log(LOG_INFO, "Using Box32 to load 32bits elf\n");
|
|
loadProtectionFromMap();
|
|
reserveHighMem();
|
|
init_pthread_helper_32();
|
|
}
|
|
#endif
|
|
LoadLDPath(my_context);
|
|
my_context->video_mem = mmap((void*)0xb0000, 0x10000, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
|
|
if(my_context->video_mem != (void*)0xb0000) {
|
|
my_context->video_mem = NULL;
|
|
printf_log(LOG_INFO, "Warning, could not allocate text video memory");
|
|
}
|
|
elfheader_t *elf_header = LoadAndCheckElfHeader(f, my_context->fullpath, 1);
|
|
if(!elf_header) {
|
|
int x86 = my_context->box86path?FileIsX86ELF(my_context->fullpath):0;
|
|
int script = my_context->bashpath?FileIsShell(my_context->fullpath):0;
|
|
int python3 = my_context->pythonpath?FileIsPython(my_context->fullpath):0;
|
|
printf_log(LOG_NONE, "Error: Reading elf header of %s, Try to launch %s instead\n", my_context->fullpath, x86?"using box86":(script?"using bash":"natively"));
|
|
fclose(f);
|
|
FreeCollection(&ld_preload);
|
|
int ret;
|
|
if(x86) {
|
|
// 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->box86path;
|
|
for(int i=0; i<my_context->argc; ++i)
|
|
newargv[i+1] = my_context->argv[i];
|
|
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->box64path;
|
|
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 if (python3) {
|
|
// duplicate the array and insert 1st arg as box64, 2nd is python3
|
|
const char** newargv = (const char**)box_calloc(my_context->argc+3, sizeof(char*));
|
|
newargv[0] = my_context->box64path;
|
|
newargv[1] = my_context->pythonpath;
|
|
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();
|
|
FreeBox64Context(&my_context);
|
|
return ret;
|
|
}
|
|
AddElfHeader(my_context, elf_header);
|
|
*elfheader = elf_header;
|
|
|
|
if(CalcLoadAddr(elf_header)) {
|
|
printf_log(LOG_NONE, "Error: Reading elf header of %s\n", my_context->fullpath);
|
|
FreeElfHeader(&elf_header);
|
|
free_contextargv();
|
|
FreeBox64Context(&my_context);
|
|
FreeCollection(&ld_preload);
|
|
return -1;
|
|
}
|
|
// allocate memory and load elf
|
|
if(AllocLoadElfMemory(my_context, elf_header, 1)) {
|
|
printf_log(LOG_NONE, "Error: Loading elf %s\n", my_context->fullpath);
|
|
FreeElfHeader(&elf_header);
|
|
free_contextargv();
|
|
FreeBox64Context(&my_context);
|
|
FreeCollection(&ld_preload);
|
|
return -1;
|
|
}
|
|
if (!strcmp(box64_guest_name, "heroic")) {
|
|
// check if heroic needs patching (for the 2.15.1 version)
|
|
uint8_t* address = GetBaseAddress(elf_header);
|
|
if(address[0x422f6e1]==0x72 && address[0x422f6e2]==0x44 && address[0x422f6e0]==0xF8 && address[0x422f727]==0xcc) {
|
|
printf_log(LOG_INFO, "Patched heroic!\n");
|
|
uintptr_t page = ((uintptr_t)&address[0x422f6e1])&~(box64_pagesize-1);
|
|
int prot = getProtection(page);
|
|
mprotect((void*)page, box64_pagesize, PROT_READ|PROT_WRITE|PROT_EXEC);
|
|
address[0x422f6e1]=0x90; address[0x422f6e2]=0x90;
|
|
mprotect((void*)page, box64_pagesize, prot);
|
|
}
|
|
}
|
|
if(ElfCheckIfUseTCMallocMinimal(elf_header)) {
|
|
if(!box64_tcmalloc_minimal) {
|
|
// need to reload with tcmalloc_minimal as a LD_PRELOAD!
|
|
printf_log(LOG_INFO, "tcmalloc_minimal.so.4 used. Reloading box64 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, "Using tcmalloc_minimal.so.4, and it's in the LD_PRELOAD command\n");
|
|
}
|
|
}
|
|
#if defined(RPI) || defined(RK3399) || defined(RK3326)
|
|
// before launching emulation, let's check if this is a mojosetup from GOG
|
|
if (((strstr(prog, "bin/linux/x86_64/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);
|
|
if (strcmp(box64_guest_name, p)) {
|
|
ApplyEnvFileEntry(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
|
|
my_context->orig_argc = argc;
|
|
my_context->orig_argv = (char**)argv;
|
|
}
|
|
box64_hasinterp = hasElfInterp(elf_header);
|
|
box64_nolibs = (NeededLibs(elf_header)==0);
|
|
if(!box64_hasinterp) printf_log(LOG_INFO, "Warning, box64 is not really compatible with staticaly linked binaries. Expect crash!\n");
|
|
box64_isglibc234 = GetNeededVersionForLib(elf_header, "libc.so.6", "GLIBC_2.34");
|
|
if(box64_isglibc234)
|
|
printf_log(LOG_DEBUG, "Program linked with GLIBC 2.34+\n");
|
|
// get and alloc stack size and align
|
|
if(CalcStackSize(my_context)) {
|
|
printf_log(LOG_NONE, "Error: Allocating stack\n");
|
|
free_contextargv();
|
|
FreeBox64Context(&my_context);
|
|
FreeCollection(&ld_preload);
|
|
return -1;
|
|
}
|
|
// init x86_64 emu
|
|
x64emu_t *emu = NewX64Emu(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
|
|
SetupX64Emu(emu, NULL);
|
|
if(box64_is32bits) {
|
|
SetEAX(emu, my_context->argc);
|
|
SetEBX(emu, my_context->argv32);
|
|
} else {
|
|
SetRSI(emu, my_context->argc);
|
|
SetRDX(emu, (uint64_t)my_context->argv);
|
|
SetRCX(emu, (uint64_t)my_context->envv);
|
|
SetRBP(emu, 0); // Frame pointer so to "No more frame pointer"
|
|
}
|
|
|
|
thread_set_emu(emu);
|
|
|
|
// export symbols
|
|
AddSymbols(my_context->maplib, elf_header);
|
|
if(wine_preloaded) {
|
|
uintptr_t wineinfo = 0;
|
|
int ver = -1, veropt = 0;
|
|
const char* vername = NULL;
|
|
if(!ElfGetGlobalSymbolStartEnd(elf_header, &wineinfo, NULL, "wine_main_preload_info", &ver, &vername, 1, &veropt))
|
|
if(!ElfGetWeakSymbolStartEnd(elf_header, &wineinfo, NULL, "wine_main_preload_info", &ver, &vername, 1, &veropt))
|
|
ElfGetLocalSymbolStartEnd(elf_header, &wineinfo, NULL, "wine_main_preload_info", &ver, &vername, 1, &veropt);
|
|
if(!wineinfo) {printf_log(LOG_DEBUG, "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 %p -> %p\n", get_wine_prereserve(), *(void**)wineinfo);
|
|
}
|
|
#ifdef DYNAREC
|
|
dynarec_wine_prereserve();
|
|
#endif
|
|
}
|
|
AddMainElfToLinkmap(elf_header); //TODO: LinkMap seems incorect
|
|
// pre-load lib if needed
|
|
if(ld_preload.size) {
|
|
my_context->preload = new_neededlib(0);
|
|
for(int i=0; i<ld_preload.size; ++i) {
|
|
needed_libs_t* tmp = new_neededlib(1);
|
|
tmp->names[0] = ld_preload.paths[i];
|
|
if(AddNeededLib(my_context->maplib, 0, 0, 0, tmp, elf_header, my_context, emu)) {
|
|
printf_log(LOG_INFO, "Warning, cannot pre-load %s\n", tmp->names[0]);
|
|
RemoveNeededLib(my_context->maplib, 0, tmp, my_context, emu);
|
|
} else {
|
|
for(int j=0; j<tmp->size; ++j)
|
|
add1lib_neededlib(my_context->preload, tmp->libs[j], tmp->names[j]);
|
|
}
|
|
free_neededlib(tmp);
|
|
}
|
|
}
|
|
FreeCollection(&ld_preload);
|
|
// Call librarian to load all dependant elf
|
|
if(LoadNeededLibs(elf_header, my_context->maplib, 0, 0, 0, my_context, emu)) {
|
|
printf_log(LOG_NONE, "Error: Loading needed libs in elf %s\n", my_context->argv[0]);
|
|
FreeBox64Context(&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, 0, elf_header)) {
|
|
printf_log(LOG_NONE, "Error: Relocating symbols in elf %s\n", my_context->argv[0]);
|
|
FreeBox64Context(&my_context);
|
|
return -1;
|
|
}
|
|
// and handle PLT
|
|
RelocateElfPlt(my_context->maplib, NULL, 0, 0, elf_header);
|
|
// deferred init
|
|
setupTraceInit();
|
|
RunDeferredElfInit(emu);
|
|
// update TLS of main elf
|
|
RefreshElfTLS(elf_header, emu);
|
|
// 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();
|
|
|
|
*emulator = emu;
|
|
return 0;
|
|
}
|
|
|
|
int emulate(x64emu_t* emu, elfheader_t* elf_header)
|
|
{
|
|
// get entrypoint
|
|
my_context->ep = GetEntryPoint(my_context->maplib, elf_header);
|
|
|
|
atexit(endBox64);
|
|
loadProtectionFromMap();
|
|
|
|
// emulate!
|
|
printf_log(LOG_DEBUG, "Start x64emu on Main\n");
|
|
// Stack is ready, with stacked: NULL env NULL argv argc
|
|
ResetFlags(emu);
|
|
#ifdef BOX32
|
|
if(box64_is32bits) {
|
|
SetEIP(emu, my_context->ep);
|
|
Push32(emu, my_context->exit_bridge); // push to pop it just after
|
|
SetEDX(emu, Pop32(emu)); // RDX is exit function
|
|
if(!box64_hasinterp) SetEDX(emu, 0);
|
|
} else
|
|
#endif
|
|
{
|
|
SetRIP(emu, my_context->ep);
|
|
Push64(emu, my_context->exit_bridge); // push to pop it just after
|
|
SetRDX(emu, Pop64(emu)); // RDX is exit function
|
|
if(!box64_hasinterp) SetRDX(emu, 0);
|
|
}
|
|
DynaRun(emu);
|
|
// Get EAX
|
|
int ret = GetEAX(emu);
|
|
printf_log(LOG_DEBUG, "Emulation finished, EAX=%d\n", ret);
|
|
endBox64();
|
|
#ifdef HAVE_TRACE
|
|
if(trace_func) {
|
|
box_free(trace_func);
|
|
trace_func = NULL;
|
|
}
|
|
#endif
|
|
|
|
#ifdef DYNAREC
|
|
if (BOX64ENV(dynarec_perf_map) && BOX64ENV(dynarec_perf_map_fd) != -1) {
|
|
close(BOX64ENV(dynarec_perf_map_fd));
|
|
SET_BOX64ENV(dynarec_perf_map_fd, -1);
|
|
}
|
|
#endif
|
|
return ret;
|
|
}
|