1
0
mirror of https://github.com/OpenRCT2/OpenRCT2 synced 2026-01-06 06:32:56 +01:00

mmap data segment to arbitrary location

This commit is contained in:
Michał Janiszewski
2016-08-06 15:07:22 +02:00
parent db00598d18
commit 7e5102dd6f
3 changed files with 110 additions and 41 deletions

View File

@@ -79,12 +79,17 @@ option(STATIC "Create a static build.")
option(FORCE64 "Force native (x86-64) build. Do not use, for experimental purposes only.")
option(DISABLE_OPENGL "Disable OpenGL support.")
option(DISABLE_RCT2 "WIP: Try building without using code and data segments from vanilla.")
option(USE_MMAP "Use mmap to try loading rct2's data segment into memory.")
if (FORCE64)
set(TARGET_M "-m64")
set(OBJ_FORMAT "elf64-x86-64")
set(LINKER_SCRIPT "ld_script_x86_64.xc")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-error=pointer-to-int-cast -Wno-error=int-to-pointer-cast")
if ((APPLE OR WIN32) AND NOT USE_MMAP)
message(WARNING "Building such configuration won't work. Enabling USE_MMAP.")
set(USE_MMAP ON)
endif()
else ()
set(TARGET_M "-m32")
set(OBJ_FORMAT "elf32-i386")
@@ -98,6 +103,10 @@ else (DISABLE_OPENGL)
add_definitions(-DOPENGL_NO_LINK)
endif (DISABLE_OPENGL)
if (USE_MMAP)
add_definitions(-DUSE_MMAP)
endif (USE_MMAP)
if (DISABLE_NETWORK)
add_definitions(-DDISABLE_NETWORK)
else (DISABLE_NETWORK)
@@ -146,35 +155,40 @@ if (UNIX)
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/openrct2.exe
)
add_custom_target(segfiles DEPENDS openrct2_text openrct2_data)
if (APPLE)
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -sectcreate rct2_text __text ${CMAKE_CURRENT_SOURCE_DIR}/build/openrct2_text -sectcreate rct2_data __data ${CMAKE_CURRENT_SOURCE_DIR}/build/openrct2_data -segaddr rct2_data 0x8a4000 -segprot rct2_data rwx rwx -segaddr rct2_text 0x401000 -segprot rct2_text rwx rwx -segaddr __TEXT 0x2000000 -fno-pie -read_only_relocs suppress")
else (APPLE)
# For Linux we have to use objcopy to wrap regular binaries into a linkable
# format. We use specific section names which are then referenced in a
# bespoke linker script so they can be placed at predefined VMAs.
add_custom_command(
OUTPUT openrct2_text_section.o
COMMAND objcopy --input binary --output ${OBJ_FORMAT} --binary-architecture i386 openrct2_text openrct2_text_section.o --rename-section .data=.rct2_text,contents,alloc,load,readonly,code
DEPENDS segfiles
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
)
add_custom_command(
OUTPUT openrct2_data_section.o
COMMAND objcopy --input binary --output ${OBJ_FORMAT} --binary-architecture i386 openrct2_data openrct2_data_section.o --rename-section .data=.rct2_data,contents,alloc,load,readonly,data
DEPENDS segfiles
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
)
add_custom_target(linkable_sections DEPENDS openrct2_text_section.o openrct2_data_section.o)
SET_SOURCE_FILES_PROPERTIES(
openrct2_text_section.o openrct2_data_section.o
PROPERTIES
EXTERNAL_OBJECT true
GENERATED true
)
# can't use GLOB here, as the files don't exist yet at cmake-time
set(RCT2_SECTIONS "${CMAKE_BINARY_DIR}/openrct2_data_section.o" "${CMAKE_BINARY_DIR}/openrct2_text_section.o")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-T,\"${CMAKE_CURRENT_SOURCE_DIR}/distribution/linux/${LINKER_SCRIPT}\"")
endif (APPLE)
if (NOT USE_MMAP)
if (APPLE)
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -sectcreate rct2_text __text ${CMAKE_CURRENT_SOURCE_DIR}/build/openrct2_text -sectcreate rct2_data __data ${CMAKE_CURRENT_SOURCE_DIR}/build/openrct2_data -segaddr rct2_data 0x8a4000 -segprot rct2_data rwx rwx -segaddr rct2_text 0x401000 -segprot rct2_text rwx rwx -segaddr __TEXT 0x2000000 -fno-pie -read_only_relocs suppress")
else (APPLE)
# For Linux we have to use objcopy to wrap regular binaries into a linkable
# format. We use specific section names which are then referenced in a
# bespoke linker script so they can be placed at predefined VMAs.
add_custom_command(
OUTPUT openrct2_text_section.o
COMMAND objcopy --input binary --output ${OBJ_FORMAT} --binary-architecture i386 openrct2_text openrct2_text_section.o --rename-section .data=.rct2_text,contents,alloc,load,readonly,code
DEPENDS segfiles
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
)
add_custom_command(
OUTPUT openrct2_data_section.o
COMMAND objcopy --input binary --output ${OBJ_FORMAT} --binary-architecture i386 openrct2_data openrct2_data_section.o --rename-section .data=.rct2_data,contents,alloc,load,readonly,data
DEPENDS segfiles
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
)
add_custom_target(linkable_sections DEPENDS openrct2_text_section.o openrct2_data_section.o)
SET_SOURCE_FILES_PROPERTIES(
openrct2_text_section.o openrct2_data_section.o
PROPERTIES
EXTERNAL_OBJECT true
GENERATED true
)
# can't use GLOB here, as the files don't exist yet at cmake-time
set(RCT2_SECTIONS "${CMAKE_BINARY_DIR}/openrct2_data_section.o" "${CMAKE_BINARY_DIR}/openrct2_text_section.o")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-T,\"${CMAKE_CURRENT_SOURCE_DIR}/distribution/linux/${LINKER_SCRIPT}\"")
endif (APPLE)
endif (NOT USE_MMAP)
elseif (USE_MMAP)
# No dd here, can't extract data segment
message(WARNING "Sorry, your platform is not supported, you have to extract data segment manually")
endif (UNIX)
set(DEBUG_LEVEL 0 CACHE STRING "Select debug level for compilation. Use value in range 03.")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DDEBUG=${DEBUG_LEVEL}")
@@ -268,7 +282,11 @@ endif (NOT DISABLE_RCT2)
if (WIN32)
# build as library for now, replace with add_executable
add_library(${PROJECT} SHARED ${ORCT2_SOURCES} ${SPEEX_SOURCES})
if (USE_MMAP)
add_executable(${PROJECT} ${ORCT2_SOURCES} ${SPEEX_SOURCES})
else ()
add_library(${PROJECT} SHARED ${ORCT2_SOURCES} ${SPEEX_SOURCES})
endif ()
else (WIN32)
add_executable(${PROJECT} ${ORCT2_SOURCES} ${ORCT2_MM_SOURCES} ${RCT2_SECTIONS})
add_dependencies(${PROJECT} segfiles)

