mirror of
https://github.com/FEX-Emu/FEX.git
synced 2025-02-23 08:12:34 +00:00
unittests: Add FEXLinuxTests, with a few signal and smc tests
This commit is contained in:
parent
c027acecf8
commit
40ec910108
13
.github/workflows/ccpp.yml
vendored
13
.github/workflows/ccpp.yml
vendored
@ -65,7 +65,7 @@ jobs:
|
||||
# Note the current convention is to use the -S and -B options here to specify source
|
||||
# and build directories, but this is only available with CMake 3.13 and higher.
|
||||
# The CMake binaries on the Github Actions machines are (as of this writing) 3.12
|
||||
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -G Ninja -DENABLE_LTO=False -DENABLE_ASSERTIONS=True -DENABLE_X86_HOST_DEBUG=True -DENABLE_INTERPRETER=True
|
||||
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -G Ninja -DENABLE_LTO=False -DENABLE_ASSERTIONS=True -DENABLE_X86_HOST_DEBUG=True -DENABLE_INTERPRETER=True -DBUILD_FEX_LINUX_TESTS=True
|
||||
|
||||
- name: Build
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
@ -167,6 +167,17 @@ jobs:
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
run: mv ${{runner.workspace}}/build/Testing/Temporary/LastTest.log ${{runner.workspace}}/build/Testing/Temporary/LastTest_APITests.log || true
|
||||
|
||||
- name: FEXLinuxTests
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
shell: bash
|
||||
run: cmake --build . --config $BUILD_TYPE --target fex_linux_tests_all
|
||||
|
||||
- name: FEXLinuxTests Results move
|
||||
if: ${{ always() }}
|
||||
shell: bash
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
run: mv ${{runner.workspace}}/build/Testing/Temporary/LastTest.log ${{runner.workspace}}/build/Testing/Temporary/LastTest_FEXLinuxTests.log || true
|
||||
|
||||
- name: Truncate test results
|
||||
if: ${{ always() }}
|
||||
shell: bash
|
||||
|
@ -2,6 +2,7 @@ cmake_minimum_required(VERSION 3.14)
|
||||
project(FEX)
|
||||
|
||||
option(BUILD_TESTS "Build unit tests to ensure sanity" TRUE)
|
||||
option(BUILD_FEX_LINUX_TESTS "Build FEXLinuxTests, requires g++/g++-multilib or g++-x86-64-linux-gnu/g++-multilib-x86-64-linux-gnu" FALSE)
|
||||
option(BUILD_THUNKS "Build thunks" FALSE)
|
||||
option(ENABLE_CLANG_FORMAT "Run clang format over the source" FALSE)
|
||||
option(ENABLE_IWYU "Enables include what you use program" FALSE)
|
||||
|
@ -14,7 +14,8 @@ known_failures_file = sys.argv[1]
|
||||
expected_output_file = sys.argv[2]
|
||||
disabled_tests_file = sys.argv[3]
|
||||
test_name = sys.argv[4]
|
||||
fexecutable = sys.argv[5]
|
||||
mode = sys.argv[5]
|
||||
fexecutable = sys.argv[6]
|
||||
|
||||
known_failures = { }
|
||||
expected_output = { }
|
||||
@ -46,14 +47,15 @@ RunnerArgs = []
|
||||
|
||||
RunnerArgs.append(fexecutable)
|
||||
|
||||
ROOTFS_ENV = os.getenv("ROOTFS")
|
||||
if ROOTFS_ENV != None:
|
||||
RunnerArgs.append("-R")
|
||||
RunnerArgs.append(ROOTFS_ENV)
|
||||
if (mode == "guest"):
|
||||
ROOTFS_ENV = os.getenv("ROOTFS")
|
||||
if ROOTFS_ENV != None:
|
||||
RunnerArgs.append("-R")
|
||||
RunnerArgs.append(ROOTFS_ENV)
|
||||
|
||||
# Add the rest of the arguments
|
||||
for i in range(len(sys.argv) - 6):
|
||||
RunnerArgs.append(sys.argv[6 + i])
|
||||
for i in range(len(sys.argv) - 7):
|
||||
RunnerArgs.append(sys.argv[7 + i])
|
||||
|
||||
#print(RunnerArgs)
|
||||
|
||||
|
@ -9,3 +9,8 @@ add_subdirectory(gcc-target-tests-64/)
|
||||
if (BUILD_THUNKS)
|
||||
add_subdirectory(ThunkLibs)
|
||||
endif()
|
||||
|
||||
|
||||
if (BUILD_FEX_LINUX_TESTS)
|
||||
add_subdirectory(FEXLinuxTests/)
|
||||
endif()
|
104
unittests/FEXLinuxTests/CMakeLists.txt
Normal file
104
unittests/FEXLinuxTests/CMakeLists.txt
Normal file
@ -0,0 +1,104 @@
|
||||
include(ExternalProject)
|
||||
ExternalProject_Add(FEXLinuxTests
|
||||
PREFIX FEXLinuxTests
|
||||
SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/tests"
|
||||
BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/FEXLinuxTests"
|
||||
CMAKE_ARGS
|
||||
"-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}"
|
||||
"-DX86_C_COMPILER:STRING=${X86_C_COMPILER}"
|
||||
"-DX86_CXX_COMPILER:STRING=${X86_CXX_COMPILER}"
|
||||
INSTALL_COMMAND ""
|
||||
BUILD_ALWAYS ON
|
||||
)
|
||||
|
||||
# this kind of sucks, but reglob
|
||||
file(GLOB_RECURSE TESTS CONFIGURE_DEPENDS tests/*.cpp)
|
||||
foreach(TEST ${TESTS})
|
||||
|
||||
get_filename_component(TEST_NAME ${TEST} NAME_WLE)
|
||||
|
||||
file(READ ${TEST} TEST_CODE)
|
||||
|
||||
# Used to insert a configuration dependency to the test file
|
||||
CONFIGURE_FILE(${TEST} ${CMAKE_BINARY_DIR}/junk.file)
|
||||
|
||||
set(ARGS_REGEX "auto args = \"([^\"]+)\";")
|
||||
string(REGEX MATCH ${ARGS_REGEX} TEST_ARGS ${TEST_CODE})
|
||||
# if cannot handle multiline variables, so we have to match the line first
|
||||
if(${TEST_ARGS} MATCHES ${ARGS_REGEX})
|
||||
string(REGEX REPLACE " |," ";" ARGS "${CMAKE_MATCH_1}")
|
||||
set(VARIATIONS "")
|
||||
foreach(ARG ${ARGS})
|
||||
list(APPEND VARIATIONS "${TEST_NAME}-${ARG}:${ARG}")
|
||||
endforeach()
|
||||
else()
|
||||
set(VARIATIONS "${TEST_NAME}:")
|
||||
endif()
|
||||
|
||||
set(ALL_BITNESS 32 64)
|
||||
foreach(VARIATION ${VARIATIONS})
|
||||
foreach(BITNESS ${ALL_BITNESS})
|
||||
string(REGEX REPLACE ":" ";" VARIATION "${VARIATION}")
|
||||
list(GET VARIATION 0 VARIATION_NAME)
|
||||
list(GET VARIATION 1 VARIATION_ARG)
|
||||
set(BIN_PATH "${CMAKE_CURRENT_BINARY_DIR}/FEXLinuxTests/${TEST_NAME}.${BITNESS}")
|
||||
|
||||
set(TEST_CASE "${VARIATION_NAME}.${BITNESS}")
|
||||
|
||||
# Add jit test case
|
||||
add_test(NAME "${TEST_CASE}.jit.flt"
|
||||
COMMAND "python3" "${CMAKE_SOURCE_DIR}/Scripts/guest_test_runner.py"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/Known_Failures"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/Expected_Output"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/Disabled_Tests"
|
||||
"${TEST_CASE}"
|
||||
"guest"
|
||||
"$<TARGET_FILE:FEXLoader>"
|
||||
"--no-silent" "-c" "irjit" "-n" "500" "--"
|
||||
"${BIN_PATH}"
|
||||
"${VARIATION_ARG}")
|
||||
if (_M_X86_64)
|
||||
# Add host test case
|
||||
add_test(NAME "${TEST_CASE}.host.flt"
|
||||
COMMAND "python3" "${CMAKE_SOURCE_DIR}/Scripts/guest_test_runner.py"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/Known_Failures_Host"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/Expected_Output"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/Disabled_Tests_Host"
|
||||
"${TEST_CASE}"
|
||||
"host"
|
||||
"${BIN_PATH}"
|
||||
"${VARIATION_ARG}")
|
||||
endif()
|
||||
endforeach()
|
||||
endforeach()
|
||||
endforeach()
|
||||
|
||||
execute_process(COMMAND "nproc" OUTPUT_VARIABLE CORES)
|
||||
string(STRIP ${CORES} CORES)
|
||||
|
||||
# Only emulated
|
||||
add_custom_target(
|
||||
fex_linux_tests
|
||||
WORKING_DIRECTORY "${CMAKE_BINARY_DIR}"
|
||||
USES_TERMINAL
|
||||
COMMAND "ctest" "--timeout" "30" "-j${CORES}" "-R" "\.*\.jit\.flt$$" "--output-on-failure"
|
||||
DEPENDS FEXLinuxTests FEXLoader
|
||||
)
|
||||
|
||||
# Only host
|
||||
add_custom_target(
|
||||
fex_linux_tests_host
|
||||
WORKING_DIRECTORY "${CMAKE_BINARY_DIR}"
|
||||
USES_TERMINAL
|
||||
COMMAND "ctest" "--timeout" "30" "-j${CORES}" "-R" "\.*\.host\.flt$$" "--output-on-failure"
|
||||
DEPENDS FEXLinuxTests
|
||||
)
|
||||
|
||||
# Both host and emulated
|
||||
add_custom_target(
|
||||
fex_linux_tests_all
|
||||
WORKING_DIRECTORY "${CMAKE_BINARY_DIR}"
|
||||
USES_TERMINAL
|
||||
COMMAND "ctest" "--timeout" "30" "-j${CORES}" "-R" "\.*\.flt$$" "--output-on-failure"
|
||||
DEPENDS FEXLinuxTests FEXLoader
|
||||
)
|
17
unittests/FEXLinuxTests/Disabled_Tests
Normal file
17
unittests/FEXLinuxTests/Disabled_Tests
Normal file
@ -0,0 +1,17 @@
|
||||
###
|
||||
### Disabled tests ###
|
||||
###
|
||||
|
||||
# These sometimes crash FEX with SIGSEGV
|
||||
timer-sigev-thread.32
|
||||
timer-sigev-thread.64
|
||||
|
||||
# These fail on arm because of sigbus handling
|
||||
synchronous-signal-block-sbus.32
|
||||
synchronous-signal-block-sbus.64
|
||||
synchronous-signal-block-abus.32
|
||||
synchronous-signal-block-abus.64
|
||||
|
||||
# These fail on arm because we don't raise FPE
|
||||
synchronous-signal-block-sfpe.32
|
||||
synchronous-signal-block-sfpe.64
|
0
unittests/FEXLinuxTests/Disabled_Tests_Host
Normal file
0
unittests/FEXLinuxTests/Disabled_Tests_Host
Normal file
8
unittests/FEXLinuxTests/Expected_Output
Normal file
8
unittests/FEXLinuxTests/Expected_Output
Normal file
@ -0,0 +1,8 @@
|
||||
synchronous-signal-block-ssegv.32 -11
|
||||
synchronous-signal-block-ssegv.64 -11
|
||||
synchronous-signal-block-sill.32 -4
|
||||
synchronous-signal-block-sill.64 -4
|
||||
synchronous-signal-block-sbus.32 -7
|
||||
synchronous-signal-block-sbus.64 -7
|
||||
synchronous-signal-block-sfpe.32 -8
|
||||
synchronous-signal-block-sfpe.64 -8
|
39
unittests/FEXLinuxTests/Known_Failures
Normal file
39
unittests/FEXLinuxTests/Known_Failures
Normal file
@ -0,0 +1,39 @@
|
||||
###
|
||||
### Disabled tests ###
|
||||
###
|
||||
|
||||
# These sometimes crash FEX with SIGSEGV
|
||||
timer-sigev-thread.32
|
||||
timer-sigev-thread.64
|
||||
|
||||
# These fail on arm because of sigbus handling
|
||||
synchronous-signal-block-sbus.32
|
||||
synchronous-signal-block-sbus.64
|
||||
synchronous-signal-block-abus.32
|
||||
synchronous-signal-block-abus.64
|
||||
|
||||
# These fail on arm because we don't raise FPE
|
||||
synchronous-signal-block-sfpe.32
|
||||
synchronous-signal-block-sfpe.64
|
||||
|
||||
###
|
||||
### Failing Tests ###
|
||||
###
|
||||
|
||||
# these will be fixed with FEX_TICKET(1725)
|
||||
sigtest_samask.32
|
||||
sigtest_samask.64
|
||||
sigtest_sigmask.32
|
||||
sigtest_sigmask.64
|
||||
|
||||
# These fail to do default signal catching behaviour
|
||||
synchronous-signal-block-ssegv.32
|
||||
synchronous-signal-block-ssegv.64
|
||||
synchronous-signal-block-sill.32
|
||||
synchronous-signal-block-sill.64
|
||||
|
||||
# These fail to queue the signals
|
||||
synchronous-signal-block-asegv.32
|
||||
synchronous-signal-block-asegv.64
|
||||
synchronous-signal-block-aill.32
|
||||
synchronous-signal-block-aill.64
|
0
unittests/FEXLinuxTests/Known_Failures_Host
Normal file
0
unittests/FEXLinuxTests/Known_Failures_Host
Normal file
68
unittests/FEXLinuxTests/tests/CMakeLists.txt
Normal file
68
unittests/FEXLinuxTests/tests/CMakeLists.txt
Normal file
@ -0,0 +1,68 @@
|
||||
cmake_minimum_required(VERSION 3.14)
|
||||
project(FEXLinuxTests)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
|
||||
set (X86_C_COMPILER "x86_64-linux-gnu-gcc" CACHE STRING "c compiler for compiling x86 guest libs")
|
||||
set (X86_CXX_COMPILER "x86_64-linux-gnu-g++" CACHE STRING "c++ compiler for compiling x86 guest libs")
|
||||
|
||||
set(CMAKE_C_COMPILER "${X86_C_COMPILER}")
|
||||
set(CMAKE_CXX_COMPILER "${X86_CXX_COMPILER}")
|
||||
|
||||
unset (CMAKE_C_FLAGS)
|
||||
unset (CMAKE_CXX_FLAGS)
|
||||
|
||||
set(GENERATE_GUEST_INSTALL_TARGETS TRUE)
|
||||
|
||||
file(GLOB_RECURSE TESTS CONFIGURE_DEPENDS *.cpp)
|
||||
|
||||
foreach(TEST ${TESTS})
|
||||
get_filename_component(TEST_NAME ${TEST} NAME_WLE)
|
||||
|
||||
# Used to insert a configuration dependency to the test file
|
||||
CONFIGURE_FILE(${TEST} ${CMAKE_BINARY_DIR}/junk.file)
|
||||
|
||||
file(READ ${TEST} TEST_CODE)
|
||||
set(FLAGS_REGEX "//[ ]*append cxxflags: ([^\n]+)")
|
||||
|
||||
string(REGEX MATCH ${FLAGS_REGEX} APPEND_CXX_FLAGS ${TEST_CODE})
|
||||
# if cannot handle multiline variables, so we have to match the line first
|
||||
if(${APPEND_CXX_FLAGS} MATCHES ${FLAGS_REGEX})
|
||||
set(APPEND_CXX_FLAGS "${CMAKE_MATCH_1}")
|
||||
else()
|
||||
set(APPEND_CXX_FLAGS "")
|
||||
endif()
|
||||
|
||||
set(FLAGS_REGEX "//[ ]*append ldflags: ([^\n]+)")
|
||||
|
||||
string(REGEX MATCH ${FLAGS_REGEX} APPEND_LD_FLAGS ${TEST_CODE})
|
||||
# if cannot handle multiline variables, so we have to match the line first
|
||||
if(${APPEND_LD_FLAGS} MATCHES ${FLAGS_REGEX})
|
||||
set(APPEND_LD_FLAGS "${CMAKE_MATCH_1}" )
|
||||
else()
|
||||
set(APPEND_LD_FLAGS "")
|
||||
endif()
|
||||
|
||||
set(FLAGS_REGEX "//[ ]*libs: ([^\n]+)")
|
||||
|
||||
string(REGEX MATCH ${FLAGS_REGEX} LIBS ${TEST_CODE})
|
||||
# if cannot handle multiline variables, so we have to match the line first
|
||||
if(${LIBS} MATCHES ${FLAGS_REGEX})
|
||||
set(LIBS "${CMAKE_MATCH_1}" )
|
||||
string(REGEX REPLACE " |," ";" LIBS "${LIBS}")
|
||||
else()
|
||||
set(LIBS "")
|
||||
endif()
|
||||
|
||||
set(BIN_NAME_32 "${TEST_NAME}.32")
|
||||
set(BIN_NAME_64 "${TEST_NAME}.64")
|
||||
|
||||
add_executable(${BIN_NAME_32} ${TEST})
|
||||
set_target_properties(${BIN_NAME_32} PROPERTIES COMPILE_FLAGS "${APPEND_CXX_FLAGS} -m32 -g -O2 " LINK_FLAGS "${APPEND_LD_FLAGS} -m32")
|
||||
target_link_libraries(${BIN_NAME_32} ${LIBS})
|
||||
|
||||
add_executable(${BIN_NAME_64} ${TEST})
|
||||
set_target_properties(${BIN_NAME_64} PROPERTIES COMPILE_FLAGS "${APPEND_CXX_FLAGS} -g -O2" LINK_FLAGS "${APPEND_LD_FLAGS}")
|
||||
target_link_libraries(${BIN_NAME_64} ${LIBS})
|
||||
|
||||
endforeach()
|
95
unittests/FEXLinuxTests/tests/signal/pthread_cancel.cpp
Normal file
95
unittests/FEXLinuxTests/tests/signal/pthread_cancel.cpp
Normal file
@ -0,0 +1,95 @@
|
||||
//libs: pthread
|
||||
#include <atomic>
|
||||
#include <errno.h>
|
||||
#include <pthread.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
// Derived from example in https://manual.cs50.io/3/pthread_cancel
|
||||
// <<Manual pages for the C standard library, C POSIX library, and the CS50 Library>>
|
||||
|
||||
std::atomic<bool> thread_ready;
|
||||
std::atomic<bool> cancel_sent;
|
||||
|
||||
static pthread_key_t key;
|
||||
|
||||
void key_dtor(void *ptr) {
|
||||
puts("key_dtor: Thread aborted\n");
|
||||
free(ptr);
|
||||
}
|
||||
|
||||
#define handle_error_en(en, msg) \
|
||||
do { \
|
||||
errno = en; \
|
||||
perror(msg); \
|
||||
exit(EXIT_FAILURE); \
|
||||
} while (0)
|
||||
|
||||
static void *thread_func(void *ignored_argument) {
|
||||
pthread_key_create(&key, &key_dtor);
|
||||
pthread_setspecific(key, malloc(32));
|
||||
int s;
|
||||
|
||||
/* Disable cancellation for a while, so that we don't
|
||||
immediately react to a cancellation request. */
|
||||
|
||||
s = pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
|
||||
if (s != 0)
|
||||
handle_error_en(s, "pthread_setcancelstate");
|
||||
|
||||
printf("thread_func(): started; cancellation disabled\n");
|
||||
thread_ready = true;
|
||||
|
||||
while (!cancel_sent.load())
|
||||
;
|
||||
printf("thread_func(): about to enable cancellation\n");
|
||||
|
||||
s = pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
|
||||
if (s != 0)
|
||||
handle_error_en(s, "pthread_setcancelstate");
|
||||
|
||||
/* sleep() is a cancellation point. */
|
||||
|
||||
for (;;)
|
||||
sleep(1000); /* Should get canceled while we sleep */
|
||||
|
||||
/* Should never get here. */
|
||||
|
||||
printf("thread_func(): not canceled!\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
pthread_t thr;
|
||||
void *res;
|
||||
int s;
|
||||
|
||||
/* Start a thread and then send it a cancellation request. */
|
||||
|
||||
s = pthread_create(&thr, NULL, &thread_func, NULL);
|
||||
if (s != 0)
|
||||
handle_error_en(s, "pthread_create");
|
||||
|
||||
while (!thread_ready.load())
|
||||
;
|
||||
|
||||
printf("main(): sending cancellation request\n");
|
||||
s = pthread_cancel(thr);
|
||||
if (s != 0)
|
||||
handle_error_en(s, "pthread_cancel");
|
||||
|
||||
cancel_sent = true;
|
||||
|
||||
/* Join with thread to see what its exit status was. */
|
||||
|
||||
s = pthread_join(thr, &res);
|
||||
if (s != 0)
|
||||
handle_error_en(s, "pthread_join");
|
||||
|
||||
if (res == PTHREAD_CANCELED)
|
||||
printf("main(): thread was canceled\n");
|
||||
else
|
||||
printf("main(): thread wasn't canceled (shouldn't happen!)\n");
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
60
unittests/FEXLinuxTests/tests/signal/sigtest_no_defer.cpp
Normal file
60
unittests/FEXLinuxTests/tests/signal/sigtest_no_defer.cpp
Normal file
@ -0,0 +1,60 @@
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
volatile bool loop = false;
|
||||
volatile int count = 0;
|
||||
volatile int count2 = 0;
|
||||
|
||||
#define NUMCOUNT 10
|
||||
#define SIGN SIGTSTP
|
||||
|
||||
void sig_handler(int signum, siginfo_t *info, void *context) {
|
||||
loop = false;
|
||||
|
||||
printf("Inside handler function\n");
|
||||
if (count != 0) {
|
||||
printf("SA_NODEFER bug\n");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
if (count2 != 0) {
|
||||
printf("Nested raise correctly raised, trying sigprocmask\n");
|
||||
sigset_t old;
|
||||
// test if sigmask returned by sigprocmask is the one currently active
|
||||
sigprocmask(0, 0, &old);
|
||||
sigprocmask(SIG_SETMASK, &old, 0);
|
||||
}
|
||||
|
||||
if (count2 < NUMCOUNT) {
|
||||
printf("Nested Raising %d, %d of %d times\n", signum, 1 + count, NUMCOUNT);
|
||||
count2++;
|
||||
raise(signum);
|
||||
count++;
|
||||
} else {
|
||||
exit(0);
|
||||
printf("Exiting\n");
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
struct sigaction act = {0};
|
||||
|
||||
act.sa_flags = SA_SIGINFO | SA_NODEFER;
|
||||
act.sa_sigaction = &sig_handler;
|
||||
if (sigaction(SIGN, &act, NULL) != 0) {
|
||||
printf("sigaction failed\n");
|
||||
return -3;
|
||||
}
|
||||
loop = true;
|
||||
while (loop) {
|
||||
printf("Inside main loop, raising signal\n");
|
||||
raise(SIGN);
|
||||
if (loop) {
|
||||
printf("Error: Signal did not get raised\n");
|
||||
return -3;
|
||||
}
|
||||
}
|
||||
return -2;
|
||||
}
|
80
unittests/FEXLinuxTests/tests/signal/sigtest_samask.cpp
Normal file
80
unittests/FEXLinuxTests/tests/signal/sigtest_samask.cpp
Normal file
@ -0,0 +1,80 @@
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
volatile bool loop = true;
|
||||
volatile bool last = false;
|
||||
volatile int count = 0;
|
||||
volatile int count2 = 0;
|
||||
|
||||
// OPTIONS
|
||||
// TESTSIGPROCMASK
|
||||
|
||||
#define NUMCOUNT 10
|
||||
#define SIGN SIGTSTP
|
||||
|
||||
void sig_handler(int signum) {
|
||||
loop = false;
|
||||
printf("Inside handler function\n");
|
||||
|
||||
if (last) {
|
||||
printf("Handling last raise\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (count2 != count) {
|
||||
printf("Signal reentering bug\n");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
if (count < NUMCOUNT) {
|
||||
printf("Nested Raising sig%d, %d of %d times\n", signum, 1 + count, NUMCOUNT);
|
||||
count2++;
|
||||
raise(signum);
|
||||
printf("Nested raise correctly blocked, trying sigprocmask\n");
|
||||
sigset_t old;
|
||||
// test if sigmask returned by sigprocmask is the one currently active
|
||||
sigprocmask(0, 0, &old);
|
||||
sigprocmask(SIG_SETMASK, &old, 0);
|
||||
printf("sigprocmask worked correctly, should trigger next iteration on signal return\n");
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
if (signal(SIGN, sig_handler) != 0) {
|
||||
printf("Signal() failed\n");
|
||||
return -2;
|
||||
}
|
||||
|
||||
// test if sigmask blocks during execution as expected
|
||||
last = false;
|
||||
loop = true;
|
||||
while (loop) {
|
||||
printf("Inside main loop, raising signal\n");
|
||||
raise(SIGN);
|
||||
if (loop) {
|
||||
printf("Error: Signal did not get raised\n");
|
||||
return -4;
|
||||
}
|
||||
}
|
||||
last = true;
|
||||
loop = true;
|
||||
|
||||
// test if sigmask returned by sigprocmask is the one set by the signal return
|
||||
sigset_t old;
|
||||
sigprocmask(0, 0, &old);
|
||||
sigprocmask(SIG_SETMASK, &old, 0);
|
||||
|
||||
while (loop) {
|
||||
printf("Inside last loop, raising signal\n");
|
||||
raise(SIGN);
|
||||
if (loop) {
|
||||
printf("Error: Signal did not get raised\n");
|
||||
return -3;
|
||||
}
|
||||
}
|
||||
printf("All good, Exiting\n");
|
||||
return 0;
|
||||
}
|
48
unittests/FEXLinuxTests/tests/signal/sigtest_sigmask.cpp
Normal file
48
unittests/FEXLinuxTests/tests/signal/sigtest_sigmask.cpp
Normal file
@ -0,0 +1,48 @@
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <ucontext.h>
|
||||
#include <unistd.h>
|
||||
|
||||
volatile bool loop = false;
|
||||
volatile bool inhandler = false;
|
||||
|
||||
#define SIGN SIGTSTP
|
||||
|
||||
void sig_handler(int signum, siginfo_t *info, void *context) {
|
||||
loop = false;
|
||||
printf("Inside handler function\n");
|
||||
if (inhandler) {
|
||||
printf("Signal reentering bug\n");
|
||||
exit(-1);
|
||||
}
|
||||
inhandler = true;
|
||||
raise(signum);
|
||||
|
||||
auto uctx = (ucontext_t *)context;
|
||||
sigfillset(&uctx->uc_sigmask);
|
||||
}
|
||||
|
||||
int main() {
|
||||
struct sigaction act = {0};
|
||||
|
||||
act.sa_flags = SA_SIGINFO;
|
||||
act.sa_sigaction = &sig_handler;
|
||||
if (sigaction(SIGN, &act, NULL) != 0) {
|
||||
printf("sigaction() failed\n");
|
||||
return -2;
|
||||
}
|
||||
|
||||
loop = true;
|
||||
while (loop) {
|
||||
printf("Inside main loop, raising signal\n");
|
||||
raise(SIGN);
|
||||
if (loop) {
|
||||
printf("Error: Signal did not get raised\n");
|
||||
return -3;
|
||||
}
|
||||
}
|
||||
|
||||
printf("Exiting\n");
|
||||
return 0;
|
||||
}
|
@ -0,0 +1,73 @@
|
||||
auto args = "ssegv, asegv, sill, aill, sbus, abus, sfpe, afpe";
|
||||
|
||||
#include <cstring>
|
||||
#include <errno.h>
|
||||
#include <malloc.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#define handle_error(msg) \
|
||||
do { \
|
||||
perror(msg); \
|
||||
exit(EXIT_FAILURE); \
|
||||
} while (0)
|
||||
|
||||
char *buffer;
|
||||
int flag = 0;
|
||||
|
||||
static void handler(int sig, siginfo_t *si, void *unused) {
|
||||
printf("Got %d at address: 0x%lx\n", sig, (long)si->si_addr);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
if (argc == 1) {
|
||||
printf("please specify one of %s\n", args);
|
||||
}
|
||||
|
||||
struct sigaction sa;
|
||||
|
||||
sa.sa_flags = SA_SIGINFO;
|
||||
sigemptyset(&sa.sa_mask);
|
||||
sa.sa_sigaction = handler;
|
||||
sigaction(SIGSEGV, &sa, NULL);
|
||||
sigaction(SIGBUS, &sa, NULL);
|
||||
sigaction(SIGILL, &sa, NULL);
|
||||
sigaction(SIGFPE, &sa, NULL);
|
||||
|
||||
auto map1 = mmap(nullptr, 4096, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANON, -1, 0);
|
||||
auto map2 = (char *)mremap(map1, 4096, 8192, MREMAP_MAYMOVE);
|
||||
|
||||
sigset_t set;
|
||||
sigfillset(&set);
|
||||
|
||||
sigprocmask(SIG_SETMASK, &set, nullptr);
|
||||
|
||||
if (strcmp(argv[1], "ssegv") == 0) {
|
||||
*(int *)(0x32) = 0x64;
|
||||
} else if (strcmp(argv[1], "sill") == 0) {
|
||||
asm volatile("ud2\n");
|
||||
} else if (strcmp(argv[1], "sbus") == 0) {
|
||||
map2[4096] = 2;
|
||||
} else if (strcmp(argv[1], "sfpe") == 0) {
|
||||
volatile int a = 10;
|
||||
volatile int b = 0;
|
||||
volatile int c = a / b;
|
||||
printf("result: %d\n", c);
|
||||
} else if (strcmp(argv[1], "asegv") == 0) {
|
||||
raise(SIGSEGV);
|
||||
} else if (strcmp(argv[1], "aill") == 0) {
|
||||
raise(SIGILL);
|
||||
} else if (strcmp(argv[1], "abus") == 0) {
|
||||
raise(SIGBUS);
|
||||
} else if (strcmp(argv[1], "afpe") == 0) {
|
||||
raise(SIGFPE);
|
||||
} else {
|
||||
printf("Invalid argument %s\n", argv[1]);
|
||||
printf("please specify one of %s\n", args);
|
||||
}
|
||||
|
||||
exit(0);
|
||||
}
|
49
unittests/FEXLinuxTests/tests/signal/timer-sigev-thread.cpp
Normal file
49
unittests/FEXLinuxTests/tests/signal/timer-sigev-thread.cpp
Normal file
@ -0,0 +1,49 @@
|
||||
//libs: rt pthread
|
||||
|
||||
// Simple test of timer_create + SIGEV_THREAD, glibc implements it via SIG32
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <cassert>
|
||||
#include <signal.h>
|
||||
#include <time.h>
|
||||
|
||||
int test;
|
||||
|
||||
void timer_handler(union sigval sv) {
|
||||
auto ok = sv.sival_ptr == &test;
|
||||
printf("timer_handler called, ok = %d\n", ok);
|
||||
|
||||
exit(ok ? 0 : -1);
|
||||
}
|
||||
|
||||
int main() {
|
||||
|
||||
timer_t timer;
|
||||
sigevent sige;
|
||||
itimerspec spec;
|
||||
|
||||
memset(&sige, 0, sizeof(sige));
|
||||
|
||||
sige.sigev_notify = SIGEV_THREAD;
|
||||
sige.sigev_notify_function = &timer_handler;
|
||||
sige.sigev_value.sival_ptr = &test;
|
||||
|
||||
timer_create(CLOCK_REALTIME, &sige, &timer);
|
||||
|
||||
memset(&spec, 0, sizeof(spec));
|
||||
|
||||
spec.it_value.tv_sec = 0;
|
||||
spec.it_value.tv_nsec = 1;
|
||||
|
||||
timer_settime(timer, 0, &spec, NULL);
|
||||
|
||||
for (;;)
|
||||
sleep(1);
|
||||
|
||||
assert(false && "should never get here");
|
||||
return -2;
|
||||
}
|
11
unittests/FEXLinuxTests/tests/smc/smc-1-dynamic.cpp
Normal file
11
unittests/FEXLinuxTests/tests/smc/smc-1-dynamic.cpp
Normal file
@ -0,0 +1,11 @@
|
||||
// append ldflags: -z execstack
|
||||
auto args = "stack, data_sym, text_sym";
|
||||
|
||||
#define EXECSTACK
|
||||
#include "smc-1.inl"
|
||||
|
||||
/*
|
||||
We cannot test the omagic or the static version of this, due to cross compiling issues
|
||||
//#define OMAGIC // when the g++ driver is used to link, -Wl,--omagic breaks -static, so this can't be tested
|
||||
#include "smc-1.inl"
|
||||
*/
|
45
unittests/FEXLinuxTests/tests/smc/smc-1.inl
Normal file
45
unittests/FEXLinuxTests/tests/smc/smc-1.inl
Normal file
@ -0,0 +1,45 @@
|
||||
/*
|
||||
tests for smc changes in .text, stack and bss
|
||||
*/
|
||||
|
||||
char data_sym[16384];
|
||||
char text_sym[16384] __attribute__((section(".text")));
|
||||
|
||||
#include "smc-common.h"
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
|
||||
if (argc == 2) {
|
||||
|
||||
if (strcmp(argv[1], "stack") == 0) {
|
||||
// stack, depends on -z execstack or mprotect
|
||||
char stack[16384];
|
||||
auto code = (char *)(((uintptr_t)stack + 4095) & ~4095);
|
||||
|
||||
#if !defined(EXECSTACK)
|
||||
mprotect(code, 4096, PROT_READ | PROT_WRITE | PROT_EXEC);
|
||||
#endif
|
||||
|
||||
return test(code, "stack");
|
||||
} else if (strcmp(argv[1], "data_sym") == 0) {
|
||||
// data_sym, must use mprotect
|
||||
auto code = (char *)(((uintptr_t)data_sym + 4095) & ~4095);
|
||||
mprotect(code, 4096, PROT_READ | PROT_WRITE | PROT_EXEC);
|
||||
return test(code, "data_sym");
|
||||
} else if (strcmp(argv[1], "text_sym") == 0) {
|
||||
// text_sym, depends on -Wl,omagic or mprotect
|
||||
auto code = (char *)(((uintptr_t)text_sym + 4095) & ~4095);
|
||||
|
||||
#if !defined(OMAGIC)
|
||||
mprotect(code, 4096, PROT_READ | PROT_WRITE | PROT_EXEC);
|
||||
#endif
|
||||
|
||||
return test(code, "text_sym");
|
||||
}
|
||||
}
|
||||
|
||||
printf("Invalid arguments\n");
|
||||
printf("please specify one of %s\n", args);
|
||||
|
||||
return -1;
|
||||
}
|
43
unittests/FEXLinuxTests/tests/smc/smc-2.cpp
Normal file
43
unittests/FEXLinuxTests/tests/smc/smc-2.cpp
Normal file
@ -0,0 +1,43 @@
|
||||
/*
|
||||
tests for smc changes memory mapped via mmap, mremap, shmat without mirroring
|
||||
*/
|
||||
|
||||
auto args = "mmap, mremap, shmat, shmat_mremap, mmap_shmdt";
|
||||
|
||||
#include "smc-common.h"
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
|
||||
if (argc == 2) {
|
||||
if (strcmp(argv[1], "mmap") == 0) {
|
||||
auto code = (char *)mmap(0, 4096, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANON, 0, 0);
|
||||
return test(code, argv[1]);
|
||||
} else if (strcmp(argv[1], "mremap") == 0) {
|
||||
auto code = (char *)mmap(0, 4096, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_SHARED | MAP_ANON, 0, 0);
|
||||
auto code2 = (char *)mremap(code, 0, 4096, MREMAP_MAYMOVE);
|
||||
return test(code2, argv[1]);
|
||||
} else if (strcmp(argv[1], "shmat") == 0) {
|
||||
auto shm = shmget(IPC_PRIVATE, 4096, IPC_CREAT | 0777);
|
||||
auto code = (char *)shmat(shm, nullptr, SHM_EXEC);
|
||||
return test(code, argv[1]);
|
||||
} else if (strcmp(argv[1], "shmat_mremap") == 0) {
|
||||
auto shm = shmget(IPC_PRIVATE, 4096, IPC_CREAT | 0777);
|
||||
auto code = (char *)shmat(shm, nullptr, SHM_EXEC);
|
||||
auto code2 = (char *)mremap(code, 0, 4096, MREMAP_MAYMOVE);
|
||||
return test(code2, argv[1]);
|
||||
} else if (strcmp(argv[1], "mmap_shmdt") == 0) {
|
||||
auto shmid = shmget(IPC_PRIVATE, 4096 * 3, IPC_CREAT | 0777);
|
||||
auto ptrshm = (char *)shmat(shmid, 0, 0);
|
||||
shmctl(shmid, IPC_RMID, NULL);
|
||||
auto ptrmmap = (char *)mmap(ptrshm + 4096, 4096, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_FIXED | MAP_PRIVATE | MAP_ANON, 0, 0);
|
||||
shmdt(ptrshm);
|
||||
test(ptrmmap, argv[1]);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
printf("Invalid arguments\n");
|
||||
printf("please specify one of %s\n", args);
|
||||
|
||||
return -1;
|
||||
}
|
114
unittests/FEXLinuxTests/tests/smc/smc-common.h
Normal file
114
unittests/FEXLinuxTests/tests/smc/smc-common.h
Normal file
@ -0,0 +1,114 @@
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include <sys/mman.h>
|
||||
#include <sys/shm.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
|
||||
int test(char *code, const char *name) {
|
||||
// mov eax, imm32
|
||||
code[0] = 0xB8;
|
||||
code[1] = 0xAA;
|
||||
code[2] = 0xBB;
|
||||
code[3] = 0xCC;
|
||||
code[4] = 0xDD;
|
||||
|
||||
// ret
|
||||
code[5] = 0xC3;
|
||||
|
||||
auto fn = (int (*)())code;
|
||||
auto e1 = fn();
|
||||
|
||||
// patch imm
|
||||
code[3] = 0xFE;
|
||||
auto e2 = fn();
|
||||
|
||||
mprotect(code, 4096, PROT_READ | PROT_EXEC);
|
||||
|
||||
mprotect(code, 4096, PROT_READ | PROT_WRITE | PROT_EXEC);
|
||||
|
||||
// patch imm
|
||||
code[3] = 0xF3;
|
||||
|
||||
mprotect(code, 4096, PROT_READ | PROT_EXEC);
|
||||
|
||||
auto e3 = fn();
|
||||
|
||||
mprotect(code, 4096, PROT_READ | PROT_WRITE | PROT_EXEC);
|
||||
|
||||
// patch imm
|
||||
code[3] = 0xF1;
|
||||
|
||||
auto e4 = fn();
|
||||
|
||||
int failure_set = 0;
|
||||
|
||||
failure_set |= (e1 != 0xDDCCBBAA) << 0;
|
||||
printf("%s-1: %X, %s\n", name, e1, e1 != 0xDDCCBBAA ? "FAIL" : "PASS");
|
||||
failure_set |= (e2 != 0xDDFEBBAA) << 1;
|
||||
printf("%s-2: %X, %s\n", name, e2, e2 != 0xDDFEBBAA ? "FAIL" : "PASS");
|
||||
failure_set |= (e3 != 0xDDF3BBAA) << 2;
|
||||
printf("%s-3: %X, %s\n", name, e3, e3 != 0xDDF3BBAA ? "FAIL" : "PASS");
|
||||
failure_set |= (e4 != 0xDDF1BBAA) << 3;
|
||||
printf("%s-4: %X, %s\n", name, e4, e4 != 0xDDF1BBAA ? "FAIL" : "PASS");
|
||||
|
||||
return failure_set;
|
||||
}
|
||||
|
||||
int test_shared(char* code, char* codeexec, const char* name) {
|
||||
assert(code != codeexec);
|
||||
code[0] = 0xB8;
|
||||
code[1] = 0xAA;
|
||||
code[2] = 0xBB;
|
||||
code[3] = 0xCC;
|
||||
code[4] = 0xDD;
|
||||
|
||||
code[5] = 0xC3;
|
||||
|
||||
auto fn = (int(*)())codeexec;
|
||||
auto e1 = fn();
|
||||
code[3]=0xFE;
|
||||
auto e2 = fn();
|
||||
|
||||
int failure_set = 0;
|
||||
|
||||
failure_set |= (e1 != 0xDDCCBBAA) << 0;
|
||||
printf("%s-1: %X, %s\n", name, e1, e1 != 0xDDCCBBAA? "FAIL" : "PASS");
|
||||
failure_set |= (e2 != 0xDDFEBBAA) << 1;
|
||||
printf("%s-2: %X, %s\n", name, e2, e2 != 0xDDFEBBAA? "FAIL" : "PASS");
|
||||
|
||||
return failure_set;
|
||||
}
|
||||
|
||||
int test_forked(char* code, char* codeexec, const char* name) {
|
||||
code[0] = 0xB8;
|
||||
code[1] = 0xAA;
|
||||
code[2] = 0xBB;
|
||||
code[3] = 0xCC;
|
||||
code[4] = 0xDD;
|
||||
|
||||
code[5] = 0xC3;
|
||||
|
||||
auto fn = (int(*)())codeexec;
|
||||
auto e1 = fn();
|
||||
auto pid = fork();
|
||||
if (pid == 0) {
|
||||
code[3]=0xFE;
|
||||
exit(0);
|
||||
} else {
|
||||
int status;
|
||||
wait(&status);
|
||||
return WEXITSTATUS(status);
|
||||
}
|
||||
auto e2 = fn();
|
||||
|
||||
printf("%s-1: %X, %s\n", name, e1, e1 != 0xDDCCBBAA? "FAIL" : "PASS");
|
||||
printf("%s-2: %X, %s\n", name, e2, e2 != 0xDDFEBBAA? "FAIL" : "PASS");
|
||||
}
|
82
unittests/FEXLinuxTests/tests/smc/smc-mt-1.cpp
Normal file
82
unittests/FEXLinuxTests/tests/smc/smc-mt-1.cpp
Normal file
@ -0,0 +1,82 @@
|
||||
// libs: pthread
|
||||
|
||||
/*
|
||||
tests concurrent invalidation of different code from different threads
|
||||
|
||||
creates 10 threads
|
||||
each thread does an smc test 10 times
|
||||
|
||||
*/
|
||||
#include <cstdio>
|
||||
#include <pthread.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include <atomic>
|
||||
|
||||
std::atomic<int> result;
|
||||
std::atomic<bool> go;
|
||||
|
||||
void *thread(void *) {
|
||||
|
||||
auto code = (char *)mmap(0, 4096, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANON, 0, 0);
|
||||
|
||||
for (int k = 0; k < 10; k++) {
|
||||
code[0] = 0xB8;
|
||||
code[1] = 0xAA;
|
||||
code[2] = 0xBB;
|
||||
code[3] = 0xCC;
|
||||
code[4] = 0xDD;
|
||||
|
||||
code[5] = 0xC3;
|
||||
|
||||
while(!go) ;
|
||||
|
||||
auto fn = (int (*)())code;
|
||||
auto e1 = fn();
|
||||
code[3] = 0xFE;
|
||||
auto e2 = fn();
|
||||
|
||||
mprotect(code, 4096, PROT_READ | PROT_EXEC);
|
||||
|
||||
mprotect(code, 4096, PROT_READ | PROT_WRITE | PROT_EXEC);
|
||||
|
||||
code[3] = 0xF3;
|
||||
|
||||
mprotect(code, 4096, PROT_READ | PROT_EXEC);
|
||||
|
||||
auto e3 = fn();
|
||||
|
||||
mprotect(code, 4096, PROT_READ | PROT_WRITE | PROT_EXEC);
|
||||
|
||||
code[3] = 0xF1;
|
||||
|
||||
auto e4 = fn();
|
||||
|
||||
result |= e1 != 0xDDCCBBAA;
|
||||
printf("Exec1: %X, %s\n", e1, e1 != 0xDDCCBBAA ? "FAIL" : "PASS");
|
||||
result |= e2 != 0xDDFEBBAA;
|
||||
printf("Exec2: %X, %s\n", e2, e2 != 0xDDFEBBAA ? "FAIL" : "PASS");
|
||||
result |= e3 != 0xDDF3BBAA;
|
||||
printf("Exec3: %X, %s\n", e3, e3 != 0xDDF3BBAA ? "FAIL" : "PASS");
|
||||
result |= e4 != 0xDDF1BBAA;
|
||||
printf("Exec4: %X, %s\n", e4, e4 != 0xDDF1BBAA ? "FAIL" : "PASS");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main() {
|
||||
pthread_t tid[10];
|
||||
for (int i = 0; i < 10; i++) {
|
||||
pthread_create(&tid[i], 0, &thread, 0);
|
||||
}
|
||||
|
||||
go = true;
|
||||
|
||||
for (int i = 0; i < 10; i++) {
|
||||
void *rv;
|
||||
pthread_join(tid[i], &rv);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
99
unittests/FEXLinuxTests/tests/smc/smc-mt-2.cpp
Normal file
99
unittests/FEXLinuxTests/tests/smc/smc-mt-2.cpp
Normal file
@ -0,0 +1,99 @@
|
||||
// libs: pthread
|
||||
|
||||
/*
|
||||
tests one thread modifying another thread's code
|
||||
|
||||
main thread
|
||||
- allocates code buffer
|
||||
- starts secondary thread
|
||||
- waits to be signaled from secondary thread
|
||||
- modifies the code
|
||||
- waits for secondary thread to exit, while making sure it doesn't run the old code after modification
|
||||
- exits
|
||||
|
||||
|
||||
secondary thread
|
||||
- generates some code and runs it once
|
||||
- signals main thread to modify the code
|
||||
- calls the to be code and checks if the result is the modified or non modified one
|
||||
- exits
|
||||
|
||||
*/
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <pthread.h>
|
||||
#include <sys/mman.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <atomic>
|
||||
|
||||
std::atomic<bool> ready_for_modification;
|
||||
std::atomic<bool> thread_unblocked;
|
||||
std::atomic<int> thread_counter;
|
||||
|
||||
char *code;
|
||||
|
||||
void *thread(void *) {
|
||||
printf("Generating code on thread\n");
|
||||
code[0] = 0xB8;
|
||||
code[1] = 0xAA;
|
||||
code[2] = 0xBB;
|
||||
code[3] = 0xCC;
|
||||
code[4] = 0xDD;
|
||||
|
||||
code[5] = 0xC3;
|
||||
|
||||
auto fn = (int (*)())code;
|
||||
|
||||
fn();
|
||||
|
||||
ready_for_modification = true;
|
||||
printf("Waiting for code to be modified\n");
|
||||
|
||||
while (fn() == 0xDDCCBBAA)
|
||||
thread_counter++;
|
||||
|
||||
thread_unblocked = true;
|
||||
printf("Thread exiting\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main() {
|
||||
code = (char *)mmap(0, 4096, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANON, 0, 0);
|
||||
|
||||
pthread_t tid;
|
||||
pthread_create(&tid, 0, &thread, 0);
|
||||
|
||||
while (!ready_for_modification)
|
||||
;
|
||||
|
||||
printf("Modifying code from another thread\n");
|
||||
|
||||
code[3] = 0xFE;
|
||||
|
||||
auto counter = thread_counter.load();
|
||||
|
||||
printf("Waiting for thread to get unblocked\n");
|
||||
|
||||
bool once = false;
|
||||
while (!thread_unblocked) {
|
||||
if (thread_counter != counter) {
|
||||
// depending on the patch timing, this might happen once
|
||||
if (once) {
|
||||
printf("Thread should have been patched to not modify counter here\n");
|
||||
exit(1);
|
||||
}
|
||||
printf("Thread overshoot once, this is non fatal\n");
|
||||
once = true;
|
||||
counter = thread_counter.load();
|
||||
}
|
||||
}
|
||||
|
||||
printf("Should exit now\n");
|
||||
void *rv;
|
||||
pthread_join(tid, &rv);
|
||||
|
||||
return 0;
|
||||
}
|
90
unittests/FEXLinuxTests/tests/smc/smc-shared-1.cpp
Normal file
90
unittests/FEXLinuxTests/tests/smc/smc-shared-1.cpp
Normal file
@ -0,0 +1,90 @@
|
||||
/*
|
||||
tests shared / mirrored mappings
|
||||
*/
|
||||
|
||||
// libs: rt pthread
|
||||
|
||||
auto args = "mmap_mremap, mmap_mremap_mid, shmat, shmat_mremap, shmat_mremap_mid, mmap_mmap, mmap_mmap_fd_fd2, shm_open_mmap_mmap, shm_open_mmap_mmap_fd_fd2";
|
||||
|
||||
#include "smc-common.h"
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
|
||||
if (argc == 2) {
|
||||
if (strcmp(argv[1], "mmap_mremap") == 0) {
|
||||
auto code = (char *)mmap(0, 4096, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_SHARED | MAP_ANON, 0, 0);
|
||||
|
||||
auto code2 = (char *)mremap(code, 0, 4096, MREMAP_MAYMOVE);
|
||||
|
||||
return test_shared(code, code2, argv[1]);
|
||||
} else if (strcmp(argv[1], "mmap_mremap_mid") == 0) {
|
||||
auto code = (char *)mmap(0, 8192, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_SHARED | MAP_ANON, 0, 0);
|
||||
|
||||
auto code2 = (char *)mremap(code + 4096, 0, 4096, MREMAP_MAYMOVE);
|
||||
|
||||
return test_shared(code + 4096, code2, argv[1]);
|
||||
} else if (strcmp(argv[1], "shmat") == 0) {
|
||||
auto shm = shmget(IPC_PRIVATE, 4096, IPC_CREAT | 0777);
|
||||
auto code3 = (char *)shmat(shm, nullptr, 0);
|
||||
auto code4 = (char *)shmat(shm, nullptr, SHM_EXEC);
|
||||
return test_shared(code3, code4, "shmat");
|
||||
} else if (strcmp(argv[1], "shmat_mremap") == 0) {
|
||||
auto shm2 = shmget(IPC_PRIVATE, 4096, IPC_CREAT | 0777);
|
||||
auto code5 = (char *)shmat(shm2, nullptr, SHM_EXEC);
|
||||
auto code6 = (char *)mremap(code5, 0, 4096, MREMAP_MAYMOVE);
|
||||
|
||||
return test_shared(code5, code6, argv[1]);
|
||||
} else if (strcmp(argv[1], "shmat_mremap_mid") == 0) {
|
||||
auto shm2 = shmget(IPC_PRIVATE, 8192, IPC_CREAT | 0777);
|
||||
auto code5 = (char *)shmat(shm2, nullptr, SHM_EXEC);
|
||||
auto code6 = (char *)mremap(code5 + 4096, 0, 4096, MREMAP_MAYMOVE);
|
||||
|
||||
return test_shared(code5 + 4096, code6, argv[1]);
|
||||
} else if (strcmp(argv[1], "mmap_mmap") == 0) {
|
||||
char file[] = "smc-tests.XXXXXXXX";
|
||||
int fd = mkstemp(file);
|
||||
unlink(file);
|
||||
ftruncate(fd, 4096);
|
||||
|
||||
auto code7 = (char *)mmap(0, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
||||
auto code8 = (char *)mmap(0, 4096, PROT_READ | PROT_EXEC, MAP_SHARED, fd, 0);
|
||||
return test_shared(code7, code8, argv[1]);
|
||||
} else if (strcmp(argv[1], "mmap_mmap_fd_fd2") == 0) {
|
||||
char file[] = "smc-tests.XXXXXXXX";
|
||||
int fd = mkstemp(file);
|
||||
int fd2 = open(file, O_RDONLY);
|
||||
unlink(file);
|
||||
ftruncate(fd, 4096);
|
||||
|
||||
auto code = (char *)mmap(0, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
||||
auto code2 = (char *)mmap(0, 4096, PROT_READ | PROT_EXEC, MAP_SHARED, fd2, 0);
|
||||
return test_shared(code, code2, argv[1]);
|
||||
} else if (strcmp(argv[1], "shm_open_mmap_mmap") == 0) {
|
||||
char file[] = "smc-tests.XXXXXXXX";
|
||||
mktemp(file);
|
||||
int fd = shm_open(file, O_RDWR | O_CREAT, 0700);
|
||||
shm_unlink(file);
|
||||
ftruncate(fd, 4096);
|
||||
|
||||
auto code7 = (char *)mmap(0, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
||||
auto code8 = (char *)mmap(0, 4096, PROT_READ | PROT_EXEC, MAP_SHARED, fd, 0);
|
||||
return test_shared(code7, code8, argv[1]);
|
||||
} else if (strcmp(argv[1], "shm_open_mmap_mmap_fd_fd2") == 0) {
|
||||
char file[] = "smc-tests.XXXXXXXX";
|
||||
mktemp(file);
|
||||
int fd = shm_open(file, O_RDWR | O_CREAT, 0700);
|
||||
int fd2 = shm_open(file, O_RDONLY, 0700);
|
||||
shm_unlink(file);
|
||||
ftruncate(fd, 4096);
|
||||
|
||||
auto code7 = (char *)mmap(0, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
||||
auto code8 = (char *)mmap(0, 4096, PROT_READ | PROT_EXEC, MAP_SHARED, fd2, 0);
|
||||
return test_shared(code7, code8, argv[1]);
|
||||
}
|
||||
}
|
||||
|
||||
printf("Invalid arguments\n");
|
||||
printf("please specify one of %s\n", args);
|
||||
|
||||
return -1;
|
||||
}
|
42
unittests/FEXLinuxTests/tests/smc/smc-shared-2.cpp
Normal file
42
unittests/FEXLinuxTests/tests/smc/smc-shared-2.cpp
Normal file
@ -0,0 +1,42 @@
|
||||
|
||||
/*
|
||||
tests shared / mirrored mappings
|
||||
*/
|
||||
|
||||
// libs: rt pthread
|
||||
|
||||
auto args = "mmap_fork, shmat_fork, fork_shmat_same_shmid";
|
||||
|
||||
#include "smc-common.h"
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
|
||||
if (argc == 2) {
|
||||
|
||||
if (strcmp(argv[1], "mmap_fork") == 0) {
|
||||
auto code = (char *)mmap(0, 4096, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_SHARED | MAP_ANON, 0, 0);
|
||||
|
||||
return test_forked(code, code, argv[1]);
|
||||
} else if (strcmp(argv[1], "shmat_fork") == 0) {
|
||||
auto shm = shmget(IPC_PRIVATE, 4096, IPC_CREAT | 0777);
|
||||
auto code = (char *)shmat(shm, nullptr, SHM_EXEC);
|
||||
return test_forked(code, code, argv[1]);
|
||||
} else if (strcmp(argv[1], "fork_shmat_same_shmid") == 0) {
|
||||
auto shm = shmget(IPC_PRIVATE, 4096, IPC_CREAT | 0777);
|
||||
auto code3 = (char *)shmat(shm, nullptr, 0);
|
||||
if (fork() == 0) {
|
||||
auto code4 = (char *)shmat(shm, nullptr, SHM_EXEC);
|
||||
return test_shared(code3, code4, argv[1]);
|
||||
} else {
|
||||
int status;
|
||||
wait(&status);
|
||||
return WEXITSTATUS(status);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
printf("Invalid arguments\n");
|
||||
printf("please specify one of %s\n", args);
|
||||
|
||||
return -1;
|
||||
}
|
@ -16,6 +16,7 @@ foreach(POSIX_TEST ${POSIX_TESTS})
|
||||
"${CMAKE_SOURCE_DIR}/unittests/POSIX/Expected_Output"
|
||||
"${CMAKE_SOURCE_DIR}/unittests/POSIX/Disabled_Tests"
|
||||
"${TEST_NAME}"
|
||||
"guest"
|
||||
"${CMAKE_BINARY_DIR}/Bin/FEXLoader"
|
||||
"--no-silent" "-c" "irint" "-n" "500" "--"
|
||||
"${POSIX_TEST}")
|
||||
@ -27,6 +28,7 @@ foreach(POSIX_TEST ${POSIX_TESTS})
|
||||
"${CMAKE_SOURCE_DIR}/unittests/POSIX/Expected_Output"
|
||||
"${CMAKE_SOURCE_DIR}/unittests/POSIX/Disabled_Tests"
|
||||
"${TEST_NAME}"
|
||||
"guest"
|
||||
"${CMAKE_BINARY_DIR}/Bin/FEXLoader"
|
||||
"--no-silent" "-c" "irjit" "-n" "500" "--"
|
||||
"${POSIX_TEST}")
|
||||
|
@ -17,6 +17,7 @@ foreach(TEST ${TESTS})
|
||||
"${CMAKE_SOURCE_DIR}/unittests/gcc-target-tests-32/Expected_Output"
|
||||
"${CMAKE_SOURCE_DIR}/unittests/gcc-target-tests-32/Disabled_Tests"
|
||||
"${TEST_NAME}"
|
||||
"guest"
|
||||
"${CMAKE_BINARY_DIR}/Bin/FEXLoader"
|
||||
"--no-silent" "-c" "irjit" "-n" "500" "--"
|
||||
"${TEST}")
|
||||
|
@ -17,6 +17,7 @@ foreach(TEST ${TESTS})
|
||||
"${CMAKE_SOURCE_DIR}/unittests/gcc-target-tests-64/Expected_Output"
|
||||
"${CMAKE_SOURCE_DIR}/unittests/gcc-target-tests-64/Disabled_Tests"
|
||||
"${TEST_NAME}"
|
||||
"guest"
|
||||
"${CMAKE_BINARY_DIR}/Bin/FEXLoader"
|
||||
"--no-silent" "-c" "irjit" "-n" "500" "--"
|
||||
"${TEST}")
|
||||
|
@ -17,6 +17,7 @@ foreach(TEST ${TESTS})
|
||||
"${CMAKE_SOURCE_DIR}/unittests/gvisor-tests/Expected_Output"
|
||||
"${CMAKE_SOURCE_DIR}/unittests/gvisor-tests/Disabled_Tests"
|
||||
"${TEST_NAME}"
|
||||
"guest"
|
||||
"${CMAKE_BINARY_DIR}/Bin/FEXLoader"
|
||||
"--no-silent" "-c" "irjit" "-n" "500" "--"
|
||||
"${TEST}")
|
||||
|
Loading…
x
Reference in New Issue
Block a user