mirror of
https://github.com/OpenRCT2/OpenRCT2
synced 2026-01-06 06:32:56 +01:00
Extract and embed sections into binary itself
Fixed some CMakeLists.txt problems, made another target for converting section images into something palatable by ld, modified the linker script to place these ssection at predefined VMAs and updated openrct2.c with new checks.
This commit is contained in:
@@ -32,13 +32,6 @@ INCLUDE(FindPkgConfig)
|
||||
option(DISABLE_HTTP_TWITCH "Disable HTTP and Twitch support.")
|
||||
if (DISABLE_HTTP_TWITCH)
|
||||
add_definitions(-DDISABLE_HTTP -DDISABLE_TWITCH)
|
||||
else (DISABLE_HTTP_TWITCH)
|
||||
PKG_CHECK_MODULES(LIBCURL REQUIRED libcurl)
|
||||
PKG_CHECK_MODULES(JANSSON REQUIRED jansson)
|
||||
SET(HTTPLIBS ${LIBCURL_LIBRARIES} ${JANSSON_LIBRARIES})
|
||||
if (WIN32)
|
||||
SET(HTTPLIBS ${HTTPLIBS} ssl crypto winmm.lib ws2_32)
|
||||
endif (WIN32)
|
||||
endif (DISABLE_HTTP_TWITCH)
|
||||
|
||||
option(DISABLE_NETWORK "Disable multiplayer functionality. Mainly for testing.")
|
||||
@@ -64,15 +57,21 @@ if (UNIX)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -m32 -std=gnu99 -fno-omit-frame-pointer -fno-pie")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -m32 -std=gnu++11 -fno-omit-frame-pointer -fno-pie")
|
||||
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -m32")
|
||||
if (APPLE)
|
||||
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_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 -fno-pie -read_only_relocs suppress")
|
||||
endif (APPLE)
|
||||
set(CMAKE_EXE_LINKER_FLAGS ${CMAKE_SHARED_LINKER_FLAGS})
|
||||
endif (UNIX)
|
||||
|
||||
# find and include SDL2
|
||||
PKG_CHECK_MODULES(SDL2 REQUIRED sdl2 SDL2_ttf)
|
||||
|
||||
if (NOT DISABLE_HTTP_TWITCH)
|
||||
PKG_CHECK_MODULES(LIBCURL REQUIRED libcurl)
|
||||
PKG_CHECK_MODULES(JANSSON REQUIRED jansson)
|
||||
SET(HTTPLIBS ${LIBCURL_LIBRARIES} ${JANSSON_LIBRARIES})
|
||||
if (WIN32)
|
||||
SET(HTTPLIBS ${HTTPLIBS} ssl crypto winmm.lib ws2_32)
|
||||
endif (WIN32)
|
||||
endif (NOT DISABLE_HTTP_TWITCH)
|
||||
|
||||
# speex v1.1.15 is supplied in our zipped library, but distributions provide
|
||||
# updated version, with required functions extracted out to libspeexdsp.
|
||||
# This largely takes care of the problem
|
||||
@@ -110,8 +109,10 @@ if (APPLE)
|
||||
TARGET_LINK_LIBRARIES(${PROJECT} ${ICONV_LIBRARIES})
|
||||
endif (APPLE)
|
||||
|
||||
# handle creating the rct2 text and data files on OS X
|
||||
if (APPLE)
|
||||
# Handle creating the rct2 text and data files on OS X and Linux
|
||||
# See details in src/openrct2.c:openrct2_setup_rct2_segment for how the values
|
||||
# were derived.
|
||||
if (UNIX)
|
||||
add_custom_command(
|
||||
OUTPUT openrct2_text
|
||||
COMMAND dd if=${CMAKE_CURRENT_SOURCE_DIR}/openrct2.exe of=${CMAKE_BINARY_DIR}/openrct2_text bs=4096 skip=1 count=1187
|
||||
@@ -126,7 +127,37 @@ if (APPLE)
|
||||
)
|
||||
add_custom_target(segfiles DEPENDS openrct2_text openrct2_data)
|
||||
add_dependencies(${PROJECT} segfiles)
|
||||
endif (APPLE)
|
||||
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 -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 elf32-i386 --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 elf32-i386 --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)
|
||||
add_dependencies(${PROJECT} linkable_sections)
|
||||
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
|
||||
list(APPEND 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/ld_script.xc")
|
||||
endif (APPLE)
|
||||
endif (UNIX)
|
||||
|
||||
# install into ${CMAKE_INSTALL_PREFIX}/bin/
|
||||
#install (TARGETS ${PROJECT} DESTINATION bin)
|
||||
@@ -134,4 +165,4 @@ endif (APPLE)
|
||||
# libopenrct2.dll -> openrct2.dll
|
||||
set_target_properties(${PROJECT} PROPERTIES PREFIX "")
|
||||
|
||||
TARGET_LINK_LIBRARIES(${PROJECT} ${SDL2_LIBRARIES} ${ORCTLIBS_LIB} ${JANSSON_LIBRARIES} ${HTTPLIBS} ${NETWORKLIBS} ${SPEEX_LIBRARIES} ${DLLIB})
|
||||
TARGET_LINK_LIBRARIES(${PROJECT} ${SDL2_LIBRARIES} ${ORCTLIBS_LIB} ${HTTPLIBS} ${NETWORKLIBS} ${SPEEX_LIBRARIES} ${DLLIB} ${RCT2_SECTIONS})
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* This script is based on elf_i386.xc with modifications for OpenRCT2 */
|
||||
/* Script for -z combreloc: combine and sort reloc sections */
|
||||
/* Copyright (C) 2014 Free Software Foundation, Inc.
|
||||
Copying and distribution of this script, with or without modification,
|
||||
@@ -11,7 +12,8 @@ SEARCH_DIR("/usr/i386-unknown-linux-gnu/lib32"); SEARCH_DIR("/usr/x86_64-unknown
|
||||
SECTIONS
|
||||
{
|
||||
/* Read-only sections, merged into text segment: */
|
||||
PROVIDE (__executable_start = SEGMENT_START("text-segment", 0x08048000)); . = SEGMENT_START("text-segment", 0x08048000) + SIZEOF_HEADERS;
|
||||
PROVIDE (__executable_start = SEGMENT_START("text-segment", 0x010000)); . = SEGMENT_START("text-segment", 0x010000) + SIZEOF_HEADERS;
|
||||
|
||||
.interp : { *(.interp) }
|
||||
.note.gnu.build-id : { *(.note.gnu.build-id) }
|
||||
.hash : { *(.hash) }
|
||||
@@ -49,6 +51,8 @@ SECTIONS
|
||||
KEEP (*(SORT_NONE(.init)))
|
||||
}
|
||||
.plt : { *(.plt) *(.iplt) }
|
||||
.rct2_text 0x401000 : { *(.rct2_text) }
|
||||
.rct2_data : { *(.rct2_data) }
|
||||
.text :
|
||||
{
|
||||
*(.text.unlikely .text.*_unlikely .text.unlikely.*)
|
||||
@@ -206,4 +210,5 @@ SECTIONS
|
||||
.debug_macro 0 : { *(.debug_macro) }
|
||||
.gnu.attributes 0 : { KEEP (*(.gnu.attributes)) }
|
||||
/DISCARD/ : { *(.note.GNU-stack) *(.gnu_debuglink) *(.gnu.lto_*) }
|
||||
|
||||
}
|
||||
|
||||
115
src/openrct2.c
115
src/openrct2.c
@@ -59,12 +59,6 @@ bool gOpenRCT2Headless = false;
|
||||
|
||||
bool gOpenRCT2ShowChangelog;
|
||||
|
||||
#if defined(__unix__)
|
||||
void *gDataSegment;
|
||||
void *gTextSegment;
|
||||
int gExeFd;
|
||||
#endif // defined(__unix__)
|
||||
|
||||
/** If set, will end the OpenRCT2 game loop. Intentially private to this module so that the flag can not be set back to 0. */
|
||||
int _finished;
|
||||
|
||||
@@ -73,7 +67,6 @@ static struct { sint16 x, y, z; } _spritelocations1[MAX_SPRITES], _spritelocatio
|
||||
|
||||
static void openrct2_loop();
|
||||
static bool openrct2_setup_rct2_segment();
|
||||
static bool openrct2_release_rct2_segment();
|
||||
static void openrct2_setup_rct2_hooks();
|
||||
|
||||
void openrct2_write_full_version_info(utf8 *buffer, size_t bufferSize)
|
||||
@@ -317,7 +310,6 @@ void openrct2_dispose()
|
||||
network_close();
|
||||
http_dispose();
|
||||
language_close_all();
|
||||
openrct2_release_rct2_segment();
|
||||
platform_free();
|
||||
}
|
||||
|
||||
@@ -469,19 +461,12 @@ void openrct2_reset_object_tween_locations()
|
||||
*/
|
||||
static bool openrct2_setup_rct2_segment()
|
||||
{
|
||||
// POSIX OSes will run OpenRCT2 as a native application and then load in the Windows PE, mapping the appropriate addresses as
|
||||
// OpenRCT2 on Linux and OS X 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__)
|
||||
#define RDATA_OFFSET 0x004A4000
|
||||
#define DATASEG_OFFSET 0x005E2000
|
||||
|
||||
const char *exepath = "openrct2.exe";
|
||||
gExeFd = open(exepath, O_RDONLY);
|
||||
if (gExeFd < 0) {
|
||||
log_fatal("failed to open %s, errno = %d", exepath, errno);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Using PE-bear I was able to figure out all the needed addresses to be filled.
|
||||
// There are three sections to be loaded: .rdata, .data and .text, plus another
|
||||
// one to be mapped: DATASEG.
|
||||
@@ -504,10 +489,9 @@ static bool openrct2_setup_rct2_segment()
|
||||
// 0x9a6000 + 0xA81C3C = 0x1427C3C, which after alignment to page size becomes
|
||||
// 0x1428000, which can be seen as next section, DATASEG
|
||||
//
|
||||
// Since mmap does not provide a way to create a mapping with virtual size,
|
||||
// I resorted to creating a one large map for data and memcpy'ing data where
|
||||
// required.
|
||||
// Another section is needed for .text, as it requires PROT_EXEC flag.
|
||||
// The data is now loaded into memory with a linker script, which proves to
|
||||
// be more reliable, as mallocs that happen before we reach segment setup
|
||||
// could have already taken the space we need.
|
||||
|
||||
// TODO: UGLY, UGLY HACK!
|
||||
off_t file_size = 6750208;
|
||||
@@ -517,7 +501,7 @@ static bool openrct2_setup_rct2_segment()
|
||||
int numPages = (len + pageSize - 1) / pageSize;
|
||||
unsigned char *dummy = malloc(numPages);
|
||||
int err = mincore((void *)0x8a4000, len, dummy);
|
||||
bool pagesDirty = false;
|
||||
bool pagesMissing = false;
|
||||
if (err != 0)
|
||||
{
|
||||
err = errno;
|
||||
@@ -525,65 +509,41 @@ static bool openrct2_setup_rct2_segment()
|
||||
// On Linux ENOMEM means all requested range is unmapped
|
||||
if (err != ENOMEM)
|
||||
{
|
||||
pagesDirty = true;
|
||||
pagesMissing = true;
|
||||
perror("mincore");
|
||||
}
|
||||
#else
|
||||
pagesDirty = true;
|
||||
pagesMissing = true;
|
||||
perror("mincore");
|
||||
#endif // __linux__
|
||||
} else {
|
||||
log_warning("mincore ok");
|
||||
for (int i = 0; i < numPages; i++)
|
||||
{
|
||||
if (dummy[i] != 0)
|
||||
if (dummy[i] != 1)
|
||||
{
|
||||
pagesDirty = true;
|
||||
pagesMissing = true;
|
||||
void *start = (void *)0x8a4000 + i * pageSize;
|
||||
void *end = (void *)0x8a4000 + (i + 1) * pageSize - 1;
|
||||
log_warning("page %p - %p has flags: %x, you're in for bad time!", start, end, dummy[i]);
|
||||
log_warning("required page %p - %p is not in memory!", start, end);
|
||||
}
|
||||
}
|
||||
}
|
||||
free(dummy);
|
||||
if (pagesDirty)
|
||||
if (pagesMissing)
|
||||
{
|
||||
log_error("Found already mapped pages in region we want to claim. This means something accessed memory before we got to and following mmap (or next malloc) call will likely fail.");
|
||||
log_error("At least one of required pages was not found in memory. This can cause segfaults later on.");
|
||||
}
|
||||
// section: rw data
|
||||
gDataSegment = mmap((void *)0x8a4000, len, PROT_READ | PROT_WRITE, MAP_FIXED | MAP_ANONYMOUS | MAP_SHARED, -1, 0);
|
||||
if (gDataSegment != (void *)0x8a4000) {
|
||||
log_fatal("mmap failed to get required offset for data segment! got %p, expected %p, errno = %d", gDataSegment, (void *)(0x8a4000), errno);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
len = 0x004A3000;
|
||||
// section: text
|
||||
gTextSegment = mmap((void *)(0x401000), len, PROT_EXEC | PROT_WRITE | PROT_READ, MAP_FIXED | MAP_PRIVATE, gExeFd, 0x1000);
|
||||
if (gTextSegment != (void *)(0x401000))
|
||||
{
|
||||
log_fatal("mmap failed to get required offset for text segment! got %p, expected %p, errno = %d", gTextSegment, (void *)(0x401000), errno);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
void *fbase = mmap(NULL, file_size, PROT_READ, MAP_PRIVATE, gExeFd, 0);
|
||||
err = errno;
|
||||
log_warning("mmapped file to %p", fbase);
|
||||
if (fbase == MAP_FAILED)
|
||||
{
|
||||
log_fatal("mmap failed to get required offset! got %p, errno = %d", fbase, err);
|
||||
exit(1);
|
||||
}
|
||||
// .rdata and real part of .data
|
||||
// 0x9e2000 - 0x8a4000 = 0x13e000
|
||||
memcpy(gDataSegment, fbase + RDATA_OFFSET, 0x13e000);
|
||||
// 0x8a4000 + 0xb84000 = 0x1428000 aka DATASEG
|
||||
memcpy(gDataSegment + 0xB84000, fbase + DATASEG_OFFSET, 0x1000);
|
||||
err = munmap(fbase, file_size);
|
||||
err = mprotect((void *)0x401000, 0x8a4000 - 0x401000, PROT_READ | PROT_WRITE | PROT_EXEC);
|
||||
if (err != 0)
|
||||
{
|
||||
err = errno;
|
||||
log_error("Failed to unmap file! errno = %d", err);
|
||||
perror("mprotect");
|
||||
}
|
||||
// section: rw data
|
||||
err = mprotect((void *)0x8a4000, 0x01429000 - 0x8a4000, PROT_READ | PROT_WRITE);
|
||||
if (err != 0)
|
||||
{
|
||||
perror("mprotect");
|
||||
}
|
||||
#endif // defined(__unix__)
|
||||
|
||||
@@ -603,41 +563,6 @@ static bool openrct2_setup_rct2_segment()
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Releases segments created with @ref openrct2_setup_rct2_segment, if any.
|
||||
*/
|
||||
static bool openrct2_release_rct2_segment()
|
||||
{
|
||||
bool result = true;
|
||||
#if defined(__unix__)
|
||||
int len = 0x01429000 - 0x8a4000; // 0xB85000, 12079104 bytes or around 11.5MB
|
||||
int err;
|
||||
err = munmap(gDataSegment, len);
|
||||
if (err != 0)
|
||||
{
|
||||
err = errno;
|
||||
log_error("Failed to unmap data segment! errno = %d", err);
|
||||
result = false;
|
||||
}
|
||||
len = 0x004A3000;
|
||||
err = munmap(gTextSegment, len);
|
||||
if (err != 0)
|
||||
{
|
||||
err = errno;
|
||||
log_error("Failed to unmap text segment! errno = %d", err);
|
||||
result = false;
|
||||
}
|
||||
err = close(gExeFd);
|
||||
if (err != 0)
|
||||
{
|
||||
err = errno;
|
||||
log_error("Failed to close file! errno = %d", err);
|
||||
result = false;
|
||||
}
|
||||
#endif // defined(__unix__)
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup hooks to allow RCT2 to call OpenRCT2 functions instead.
|
||||
*/
|
||||
|
||||
@@ -44,4 +44,4 @@ void openrct2_dispose();
|
||||
void openrct2_finish();
|
||||
void openrct2_reset_object_tween_locations();
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user