mirror of
https://github.com/ptitSeb/box64.git
synced 2024-11-22 22:20:08 +00:00
Added some minimal set of source (now box64 compile and say hello at least)
This commit is contained in:
parent
7b50468b61
commit
e753c19da1
273
CMakeLists.txt
Executable file
273
CMakeLists.txt
Executable file
@ -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()
|
21
cmake_uninstall.cmake.in
Normal file
21
cmake_uninstall.cmake.in
Normal file
@ -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)
|
50
runTest.cmake
Executable file
50
runTest.cmake
Executable file
@ -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}" )
|
200
src/box64context.c
Executable file
200
src/box64context.c
Executable file
@ -0,0 +1,200 @@
|
||||
#define _GNU_SOURCE /* See feature_test_macros(7) */
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <dlfcn.h>
|
||||
#include <signal.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#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; i<ctx->envc; ++i)
|
||||
free(ctx->envv[i]);
|
||||
free(ctx->envv);
|
||||
|
||||
for(int i=0; i<MAX_SIGNAL; ++i)
|
||||
if(ctx->signals[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;
|
||||
}
|
||||
*/
|
8
src/box64version.h
Executable file
8
src/box64version.h
Executable file
@ -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_
|
27
src/build_info.c
Executable file
27
src/build_info.c
Executable file
@ -0,0 +1,27 @@
|
||||
#include <stdio.h>
|
||||
#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__);
|
||||
}
|
||||
|
6
src/build_info.h
Executable file
6
src/build_info.h
Executable file
@ -0,0 +1,6 @@
|
||||
#ifndef __BUILD_INFO_H__
|
||||
#define __BUILD_INFO_H__
|
||||
|
||||
void PrintBox64Version();
|
||||
|
||||
#endif //__BUILD_INFO_H__
|
74
src/include/box64context.h
Executable file
74
src/include/box64context.h
Executable file
@ -0,0 +1,74 @@
|
||||
#ifndef __BOX64CONTEXT_H_
|
||||
#define __BOX64CONTEXT_H_
|
||||
#include <stdint.h>
|
||||
#include <pthread.h>
|
||||
#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_
|
42
src/include/debug.h
Executable file
42
src/include/debug.h
Executable file
@ -0,0 +1,42 @@
|
||||
#ifndef __DEBUG_H_
|
||||
#define __DEBUG_H_
|
||||
#include <stdint.h>
|
||||
|
||||
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_
|
23
src/include/fileutils.h
Executable file
23
src/include/fileutils.h
Executable file
@ -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_
|
678
src/include/khash.h
Executable file
678
src/include/khash.h
Executable file
@ -0,0 +1,678 @@
|
||||
/* The MIT License
|
||||
|
||||
Copyright (c) 2008, 2009, 2011 by Attractive Chaos <attractor@live.co.uk>
|
||||
|
||||
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 <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <limits.h>
|
||||
|
||||
/* 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 */
|
23
src/include/pathcoll.h
Executable file
23
src/include/pathcoll.h
Executable file
@ -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_
|
13
src/include/wine_tools.h
Executable file
13
src/include/wine_tools.h
Executable file
@ -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__
|
734
src/main.c
Executable file
734
src/main.c
Executable file
@ -0,0 +1,734 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <limits.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <dirent.h>
|
||||
#include <signal.h>
|
||||
#include <sys/syscall.h>
|
||||
|
||||
#include "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; i<col->size; 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; i<context->box64_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; i<my_context->argc; ++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 = (nextarg<argc)?FileIsX64ELF(argv[nextarg]):0;
|
||||
if(x64) {
|
||||
prog = argv[++nextarg];
|
||||
printf_log(LOG_INFO, "BOX64: Wine preloader detected, loading \"%s\" directly\n", prog);
|
||||
//wine_preloaded = 1;
|
||||
}
|
||||
}
|
||||
// check if this is wine
|
||||
if(!strcmp(prog, "wine") || (strlen(prog)>5 && !strcmp(prog+strlen(prog)-strlen("/wine"), "/wine"))) {
|
||||
const char* prereserve = getenv("WINEPRELOADRESERVE");
|
||||
printf_log(LOG_INFO, "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; i<my_context->envc; ++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; i<ld_preload.size; ++i)
|
||||
printf_log(LOG_INFO, "%s ", ld_preload.paths[i]);
|
||||
printf_log(LOG_INFO, "\n");
|
||||
}
|
||||
} else {
|
||||
if(getenv("LD_PRELOAD")) {
|
||||
char* p = getenv("LD_PRELOAD");
|
||||
if(strstr(p, "libtcmalloc_minimal.so.4"))
|
||||
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
|
||||
ParseList(p, &ld_preload, 0);
|
||||
if (ld_preload.size && box64_log) {
|
||||
printf_log(LOG_INFO, "BOX64 try to Preload ");
|
||||
for (int i=0; i<ld_preload.size; ++i)
|
||||
printf_log(LOG_INFO, "%s ", ld_preload.paths[i]);
|
||||
printf_log(LOG_INFO, "\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
// lets build argc/argv stuff
|
||||
printf_log(LOG_INFO, "Looking for %s\n", prog);
|
||||
if(strchr(prog, '/'))
|
||||
my_context->argv[0] = 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; i<my_context->argc; ++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;
|
||||
}
|
129
src/tools/fileutils.c
Executable file
129
src/tools/fileutils.c
Executable file
@ -0,0 +1,129 @@
|
||||
#define _FILE_OFFSET_BITS 64
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <limits.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#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; i<paths->size; ++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
|
182
src/tools/pathcoll.c
Executable file
182
src/tools/pathcoll.c
Executable file
@ -0,0 +1,182 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <limits.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#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; i<collection->size; ++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; i<to->size; ++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; i<collection->size; ++i)
|
||||
if(strcmp(path, collection->paths[i])==0)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
107
src/tools/wine_tools.c
Executable file
107
src/tools/wine_tools.c
Executable file
@ -0,0 +1,107 @@
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <sys/mman.h>
|
||||
#include <errno.h>
|
||||
|
||||
#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) {
|
||||
// overlap
|
||||
if (addr+size > 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<sizeof(my_wine_reserve)/sizeof(my_wine_reserve[0]); ++i)
|
||||
if(my_wine_reserve[i].addr && my_wine_reserve[i].size)
|
||||
addDBFromAddressRange(my_context, (uintptr_t)my_wine_reserve[i].addr, my_wine_reserve[i].size, 0); // prepare the prereserved area for exec, with linker
|
||||
#endif
|
||||
}
|
||||
#endif
|
Loading…
Reference in New Issue
Block a user