From f354c0ec3f1c5472e6dca21ff01dde123b835364 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Janiszewski?= Date: Tue, 6 Sep 2016 14:55:44 +0200 Subject: [PATCH] Fix testpaint target for Linux --- CMakeLists.txt | 2 +- src/openrct2.c | 2 +- test/testpaint/main.cpp | 99 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 101 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ae260cfc2d..7ab7d3572f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -370,7 +370,7 @@ file(GLOB_RECURSE ORCT2_RIDE_SOURCES "src/ride/*/*.c") file(GLOB_RECURSE ORCT2_RIDE_DEP_SOURCES "src/ride/ride_data.c" "src/ride/track_data.c" "src/ride/track_paint.c" "src/addresses.c" "src/diagnostic.c" "src/hook.c" "src/paint/map_element/map_element.c") file(GLOB_RECURSE ORCT2_PAINT_TEST_SOURCES "test/testpaint/*.c" "test/testpaint/*.cpp" "test/testpaint/*.h") -add_executable(testpaint EXCLUDE_FROM_ALL ${ORCT2_RIDE_SOURCES} ${ORCT2_RIDE_DEP_SOURCES} ${ORCT2_PAINT_TEST_SOURCES}) +add_executable(testpaint EXCLUDE_FROM_ALL ${ORCT2_RIDE_SOURCES} ${ORCT2_RIDE_DEP_SOURCES} ${ORCT2_PAINT_TEST_SOURCES} ${RCT2_SECTIONS}) set_target_properties(testpaint PROPERTIES COMPILE_FLAGS "-DNO_VEHICLES") add_dependencies(testpaint segfiles) diff --git a/src/openrct2.c b/src/openrct2.c index 94a9546c67..44c980e5c7 100644 --- a/src/openrct2.c +++ b/src/openrct2.c @@ -594,7 +594,7 @@ bool openrct2_setup_rct2_segment() } #if !defined(USE_MMAP) // section: text - err = mprotect((void *)0x401000, 0x8a4000 - 0x401000, PROT_READ | PROT_EXEC); + err = mprotect((void *)0x401000, 0x8a4000 - 0x401000, PROT_READ | PROT_EXEC | PROT_WRITE); if (err != 0) { perror("mprotect"); diff --git a/test/testpaint/main.cpp b/test/testpaint/main.cpp index cf4e3572b2..7e3ac58b90 100644 --- a/test/testpaint/main.cpp +++ b/test/testpaint/main.cpp @@ -18,6 +18,11 @@ #include #include +#if defined(__unix__) +#include +#include +#endif // defined(__unix__) + extern "C" { #include "data.h" #include "intercept.h" @@ -124,6 +129,99 @@ __declspec(dllexport) int StartOpenRCT(HINSTANCE hInstance, HINSTANCE hPrevInsta #endif +char *segments = (char *)(GOOD_PLACE_FOR_DATA_SEGMENT); + +static uint32 sawyercoding_calculate_checksum(const uint8* buffer, size_t length) +{ + size_t i; + uint32 checksum = 0; + for (i = 0; i < length; i++) + checksum += buffer[i]; + + return checksum; +} + +/** + * Loads RCT2's data model and remaps the addresses. + * @returns true if the data integrity check succeeded, otherwise false. + */ +static bool openrct2_setup_rct2_segment() +{ + // OpenRCT2 on Linux and macOS is wired to have the original Windows PE sections loaded + // necessary. Windows does not need to do this as OpenRCT2 runs as a DLL loaded from the Windows PE. + int len = 0x01429000 - 0x8a4000; // 0xB85000, 12079104 bytes or around 11.5MB + int err = 0; + +#if defined(__unix__) + int pageSize = getpagesize(); + int numPages = (len + pageSize - 1) / pageSize; + unsigned char *dummy = (unsigned char *)malloc(numPages); + + err = mincore((void *)segments, len, dummy); + bool pagesMissing = false; + if (err != 0) + { + err = errno; +#ifdef __LINUX__ + // On Linux ENOMEM means all requested range is unmapped + if (err != ENOMEM) + { + pagesMissing = true; + perror("mincore"); + } +#else + pagesMissing = true; + perror("mincore"); +#endif // __LINUX__ + } else { + for (int i = 0; i < numPages; i++) + { + if (dummy[i] != 1) + { + pagesMissing = true; + void *start = (void *)(segments + i * pageSize); + void *end = (void *)(segments + (i + 1) * pageSize - 1); + log_warning("required page %p - %p is not in memory!", start, end); + } + } + } + free(dummy); + if (pagesMissing) + { + log_error("At least one of required pages was not found in memory. This can cause segfaults later on."); + } + + // section: text + err = mprotect((void *)0x401000, 0x8a4000 - 0x401000, PROT_READ | PROT_EXEC | PROT_WRITE); + if (err != 0) + { + perror("mprotect"); + } + + // section: rw data + err = mprotect((void *)segments, 0x01429000 - 0x8a4000, PROT_READ | PROT_WRITE); + if (err != 0) + { + perror("mprotect"); + } +#endif // defined(__unix__) + + // Check that the expected data is at various addresses. + // Start at 0x9a6000, which is start of .data, to skip the region containing addresses to DLL + // calls, which can be changed by windows/wine loader. + const uint32 c1 = sawyercoding_calculate_checksum((const uint8*)(segments + (uintptr_t)(0x009A6000 - 0x8a4000)), 0x009E0000 - 0x009A6000); + const uint32 c2 = sawyercoding_calculate_checksum((const uint8*)(segments + (uintptr_t)(0x01428000 - 0x8a4000)), 0x014282BC - 0x01428000); + const uint32 exp_c1 = 10114815; + const uint32 exp_c2 = 23564; + if (c1 != exp_c1 || c2 != exp_c2) { + log_warning("c1 = %u, expected %u, match %d", c1, exp_c1, c1 == exp_c1); + log_warning("c2 = %u, expected %u, match %d", c2, exp_c2, c2 == exp_c2); + return false; + } + + return true; +} + int main(int argc, char *argv[]) { std::vector testCases; @@ -166,6 +264,7 @@ int main(int argc, char *argv[]) { ColouredPrintF(CLIColour::GREEN, "[----------] "); printf("Global test environment set-up.\n"); + openrct2_setup_rct2_segment(); initHooks(); int successCount = 0;