1
0
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:
Michał Janiszewski
2015-12-16 01:30:11 +01:00
parent 072db27968
commit 7d132d8fe1
4 changed files with 72 additions and 111 deletions

View File

@@ -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})

View File

@@ -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_*) }
}

View File

@@ -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.
*/

View File

@@ -44,4 +44,4 @@ void openrct2_dispose();
void openrct2_finish();
void openrct2_reset_object_tween_locations();
#endif
#endif