diff --git a/core/relinker/CMakeLists.txt b/core/relinker/CMakeLists.txt index a388ec7a3..fb705627a 100644 --- a/core/relinker/CMakeLists.txt +++ b/core/relinker/CMakeLists.txt @@ -125,6 +125,12 @@ endif() if(BUILD_TESTING) + add_test_executable(autorun_exit_tests + ${CMAKE_CURRENT_SOURCE_DIR}/cli/tests/AutorunExitTests.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/cli/src/Autorun.cpp) + target_include_directories(autorun_exit_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/cli/include) + add_test(NAME autorun_exit_status COMMAND autorun_exit_tests) + set_tests_properties(autorun_exit_status PROPERTIES TIMEOUT 20) add_test_executable(buffer_bounds_tests ${CMAKE_CURRENT_SOURCE_DIR}/io/tests/BufferBoundsTests.cpp ${CMAKE_CURRENT_SOURCE_DIR}/io/src/ByteReader.cpp diff --git a/core/relinker/cli/src/Autorun.cpp b/core/relinker/cli/src/Autorun.cpp index 30979a985..3bb7016be 100644 --- a/core/relinker/cli/src/Autorun.cpp +++ b/core/relinker/cli/src/Autorun.cpp @@ -20,12 +20,15 @@ int Autorun(const std::string& absPath, bool toWindows) { const int rawCode = std::system(cmd.c_str()); int exitCode = rawCode; - if (toWindows) { - std::cout << "\nExit code: " << rawCode << '\n'; - } else { - if (rawCode != -1) exitCode = (rawCode >> 8) & 0xFF; - std::cout << "\nRaw exit code: " << rawCode << "; Unpacked: " << exitCode << '\n'; +#ifdef _WIN32 + std::cout << "\nExit code: " << rawCode << '\n'; +#else + if (rawCode != -1) { + const int signal = rawCode & 0x7F; + exitCode = signal ? 128 + signal : (rawCode >> 8) & 0xFF; } + std::cout << "\nRaw exit code: " << rawCode << "; Unpacked: " << exitCode << '\n'; +#endif std::cout << "\nPress Enter to exit...\n"; std::cin.ignore(std::numeric_limits::max(), '\n'); diff --git a/core/relinker/cli/tests/AutorunExitTests.cpp b/core/relinker/cli/tests/AutorunExitTests.cpp new file mode 100644 index 000000000..f6fc70829 --- /dev/null +++ b/core/relinker/cli/tests/AutorunExitTests.cpp @@ -0,0 +1,46 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +int main(int, char* argv[]) { + const auto executable = std::filesystem::absolute(argv[0]); + const auto name = executable.stem().string(); + if (name.starts_with("anyps5-autorun-child-")) { + if (name.find("signal") != std::string::npos) std::raise(SIGILL); + return 42; + } + const auto child = std::filesystem::temp_directory_path() / ("anyps5-autorun-child-" + + std::to_string(std::chrono::steady_clock::now().time_since_epoch().count()) + " with spaces" + executable.extension().string()); +#ifndef _WIN32 + const auto signaled = child.parent_path() / ("anyps5-autorun-child-signal-" + child.filename().string()); +#endif + std::istringstream input("\n\n\n"); + auto* savedInput = std::cin.rdbuf(input.rdbuf()); + int result = 0; + try { + std::filesystem::copy_file(executable, child); + for (const bool windows : {false, true}) { + const int code = Cli::Autorun(child.string(), windows); + if (code != 42) throw std::runtime_error("Autorun changed exit code 42 to " + std::to_string(code)); + } +#ifndef _WIN32 + std::filesystem::rename(child, signaled); + const int code = Cli::Autorun(signaled.string(), false); + if (code != 128 + SIGILL) throw std::runtime_error("Autorun lost the child signal"); +#endif + } catch (const std::exception& error) { + std::cerr << error.what() << '\n'; + result = 1; + } + std::cin.rdbuf(savedInput); + std::filesystem::remove(child); +#ifndef _WIN32 + std::filesystem::remove(signaled); +#endif + return result; +}