From e753c19da1b621b1e667de85ce5ef60d186f0684 Mon Sep 17 00:00:00 2001 From: ptitSeb Date: Sun, 28 Feb 2021 14:19:04 +0100 Subject: [PATCH] Added some minimal set of source (now box64 compile and say hello at least) --- CMakeLists.txt | 273 ++++++++++++++ cmake_uninstall.cmake.in | 21 ++ runTest.cmake | 50 +++ src/box64context.c | 200 ++++++++++ src/box64version.h | 8 + src/build_info.c | 27 ++ src/build_info.h | 6 + src/include/box64context.h | 74 ++++ src/include/debug.h | 42 +++ src/include/fileutils.h | 23 ++ src/include/khash.h | 678 ++++++++++++++++++++++++++++++++++ src/include/pathcoll.h | 23 ++ src/include/wine_tools.h | 13 + src/main.c | 734 +++++++++++++++++++++++++++++++++++++ src/tools/fileutils.c | 129 +++++++ src/tools/pathcoll.c | 182 +++++++++ src/tools/wine_tools.c | 107 ++++++ 17 files changed, 2590 insertions(+) create mode 100755 CMakeLists.txt create mode 100644 cmake_uninstall.cmake.in create mode 100755 runTest.cmake create mode 100755 src/box64context.c create mode 100755 src/box64version.h create mode 100755 src/build_info.c create mode 100755 src/build_info.h create mode 100755 src/include/box64context.h create mode 100755 src/include/debug.h create mode 100755 src/include/fileutils.h create mode 100755 src/include/khash.h create mode 100755 src/include/pathcoll.h create mode 100755 src/include/wine_tools.h create mode 100755 src/main.c create mode 100755 src/tools/fileutils.c create mode 100755 src/tools/pathcoll.c create mode 100755 src/tools/wine_tools.c diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100755 index 00000000..42b9a942 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,273 @@ +cmake_minimum_required(VERSION 3.0) + +option(RPI4ARM64 "Set to ON if targeting an RaspberryPI4 device with multiarch arm64 and armhf" ${RPI4ARM64}) +option(RK3399 "Set to ON if targeting an Rockchip RK3399 based device" ${RK3399}) +option(USE_CCACHE "Set to ON to use ccache if present in the system" ${USE_CCACHE}) +option(HAVE_TRACE "Set to ON to have Trace ability (needs ZydisInfo library)" ${HAVE_TRACE}) +option(NOGIT "Set to ON if not building from a git clone repo (like when building from a zip download from github)" ${NOGIT}) +option(LD80BITS "Set to ON if host device have 80bits long double (i.e. i386)" ${LD80BITS}) +option(NOALIGN "Set to ON if host device doesn't need re-align (i.e. i386)" ${NOALIGN}) + +if(${CMAKE_VERSION} VERSION_LESS "3.12.2") + find_package(PythonInterp 3) + if(NOT PYTHONINTERP_FOUND) + message( FATAL_ERROR "You need a Python interpretor, CMake will exit." ) + endif() + if(${PYTHON_VERSION_MAJOR} LESS 3) + message( FATAL_ERROR "You need a Python 3 interpretor, CMake will exit." ) + endif() +else() + find_package(Python3) + if(NOT Python3_Interpreter_FOUND) + message( FATAL_ERROR "You need a Python interpretor, CMake will exit." ) + endif() + set(PYTHON_EXECUTABLE ${Python3_EXECUTABLE} CACHE INTERNAL "The Python3 executable" FORCE) +endif() + +project(box64 C ASM) + +enable_testing() + +set(default_build_type "RelwithDebInfo") +if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + message(STATUS "Setting build type to '${default_build_type}' as none was specified.") + set(CMAKE_BUILD_TYPE "${default_build_type}" CACHE STRING "Choose the type of build." FORCE) + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "MinSizeRel" "RelWithDebInfo") +endif() + +if(RPI4ARM64) + add_definitions(-DRPI) + add_definitions(-DRPI4ARM64) + add_definitions(-pipe -march=armv8-a+crc -mtune=cortex-a72 -mfpu=neon-fp-armv8) + set(CMAKE_ASM_FLAGS "-pipe -march=armv8-a+crc -mtune=cortex-a72 -mfpu=neon-fp-armv8") +endif() +if(RK3399) + add_definitions(-DRK3399) + add_definitions(-pipe -march=armv8-a+crc+simd+crypto -mcpu=cortex-a72+crypto) + set(CMAKE_ASM_FLAGS "-pipe -march=armv8-a+crc+simd+crypto -mcpu=cortex-a72+crypto") +endif() + +if(NOGIT) + add_definitions(-DNOGIT) +endif() + +if(HAVE_TRACE) + set(BOX64 box64t) +else() + set(BOX64 box64) +endif() + +set(BOX64_ELF_ADDRESS "0x50062800000") #random load address... + +if(LD80BITS) + add_definitions(-DHAVE_LD80BITS) +endif() + +if(NOALIGN) + add_definitions(-DNOALIGN) +endif() + +if(HAVE_TRACE) + add_definitions(-DHAVE_TRACE) +endif() + +set(BOX64_ROOT ${CMAKE_SOURCE_DIR}) + +add_definitions(-std=gnu11 -funwind-tables -fvisibility=hidden) + +if(USE_CCACHE) + find_program(CCACHE_FOUND ccache) + if(CCACHE_FOUND) + set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE ccache) + set_property(GLOBAL PROPERTY RULE_LAUNCH_LINK ccache) + endif() +endif() + +include_directories( + "${BOX64_ROOT}/src/include" + "${BOX64_ROOT}/src" + "${BOX64_ROOT}/src/wrapped/generated" +) + +# git_head.h is a generated file +set_source_files_properties( + "${BOX64_ROOT}/src/git_head.h" + PROPERTIES GENERATED TRUE + HEADER_FILE_ONLY TRUE) + +set(ELFLOADER_SRC + "${BOX64_ROOT}/src/main.c" + "${BOX64_ROOT}/src/box64context.c" + "${BOX64_ROOT}/src/build_info.c" + "${BOX64_ROOT}/src/tools/pathcoll.c" + "${BOX64_ROOT}/src/tools/fileutils.c" + "${BOX64_ROOT}/src/tools/wine_tools.c" +) + +#set(WRAPPEDS +# "${BOX64_ROOT}/src/wrapped/wrappedlibc.c" +#) + +#set(WRAPPEDS_HEAD "${BOX64_ROOT}/src/wrapped/wrappedd3dadapter9_gen.h") +#foreach(A ${WRAPPEDS}) +# string(REPLACE ".c" "_private.h" B ${A}) +# set(WRAPPEDS_HEAD ${WRAPPEDS_HEAD} ${B}) +# set_source_files_properties(${A} PROPERTIES OBJECT_DEPENDS ${B}) +#endforeach() + +#set(WRAPPER "${BOX64_ROOT}/src/wrapped/generated/wrapper.c" "${BOX64_ROOT}/src/wrapped/generated/wrapper.h") + +#add_custom_command( +# OUTPUT "${BOX64_ROOT}/src/wrapped/generated/functions_list.txt" +# COMMAND "${PYTHON_EXECUTABLE}" "${BOX64_ROOT}/rebuild_wrappers.py" +# "${BOX64_ROOT}" +# "PANDORA" "HAVE_LD80BITS" "NOALIGN" "HAVE_TRACE" "POWERPCLE" "--" +# ${WRAPPEDS_HEAD} +# MAIN_DEPENDENCY "${BOX64_ROOT}/rebuild_wrappers.py" +# DEPENDS ${WRAPPEDS} ${WRAPPEDS_HEAD} +# BYPRODUCTS ${WRAPPER} +#) + +#add_custom_command( +# OUTPUT "${BOX64_ROOT}/src/dynarec/last_run.txt" +# COMMAND "${PYTHON_EXECUTABLE}" "${BOX64_ROOT}/rebuild_printer.py" "${BOX64_ROOT}" +# MAIN_DEPENDENCY "${BOX64_ROOT}/rebuild_printer.py" +# DEPENDS "${BOX64_ROOT}/src/dynarec/arm_instructions.txt" +# BYPRODUCTS "${BOX64_ROOT}/src/dynarec/arm_printer.c" +#) + +#add_custom_target(WRAPPERS DEPENDS "${BOX64_ROOT}/src/wrapped/generated/functions_list.txt") +#add_custom_target(PRINTER DEPENDS "${BOX64_ROOT}/src/dynarec/last_run.txt") + +# creates git_head.h +#if(ARM_DYNAREC) +# add_custom_command( +# OUTPUT "${BOX64_ROOT}/src/git_head.h" +# COMMAND bash -c "echo \\\#define GITREV \\\"$(git rev-parse --short HEAD)\\\">\"${BOX64_ROOT}/src/git_head.h\"" +# DEPENDS dynarec ${ELFLOADER_SRC} ${WRAPPEDS} +# VERBATIM) +#else() + add_custom_command( + OUTPUT "${BOX64_ROOT}/src/git_head.h" + COMMAND bash -c "echo \\\#define GITREV \\\"$(git rev-parse --short HEAD)\\\">\"${BOX64_ROOT}/src/git_head.h\"" + DEPENDS ${ELFLOADER_SRC} ${WRAPPEDS} + VERBATIM) +#endif() + +add_executable(${BOX64} ${ELFLOADER_SRC} "${BOX64_ROOT}/src/git_head.h") +#add_executable(${BOX64} ${ELFLOADER_SRC} ${WRAPPEDS} "${BOX64_ROOT}/src/git_head.h") +#add_dependencies(${BOX64} WRAPPERS) +#add_dependencies(${BOX64} PRINTER) +target_link_libraries(${BOX64} m dl rt pthread) +if(ARM_DYNAREC) + target_link_libraries(${BOX64} dynarec) +endif() + +if(${CMAKE_VERSION} VERSION_LESS "3.13") + set_target_properties(${BOX64} PROPERTIES LINK_FLAGS "-rdynamic -Wl,-Ttext-segment,${BOX64_ELF_ADDRESS}") +else() + target_link_options(${BOX64} PUBLIC -rdynamic) + target_link_options(${BOX64} PUBLIC -Wl,-Ttext-segment,${BOX64_ELF_ADDRESS}) +endif() + +string(COMPARE EQUAL "${CMAKE_HOST_SYSTEM_PROCESSOR}" "i686" _x86) +string(COMPARE EQUAL "${CMAKE_HOST_SYSTEM_PROCESSOR}" "x86_64" _x86_64) + +if(NOT _x86 AND NOT _x86_64) + install(TARGETS ${BOX64} + RUNTIME DESTINATION bin) + #configure_file(system/box64.conf.cmake system/box64.conf) + #install(FILES ${CMAKE_BINARY_DIR}/system/box64.conf DESTINATION /etc/binfmt.d/) + install(FILES ${CMAKE_SOURCE_DIR}/x64lib/libstdc++.so.6 DESTINATION /usr/lib/i386-linux-gnu/) + install(FILES ${CMAKE_SOURCE_DIR}/x64lib/libstdc++.so.5 DESTINATION /usr/lib/i386-linux-gnu/) + install(FILES ${CMAKE_SOURCE_DIR}/x64lib/libgcc_s.so.1 DESTINATION /usr/lib/i386-linux-gnu/) +endif() + +if(NOT TARGET uninstall) + configure_file( + "${CMAKE_CURRENT_SOURCE_DIR}/cmake_uninstall.cmake.in" + "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake" + IMMEDIATE @ONLY) + + add_custom_target(uninstall + COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake) +endif() + +add_test(test01 ${CMAKE_COMMAND} -D TEST_PROGRAM=${CMAKE_BINARY_DIR}/${BOX64} + -D TEST_ARGS=${CMAKE_SOURCE_DIR}/tests/test01 -D TEST_OUTPUT=tmpfile.txt + -D TEST_REFERENCE=${CMAKE_SOURCE_DIR}/tests/ref01.txt + -P ${CMAKE_SOURCE_DIR}/runTest.cmake ) + +add_test(test02 ${CMAKE_COMMAND} -D TEST_PROGRAM=${CMAKE_BINARY_DIR}/${BOX64} + -D TEST_ARGS=${CMAKE_SOURCE_DIR}/tests/test02 -D TEST_OUTPUT=tmpfile.txt + -D TEST_REFERENCE=${CMAKE_SOURCE_DIR}/tests/ref02.txt + -P ${CMAKE_SOURCE_DIR}/runTest.cmake ) + +add_test(test03 ${CMAKE_COMMAND} -D TEST_PROGRAM=${CMAKE_BINARY_DIR}/${BOX64} + -D TEST_ARGS=${CMAKE_SOURCE_DIR}/tests/test03 -D TEST_OUTPUT=tmpfile.txt + -D TEST_REFERENCE=${CMAKE_SOURCE_DIR}/tests/ref03.txt + -P ${CMAKE_SOURCE_DIR}/runTest.cmake ) + +add_test(test04 ${CMAKE_COMMAND} -D TEST_PROGRAM=${CMAKE_BINARY_DIR}/${BOX64} + -D TEST_ARGS=${CMAKE_SOURCE_DIR}/tests/test04 -D TEST_ARGS2=yeah -D TEST_OUTPUT=tmpfile.txt + -D TEST_REFERENCE=${CMAKE_SOURCE_DIR}/tests/ref04.txt + -P ${CMAKE_SOURCE_DIR}/runTest.cmake ) + +add_test(test05 ${CMAKE_COMMAND} -D TEST_PROGRAM=${CMAKE_BINARY_DIR}/${BOX64} + -D TEST_ARGS=${CMAKE_SOURCE_DIR}/tests/test05 -D TEST_ARGS2=7 -D TEST_OUTPUT=tmpfile.txt + -D TEST_REFERENCE=${CMAKE_SOURCE_DIR}/tests/ref05.txt + -P ${CMAKE_SOURCE_DIR}/runTest.cmake ) + +add_test(test06 ${CMAKE_COMMAND} -D TEST_PROGRAM=${CMAKE_BINARY_DIR}/${BOX64} + -D TEST_ARGS=${CMAKE_SOURCE_DIR}/tests/test06 -D TEST_OUTPUT=tmpfile.txt + -D TEST_REFERENCE=${CMAKE_SOURCE_DIR}/tests/ref06.txt + -P ${CMAKE_SOURCE_DIR}/runTest.cmake ) + +add_test(test07 ${CMAKE_COMMAND} -D TEST_PROGRAM=${CMAKE_BINARY_DIR}/${BOX64} + -D TEST_ARGS=${CMAKE_SOURCE_DIR}/tests/test07 -D TEST_OUTPUT=tmpfile.txt + -D TEST_REFERENCE=${CMAKE_SOURCE_DIR}/tests/ref07.txt + -P ${CMAKE_SOURCE_DIR}/runTest.cmake ) + +add_test(test08 ${CMAKE_COMMAND} -D TEST_PROGRAM=${CMAKE_BINARY_DIR}/${BOX64} + -D TEST_ARGS=${CMAKE_SOURCE_DIR}/tests/test08 -D TEST_OUTPUT=tmpfile.txt + -D TEST_REFERENCE=${CMAKE_SOURCE_DIR}/tests/ref08.txt + -P ${CMAKE_SOURCE_DIR}/runTest.cmake ) + +add_test(test09 ${CMAKE_COMMAND} -D TEST_PROGRAM=${CMAKE_BINARY_DIR}/${BOX64} + -D TEST_ARGS=${CMAKE_SOURCE_DIR}/tests/test09 -D TEST_OUTPUT=tmpfile.txt + -D TEST_REFERENCE=${CMAKE_SOURCE_DIR}/tests/ref09.txt + -P ${CMAKE_SOURCE_DIR}/runTest.cmake ) + +add_test(test10 ${CMAKE_COMMAND} -D TEST_PROGRAM=${CMAKE_BINARY_DIR}/${BOX64} + -D TEST_ARGS=${CMAKE_SOURCE_DIR}/tests/test10 -D TEST_OUTPUT=tmpfile.txt + -D TEST_REFERENCE=${CMAKE_SOURCE_DIR}/tests/ref10.txt + -P ${CMAKE_SOURCE_DIR}/runTest.cmake ) + +add_test(test11 ${CMAKE_COMMAND} -D TEST_PROGRAM=${CMAKE_BINARY_DIR}/${BOX64} + -D TEST_ARGS=${CMAKE_SOURCE_DIR}/tests/test11 -D TEST_OUTPUT=tmpfile.txt + -D TEST_REFERENCE=${CMAKE_SOURCE_DIR}/tests/ref11.txt + -P ${CMAKE_SOURCE_DIR}/runTest.cmake ) + +add_test(test12 ${CMAKE_COMMAND} -D TEST_PROGRAM=${CMAKE_BINARY_DIR}/${BOX64} + -D TEST_ARGS=${CMAKE_SOURCE_DIR}/tests/test12 -D TEST_OUTPUT=tmpfile.txt + -D TEST_REFERENCE=${CMAKE_SOURCE_DIR}/tests/ref12.txt + -P ${CMAKE_SOURCE_DIR}/runTest.cmake ) + +add_test(test13 ${CMAKE_COMMAND} -D TEST_PROGRAM=${CMAKE_BINARY_DIR}/${BOX64} + -D TEST_ARGS=${CMAKE_SOURCE_DIR}/tests/test13 -D TEST_OUTPUT=tmpfile.txt + -D TEST_REFERENCE=${CMAKE_SOURCE_DIR}/tests/ref13.txt + -P ${CMAKE_SOURCE_DIR}/runTest.cmake ) + +add_test(test14 ${CMAKE_COMMAND} -D TEST_PROGRAM=${CMAKE_BINARY_DIR}/${BOX64} + -D TEST_ARGS=${CMAKE_SOURCE_DIR}/tests/test14 -D TEST_OUTPUT=tmpfile.txt + -D TEST_REFERENCE=${CMAKE_SOURCE_DIR}/tests/ref14.txt + -P ${CMAKE_SOURCE_DIR}/runTest.cmake ) + +file(GLOB extension_tests "${CMAKE_SOURCE_DIR}/tests/extensions/*.c") +foreach(file ${extension_tests}) + get_filename_component(testname "${file}" NAME_WE) + add_test(NAME "${testname}" COMMAND ${CMAKE_COMMAND} -D TEST_PROGRAM=${CMAKE_BINARY_DIR}/${BOX64} + -D TEST_ARGS=${CMAKE_SOURCE_DIR}/tests/extensions/${testname} -D TEST_OUTPUT=tmpfile.txt + -D TEST_REFERENCE=${CMAKE_SOURCE_DIR}/tests/extensions/${testname}.txt + -P ${CMAKE_SOURCE_DIR}/runTest.cmake) +endforeach() diff --git a/cmake_uninstall.cmake.in b/cmake_uninstall.cmake.in new file mode 100644 index 00000000..4c07dc7b --- /dev/null +++ b/cmake_uninstall.cmake.in @@ -0,0 +1,21 @@ +if(NOT EXISTS "@CMAKE_BINARY_DIR@/install_manifest.txt") + message(FATAL_ERROR "Cannot find install manifest: @CMAKE_BINARY_DIR@/install_manifest.txt") +endif(NOT EXISTS "@CMAKE_BINARY_DIR@/install_manifest.txt") + +file(READ "@CMAKE_BINARY_DIR@/install_manifest.txt" files) +string(REGEX REPLACE "\n" ";" files "${files}") +foreach(file ${files}) + message(STATUS "Uninstalling $ENV{DESTDIR}${file}") + if(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}") + exec_program( + "@CMAKE_COMMAND@" ARGS "-E remove \"$ENV{DESTDIR}${file}\"" + OUTPUT_VARIABLE rm_out + RETURN_VALUE rm_retval + ) + if(NOT "${rm_retval}" STREQUAL 0) + message(FATAL_ERROR "Problem when removing $ENV{DESTDIR}${file}") + endif(NOT "${rm_retval}" STREQUAL 0) + else(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}") + message(STATUS "File $ENV{DESTDIR}${file} does not exist.") + endif(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}") +endforeach(file) diff --git a/runTest.cmake b/runTest.cmake new file mode 100755 index 00000000..cb6706ae --- /dev/null +++ b/runTest.cmake @@ -0,0 +1,50 @@ +# arguments checking +if( NOT TEST_PROGRAM ) + message( FATAL_ERROR "Require TEST_PROGRAM to be defined" ) +endif( NOT TEST_PROGRAM ) +if( NOT TEST_ARGS ) + message( FATAL_ERROR "Require TEST_ARGS to be defined" ) +endif( NOT TEST_ARGS ) +if( NOT TEST_OUTPUT ) + message( FATAL_ERROR "Require TEST_OUTPUT to be defined" ) +endif( NOT TEST_OUTPUT ) +if( NOT TEST_REFERENCE ) + message( FATAL_ERROR "Require TEST_REFERENCE to be defined" ) +endif( NOT TEST_REFERENCE ) + +set(ENV{BOX64_LOG} 0) +set(ENV{BOX64_NOBANNER} 1) +if( EXISTS ${CMAKE_SOURCE_DIR}/x64lib ) + # we are inside box64 folder + set(ENV{LD_LIBRARY_PATH} ${CMAKE_SOURCE_DIR}/x64lib) +else() + # we are inside build folder + set(ENV{LD_LIBRARY_PATH} ${CMAKE_SOURCE_DIR}/../x64lib) +endif( EXISTS ${CMAKE_SOURCE_DIR}/x64lib ) + +# run the test program, capture the stdout/stderr and the result var +execute_process( + COMMAND ${TEST_PROGRAM} ${TEST_ARGS} ${TEST_ARGS2} + OUTPUT_FILE ${TEST_OUTPUT} + ERROR_VARIABLE TEST_ERROR + RESULT_VARIABLE TEST_RESULT + ) + +# if the return value is !=0 bail out +if( TEST_RESULT ) + message( FATAL_ERROR "Failed: Test program ${TEST_PROGRAM} exited != 0.\n${TEST_ERROR}" ) +endif( TEST_RESULT ) + +# now compare the output with the reference +execute_process( + COMMAND ${CMAKE_COMMAND} -E compare_files ${TEST_OUTPUT} ${TEST_REFERENCE} + RESULT_VARIABLE TEST_RESULT + ) + +# again, if return value is !=0 scream and shout +if( TEST_RESULT ) + message( FATAL_ERROR "Failed: The output of ${TEST_PROGRAM} did not match ${TEST_REFERENCE}") +endif( TEST_RESULT ) + +# everything went fine... +message( "Passed: The output of ${TEST_PROGRAM} matches ${TEST_REFERENCE}" ) diff --git a/src/box64context.c b/src/box64context.c new file mode 100755 index 00000000..21768fb2 --- /dev/null +++ b/src/box64context.c @@ -0,0 +1,200 @@ +#define _GNU_SOURCE /* See feature_test_macros(7) */ +#include +#include +#include +#include +#include +#include + +#include "box64context.h" +#include "debug.h" + + +EXPORTDYN +void initAllHelpers(box64context_t* context) +{ + static int inited = 0; + if(inited) + return; + my_context = context; + //init_pthread_helper(); + //init_signal_helper(context); + inited = 1; +} + +EXPORTDYN +void finiAllHelpers(box64context_t* context) +{ + static int finied = 0; + if(finied) + return; + //fini_pthread_helper(context); + //fini_signal_helper(); + //cleanAlternate(); + //fini_custommem_helper(context); + finied = 1; +} + +/// maxval not inclusive +int getrand(int maxval) +{ + if(maxval<1024) { + return ((random()&0x7fff)*maxval)/0x7fff; + } + uint64_t r = random(); + r = (r*maxval) / RAND_MAX; + return r; + +} + +void free_tlsdatasize(void* p) +{ + if(!p) + return; + tlsdatasize_t *data = (tlsdatasize_t*)p; + free(data->tlsdata); + free(p); +} + +EXPORTDYN +box64context_t *NewBox64Context(int argc) +{ +#ifdef BUILD_DYNAMIC + if(my_context) { + ++my_context->count; + return my_context; + } +#endif + // init and put default values + box64context_t *context = my_context = (box64context_t*)calloc(1, sizeof(box64context_t)); + + context->deferedInit = 1; + context->sel_serial = 1; + + //init_custommem_helper(context); + + context->box64lib = dlopen(NULL, RTLD_NOW|RTLD_GLOBAL); + //context->dlprivate = NewDLPrivate(); + + context->argc = argc; + context->argv = (char**)calloc(context->argc+1, sizeof(char*)); + + for (int i=0; i<4; ++i) context->canary[i] = 1 + getrand(255); + context->canary[getrand(4)] = 0; + printf_log(LOG_DEBUG, "Setting up canary (for Stack protector) at GS:0x14, value:%08X\n", *(uint32_t*)context->canary); + + initAllHelpers(context); + + return context; +} + +EXPORTDYN +void FreeBox64Context(box64context_t** context) +{ + if(!context) + return; + + box64context_t* ctx = *context; // local copy to do the cleanning + + FreeCollection(&ctx->box64_path); + FreeCollection(&ctx->box64_ld_lib); + FreeCollection(&ctx->box64_emulated_libs); + // stop trace now + /*if(ctx->dec) + DeleteX86TraceDecoder(&ctx->dec); + if(ctx->zydis) + DeleteX86Trace(ctx);*/ + + free(ctx->argv); + + for (int i=0; ienvc; ++i) + free(ctx->envv[i]); + free(ctx->envv); + + for(int i=0; isignals[i]!=0 && ctx->signals[i]!=1) { + signal(i, SIG_DFL); + } + + *context = NULL; // bye bye my_context + + //CleanStackSize(ctx); + +#ifndef BUILD_LIB + if(ctx->box64lib) + dlclose(ctx->box64lib); +#endif + + //FreeDLPrivate(&ctx->dlprivate); + + free(ctx->stack); + + free(ctx->fullpath); + free(ctx->box64path); + + void* ptr; + if ((ptr = pthread_getspecific(ctx->tlskey)) != NULL) { + free_tlsdatasize(ptr); + pthread_setspecific(ctx->tlskey, NULL); + } + pthread_key_delete(ctx->tlskey); + + if(ctx->tlsdata) + free(ctx->tlsdata); + + finiAllHelpers(ctx); + + free(ctx); +} +/* +int AddElfHeader(box64context_t* ctx, elfheader_t* head) { + int idx = ctx->elfsize; + if(idx==ctx->elfcap) { + // resize... + ctx->elfcap += 16; + ctx->elfs = (elfheader_t**)realloc(ctx->elfs, sizeof(elfheader_t*) * ctx->elfcap); + } + ctx->elfs[idx] = head; + ctx->elfsize++; + printf_log(LOG_DEBUG, "Adding \"%s\" as #%d in elf collection\n", ElfName(head), idx); + return idx; +} +*/ +int AddTLSPartition(box64context_t* context, int tlssize) { + int oldsize = context->tlssize; + context->tlssize += tlssize; + context->tlsdata = realloc(context->tlsdata, context->tlssize); + memmove(context->tlsdata+tlssize, context->tlsdata, oldsize); // move to the top, using memmove as regions will probably overlap + memset(context->tlsdata, 0, tlssize); // fill new space with 0 (not mandatory) + // clean GS segment for current emu + if(my_context) { + //ResetSegmentsCache(thread_get_emu()); + if(!(++context->sel_serial)) + ++context->sel_serial; + } + + return -context->tlssize; // negative offset +} +/* +void add_neededlib(needed_libs_t* needed, library_t* lib) +{ + if(!needed) + return; + if(needed->size == needed->cap) { + needed->cap += 8; + needed->libs = (library_t**)realloc(needed->libs, needed->cap*sizeof(library_t*)); + } + needed->libs[needed->size++] = lib; +} + +void free_neededlib(needed_libs_t* needed) +{ + if(!needed) + return; + needed->cap = 0; + needed->size = 0; + if(needed->libs) + free(needed->libs); + needed->libs = NULL; +} +*/ \ No newline at end of file diff --git a/src/box64version.h b/src/box64version.h new file mode 100755 index 00000000..d9c4c042 --- /dev/null +++ b/src/box64version.h @@ -0,0 +1,8 @@ +#ifndef __BOX64_VERSION_H_ +#define __BOX64_VERSION_H_ + +#define BOX64_MAJOR 0 +#define BOX64_MINOR 0 +#define BOX64_REVISION 1 + +#endif //__BOX64_VERSION_H_ \ No newline at end of file diff --git a/src/build_info.c b/src/build_info.c new file mode 100755 index 00000000..02c19b4f --- /dev/null +++ b/src/build_info.c @@ -0,0 +1,27 @@ +#include +#include "debug.h" +#include "box64version.h" +#ifdef NOGIT +#define GITREV "nogit" +#else +#include "git_head.h" +#endif + +void PrintBox64Version() +{ + printf("Box64%s%s v%d.%d.%d %s built on %s %s\n", + #ifdef HAVE_TRACE + " with trace", + #else + "", + #endif + #ifdef DYNAREC + " with Dynarec", + #else + "", + #endif + BOX64_MAJOR, BOX64_MINOR, BOX64_REVISION, + GITREV, + __DATE__, __TIME__); +} + diff --git a/src/build_info.h b/src/build_info.h new file mode 100755 index 00000000..45182a02 --- /dev/null +++ b/src/build_info.h @@ -0,0 +1,6 @@ +#ifndef __BUILD_INFO_H__ +#define __BUILD_INFO_H__ + +void PrintBox64Version(); + +#endif //__BUILD_INFO_H__ \ No newline at end of file diff --git a/src/include/box64context.h b/src/include/box64context.h new file mode 100755 index 00000000..680b2d5e --- /dev/null +++ b/src/include/box64context.h @@ -0,0 +1,74 @@ +#ifndef __BOX64CONTEXT_H_ +#define __BOX64CONTEXT_H_ +#include +#include +#include "pathcoll.h" + +typedef void* (*procaddess_t)(const char* name); +typedef void* (*vkprocaddess_t)(void* instance, const char* name); + +#define MAX_SIGNAL 64 + +typedef struct tlsdatasize_s { + int32_t tlssize; + void* tlsdata; +} tlsdatasize_t; + +void free_tlsdatasize(void* p); + +typedef struct box64context_s { + path_collection_t box64_path; // PATH env. variable + path_collection_t box64_ld_lib; // LD_LIBRARY_PATH env. variable + + path_collection_t box64_emulated_libs; // Collection of libs that should not be wrapped + + int x86trace; + int trace_tid; + + uint32_t sel_serial; // will be increment each time selectors changes + + //zydis_t *zydis; // dlopen the zydis dissasembler + void* box64lib; // dlopen on box86 itself + + int argc; + char** argv; + + int envc; + char** envv; + + char* fullpath; + char* box64path; // path of current box86 executable + + uint32_t stacksz; + int stackalign; + void* stack; // alocated stack + + int deferedInit; + + pthread_key_t tlskey; // then tls key to have actual tlsdata + void* tlsdata; // the initial global tlsdata + int32_t tlssize; // wanted size of tlsdata + + //zydis_dec_t *dec; // trace + + uint8_t canary[4]; + + uintptr_t signals[MAX_SIGNAL]; + uintptr_t restorer[MAX_SIGNAL]; + int onstack[MAX_SIGNAL]; + int is_sigaction[MAX_SIGNAL]; + + int no_sigsegv; + int no_sigill; + +} box64context_t; + +extern box64context_t *my_context; // global context + +box64context_t *NewBox64Context(int argc); +void FreeBox64Context(box64context_t** context); + +// return the tlsbase (negative) for the new TLS partition created (no partition index is stored in the context) +int AddTLSPartition(box64context_t* context, int tlssize); + +#endif //__BOX64CONTEXT_H_ \ No newline at end of file diff --git a/src/include/debug.h b/src/include/debug.h new file mode 100755 index 00000000..8b440787 --- /dev/null +++ b/src/include/debug.h @@ -0,0 +1,42 @@ +#ifndef __DEBUG_H_ +#define __DEBUG_H_ +#include + +typedef struct box64context_s box64context_t; +extern int box64_log; // log level +extern int box64_dynarec_log; +extern int box64_dynarec; +extern int box64_pagesize; +extern int dlsym_error; // log dlsym error +extern int trace_xmm; // include XMM reg in trace? +extern int trace_emm; // include EMM reg in trace? +extern int allow_missing_libs; +extern int box64_steam; +extern int box64_nopulse; // disabling the use of wrapped pulseaudio +extern int box64_nogtk; // disabling the use of wrapped gtk +extern int box64_novulkan; // disabling the use of wrapped vulkan +extern uintptr_t trace_start, trace_end; +extern char* trace_func; +extern uintptr_t fmod_smc_start, fmod_smc_end; // to handle libfmod (from Unreal) SMC (self modifying code) +extern uint32_t default_fs; +extern int jit_gdb; // launch gdb when a segfault is trapped +extern int box64_tcmalloc_minimal; // when using tcmalloc_minimal +#define LOG_NONE 0 +#define LOG_INFO 1 +#define LOG_DEBUG 2 +#define LOG_DUMP 3 + +extern FILE* ftrace; + +#define printf_log(L, ...) do {if(L<=box64_log) {fprintf(ftrace, __VA_ARGS__); fflush(ftrace);}} while(0) + +#define dynarec_log(L, ...) do {if(L<=box64_dynarec_log) {fprintf(ftrace, __VA_ARGS__); /*fflush(ftrace);*/}} while(0) + +#define EXPORT __attribute__((visibility("default"))) +#ifdef BUILD_DYNAMIC +#define EXPORTDYN __attribute__((visibility("default"))) +#else +#define EXPORTDYN +#endif + +#endif //__DEBUG_H_ diff --git a/src/include/fileutils.h b/src/include/fileutils.h new file mode 100755 index 00000000..27256909 --- /dev/null +++ b/src/include/fileutils.h @@ -0,0 +1,23 @@ +#ifndef __FILEUTILS_H_ +#define __FILEUTILS_H_ + +#include "pathcoll.h" + +#define IS_EXECUTABLE (1<<0) +#define IS_FILE (1<<1) + + +// 0 : doesn't exist, 1: Does exist +int FileExist(const char* filename, int flags); + +// find a file, using Path if needed +char* ResolveFile(const char* filename, path_collection_t* paths); + +// 1: if file is an x86 elf, 0: if not (or not found) +int FileIsX64ELF(const char* filename); + +#if defined(RPI) || defined(RK3399) +void sanitize_mojosetup_gtk_background(); +#endif + +#endif //__FILEUTILS_H_ \ No newline at end of file diff --git a/src/include/khash.h b/src/include/khash.h new file mode 100755 index 00000000..dfce7aff --- /dev/null +++ b/src/include/khash.h @@ -0,0 +1,678 @@ +/* The MIT License + + Copyright (c) 2008, 2009, 2011 by Attractive Chaos + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +*/ + +/* + An example: + +#include "khash.h" +KHASH_MAP_INIT_INT(32, char) +int main() { + int ret, is_missing; + khiter_t k; + khash_t(32) *h = kh_init(32); + k = kh_put(32, h, 5, &ret); + kh_value(h, k) = 10; + k = kh_get(32, h, 10); + is_missing = (k == kh_end(h)); + k = kh_get(32, h, 5); + kh_del(32, h, k); + for (k = kh_begin(h); k != kh_end(h); ++k) + if (kh_exist(h, k)) kh_value(h, k) = 1; + kh_destroy(32, h); + return 0; +} +*/ + +/* + 2013-05-02 (0.2.8): + + * Use quadratic probing. When the capacity is power of 2, stepping function + i*(i+1)/2 guarantees to traverse each bucket. It is better than double + hashing on cache performance and is more robust than linear probing. + + In theory, double hashing should be more robust than quadratic probing. + However, my implementation is probably not for large hash tables, because + the second hash function is closely tied to the first hash function, + which reduce the effectiveness of double hashing. + + Reference: http://research.cs.vt.edu/AVresearch/hashing/quadratic.php + + 2011-12-29 (0.2.7): + + * Minor code clean up; no actual effect. + + 2011-09-16 (0.2.6): + + * The capacity is a power of 2. This seems to dramatically improve the + speed for simple keys. Thank Zilong Tan for the suggestion. Reference: + + - http://code.google.com/p/ulib/ + - http://nothings.org/computer/judy/ + + * Allow to optionally use linear probing which usually has better + performance for random input. Double hashing is still the default as it + is more robust to certain non-random input. + + * Added Wang's integer hash function (not used by default). This hash + function is more robust to certain non-random input. + + 2011-02-14 (0.2.5): + + * Allow to declare global functions. + + 2009-09-26 (0.2.4): + + * Improve portability + + 2008-09-19 (0.2.3): + + * Corrected the example + * Improved interfaces + + 2008-09-11 (0.2.2): + + * Improved speed a little in kh_put() + + 2008-09-10 (0.2.1): + + * Added kh_clear() + * Fixed a compiling error + + 2008-09-02 (0.2.0): + + * Changed to token concatenation which increases flexibility. + + 2008-08-31 (0.1.2): + + * Fixed a bug in kh_get(), which has not been tested previously. + + 2008-08-31 (0.1.1): + + * Added destructor +*/ + + +#ifndef __AC_KHASH_H +#define __AC_KHASH_H + +/*! + @header + + Generic hash table library. + */ + +#define AC_VERSION_KHASH_H "0.2.8" + +#include +#include +#include + +/* compiler specific configuration */ + +#if UINT_MAX == 0xffffffffu +typedef unsigned int khint32_t; +#elif ULONG_MAX == 0xffffffffu +typedef unsigned long khint32_t; +#endif + +#if ULONG_MAX == ULLONG_MAX +typedef unsigned long khint64_t; +#else +typedef unsigned long long khint64_t; +#endif + +#ifdef _MSC_VER +#define kh_inline __inline +#else +#define kh_inline inline +#endif + +typedef khint32_t khint_t; +typedef khint_t khiter_t; + +#define __ac_isempty(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&2) +#define __ac_isdel(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&1) +#define __ac_iseither(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&3) +#define __ac_set_isdel_false(flag, i) (flag[i>>4]&=~(1ul<<((i&0xfU)<<1))) +#define __ac_set_isempty_false(flag, i) (flag[i>>4]&=~(2ul<<((i&0xfU)<<1))) +#define __ac_set_isboth_false(flag, i) (flag[i>>4]&=~(3ul<<((i&0xfU)<<1))) +#define __ac_set_isdel_true(flag, i) (flag[i>>4]|=1ul<<((i&0xfU)<<1)) + +#define __ac_fsize(m) ((m) < 16? 1 : (m)>>4) + +#ifndef kroundup32 +#define kroundup32(x) (--(x), (x)|=(x)>>1, (x)|=(x)>>2, (x)|=(x)>>4, (x)|=(x)>>8, (x)|=(x)>>16, ++(x)) +#endif + +#ifndef kcalloc +#define kcalloc(N,Z) calloc(N,Z) +#endif +#ifndef kmalloc +#define kmalloc(Z) malloc(Z) +#endif +#ifndef krealloc +#define krealloc(P,Z) realloc(P,Z) +#endif +#ifndef kfree +#define kfree(P) free(P) +#endif + +static const double __ac_HASH_UPPER = 0.77; + +#define __KHASH_TYPE(name, khkey_t, khval_t) \ + typedef struct kh_##name##_s{ \ + khint_t n_buckets, size, n_occupied, upper_bound; \ + khint32_t *flags; \ + khkey_t *keys; \ + khval_t *vals; \ + } kh_##name##_t; + +#define __KHASH_PROTOTYPES(name, khkey_t, khval_t) \ + extern kh_##name##_t *kh_init_##name(void); \ + extern void kh_destroy_##name(kh_##name##_t *h); \ + extern void kh_clear_##name(kh_##name##_t *h); \ + extern khint_t kh_get_##name(const kh_##name##_t *h, khkey_t key); \ + extern int kh_resize_##name(kh_##name##_t *h, khint_t new_n_buckets); \ + extern khint_t kh_put_##name(kh_##name##_t *h, khkey_t key, int *ret); \ + extern void kh_del_##name(kh_##name##_t *h, khint_t x); + +#define __KHASH_IMPL(name, SCOPE, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \ + SCOPE kh_##name##_t *kh_init_##name(void) { \ + return (kh_##name##_t*)kcalloc(1, sizeof(kh_##name##_t)); \ + } \ + SCOPE void kh_destroy_##name(kh_##name##_t *h) \ + { \ + if (h) { \ + kfree((void *)h->keys); kfree(h->flags); \ + kfree((void *)h->vals); \ + kfree(h); \ + } \ + } \ + SCOPE void kh_clear_##name(kh_##name##_t *h) \ + { \ + if (h && h->flags) { \ + memset(h->flags, 0xaa, __ac_fsize(h->n_buckets) * sizeof(khint32_t)); \ + h->size = h->n_occupied = 0; \ + } \ + } \ + SCOPE khint_t kh_get_##name(const kh_##name##_t *h, khkey_t key) \ + { \ + if (h->n_buckets) { \ + khint_t k, i, last, mask, step = 0; \ + mask = h->n_buckets - 1; \ + k = __hash_func(key); i = k & mask; \ + last = i; \ + while (!__ac_isempty(h->flags, i) && (__ac_isdel(h->flags, i) || !__hash_equal(h->keys[i], key))) { \ + i = (i + (++step)) & mask; \ + if (i == last) return h->n_buckets; \ + } \ + return __ac_iseither(h->flags, i)? h->n_buckets : i; \ + } else return 0; \ + } \ + SCOPE int kh_resize_##name(kh_##name##_t *h, khint_t new_n_buckets) \ + { /* This function uses 0.25*n_buckets bytes of working space instead of [sizeof(key_t+val_t)+.25]*n_buckets. */ \ + khint32_t *new_flags = 0; \ + khint_t j = 1; \ + { \ + kroundup32(new_n_buckets); \ + if (new_n_buckets < 4) new_n_buckets = 4; \ + if (h->size >= (khint_t)(new_n_buckets * __ac_HASH_UPPER + 0.5)) j = 0; /* requested size is too small */ \ + else { /* hash table size to be changed (shrink or expand); rehash */ \ + new_flags = (khint32_t*)kmalloc(__ac_fsize(new_n_buckets) * sizeof(khint32_t)); \ + if (!new_flags) return -1; \ + memset(new_flags, 0xaa, __ac_fsize(new_n_buckets) * sizeof(khint32_t)); \ + if (h->n_buckets < new_n_buckets) { /* expand */ \ + khkey_t *new_keys = (khkey_t*)krealloc((void *)h->keys, new_n_buckets * sizeof(khkey_t)); \ + if (!new_keys) return -1; \ + h->keys = new_keys; \ + if (kh_is_map) { \ + khval_t *new_vals = (khval_t*)krealloc((void *)h->vals, new_n_buckets * sizeof(khval_t)); \ + if (!new_vals) return -1; \ + h->vals = new_vals; \ + } \ + } /* otherwise shrink */ \ + } \ + } \ + if (j) { /* rehashing is needed */ \ + for (j = 0; j != h->n_buckets; ++j) { \ + if (__ac_iseither(h->flags, j) == 0) { \ + khkey_t key = h->keys[j]; \ + khval_t val; \ + khint_t new_mask; \ + new_mask = new_n_buckets - 1; \ + if (kh_is_map) val = h->vals[j]; \ + __ac_set_isdel_true(h->flags, j); \ + while (1) { /* kick-out process; sort of like in Cuckoo hashing */ \ + khint_t k, i, step = 0; \ + k = __hash_func(key); \ + i = k & new_mask; \ + while (!__ac_isempty(new_flags, i)) i = (i + (++step)) & new_mask; \ + __ac_set_isempty_false(new_flags, i); \ + if (i < h->n_buckets && __ac_iseither(h->flags, i) == 0) { /* kick out the existing element */ \ + { khkey_t tmp = h->keys[i]; h->keys[i] = key; key = tmp; } \ + if (kh_is_map) { khval_t tmp = h->vals[i]; h->vals[i] = val; val = tmp; } \ + __ac_set_isdel_true(h->flags, i); /* mark it as deleted in the old hash table */ \ + } else { /* write the element and jump out of the loop */ \ + h->keys[i] = key; \ + if (kh_is_map) h->vals[i] = val; \ + break; \ + } \ + } \ + } \ + } \ + if (h->n_buckets > new_n_buckets) { /* shrink the hash table */ \ + h->keys = (khkey_t*)krealloc((void *)h->keys, new_n_buckets * sizeof(khkey_t)); \ + if (kh_is_map) h->vals = (khval_t*)krealloc((void *)h->vals, new_n_buckets * sizeof(khval_t)); \ + } \ + kfree(h->flags); /* free the working space */ \ + h->flags = new_flags; \ + h->n_buckets = new_n_buckets; \ + h->n_occupied = h->size; \ + h->upper_bound = (khint_t)(h->n_buckets * __ac_HASH_UPPER + 0.5); \ + } \ + return 0; \ + } \ + SCOPE khint_t kh_put_##name(kh_##name##_t *h, khkey_t key, int *ret) \ + { \ + khint_t x; \ + if (h->n_occupied >= h->upper_bound) { /* update the hash table */ \ + if (h->n_buckets > (h->size<<1)) { \ + if (kh_resize_##name(h, h->n_buckets - 1) < 0) { /* clear "deleted" elements */ \ + *ret = -1; return h->n_buckets; \ + } \ + } else if (kh_resize_##name(h, h->n_buckets + 1) < 0) { /* expand the hash table */ \ + *ret = -1; return h->n_buckets; \ + } \ + } /* TODO: to implement automatically shrinking; resize() already support shrinking */ \ + { \ + khint_t k, i, site, last, mask = h->n_buckets - 1, step = 0; \ + x = site = h->n_buckets; k = __hash_func(key); i = k & mask; \ + if (__ac_isempty(h->flags, i)) x = i; /* for speed up */ \ + else { \ + last = i; \ + while (!__ac_isempty(h->flags, i) && (__ac_isdel(h->flags, i) || !__hash_equal(h->keys[i], key))) { \ + if (__ac_isdel(h->flags, i)) site = i; \ + i = (i + (++step)) & mask; \ + if (i == last) { x = site; break; } \ + } \ + if (x == h->n_buckets) { \ + if (__ac_isempty(h->flags, i) && site != h->n_buckets) x = site; \ + else x = i; \ + } \ + } \ + } \ + if (__ac_isempty(h->flags, x)) { /* not present at all */ \ + h->keys[x] = key; \ + __ac_set_isboth_false(h->flags, x); \ + ++h->size; ++h->n_occupied; \ + *ret = 1; \ + } else if (__ac_isdel(h->flags, x)) { /* deleted */ \ + h->keys[x] = key; \ + __ac_set_isboth_false(h->flags, x); \ + ++h->size; \ + *ret = 2; \ + } else *ret = 0; /* Don't touch h->keys[x] if present and not deleted */ \ + return x; \ + } \ + SCOPE void kh_del_##name(kh_##name##_t *h, khint_t x) \ + { \ + if (x != h->n_buckets && !__ac_iseither(h->flags, x)) { \ + __ac_set_isdel_true(h->flags, x); \ + --h->size; \ + } \ + } + +#define KHASH_DECLARE(name, khkey_t, khval_t) \ + __KHASH_TYPE(name, khkey_t, khval_t) \ + __KHASH_PROTOTYPES(name, khkey_t, khval_t) + +#define KHASH_INIT2(name, SCOPE, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \ + __KHASH_TYPE(name, khkey_t, khval_t) \ + __KHASH_IMPL(name, SCOPE, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) + +#define KHASH_INIT(name, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \ + KHASH_INIT2(name, static kh_inline, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) + +/* --- BEGIN OF HASH FUNCTIONS --- */ + +/*! @function + @abstract Integer hash function + @param key The integer [khint32_t] + @return The hash value [khint_t] + */ +#define kh_int_hash_func(key) (khint32_t)(key) +/*! @function + @abstract Integer comparison function + */ +#define kh_int_hash_equal(a, b) ((a) == (b)) +/*! @function + @abstract 64-bit integer hash function + @param key The integer [khint64_t] + @return The hash value [khint_t] + */ +#define kh_int64_hash_func(key) (khint32_t)((key)>>33^(key)^(key)<<11) +/*! @function + @abstract 64-bit integer comparison function + */ +#define kh_int64_hash_equal(a, b) ((a) == (b)) +/*! @function + @abstract const char* hash function + @param s Pointer to a null terminated string + @return The hash value + */ +static kh_inline khint_t __ac_X31_hash_string(const char *s) +{ + khint_t h = (khint_t)*s; + if (h) for (++s ; *s; ++s) h = (h << 5) - h + (khint_t)*s; + return h; +} +/*! @function + @abstract Another interface to const char* hash function + @param key Pointer to a null terminated string [const char*] + @return The hash value [khint_t] + */ +#define kh_str_hash_func(key) __ac_X31_hash_string(key) +/*! @function + @abstract Const char* comparison function + */ +#define kh_str_hash_equal(a, b) (strcmp(a, b) == 0) + +static kh_inline khint_t __ac_Wang_hash(khint_t key) +{ + key += ~(key << 15); + key ^= (key >> 10); + key += (key << 3); + key ^= (key >> 6); + key += ~(key << 11); + key ^= (key >> 16); + return key; +} +#define kh_int_hash_func2(k) __ac_Wang_hash((khint_t)key) + +/* --- END OF HASH FUNCTIONS --- */ + +/* Other convenient macros... */ + +/*! + @abstract Type of the hash table. + @param name Name of the hash table [symbol] + */ +#define khash_t(name) kh_##name##_t + +/*! @function + @abstract Initiate a hash table. + @param name Name of the hash table [symbol] + @return Pointer to the hash table [khash_t(name)*] + */ +#define kh_init(name) kh_init_##name() + +/*! @function + @abstract Destroy a hash table. + @param name Name of the hash table [symbol] + @param h Pointer to the hash table [khash_t(name)*] + */ +#define kh_destroy(name, h) kh_destroy_##name(h) + +/*! @function + @abstract Reset a hash table without deallocating memory. + @param name Name of the hash table [symbol] + @param h Pointer to the hash table [khash_t(name)*] + */ +#define kh_clear(name, h) kh_clear_##name(h) + +/*! @function + @abstract Resize a hash table. + @param name Name of the hash table [symbol] + @param h Pointer to the hash table [khash_t(name)*] + @param s New size [khint_t] + */ +#define kh_resize(name, h, s) kh_resize_##name(h, s) + +/*! @function + @abstract Insert a key to the hash table. + @param name Name of the hash table [symbol] + @param h Pointer to the hash table [khash_t(name)*] + @param k Key [type of keys] + @param r Extra return code: 0 if the key is present in the hash table; + 1 if the bucket is empty (never used); 2 if the element in + the bucket has been deleted [int*] + @return Iterator to the inserted element [khint_t] + */ +#define kh_put(name, h, k, r) kh_put_##name(h, k, r) + +/*! @function + @abstract Retrieve a key from the hash table. + @param name Name of the hash table [symbol] + @param h Pointer to the hash table [khash_t(name)*] + @param k Key [type of keys] + @return Iterator to the found element, or kh_end(h) if the element is absent [khint_t] + */ +#define kh_get(name, h, k) kh_get_##name(h, k) + +/*! @function + @abstract Remove a key from the hash table. + @param name Name of the hash table [symbol] + @param h Pointer to the hash table [khash_t(name)*] + @param k Iterator to the element to be deleted [khint_t] + */ +#define kh_del(name, h, k) kh_del_##name(h, k) + +/*! @function + @abstract Test whether a bucket contains data. + @param h Pointer to the hash table [khash_t(name)*] + @param x Iterator to the bucket [khint_t] + @return 1 if containing data; 0 otherwise [int] + */ +#define kh_exist(h, x) (!__ac_iseither((h)->flags, (x))) + +/*! @function + @abstract Get key given an iterator + @param h Pointer to the hash table [khash_t(name)*] + @param x Iterator to the bucket [khint_t] + @return Key [type of keys] + */ +#define kh_key(h, x) ((h)->keys[x]) + +/*! @function + @abstract Get value given an iterator + @param h Pointer to the hash table [khash_t(name)*] + @param x Iterator to the bucket [khint_t] + @return Value [type of values] + @discussion For hash sets, calling this results in segfault. + */ +#define kh_val(h, x) ((h)->vals[x]) + +/*! @function + @abstract Alias of kh_val() + */ +#define kh_value(h, x) ((h)->vals[x]) + +/*! @function + @abstract Get the start iterator + @param h Pointer to the hash table [khash_t(name)*] + @return The start iterator [khint_t] + */ +#define kh_begin(h) (khint_t)(0) + +/*! @function + @abstract Get the end iterator + @param h Pointer to the hash table [khash_t(name)*] + @return The end iterator [khint_t] + */ +#define kh_end(h) ((h)->n_buckets) + +/*! @function + @abstract Get the number of elements in the hash table + @param h Pointer to the hash table [khash_t(name)*] + @return Number of elements in the hash table [khint_t] + */ +#define kh_size(h) ((h)->size) + +/*! @function + @abstract Get the number of buckets in the hash table + @param h Pointer to the hash table [khash_t(name)*] + @return Number of buckets in the hash table [khint_t] + */ +#define kh_n_buckets(h) ((h)->n_buckets) + +/*! @function + @abstract Iterate over the entries in the hash table + @param h Pointer to the hash table [khash_t(name)*] + @param kvar Variable to which key will be assigned + @param vvar Variable to which value will be assigned + @param code Block of code to execute + */ +#define kh_foreach(h, kvar, vvar, code) { khint_t __i; \ + for (__i = kh_begin(h); __i != kh_end(h); ++__i) { \ + if (!kh_exist(h,__i)) continue; \ + (kvar) = kh_key(h,__i); \ + (vvar) = kh_val(h,__i); \ + code; \ + } } + +/*! @function + @abstract Iterate over the entries in the hash table + @param h Pointer to the hash table [khash_t(name)*] + @param kvar Variable to which key will be assigned + @param code Block of code to execute + */ +#define kh_foreach_key(h, kvar, code) { khint_t __i; \ + for (__i = kh_begin(h); __i != kh_end(h); ++__i) { \ + if (!kh_exist(h,__i)) continue; \ + (kvar) = kh_key(h,__i); \ + code; \ + } } + +/*! @function + @abstract Iterate over the values in the hash table + @param h Pointer to the hash table [khash_t(name)*] + @param vvar Variable to which value will be assigned + @param code Block of code to execute + */ +#define kh_foreach_value(h, vvar, code) { khint_t __i; \ + for (__i = kh_begin(h); __i != kh_end(h); ++__i) { \ + if (!kh_exist(h,__i)) continue; \ + (vvar) = kh_val(h,__i); \ + code; \ + } } + +/*! @function + @abstract Iterate over the values in the hash table + @param h Pointer to the hash table [khash_t(name)*] + @param rvar Variable to which value will be assigned + @param code Block of code to execute + */ +#define kh_foreach_value_ref(h, rvar, code) { khint_t __i; \ + for (__i = kh_begin(h); __i != kh_end(h); ++__i) { \ + if (!kh_exist(h,__i)) continue; \ + (rvar) = &kh_val(h,__i); \ + code; \ + } } + +/* More conenient interfaces */ + +/*! @function + @abstract Instantiate a hash set containing integer keys + @param name Name of the hash table [symbol] + */ +#define KHASH_SET_INIT_INT(name) \ + KHASH_INIT(name, khint32_t, char, 0, kh_int_hash_func, kh_int_hash_equal) + +#define KHASH_SET_DECLARE_INT(name) \ + KHASH_DECLARE(name, khint32_t, char) + +#define KHASH_SET_IMPL_INT(name) \ + __KHASH_IMPL(name, , khint32_t, char, 0, kh_int_hash_func, kh_int_hash_equal) + +/*! @function + @abstract Instantiate a hash map containing integer keys + @param name Name of the hash table [symbol] + @param khval_t Type of values [type] + */ +#define KHASH_MAP_INIT_INT(name, khval_t) \ + KHASH_INIT(name, khint32_t, khval_t, 1, kh_int_hash_func, kh_int_hash_equal) + +#define KHASH_MAP_DECLARE_INT(name, khval_t) \ + KHASH_DECLARE(name, khint32_t, khval_t) + +#define KHASH_MAP_IMPL_INT(name, khval_t) \ + __KHASH_IMPL(name, , khint32_t, khval_t, 1, kh_int_hash_func, kh_int_hash_equal) + +/*! @function + @abstract Instantiate a hash map containing 64-bit integer keys + @param name Name of the hash table [symbol] + */ +#define KHASH_SET_INIT_INT64(name) \ + KHASH_INIT(name, khint64_t, char, 0, kh_int64_hash_func, kh_int64_hash_equal) + +#define KHASH_SET_DECLARE_INT64(name) \ + KHASH_DECLARE(name, khint64_t, char) + +#define KHASH_SET_IMPL_INT64(name) \ + __KHASH_IMPL(name, , khint64_t, char, 0, kh_int64_hash_func, kh_int64_hash_equal) + +/*! @function + @abstract Instantiate a hash map containing 64-bit integer keys + @param name Name of the hash table [symbol] + @param khval_t Type of values [type] + */ +#define KHASH_MAP_INIT_INT64(name, khval_t) \ + KHASH_INIT(name, khint64_t, khval_t, 1, kh_int64_hash_func, kh_int64_hash_equal) + +#define KHASH_MAP_DECLARE_INT64(name, khval_t) \ + KHASH_DECLARE(name, khint64_t, khval_t) + +#define KHASH_MAP_IMPL_INT64(name, khval_t) \ + __KHASH_IMPL(name, , khint64_t, khval_t, 1, kh_int64_hash_func, kh_int64_hash_equal) + +typedef const char *kh_cstr_t; +/*! @function + @abstract Instantiate a hash map containing const char* keys + @param name Name of the hash table [symbol] + */ +#define KHASH_SET_INIT_STR(name) \ + KHASH_INIT(name, kh_cstr_t, char, 0, kh_str_hash_func, kh_str_hash_equal) + +#define KHASH_SET_DECLARE_STR(name) \ + KHASH_DECLARE(name, kh_cstr_t, char) + +#define KHASH_SET_IMPL_STR(name) \ + __KHASH_IMPL(name, , kh_cstr_t, char, 0, kh_str_hash_func, kh_str_hash_equal) + +/*! @function + @abstract Instantiate a hash map containing const char* keys + @param name Name of the hash table [symbol] + @param khval_t Type of values [type] + */ +#define KHASH_MAP_INIT_STR(name, khval_t) \ + KHASH_INIT(name, kh_cstr_t, khval_t, 1, kh_str_hash_func, kh_str_hash_equal) + +#define KHASH_MAP_DECLARE_STR(name, khval_t) \ + KHASH_DECLARE(name, kh_cstr_t, khval_t) + +#define KHASH_MAP_IMPL_STR(name, khval_t) \ + __KHASH_IMPL(name, , kh_cstr_t, khval_t, 1, kh_str_hash_func, kh_str_hash_equal) + +#endif /* __AC_KHASH_H */ diff --git a/src/include/pathcoll.h b/src/include/pathcoll.h new file mode 100755 index 00000000..f1a0b93e --- /dev/null +++ b/src/include/pathcoll.h @@ -0,0 +1,23 @@ +#ifndef __PATHCOLL_H_ +#define __PATHCOLL_H_ + +// utility to handle path collection (like BOX86_PATH or BOX86_LD_LIBRARY_PATH) + +// paths can be resized with realloc, so don't take address as invariant +typedef struct path_collection_s +{ + int size; + int cap; + char** paths; +} path_collection_t; + +void ParseList(const char* List, path_collection_t* collection, int folder); +void FreeCollection(path_collection_t* collection); +void CopyCollection(path_collection_t* to, path_collection_t* from); +void AddPath(const char* path, path_collection_t* collection, int folder); +void PrependPath(const char* path, path_collection_t* collection, int folder); +void AppendList(path_collection_t* collection, const char* List, int folder); +void PrependList(path_collection_t* collection, const char* List, int folder); +int FindInCollection(const char* path, path_collection_t* collection); + +#endif //__PATHCOLL_H_ diff --git a/src/include/wine_tools.h b/src/include/wine_tools.h new file mode 100755 index 00000000..52993cea --- /dev/null +++ b/src/include/wine_tools.h @@ -0,0 +1,13 @@ +#ifndef __WINE_TOOLS_H__ +#define __WINE_TOOLS_H__ + +void wine_prereserve(const char* reserve); + +extern int wine_preloaded; +void* get_wine_prereserve(); + +#ifdef DYNAREC +void dynarec_wine_prereserve(); +#endif + +#endif //__WINE_TOOLS_H__ diff --git a/src/main.c b/src/main.c new file mode 100755 index 00000000..4858e797 --- /dev/null +++ b/src/main.c @@ -0,0 +1,734 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "build_info.h" +#include "debug.h" +#include "fileutils.h" +#include "box64context.h" +#include "wine_tools.h" + +box64context_t *my_context = NULL; +int box64_log = LOG_NONE; +int box64_nobanner = 0; +int box64_dynarec_log = LOG_INFO; //LOG_NONE; +int box64_pagesize; +int box64_dynarec = 0; +int dlsym_error = 0; +int trace_xmm = 0; +int trace_emm = 0; +#ifdef HAVE_TRACE +uint64_t start_cnt = 0; +#ifdef DYNAREC +int box64_dynarec_trace = 0; +#endif +#endif +int box64_zoom = 0; +int x11threads = 0; +int x11glx = 1; +int allow_missing_libs = 0; +int fix_64bit_inodes = 0; +int box64_steam = 0; +int box64_nopulse = 0; +int box64_nogtk = 0; +int box64_novulkan = 0; +char* libGL = NULL; +uintptr_t trace_start = 0, trace_end = 0; +char* trace_func = NULL; +uintptr_t fmod_smc_start = 0; +uintptr_t fmod_smc_end = 0; +uint32_t default_fs = 0; +int jit_gdb = 0; +int box64_tcmalloc_minimal = 0; + +FILE* ftrace = NULL; +int ftrace_has_pid = 0; + +void openFTrace() +{ + char* t = getenv("BOX64_TRACE_FILE"); + char tmp[500]; + char* p = t; + if(p && strstr(t, "%pid")) { + strcpy(tmp, p); + char* c = strstr(tmp, "%pid"); + *c = 0; // cut + char pid[10]; + sprintf(pid, "%d", getpid()); + strcat(tmp, pid); + c = strstr(p, "%pid") + strlen("%pid"); + strcat(tmp, c); + p = tmp; + ftrace_has_pid = 1; + } + if(p) { + ftrace = fopen(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(!box64_nobanner) + printf("BOX64 Trace redirected to \"%s\"\n", p); + } + } +} + +void my_child_fork() +{ + if(ftrace_has_pid) { + // open a new ftrace... + fclose(ftrace); + openFTrace(); + } +} + + +EXPORTDYN +void LoadLogEnv() +{ + ftrace = stdout; + const char *p = getenv("BOX64_NOBANNER"); + if(p) { + if(strlen(p)==1) { + if(p[0]>='0' && p[1]<='1') + box64_nobanner = p[0]-'0'; + } + } + p = getenv("BOX64_LOG"); + if(p) { + if(strlen(p)==1) { + if(p[0]>='0'+LOG_NONE && p[1]<='0'+LOG_DEBUG) + box64_log = p[0]-'0'; + } else { + if(!strcasecmp(p, "NONE")) + box64_log = LOG_NONE; + else if(!strcasecmp(p, "INFO")) + box64_log = LOG_INFO; + else if(!strcasecmp(p, "DEBUG")) + box64_log = LOG_DEBUG; + else if(!strcasecmp(p, "DUMP")) + box64_log = LOG_DUMP; + } + if(!box64_nobanner) + printf_log(LOG_INFO, "Debug level is %d\n", box64_log); + } +#ifdef HAVE_TRACE + p = getenv("BOX64_TRACE_XMM"); + if(p) { + if(strlen(p)==1) { + if(p[0]>='0' && p[1]<='0'+1) + trace_xmm = p[0]-'0'; + } + } + p = getenv("BOX64_TRACE_EMM"); + if(p) { + if(strlen(p)==1) { + if(p[0]>='0' && p[1]<='0'+1) + trace_emm = p[0]-'0'; + } + } + p = getenv("BOX64_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); + } +#endif + // grab BOX64_TRACE_FILE envvar, and change %pid to actual pid is present in the name + openFTrace(); + // Other BOX64 env. var. + p = getenv("BOX64_DLSYM_ERROR"); + if(p) { + if(strlen(p)==1) { + if(p[0]>='0' && p[1]<='0'+1) + dlsym_error = p[0]-'0'; + } + } + p = getenv("BOX64_X11THREADS"); + if(p) { + if(strlen(p)==1) { + if(p[0]>='0' && p[1]<='0'+1) + x11threads = p[0]-'0'; + } + if(x11threads) + printf_log(LOG_INFO, "Try to Call XInitThreads if libX11 is loaded\n"); + } + p = getenv("BOX64_X11GLX"); + if(p) { + if(strlen(p)==1) { + if(p[0]>='0' && p[1]<='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("BOX64_LIBGL"); + if(p) + libGL = strdup(p); + if(!libGL) { + p = getenv("SDL_VIDEO_GL_DRIVER"); + if(p) + libGL = strdup(p); + } + if(libGL) { + printf_log(LOG_INFO, "BOX64 using \"%s\" as libGL.so.1\n", p); + } + p = getenv("BOX64_ALLOWMISSINGLIBS"); + if(p) { + if(strlen(p)==1) { + if(p[0]>='0' && p[1]<='0'+1) + allow_missing_libs = p[0]-'0'; + } + if(allow_missing_libs) + printf_log(LOG_INFO, "Allow missing needed libs\n"); + } + p = getenv("BOX64_NOPULSE"); + if(p) { + if(strlen(p)==1) { + if(p[0]>='0' && p[1]<='0'+1) + box64_nopulse = p[0]-'0'; + } + if(box64_nopulse) + printf_log(LOG_INFO, "Disable the use of pulseaudio libs\n"); + } + p = getenv("BOX64_NOGTK"); + if(p) { + if(strlen(p)==1) { + if(p[0]>='0' && p[1]<='0'+1) + box64_nogtk = p[0]-'0'; + } + if(box64_nogtk) + printf_log(LOG_INFO, "Disable the use of wrapped gtk libs\n"); + } + p = getenv("BOX64_NOVULKAN"); + if(p) { + if(strlen(p)==1) { + if(p[0]>='0' && p[1]<='0'+1) + box64_novulkan = p[0]-'0'; + } + if(box64_novulkan) + printf_log(LOG_INFO, "Disable the use of wrapped vulkan libs\n"); + } + p = getenv("BOX64_FIX_64BIT_INODES"); + if(p) { + if(strlen(p)==1) { + if(p[0]>='0' && p[1]<='0'+1) + fix_64bit_inodes = p[0]-'0'; + } + if(fix_64bit_inodes) + printf_log(LOG_INFO, "Fix 64bit inodes\n"); + } + p = getenv("BOX64_JITGDB"); + if(p) { + if(strlen(p)==1) { + if(p[0]>='0' && p[1]<='0'+1) + jit_gdb = p[0]-'0'; + } + if(jit_gdb) + printf_log(LOG_INFO, "Launch %s on segfault\n", (jit_gdb==2)?"gdbserver":"gdb"); + } + box64_pagesize = sysconf(_SC_PAGESIZE); + if(!box64_pagesize) + box64_pagesize = 4096; +} + +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<=box64_log) { + for(int i=0; isize; i++) + printf_log(LOG_INFO, "%s%s", col->paths[i], (i==col->size-1)?"\n":":"); + } +} + +EXPORTDYN +int CountEnv(const char** env) +{ + // count, but remove all BOX64_* environnement + // also remove PATH and LD_LIBRARY_PATH + // but add 2 for default BOX64_PATH and BOX64_LD_LIBRARY_PATH + const char** p = env; + int c = 0; + while(*p) { + if(strncmp(*p, "BOX64_", 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, const char** env, const char* prog) +{ + // Add all but BOX64_* environnement + // but add 2 for default BOX64_PATH and BOX64_LD_LIBRARY_PATH + const char** p = env; + int idx = 0; + int path = 0; + int ld_path = 0; + while(*p) { + if(strncmp(*p, "BOX64_PATH=", 11)==0) { + (*dest)[idx++] = strdup(*p+6); + path = 1; + } else if(strncmp(*p, "BOX64_LD_LIBRARY_PATH=", 22)==0) { + (*dest)[idx++] = strdup(*p+6); + ld_path = 1; + } else if(strncmp(*p, "_=", 2)==0) { + /*int l = strlen(prog); + char tmp[l+3]; + strcpy(tmp, "_="); + strcat(tmp, prog); + (*dest)[idx++] = strdup(tmp);*/ + } else if(strncmp(*p, "BOX64_", 6)!=0) { + (*dest)[idx++] = 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(!path) { + (*dest)[idx++] = strdup("BOX64_PATH=.:bin"); + } + if(!ld_path) { + (*dest)[idx++] = strdup("BOX64_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++] = 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(" BOX64_PATH is the box86 version of PATH (default is '.:bin')\n"); + printf(" BOX64_LD_LIBRARY_PATH is the box86 version LD_LIBRARY_PATH (default is '.:lib')\n"); + printf(" BOX64_LOG with 0/1/2/3 or NONE/INFO/DEBUG/DUMP to set the printed debug info\n"); + printf(" BOX64_NOBANNER with 0/1 to enable/disable the printing of box86 version and build at start\n"); +#ifdef HAVE_TRACE + printf(" BOX64_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 BOX64_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(" BOX64_TRACE_XMM with 1 to enable dump of SSE/SSE2 register along with regular registers\n"); + printf(" BOX64_TRACE_START with N to enable trace after N instructions\n"); +#endif + printf(" BOX64_TRACE_FILE with FileName to redirect logs in a file"); + printf(" BOX64_DLSYM_ERROR with 1 to log dlsym errors\n"); + printf(" BOX64_LOAD_ADDR=0xXXXXXX try to load at 0xXXXXXX main binary (if binary is a PIE)\n"); + printf(" BOX64_NOSIGSEGV=1 to disable handling of SigSEGV\n"); + printf(" BOX64_NOSIGILL=1 to disable handling of SigILL\n"); + printf(" BOX64_X11THREADS=1 to call XInitThreads when loading X11 (for old Loki games with Loki_Compat lib)"); + printf(" BOX64_LIBGL=libXXXX set the name (and optionnaly full path) for libGL.so.1\n"); + printf(" BOX64_LD_PRELOAD=XXXX[:YYYYY] force loading XXXX (and YYYY...) libraries with the binary\n"); + printf(" BOX64_ALLOWMISSINGLIBS with 1 to allow to continue even if a lib is missing (unadvised, will probably crash later)\n"); + printf(" BOX64_NOPULSE=1 to disable the loading of pulseaudio libs\n"); + printf(" BOX64_NOGTK=1 to disable the loading of wrapped gtk libs\n"); + printf(" BOX64_NOVULKAN=1 to disable the loading of wrapped vulkan libs\n"); + printf(" BOX64_JITGDB with 1 to launch \"gdb\" when a segfault is trapped, attached to the offending process\n"); +} + +EXPORTDYN +void LoadEnvVars(box64context_t *context) +{ + // check BOX64_LD_LIBRARY_PATH and load it + LoadEnvPath(&context->box64_ld_lib, ".:lib:lib64:x86_64", "BOX64_LD_LIBRARY_PATH"); + 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("/lib/i686-pc-linux-gnu", 0)) + AddPath("/lib/i686-pc-linux-gnu", &context->box64_ld_lib, 1); + if(FileExist("/usr/lib/i686-pc-linux-gnu", 0)) + AddPath("/usr/lib/i686-pc-linux-gnu", &context->box64_ld_lib, 1); + if(FileExist("/usr/lib32", 0)) + AddPath("/usr/lib32", &context->box64_ld_lib, 1); + 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 + if(getenv("BOX64_EMULATED_LIBS")) { + char* p = getenv("BOX64_EMULATED_LIBS"); + ParseList(p, &context->box64_emulated_libs, 0); + if (my_context->box64_emulated_libs.size && box64_log) { + printf_log(LOG_INFO, "BOX64 will force the used of emulated libs for "); + for (int i=0; ibox64_emulated_libs.size; ++i) + printf_log(LOG_INFO, "%s ", context->box64_emulated_libs.paths[i]); + printf_log(LOG_INFO, "\n"); + } + } + + if(getenv("BOX64_NOSIGSEGV")) { + if (strcmp(getenv("BOX64_NOSIGSEGV"), "1")==0) + context->no_sigsegv = 1; + printf_log(LOG_INFO, "BOX64: Disabling handling of SigSEGV\n"); + } + if(getenv("BOX64_NOSIGILL")) { + if (strcmp(getenv("BOX64_NOSIGILL"), "1")==0) + context->no_sigill = 1; + printf_log(LOG_INFO, "BOX64: Disabling handling of SigILL\n"); + } + // check BOX64_PATH and load it + LoadEnvPath(&context->box64_path, ".:bin", "BOX64_PATH"); + if(getenv("PATH")) + AppendList(&context->box64_path, getenv("PATH"), 1); // in case some of the path are for x86 world +#ifdef HAVE_TRACE + char* p = getenv("BOX64_TRACE"); + if(p) { + if (strcmp(p, "0")) + context->x86trace = 1; + } + p = getenv("BOX64_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; + }*/ + context->x86trace = 0; // not implemented yet + } +#endif +} + +EXPORTDYN +void setupTraceInit(box64context_t* context) +{ +#ifdef HAVE_TRACE + char* p = getenv("BOX64_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 (0/*GetSymbolStartEnd(GetMapSymbol(my_context->maplib), p, &trace_start, &trace_end)*/) { + 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("BOX64_TRACE"); + if(p) + if (strcmp(p, "0")) + SetTraceEmu(0, 1); + } +#endif +} + +EXPORTDYN +void setupTrace(box64context_t* context) +{ +#ifdef HAVE_TRACE + char* p = getenv("BOX64_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 (0/*GetGlobalSymbolStartEnd(my_context->maplib, p, &trace_start, &trace_end)*/) { + 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(0/*GetLocalSymbolStartEnd(my_context->maplib, p, &trace_start, &trace_end, 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 = strdup(p); + } + } + } +#endif +} + +void endBox86() +{ + if(!my_context) + return; + + // all done, free context + FreeBox64Context(&my_context); + if(libGL) { + free(libGL); + libGL = NULL; + } +} + + +static void free_contextargv() +{ + for(int i=0; iargc; ++i) + free(my_context->argv[i]); +} + +const char **environ __attribute__((weak)) = NULL; +int main(int argc, const char **argv, const char **env) { + + //init_auxval(argc, argv, environ?environ:env); + // trying to open and load 1st arg + if(argc==1) { + PrintBox64Version(); + PrintHelp(); + return 1; + } + + // init random seed + srandom(time(NULL)); + + // check BOX64_LOG debug level + LoadLogEnv(); + + 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(); + 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("Box64: nothing to run\n"); + exit(0); + } + if(!box64_nobanner) + PrintBox64Version(); + // precheck, for win-preload + if(strstr(prog, "wine-preloader")==(prog+strlen(prog)-strlen("wine-preloader"))) { + // wine-preloader detecter, skipping it if next arg exist and is an x86 binary + int x64 = (nextarg5 && !strcmp(prog+strlen(prog)-strlen("/wine"), "/wine"))) { + const char* prereserve = getenv("WINEPRELOADRESERVE"); + printf_log(LOG_INFO, "BOX64: 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 + } + } + // Create a new context + my_context = NewBox64Context(argc - nextarg); + + // check BOX64_LD_LIBRARY_PATH and load it + LoadEnvVars(my_context); + + if(argv[0][0]=='/') + my_context->box64path = strdup(argv[0]); + else + 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**)calloc(my_context->envc+4, sizeof(char*)); + GatherEnv(&my_context->envv, environ?environ:env, my_context->box64path); + if(box64_log>=LOG_DUMP) { + for (int i=0; ienvc; ++i) + printf_log(LOG_DUMP, " Env[%02d]: %s\n", i, my_context->envv[i]); + } + + 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 && box64_log) { + printf_log(LOG_INFO, "BOX64 try to Preload "); + for (int i=0; iargv[0] = strdup(prog); + else + my_context->argv[0] = ResolveFile(prog, &my_context->box64_path); + + const char* prgname = strrchr(prog, '/'); + if(!prgname) + prgname = prog; + else + ++prgname; + // 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->box64_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->box64_emulated_libs, 0); + } + // 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\n"); + AddPath("libudev.so.0", &my_context->box64_emulated_libs, 0); + box64_steam = 1; + } + // 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 UnrealLinux.bin, it doesn't like "full path resolution" + if(!strcmp(prog, "UnrealLinux.bin") && my_context->argv[0]) { + free(my_context->argv[0]); + my_context->argv[0] = strdup("./UnrealLinux.bin"); + } + #ifdef RPI + // 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"); + box64_tokitori2 = 1; + } + #endif + // special case for zoom + if(strstr(prgname, "zoom")==prgname) { + printf_log(LOG_INFO, "Zoom detected, trying to use system libturbojpeg if possible\n"); + box64_zoom = 1; + } + /*if(strstr(prgname, "awesomium_process")==prgname) { + printf_log(LOG_INFO, "awesomium_process detected, forcing emulated libpng12\n"); + AddPath("libpng12.so.0", &my_context->box64_emulated_libs, 0); + }*/ + /*if(!strcmp(prgname, "gdb")) { + exit(-1); + }*/ + + for(int i=1; iargc; ++i) { + my_context->argv[i] = 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 BOX64_PATH)\n"); + 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 = realpath(my_context->argv[0], NULL))) + my_context->fullpath = strdup(my_context->argv[0]); + FILE *f = fopen(my_context->argv[0], "rb"); + if(!f) { + printf_log(LOG_NONE, "Error: Cannot open %s\n", my_context->argv[0]); + free_contextargv(); + FreeBox64Context(&my_context); + FreeCollection(&ld_preload); + return -1; + } + /*elfheader_t *elf_header = LoadAndCheckElfHeader(f, my_context->argv[0], 1); + if(!elf_header) { + printf_log(LOG_NONE, "Error: reading elf header of %s, try to launch natively instead\n", my_context->argv[0]); + fclose(f); + free_contextargv(); + FreeBox64Context(&my_context); + FreeCollection(&ld_preload); + return execvp(argv[1], (char * const*)(argv+1)); + } + AddElfHeader(my_context, elf_header);*/ + return 0; +} diff --git a/src/tools/fileutils.c b/src/tools/fileutils.c new file mode 100755 index 00000000..6a964553 --- /dev/null +++ b/src/tools/fileutils.c @@ -0,0 +1,129 @@ +#define _FILE_OFFSET_BITS 64 +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef MAX_PATH +#define MAX_PATH 4096 +#endif + +#include "debug.h" +#include "fileutils.h" + +static const char* x64sign = "\x7f" "ELF" "\x02" "\x01" "\x01" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x02" "\x00" "\x3e" "\x00"; + +int FileExist(const char* filename, int flags) +{ + struct stat sb; + if (stat(filename, &sb) == -1) + return 0; + if(flags==-1) + return 1; + // check type of file? should be executable, or folder + if(flags&IS_FILE) { + if(!S_ISREG(sb.st_mode)) + return 0; + } else if(!S_ISDIR(sb.st_mode)) + return 0; + + if(flags&IS_EXECUTABLE) { + if((sb.st_mode&S_IXUSR)!=S_IXUSR) + return 0; // nope + } + return 1; +} + +char* ResolveFile(const char* filename, path_collection_t* paths) +{ + char p[MAX_PATH]; + if(filename[0]=='/') + return strdup(filename); + for (int i=0; isize; ++i) { + if(paths->paths[i][0]!='/') { + // not an absolute path... + getcwd(p, sizeof(p)); + if(p[strlen(p)-1]!='/') + strcat(p, "/"); + strcat(p, paths->paths[i]); + } else + strcpy(p, paths->paths[i]); + strcat(p, filename); + if(FileExist(p, IS_FILE)) + return realpath(p, NULL); + } + + return NULL; +} + +int FileIsX64ELF(const char* filename) +{ + FILE *f = fopen(filename, "rb"); + if(!f) + return 0; + char head[sizeof(*x64sign)] = {0}; + int sz = fread(head, sizeof(*x64sign), 1, f); + if(sz!=1) { + fclose(f); + return 0; + } + fclose(f); + if(memcmp(head, x64sign, sizeof(*x64sign))==0) + return 1; + return 0; +} + +#if defined(RPI) || defined(RK3399) +void sanitize_mojosetup_gtk_background() +{ + // get GTK2_RC_FILES folder + const char* gtk2_rc = getenv("GTK2_RC_FILES"); + // check if $GTK2_RC_FILES/pixmaps/background.png exist + char background[1000] = {0}; + strcpy(background, gtk2_rc); + char* p = strrchr(background, '/'); // remove "/gtkrc" + // every error will just silently abort + if(!p) + return; + *p = 0; + strcat(background, "/pixmaps/background.png"); + if(!FileExist(background, IS_FILE)) + return; + // now open are read the header of the PNG to grab the width and height + //very crude reading here! + FILE* f = fopen(background, "rb"); + if(!f) + return; + char sign[8]; + if(fread(sign, 8, 1, f)!=1) { + fclose(f); return; + } + const char ref[8] = {'\211', 'P', 'N', 'G', '\r', '\n', '\032', '\n' }; + if (memcmp(sign, ref, 8)) { + fclose(f); return; + } + int32_t width, height; + fseek(f, 16, SEEK_SET); + if(fread(&width, sizeof(width), 1, f)!=1) { + fclose(f); return; + } + if(fread(&height, sizeof(height), 1, f)!=1) { + fclose(f); return; + } + fclose(f); + // need to swap bitness! + width = __builtin_bswap32(width); + height = __builtin_bswap32(height); + printf_log(LOG_INFO, "Mojosetup detected, size of background picture is %dx%d\n", width, height); + if(width!=5000 || height!=3000) + return; // not a background that will cause any issue + // delete the file! + f = fopen(background, "r+b"); + remove(background); + printf_log(LOG_INFO, "background deleted!\n"); +} +#endif diff --git a/src/tools/pathcoll.c b/src/tools/pathcoll.c new file mode 100755 index 00000000..d7e423bb --- /dev/null +++ b/src/tools/pathcoll.c @@ -0,0 +1,182 @@ +#include +#include +#include +#include +#include + +#ifndef MAX_PATH +#define MAX_PATH 4096 +#endif + +#include "debug.h" +#include "pathcoll.h" + +void FreeCollection(path_collection_t* collection) +{ + if(!collection) + return; + if(collection->cap) { + for(int i=0; isize; ++i) + free(collection->paths[i]); + free(collection->paths); + } + collection->paths = NULL; + collection->size = 0; + collection->cap = 0; + return; +} + +void ParseList(const char* List, path_collection_t* collection, int folder) +{ + if(collection->cap) + FreeCollection(collection); + // count the stings (basically count(':') + 1) to define cap + int cnt = 1; + { + const char* p = List; + while(p) { + p = strchr(p, ':'); + ++cnt; + if(p) ++p; + } + } + // alloc empty strings + collection->cap = cnt; + collection->paths = (char**)calloc(cnt, sizeof(char*)); + // and now split the paths... + char tmp[MAX_PATH]; + const char *p = List; + int idx = 0; + while(p) { + const char *p2 = strchr(p, ':'); + if(!p2) { + strncpy(tmp, p, MAX_PATH - 1); + p=NULL; + } else { + int l = (uintptr_t)p2 - (uintptr_t)p; + strncpy(tmp, p, l); + tmp[l]='\0'; + p=p2+1; + } + // check if there is terminal '/', add it if not + int l = strlen(tmp); + // skip empty strings + if(l) { + if(folder && tmp[l-1]!='/') + strcat(tmp, "/"); + collection->paths[idx] =strdup(tmp); + collection->size=++idx; + } + } +} + +void CopyCollection(path_collection_t* to, path_collection_t* from) +{ + to->cap = from->cap; + to->paths = (char**)calloc(to->cap, sizeof(char*)); + to->size = from->size; + for (int i=0; isize; ++i) + to->paths[i] = strdup(from->paths[i]); +} + +void AddPath(const char* path, path_collection_t* collection, int folder) +{ + char tmp[MAX_PATH]; + strcpy(tmp, path); + int l = strlen(tmp); + // skip empty strings + if(l) { + if(folder && tmp[l-1]!='/') + strcat(tmp, "/"); + if(collection->size==collection->cap) { + collection->cap += 4; + collection->paths = (char**)realloc(collection->paths, collection->cap*sizeof(char*)); + } + collection->paths[collection->size++]=strdup(tmp); + } +} +void PrependPath(const char* path, path_collection_t* collection, int folder) +{ + char tmp[MAX_PATH]; + strcpy(tmp, path); + int l = strlen(tmp); + // skip empty strings + if(l) { + if(folder && tmp[l-1]!='/') + strcat(tmp, "/"); + if(collection->size==collection->cap) { + collection->cap += 4; + collection->paths = (char**)realloc(collection->paths, collection->cap*sizeof(char*)); + } + memmove(collection->paths+1, collection->paths, sizeof(char*)*collection->size); + collection->paths[0]=strdup(tmp); + ++collection->size; + } +} + +void AppendList(path_collection_t* collection, const char* List, int folder) +{ + if(!List) + return; + // and now split the paths... + char tmp[MAX_PATH]; + const char *p = List; + while(p) { + const char *p2 = strchr(p, ':'); + if(!p2) { + strncpy(tmp, p, MAX_PATH - 1); + p=NULL; + } else { + int l = (uintptr_t)p2 - (uintptr_t)p; + strncpy(tmp, p, l); + tmp[l]='\0'; + p=p2+1; + } + // check if there is terminal '/', add it if not + int l = strlen(tmp); + // skip empty strings + if(l) { + if(folder && tmp[l-1]!='/') + strcat(tmp, "/"); + AddPath(tmp, collection, folder); + } + } + +} +void PrependList(path_collection_t* collection, const char* List, int folder) +{ + if(!List) + return; + // and now split the paths... + char tmp[MAX_PATH]; + char *p = strdup(List); + while(p) { + char *p2 = strrchr(p, ':'); + if(!p2) { + strncpy(tmp, p, MAX_PATH - 1); + free(p); + p=NULL; + } else { + strncpy(tmp, p2+1, MAX_PATH - 1); + tmp[MAX_PATH - 1]='\0'; + *p2 = '\0'; + } + // check if there is terminal '/', add it if not + int l = strlen(tmp); + // skip empty strings + if(l) { + if(folder && tmp[l-1]!='/') + strcat(tmp, "/"); + PrependPath(tmp, collection, folder); + } + } + +} + +int FindInCollection(const char* path, path_collection_t* collection) +{ + for (int i=0; isize; ++i) + if(strcmp(path, collection->paths[i])==0) + return 1; + return 0; +} diff --git a/src/tools/wine_tools.c b/src/tools/wine_tools.c new file mode 100755 index 00000000..6fa7a47d --- /dev/null +++ b/src/tools/wine_tools.c @@ -0,0 +1,107 @@ +#include +#include +#include +#include +#include +#include + +#include "wine_tools.h" +#include "debug.h" +#include "box64context.h" + +typedef struct wine_prereserve_s +{ + void* addr; + size_t size; +} wine_prereserve_t; + +// only the prereseve argument is reserved, not the other zone that wine-preloader reserve +static wine_prereserve_t my_wine_reserve[] = {{(void*)0x00010000, 0x00008000}, {(void*)0x00110000, 0x30000000}, {(void*)0x7f000000, 0x03000000}, {0, 0}, {0, 0}}; + +int wine_preloaded = 0; + +static int get_prereserve(const char* reserve, void** addr, size_t* size) +{ + if(!reserve) + return 0; + uintptr_t r = 0; + int first = 1; + while(*reserve) { + // numbers reading + if(*reserve>='0' && *reserve<='9') r=r*16+(*reserve)-'0'; + else if(*reserve>='A' && *reserve<='F') r=r*16+(*reserve)-'A'+10; + else if(*reserve>='a' && *reserve<='f') r=r*16+(*reserve)-'a'+10; + else if(*reserve=='-') {if(first) {*addr=(void*)(r&~(box64_pagesize-1)); r=0; first=0;} else {printf_log(LOG_NONE, "Warning, Wine prereserve badly formated\n"); return 0;}} + else {printf_log(LOG_NONE, "Warning, Wine prereserve badly formated\n"); return 0;} + ++reserve; + } + *size = r; + return 1; +} + +static void add_no_overlap(void* addr, size_t size) +{ + int idx = 0; + while(my_wine_reserve[idx].addr && my_wine_reserve[idx].size) { + if(addr>=my_wine_reserve[idx].addr && addr my_wine_reserve[idx].addr+my_wine_reserve[idx].size) + // need adjust size + my_wine_reserve[idx].size = (intptr_t)addr-(intptr_t)my_wine_reserve[idx].addr+size; + return; + } + ++idx; + } + my_wine_reserve[idx].addr = addr; + my_wine_reserve[idx].size = size; +} + +void wine_prereserve(const char* reserve) +{ + void* addr = NULL; + size_t size = 0; + + if(get_prereserve(reserve, &addr, &size)) { + add_no_overlap(addr, size); + } + + int idx = 0; + while(my_wine_reserve[idx].addr && my_wine_reserve[idx].size) { + void* p = mmap(my_wine_reserve[idx].addr, my_wine_reserve[idx].size, + PROT_NONE, /*MAP_FIXED |*/ MAP_PRIVATE | MAP_ANON | MAP_NORESERVE, -1, 0); + if(p==(void*)-1 || p!=my_wine_reserve[idx].addr) { + printf_log(LOG_NONE, "Warning, prereserve of %p:0x%x failed (%s)\n", my_wine_reserve[idx].addr, my_wine_reserve[idx].size, strerror(errno)); + if(p!=(void*)-1) + munmap(p, my_wine_reserve[idx].size); + my_wine_reserve[idx].addr = NULL; + my_wine_reserve[idx].size = 0; + } else { + printf_log(LOG_DEBUG, "WINE prereserve of %p:0x%x done\n", my_wine_reserve[idx].addr, my_wine_reserve[idx].size); + ++idx; + } + } + + wine_preloaded = 1; +} + +void* get_wine_prereserve() +{ + if(!wine_preloaded) + wine_prereserve(NULL); + return (void*)my_wine_reserve; +} + +#ifdef DYNAREC +void dynarec_wine_prereserve() +{ + #if 0 + // disable for now, as it break some installer + if(!wine_preloaded) + wine_prereserve(NULL); + // don't reserve the initial arbritrary block as "with linker", it's not true + for(int i=1; i