View File

@@ -23,8 +23,14 @@
#pragma warning(disable : 4731)
#endif
#define RCT2_ADDRESS(address, type) ((type*)(address))
#define RCT2_GLOBAL(address, type) (*((type*)(address)))
#ifdef USE_MMAP
#define GOOD_PLACE_FOR_DATA_SEGMENT ((uintptr_t)0x200000000)
#else
#define GOOD_PLACE_FOR_DATA_SEGMENT ((uintptr_t)0x8a4000)
#endif
#define RCT2_ADDRESS(address, type) ((type*)(GOOD_PLACE_FOR_DATA_SEGMENT - 0x8a4000 + (address)))
#define RCT2_GLOBAL(address, type) (*((type*)(GOOD_PLACE_FOR_DATA_SEGMENT - 0x8a4000 + (address))))
#pragma region Memory locations

View File

@@ -40,16 +40,18 @@
#include "version.h"
#include "world/mapgen.h"
#if defined(__unix__)
#if defined(__unix__) || defined(__MACOSX__)
#include <sys/mman.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#endif // defined(__unix__)
#endif // defined(__unix__) || defined(__MACOSX__)
int gExitCode;
int fdData;
void *segments = (void *)(GOOD_PLACE_FOR_DATA_SEGMENT);
int gOpenRCT2StartupAction = STARTUP_ACTION_TITLE;
utf8 gOpenRCT2StartupActionPath[512] = { 0 };
@@ -350,6 +352,10 @@ void openrct2_dispose()
#ifndef DISABLE_NETWORK
EVP_MD_CTX_destroy(gHashCTX);
#endif // DISABLE_NETWORK
#if defined(USE_MMAP) && (defined(__unix__) || defined(__MACOSX__))
munmap(segments, 12079104);
close(fdData);
#endif
platform_free();
}
@@ -499,7 +505,10 @@ 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.
#if defined(__unix__)
int len = 0x01429000 - 0x8a4000; // 0xB85000, 12079104 bytes or around 11.5MB
int err;
#if defined(USE_MMAP) && (defined(__unix__) || defined(__MACOSX__))
#error
#define RDATA_OFFSET 0x004A4000
#define DATASEG_OFFSET 0x005E2000
@@ -532,11 +541,27 @@ bool openrct2_setup_rct2_segment()
// TODO: UGLY, UGLY HACK!
//off_t file_size = 6750208;
int len = 0x01429000 - 0x8a4000; // 0xB85000, 12079104 bytes or around 11.5MB
fdData = open("openrct2_data", O_RDONLY);
if (fdData < 0)
{
log_fatal("failed to load openrct2_data");
exit(1);
}
log_warning("%p", GOOD_PLACE_FOR_DATA_SEGMENT);
segments = mmap((void *)(GOOD_PLACE_FOR_DATA_SEGMENT), len, PROT_EXEC | PROT_READ | PROT_WRITE, MAP_PRIVATE, fdData, 0);
log_warning("%p", segments);
if ((uintptr_t)segments != GOOD_PLACE_FOR_DATA_SEGMENT) {
perror("mmap");
return false;
}
#endif // defined(USE_MMAP) && (defined(__unix__) || defined(__MACOSX__))
#if defined(__unix__)
int pageSize = getpagesize();
int numPages = (len + pageSize - 1) / pageSize;
unsigned char *dummy = malloc(numPages);
int err = mincore((void *)0x8a4000, len, dummy);
err = mincore((void *)0x8a4000, len, dummy);
bool pagesMissing = false;
if (err != 0)
{
@@ -569,12 +594,14 @@ bool openrct2_setup_rct2_segment()
{
log_error("At least one of required pages was not found in memory. This can cause segfaults later on.");
}
#if !defined(USE_MMAP)
// section: text
err = mprotect((void *)0x401000, 0x8a4000 - 0x401000, PROT_READ | PROT_EXEC);
if (err != 0)
{
perror("mprotect");
}
#endif // !defined(USE_MMAP)
// section: rw data
err = mprotect((void *)0x8a4000, 0x01429000 - 0x8a4000, PROT_READ | PROT_WRITE);
if (err != 0)
@@ -583,12 +610,31 @@ bool openrct2_setup_rct2_segment()
}
#endif // defined(__unix__)
#if !defined(NO_RCT2) || !defined(__WINDOWS__)
#if defined(USE_MMAP) && defined(__WINDOWS__)
#error
segments = VirtualAlloc((void *)(GOOD_PLACE_FOR_DATA_SEGMENT), len, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
if ((uintptr_t)segments != GOOD_PLACE_FOR_DATA_SEGMENT) {
log_error("VirtualAlloc, segments = %p, GetLastError = 0x%x", segments, GetLastError());
return false;
}
SDL_RWops * rw = SDL_RWFromFile("openrct2_data", "rb");
if (rw == NULL)
{
log_error("failed to load file");
return false;
}
if (SDL_RWread(rw, segments, len, 1) != 1) {
log_error("Unable to read chunk header!");
return false;
}
SDL_RWclose(rw);
#endif // defined(USE_MMAP) && defined(__WINDOWS__)
// 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((void *)0x009A6000, 0x009E0000 - 0x009A6000);
const uint32 c2 = sawyercoding_calculate_checksum((void *)0x01428000, 0x014282BC - 0x01428000);
const uint32 c1 = sawyercoding_calculate_checksum(segments + (uintptr_t)(0x009A6000 - 0x8a4000), 0x009E0000 - 0x009A6000);
const uint32 c2 = sawyercoding_calculate_checksum(segments + (uintptr_t)(0x01428000 - 0x8a4000), 0x014282BC - 0x01428000);
const uint32 exp_c1 = 10114815;
const uint32 exp_c2 = 23564;
if (c1 != exp_c1 || c2 != exp_c2) {
@@ -596,7 +642,6 @@ bool openrct2_setup_rct2_segment()
log_warning("c2 = %u, expected %u, match %d", c2, exp_c2, c2 == exp_c2);
return false;
}
#endif
return true;
}