├── .github ├── FUNDING.yml └── workflows │ └── ci.yaml ├── binreloc.LICENSE ├── LICENSE ├── patches ├── corert-MONO_FORCE_COMPAT-patch-from-OpenBSD.patch ├── mono-zlib-ng-fix.patch ├── corefx-MONO_FORCE_COMPAT-patch-from-OpenBSD.patch └── corefx-MONO_IOMAP.patch ├── CMakeLists.txt ├── fixDylibs.sh ├── README ├── binreloc.h ├── monoconfig ├── kick.c └── binreloc.c /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: [flibitijibibo] 2 | -------------------------------------------------------------------------------- /binreloc.LICENSE: -------------------------------------------------------------------------------- 1 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 2 | Version 2, December 2004 3 | 4 | Copyright (C) 2004 Sam Hocevar 5 | 6 | Everyone is permitted to copy and distribute verbatim or modified 7 | copies of this license document, and changing it is allowed as long 8 | as the name is changed. 9 | 10 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 11 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 12 | 13 | 0. You just DO WHAT THE FUCK YOU WANT TO. 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | /* MonoKickstart - Kick Binaries for Mono Applications 2 | * 3 | * Copyright (c) 2012 Edward Rudd. 4 | * Modified in 2013 by Ethan Lee. 5 | * 6 | * This software is provided 'as-is', without any express or implied warranty. 7 | * In no event will the authors be held liable for any damages arising from 8 | * the use of this software. 9 | * 10 | * Permission is granted to anyone to use this software for any purpose, 11 | * including commercial applications, and to alter it and redistribute it 12 | * freely, subject to the following restrictions: 13 | * 14 | * 1. The origin of this software must not be misrepresented; you must not 15 | * claim that you wrote the original software. If you use this software in a 16 | * product, an acknowledgment in the product documentation would be 17 | * appreciated but is not required. 18 | * 19 | * 2. Altered source versions must be plainly marked as such, and must not be 20 | * misrepresented as being the original software. 21 | * 22 | * 3. This notice may not be removed or altered from any source distribution. 23 | * 24 | * Ethan "flibitijibibo" Lee 25 | * 26 | */ 27 | -------------------------------------------------------------------------------- /patches/corert-MONO_FORCE_COMPAT-patch-from-OpenBSD.patch: -------------------------------------------------------------------------------- 1 | From 70857d74046ac83886927e7c9c1fa692f09c79de Mon Sep 17 00:00:00 2001 2 | From: Ethan Lee 3 | Date: Thu, 18 Jun 2020 14:02:33 -0400 4 | Subject: [PATCH] MONO_FORCE_COMPAT patch from OpenBSD 5 | 6 | --- 7 | .../shared/System/Collections/Generic/ArraySortHelper.cs | 3 ++- 8 | 1 file changed, 2 insertions(+), 1 deletion(-) 9 | 10 | diff --git a/src/System.Private.CoreLib/shared/System/Collections/Generic/ArraySortHelper.cs b/src/System.Private.CoreLib/shared/System/Collections/Generic/ArraySortHelper.cs 11 | index 03b986504..f84786cd4 100644 12 | --- a/src/System.Private.CoreLib/shared/System/Collections/Generic/ArraySortHelper.cs 13 | +++ b/src/System.Private.CoreLib/shared/System/Collections/Generic/ArraySortHelper.cs 14 | @@ -40,7 +40,8 @@ namespace System.Collections.Generic 15 | 16 | internal static void ThrowOrIgnoreBadComparer(object comparer) 17 | { 18 | - throw new ArgumentException(SR.Format(SR.Arg_BogusIComparer, comparer)); 19 | + if (Environment.GetEnvironmentVariable ("MONO_FORCE_COMPAT") == null) 20 | + throw new ArgumentException(SR.Format(SR.Arg_BogusIComparer, comparer)); 21 | } 22 | } 23 | 24 | -- 25 | 2.26.2 26 | 27 | -------------------------------------------------------------------------------- /patches/mono-zlib-ng-fix.patch: -------------------------------------------------------------------------------- 1 | From ed68c250b8589663601aa7dd88177697d0b37ec9 Mon Sep 17 00:00:00 2001 2 | From: jo-oe <38652639+jo-oe@users.noreply.github.com> 3 | Date: Tue, 9 Apr 2024 16:57:39 +0200 4 | Subject: [PATCH] Correct the selection of alloc/free functions for zlib. 5 | 6 | Avoids segfault when working with zlib-ng. 7 | See: 8 | https://github.com/zlib-ng/zlib-ng/issues/1708 9 | --- 10 | support/zlib-helper.c | 4 ++-- 11 | 1 file changed, 2 insertions(+), 2 deletions(-) 12 | 13 | diff --git a/support/zlib-helper.c b/support/zlib-helper.c 14 | index 76d8951ad8a8..a5fbc0138822 100644 15 | --- a/support/zlib-helper.c 16 | +++ b/support/zlib-helper.c 17 | @@ -76,6 +76,8 @@ CreateZStream (gint compress, guchar gzip, read_write_func func, void *gchandle) 18 | return NULL; 19 | 20 | z = z_new0 (z_stream); 21 | + z->zalloc = z_alloc; 22 | + z->zfree = z_free; 23 | if (compress) { 24 | retval = deflateInit2 (z, Z_DEFAULT_COMPRESSION, Z_DEFLATED, gzip ? 31 : -15, 8, Z_DEFAULT_STRATEGY); 25 | } else { 26 | @@ -86,8 +88,6 @@ CreateZStream (gint compress, guchar gzip, read_write_func func, void *gchandle) 27 | free (z); 28 | return NULL; 29 | } 30 | - z->zalloc = z_alloc; 31 | - z->zfree = z_free; 32 | result = z_new0 (ZStream); 33 | result->stream = z; 34 | result->func = func; 35 | -------------------------------------------------------------------------------- /patches/corefx-MONO_FORCE_COMPAT-patch-from-OpenBSD.patch: -------------------------------------------------------------------------------- 1 | From eb882a0270d39ed7b450dc268e782eca60aec825 Mon Sep 17 00:00:00 2001 2 | From: Ethan Lee 3 | Date: Thu, 18 Jun 2020 14:04:42 -0400 4 | Subject: [PATCH] MONO_FORCE_COMPAT patch from OpenBSD 5 | 6 | --- 7 | src/Common/src/CoreLib/System/Collections/Generic/List.cs | 7 +++++-- 8 | 1 file changed, 5 insertions(+), 2 deletions(-) 9 | 10 | diff --git a/src/Common/src/CoreLib/System/Collections/Generic/List.cs b/src/Common/src/CoreLib/System/Collections/Generic/List.cs 11 | index ca00ca282e..66a2509960 100644 12 | --- a/src/Common/src/CoreLib/System/Collections/Generic/List.cs 13 | +++ b/src/Common/src/CoreLib/System/Collections/Generic/List.cs 14 | @@ -578,8 +578,11 @@ namespace System.Collections.Generic 15 | action(_items[i]); 16 | } 17 | 18 | - if (version != _version) 19 | - ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumFailedVersion(); 20 | + if (Environment.GetEnvironmentVariable ("MONO_FORCE_COMPAT") == null) 21 | + { 22 | + if (version != _version) 23 | + ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumFailedVersion(); 24 | + } 25 | } 26 | 27 | // Returns an enumerator for this list with the given 28 | -- 29 | 2.26.2 30 | 31 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | CMAKE_MINIMUM_REQUIRED(VERSION 3.10) 2 | PROJECT(kick C) 3 | 4 | if (APPLE) 5 | SET(CMAKE_OSX_DEPLOYMENT_TARGET 11.0) 6 | SET(CMAKE_EXECUTABLE_SUFFIX ".bin.osx") 7 | SET(CMAKE_EXE_LINKER_FLAGS "-framework Foundation -framework GSS") 8 | SET(BIN_RPATH "@executable_path/osx") 9 | SET(KICKLIBS 10 | iconv z 11 | "-Wl,-force_load,${CMAKE_SOURCE_DIR}/../mono/mono/native/.libs/libmono-native.a" 12 | ) 13 | else() 14 | SET(CMAKE_EXECUTABLE_SUFFIX ".bin.${CMAKE_SYSTEM_PROCESSOR}") 15 | SET(KICKLIBS 16 | m rt dl libz.a 17 | "-Wl,--whole-archive,--export-dynamic ${CMAKE_SOURCE_DIR}/../mono/mono/native/.libs/libmono-native.a -Wl,--no-whole-archive" 18 | ) 19 | SET(CMAKE_EXE_LINKER_FLAGS "-Wl,--disable-new-dtags") 20 | 21 | if (CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64") 22 | # Naming quirk for backward compatibility 23 | SET(BIN_RPATH "\$ORIGIN/lib64") 24 | else() 25 | SET(BIN_RPATH "\$ORIGIN/lib${CMAKE_SYSTEM_PROCESSOR}") 26 | endif() 27 | endif() 28 | 29 | ADD_DEFINITIONS(-DENABLE_BINRELOC) 30 | INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/../mono/) 31 | 32 | set(CMAKE_SKIP_BUILD_RPATH TRUE) 33 | set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE) 34 | set(CMAKE_INSTALL_RPATH ${BIN_RPATH}) 35 | set(CMAKE_INSTALL_RPATH_USE_LINK_PATH FALSE) 36 | 37 | ADD_EXECUTABLE(kick 38 | kick.c 39 | binreloc.c 40 | ) 41 | 42 | TARGET_LINK_LIBRARIES(kick 43 | ${CMAKE_SOURCE_DIR}/../mono/mono/mini/.libs/libmonosgen-2.0.a 44 | ${KICKLIBS} 45 | pthread 46 | ) 47 | -------------------------------------------------------------------------------- /fixDylibs.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | UNAME=`uname` 4 | if [ "$UNAME" == "Darwin" ]; then 5 | LIPO=lipo 6 | OTOOL=otool 7 | INSTALL_NAME_TOOL=install_name_tool 8 | else 9 | LIPO=x86_64-apple-darwin18-lipo 10 | OTOOL=x86_64-apple-darwin18-otool 11 | INSTALL_NAME_TOOL=x86_64-apple-darwin18-install_name_tool 12 | fi 13 | 14 | FILES=`ls osx` 15 | for f in $FILES 16 | do 17 | # Rip out i386, you should never hit 32-bit anymore 18 | if $LIPO -archs osx/$f | grep i386; then 19 | cp osx/$f osx/$f.temp 20 | $LIPO osx/$f.temp -remove i386 -output osx/$f 21 | rm osx/$f.temp 22 | echo $f 32-bit code stripped 23 | else 24 | echo $f has no 32-bit code 25 | fi 26 | 27 | # OS X's Dynamic Linker looks for an "install path" inside of 28 | # a given dynamic library. It will then try to find the library 29 | # at that location. This usually defaults to somewhere in the 30 | # system folders (e.g. /Library/Frameworks/... or /usr/lib/...) 31 | # 32 | # Instead, we want to set @rpath in the executable, then fix the paths in 33 | # the libraries to use @rpath for their link paths. 34 | $INSTALL_NAME_TOOL -id @rpath/`basename $f` osx/$f 35 | $INSTALL_NAME_TOOL -change /usr/local/lib/libSDL2-2.0.0.dylib @rpath/libSDL2-2.0.0.dylib osx/$f 36 | $INSTALL_NAME_TOOL -change /usr/local/lib/libogg.0.dylib @rpath/libogg.0.dylib osx/$f 37 | $INSTALL_NAME_TOOL -change /usr/local/lib/libvorbis.0.dylib @rpath/libvorbis.0.dylib osx/$f 38 | $INSTALL_NAME_TOOL -change @loader_path/libsteam_api.dylib @rpath/libsteam_api.dylib osx/$f 39 | 40 | # You should see @rpath/LIBNAME here 41 | $OTOOL -L osx/$f 42 | done 43 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | This is MonoKickstart, a standalone Mono "kick" application to run C# programs 2 | on GNU/Linux and macOS without depending on a system installation of Mono. 3 | 4 | License 5 | ------- 6 | kick.c is released under the zlib license. See LICENSE for details. 7 | 8 | binreloc is released under the WTFPL license. See binreloc.LICENSE for details. 9 | 10 | About MonoKickstart 11 | ------------------- 12 | Originally developed by Edward Rudd for Bastion, MonoKickstart is a reworking of 13 | the stock generated kickstart code from Mono to easily run C# applications on 14 | *nix platforms. macOS support was added in 2013 for FNA titles. 15 | 16 | About the precompiled/ Folder 17 | ----------------------------- 18 | We have provided a precompiled MonoKickstart application for you to use in your 19 | programs. Included are kick binaries and libmono binaries for macOS and Linux. 20 | We have also provided the subset of the C# BCL needed by FNA; if you need any 21 | other DLLs, you can find them in a standard binary release of Mono. 22 | 23 | To use the precompiled MonoKickstart, simply rename the kick.bin.* binaries to 24 | the name of your .exe assembly (for instance, MyGame.bin.x86 starts MyGame.exe). 25 | 26 | If you wish to use additional shared libraries (for instance, libSDL2), simply 27 | add the library to the architecture's lib folder: 28 | 29 | macOS: osx/ 30 | Linux: lib64/ 31 | 32 | Be sure to run `./fixDylibs.sh` on macOS if you add new libraries! 33 | 34 | About MONO_FORCE_COMPAT 35 | ----------------------- 36 | Recent versions of .NET broke compatibility with previous versions, in ways that 37 | are non-trivial for applications to fix. To work around the issue, we have 38 | integrated a patch from the OpenBSD community to allow for replicating old .NET 39 | behavior by simply setting MONO_FORCE_COMPAT to a non-null value: 40 | 41 | https://github.com/openbsd/ports/commit/7670058636c4985f47fa8da6dceaa9e7fec4f933 42 | 43 | Thanks to the OpenBSD community for the fix! 44 | -------------------------------------------------------------------------------- /binreloc.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef __BINRELOC_H__ 3 | #define __BINRELOC_H__ 4 | 5 | #ifdef __cplusplus 6 | extern "C" { 7 | #endif /* __cplusplus */ 8 | 9 | 10 | /** These error codes can be returned by br_init(), br_init_lib(), gbr_init() or gbr_init_lib(). */ 11 | typedef enum { 12 | /** Cannot allocate memory. */ 13 | BR_INIT_ERROR_NOMEM, 14 | /** Unable to open /proc/self/maps; see errno for details. */ 15 | BR_INIT_ERROR_OPEN_MAPS, 16 | /** Unable to read from /proc/self/maps; see errno for details. */ 17 | BR_INIT_ERROR_READ_MAPS, 18 | /** The file format of /proc/self/maps is invalid; kernel bug? */ 19 | BR_INIT_ERROR_INVALID_MAPS, 20 | /** BinReloc is disabled (the ENABLE_BINRELOC macro is not defined). */ 21 | BR_INIT_ERROR_DISABLED 22 | } BrInitError; 23 | 24 | 25 | #ifndef BINRELOC_RUNNING_DOXYGEN 26 | /* Mangle symbol names to avoid symbol 27 | * collisions with other ELF objects. 28 | */ 29 | #define br_init ZOLu33026715730109_br_init 30 | #define br_init_lib ZOLu33026715730109_br_init_lib 31 | #define br_find_exe ZOLu33026715730109_br_find_exe 32 | #define br_find_exe_dir ZOLu33026715730109_br_find_exe_dir 33 | #define br_find_prefix ZOLu33026715730109_br_find_prefix 34 | #define br_find_bin_dir ZOLu33026715730109_br_find_bin_dir 35 | #define br_find_sbin_dir ZOLu33026715730109_br_find_sbin_dir 36 | #define br_find_data_dir ZOLu33026715730109_br_find_data_dir 37 | #define br_find_locale_dir ZOLu33026715730109_br_find_locale_dir 38 | #define br_find_lib_dir ZOLu33026715730109_br_find_lib_dir 39 | #define br_find_libexec_dir ZOLu33026715730109_br_find_libexec_dir 40 | #define br_find_etc_dir ZOLu33026715730109_br_find_etc_dir 41 | #define br_strcat ZOLu33026715730109_br_strcat 42 | #define br_build_path ZOLu33026715730109_br_build_path 43 | #define br_dirname ZOLu33026715730109_br_dirname 44 | #endif 45 | 46 | int br_init (BrInitError *error); 47 | int br_init_lib (BrInitError *error); 48 | 49 | char *br_find_exe (const char *default_exe); 50 | char *br_find_exe_dir (const char *default_dir); 51 | char *br_find_prefix (const char *default_prefix); 52 | char *br_find_bin_dir (const char *default_bin_dir); 53 | char *br_find_sbin_dir (const char *default_sbin_dir); 54 | char *br_find_data_dir (const char *default_data_dir); 55 | char *br_find_locale_dir (const char *default_locale_dir); 56 | char *br_find_lib_dir (const char *default_lib_dir); 57 | char *br_find_libexec_dir (const char *default_libexec_dir); 58 | char *br_find_etc_dir (const char *default_etc_dir); 59 | 60 | /* Utility functions */ 61 | char *br_strcat (const char *str1, const char *str2); 62 | char *br_build_path (const char *dir, const char *file); 63 | char *br_dirname (const char *path); 64 | 65 | 66 | #ifdef __cplusplus 67 | } 68 | #endif /* __cplusplus */ 69 | 70 | #endif /* __BINRELOC_H__ */ 71 | -------------------------------------------------------------------------------- /monoconfig: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /kick.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | int mono_main (int argc, char* argv[]); 9 | 10 | #include 11 | #include 12 | #ifdef _WIN32 13 | #include 14 | #endif 15 | 16 | static char **mono_options = NULL; 17 | 18 | static int count_mono_options_args (void) 19 | { 20 | const char *e = getenv ("MONO_BUNDLED_OPTIONS"); 21 | const char *p, *q; 22 | int i, n; 23 | 24 | if (e == NULL) 25 | return 0; 26 | 27 | /* Don't bother with any quoting here. It is unlikely one would 28 | * want to pass options containing spaces anyway. 29 | */ 30 | 31 | p = e; 32 | n = 1; 33 | while ((q = strchr (p, ' ')) != NULL) { 34 | n++; 35 | p = q + 1; 36 | } 37 | 38 | mono_options = malloc (sizeof (char *) * (n + 1)); 39 | 40 | p = e; 41 | i = 0; 42 | while ((q = strchr (p, ' ')) != NULL) { 43 | mono_options[i] = malloc ((q - p) + 1); 44 | memcpy (mono_options[i], p, q - p); 45 | mono_options[i][q - p] = '\0'; 46 | i++; 47 | p = q + 1; 48 | } 49 | mono_options[i++] = strdup (p); 50 | mono_options[i] = NULL; 51 | 52 | return n; 53 | } 54 | 55 | #include "binreloc.h" 56 | 57 | int main (int argc, char* argv[]) 58 | { 59 | char **newargs; 60 | int i, k = 0; 61 | 62 | #ifdef __APPLE__ 63 | /* Sandboxed apps are disallowed from using shared memory */ 64 | setenv("MONO_DISABLE_SHARED_AREA", "1", 1); 65 | #endif 66 | 67 | #ifdef _WIN32 68 | /* CommandLineToArgvW() might return a different argc than the 69 | * one passed to main(), so let it overwrite that, as we won't 70 | * use argv[] on Windows anyway. 71 | */ 72 | wchar_t **wargv = CommandLineToArgvW (GetCommandLineW (), &argc); 73 | #endif 74 | 75 | newargs = (char **) malloc (sizeof (char *) * (argc + 2) + count_mono_options_args ()); 76 | 77 | #ifdef _WIN32 78 | newargs [k++] = g_utf16_to_utf8 (wargv [0], -1, NULL, NULL, NULL); 79 | #else 80 | newargs [k++] = argv [0]; 81 | #endif 82 | 83 | if (mono_options != NULL) { 84 | i = 0; 85 | while (mono_options[i] != NULL) 86 | newargs[k++] = mono_options[i++]; 87 | } 88 | 89 | BrInitError err = 0; 90 | if (br_init(&err) == 1) { 91 | #ifdef __APPLE__ 92 | char *exedir = br_find_data_dir(NULL); 93 | #else 94 | char *exedir = br_find_exe_dir(NULL); 95 | #endif 96 | if (exedir) { 97 | setenv("MONO_PATH",exedir,1); 98 | mono_set_dirs(exedir, exedir); 99 | chdir(exedir); 100 | free(exedir); 101 | } 102 | } else { 103 | switch (err) { 104 | case BR_INIT_ERROR_NOMEM: 105 | printf("Could not allocate enough memory\n"); 106 | break; 107 | case BR_INIT_ERROR_OPEN_MAPS: 108 | case BR_INIT_ERROR_READ_MAPS: 109 | case BR_INIT_ERROR_INVALID_MAPS: 110 | printf("Couldn't access /proc/self/maps!\n"); 111 | break; 112 | case BR_INIT_ERROR_DISABLED: 113 | printf("BinReloc disabled!!\n"); 114 | break; 115 | } 116 | return 1; 117 | } 118 | 119 | // Calculate image_name 120 | char *image_name; 121 | char *image_suffix; 122 | char *exe = br_find_exe(NULL); 123 | char *pos = strrchr(exe, '/'); 124 | if (pos != NULL) { 125 | image_name = pos + 1; 126 | image_suffix = strstr(image_name, ".bin."); 127 | if (image_suffix != NULL) 128 | { 129 | image_name = strdup(pos + 1); 130 | strcpy(strstr(image_name, ".bin."), ".exe"); 131 | } 132 | else 133 | { 134 | image_suffix = strstr(image_name, "."); 135 | if (image_suffix == NULL) 136 | { 137 | /* This is most likely a *nix executable, so 138 | * just append ".exe" to the end of it 139 | */ 140 | const char *exe_suffix = ".exe"; 141 | image_name = (char*) malloc( 142 | strlen(image_name) + 143 | strlen(exe_suffix) + 144 | 1 145 | ); 146 | strcpy(image_name, pos + 1); 147 | strcat(image_name, exe_suffix); 148 | } 149 | else 150 | { 151 | printf("Failed to get exe name!\n"); 152 | return 1; 153 | } 154 | } 155 | } 156 | free(exe); 157 | 158 | newargs [k++] = image_name; 159 | 160 | for (i = 1; i < argc; i++) { 161 | #ifdef _WIN32 162 | newargs [k++] = g_utf16_to_utf8 (wargv [i], -1, NULL, NULL, NULL); 163 | #else 164 | newargs [k++] = argv [i]; 165 | #endif 166 | } 167 | #ifdef _WIN32 168 | LocalFree (wargv); 169 | #endif 170 | newargs [k] = NULL; 171 | 172 | /* config */ 173 | FILE *fileIn = fopen("monoconfig", "r"); 174 | if (fileIn == NULL) 175 | { 176 | printf("monoconfig not found!\n"); 177 | return 0; 178 | } 179 | fclose(fileIn); 180 | setenv("MONO_CONFIG", "monoconfig", 0); 181 | 182 | /* machine.config */ 183 | fileIn = fopen("monomachineconfig", "r"); 184 | if (fileIn == NULL) 185 | { 186 | printf("monomachineconfig not found!\n"); 187 | return 0; 188 | } 189 | fseek(fileIn, 0, SEEK_END); 190 | long len = ftell(fileIn); 191 | char *machineconfig = (char*) malloc(len + 1); /* DO NOT FREE! -flibit */ 192 | fseek(fileIn, 0, SEEK_SET); 193 | fread(machineconfig, len, 1, fileIn); 194 | fclose(fileIn); 195 | machineconfig[len] = '\0'; 196 | mono_register_machine_config(machineconfig); 197 | 198 | /* Main(string[] args) */ 199 | return mono_main (k, newargs); 200 | } 201 | -------------------------------------------------------------------------------- /.github/workflows/ci.yaml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: [push] 3 | jobs: 4 | linux: 5 | name: Steam Linux Runtime 6 | runs-on: ubuntu-latest 7 | container: registry.gitlab.steamos.cloud/steamrt/sniper/sdk:latest 8 | steps: 9 | - uses: actions/checkout@v4 10 | with: 11 | submodules: true 12 | path: monokickstart 13 | 14 | - name: Build mono 15 | run: | 16 | git clone https://gitlab.winehq.org/mono/mono --depth=1 --recursive --shallow-submodules 17 | cd mono 18 | git apply -v ../monokickstart/patches/mono* 19 | cd external/corefx 20 | git apply -v ../../../monokickstart/patches/corefx* 21 | cd ../corert 22 | git apply -v ../../../monokickstart/patches/corert* 23 | cd ../.. 24 | ./autogen.sh --with-ikvm-native=no 25 | make -j${nproc} 26 | strip -S support/.libs/libMonoPosixHelper.so 27 | 28 | - name: Build MonoKickstart 29 | run: | 30 | cd monokickstart 31 | cmake -B build -D CMAKE_BUILD_TYPE=Release -G Ninja 32 | cmake --build build --parallel 33 | strip -S build/kick.bin.x86_64 34 | cd .. 35 | mkdir artifact 36 | cp monokickstart/build/kick.bin.x86_64 \ 37 | monokickstart/monoconfig \ 38 | mono/mcs/class/lib/net_4_x-linux/Mono.Posix.dll \ 39 | mono/mcs/class/lib/net_4_x-linux/Mono.Security.dll \ 40 | mono/mcs/class/lib/net_4_x-linux/System.Configuration.dll \ 41 | mono/mcs/class/lib/net_4_x-linux/System.Core.dll \ 42 | mono/mcs/class/lib/net_4_x-linux/System.Data.dll \ 43 | mono/mcs/class/lib/net_4_x-linux/System.Drawing.dll \ 44 | mono/mcs/class/lib/net_4_x-linux/System.Numerics.dll \ 45 | mono/mcs/class/lib/net_4_x-linux/System.Runtime.Serialization.dll \ 46 | mono/mcs/class/lib/net_4_x-linux/System.Security.dll \ 47 | mono/mcs/class/lib/net_4_x-linux/System.Xml.dll \ 48 | mono/mcs/class/lib/net_4_x-linux/System.dll \ 49 | mono/mcs/class/lib/net_4_x-linux/mscorlib.dll \ 50 | artifact 51 | cp mono/runtime/etc/mono/4.5/machine.config artifact/monomachineconfig 52 | 53 | - name: Upload kick.bin.x86_64 54 | uses: actions/upload-artifact@v4 55 | with: 56 | name: Linux-x86_64 57 | path: artifact/* 58 | 59 | - name: Upload MonoPosixHelper 60 | uses: actions/upload-artifact@v4 61 | with: 62 | name: Linux-x86_64-MonoPosixHelper 63 | path: mono/support/.libs/libMonoPosixHelper.so 64 | 65 | - name: Upload BCL 66 | uses: actions/upload-artifact@v4 67 | with: 68 | name: Linux-BCL 69 | path: mono/mcs/class/lib/net_4_x-linux/*.dll 70 | 71 | linux-aarch64: 72 | name: Steam Linux Runtime (AArch64) 73 | runs-on: ubuntu-24.04-arm 74 | container: registry.gitlab.steamos.cloud/steamrt/sniper/sdk/arm64:latest 75 | steps: 76 | - uses: actions/checkout@v4 77 | with: 78 | submodules: true 79 | path: monokickstart 80 | 81 | - name: Build mono 82 | run: | 83 | git clone https://gitlab.winehq.org/mono/mono --depth=1 --recursive --shallow-submodules 84 | cd mono 85 | git apply -v ../monokickstart/patches/mono* 86 | cd external/corefx 87 | git apply -v ../../../monokickstart/patches/corefx* 88 | cd ../corert 89 | git apply -v ../../../monokickstart/patches/corert* 90 | cd ../.. 91 | ./autogen.sh --with-ikvm-native=no 92 | make -j${nproc} 93 | strip -S support/.libs/libMonoPosixHelper.so 94 | 95 | - name: Build MonoKickstart 96 | run: | 97 | cd monokickstart 98 | cmake -B build -D CMAKE_BUILD_TYPE=Release -G Ninja 99 | cmake --build build --parallel 100 | strip -S build/kick.bin.aarch64 101 | cd .. 102 | mkdir artifact 103 | cp monokickstart/build/kick.bin.aarch64 \ 104 | monokickstart/monoconfig \ 105 | mono/mcs/class/lib/net_4_x-linux/Mono.Posix.dll \ 106 | mono/mcs/class/lib/net_4_x-linux/Mono.Security.dll \ 107 | mono/mcs/class/lib/net_4_x-linux/System.Configuration.dll \ 108 | mono/mcs/class/lib/net_4_x-linux/System.Core.dll \ 109 | mono/mcs/class/lib/net_4_x-linux/System.Data.dll \ 110 | mono/mcs/class/lib/net_4_x-linux/System.Drawing.dll \ 111 | mono/mcs/class/lib/net_4_x-linux/System.Numerics.dll \ 112 | mono/mcs/class/lib/net_4_x-linux/System.Runtime.Serialization.dll \ 113 | mono/mcs/class/lib/net_4_x-linux/System.Security.dll \ 114 | mono/mcs/class/lib/net_4_x-linux/System.Xml.dll \ 115 | mono/mcs/class/lib/net_4_x-linux/System.dll \ 116 | mono/mcs/class/lib/net_4_x-linux/mscorlib.dll \ 117 | artifact 118 | cp mono/runtime/etc/mono/4.5/machine.config artifact/monomachineconfig 119 | 120 | - name: Upload kick.bin.aarch64 121 | uses: actions/upload-artifact@v4 122 | with: 123 | name: Linux-aarch64 124 | path: artifact/* 125 | 126 | - name: Upload MonoPosixHelper 127 | uses: actions/upload-artifact@v4 128 | with: 129 | name: Linux-aarch64-MonoPosixHelper 130 | path: mono/support/.libs/libMonoPosixHelper.so 131 | 132 | macos-intel: 133 | name: macOS (x86_64) 134 | runs-on: macos-13 135 | steps: 136 | - uses: actions/checkout@v4 137 | with: 138 | submodules: true 139 | path: monokickstart 140 | 141 | - name: Install dependencies 142 | run: brew install autoconf automake cmake libtool ninja pkgconf 143 | 144 | - name: Build mono 145 | run: | 146 | git clone https://gitlab.winehq.org/mono/mono --depth=1 --recursive --shallow-submodules 147 | cd mono 148 | git apply -v ../monokickstart/patches/mono* 149 | cd external/corefx 150 | git apply -v ../../../monokickstart/patches/corefx* 151 | cd ../corert 152 | git apply -v ../../../monokickstart/patches/corert* 153 | cd ../.. 154 | CFLAGS="-mmacosx-version-min=11.0" ./autogen.sh --with-ikvm-native=no 155 | make -j${nproc} 156 | strip -S support/.libs/libMonoPosixHelper.dylib 157 | 158 | - name: Build MonoKickstart 159 | run: | 160 | cd monokickstart 161 | cmake -B build -D CMAKE_BUILD_TYPE=Release -G Ninja 162 | cmake --build build --parallel 163 | strip -S build/kick.bin.osx 164 | cd .. 165 | mkdir artifact 166 | cp monokickstart/build/kick.bin.osx \ 167 | monokickstart/monoconfig \ 168 | mono/mcs/class/lib/net_4_x-macos/Mono.Posix.dll \ 169 | mono/mcs/class/lib/net_4_x-macos/Mono.Security.dll \ 170 | mono/mcs/class/lib/net_4_x-macos/System.Configuration.dll \ 171 | mono/mcs/class/lib/net_4_x-macos/System.Core.dll \ 172 | mono/mcs/class/lib/net_4_x-macos/System.Data.dll \ 173 | mono/mcs/class/lib/net_4_x-macos/System.Drawing.dll \ 174 | mono/mcs/class/lib/net_4_x-macos/System.Numerics.dll \ 175 | mono/mcs/class/lib/net_4_x-macos/System.Runtime.Serialization.dll \ 176 | mono/mcs/class/lib/net_4_x-macos/System.Security.dll \ 177 | mono/mcs/class/lib/net_4_x-macos/System.Xml.dll \ 178 | mono/mcs/class/lib/net_4_x-macos/System.dll \ 179 | mono/mcs/class/lib/net_4_x-macos/mscorlib.dll \ 180 | artifact 181 | cp mono/runtime/etc/mono/4.5/machine.config artifact/monomachineconfig 182 | 183 | - name: Upload kick.bin.osx 184 | uses: actions/upload-artifact@v4 185 | with: 186 | name: macOS-x86_64 187 | path: artifact/* 188 | 189 | - name: Upload MonoPosixHelper 190 | uses: actions/upload-artifact@v4 191 | with: 192 | name: macOS-x86_64-MonoPosixHelper 193 | path: mono/support/.libs/libMonoPosixHelper.dylib 194 | 195 | - name: Upload BCL 196 | uses: actions/upload-artifact@v4 197 | with: 198 | name: macOS-BCL 199 | path: mono/mcs/class/lib/net_4_x-macos/*.dll 200 | 201 | macos-arm64: 202 | name: macOS (arm64) 203 | runs-on: macos-latest 204 | steps: 205 | - uses: actions/checkout@v4 206 | with: 207 | submodules: true 208 | path: monokickstart 209 | 210 | - name: Install dependencies 211 | run: brew install autoconf automake cmake libtool ninja pkgconf 212 | 213 | - name: Build mono 214 | run: | 215 | git clone https://gitlab.winehq.org/mono/mono --depth=1 --recursive --shallow-submodules 216 | cd mono 217 | git apply -v ../monokickstart/patches/mono* 218 | cd external/corefx 219 | git apply -v ../../../monokickstart/patches/corefx* 220 | cd ../corert 221 | git apply -v ../../../monokickstart/patches/corert* 222 | cd ../.. 223 | CFLAGS="-mmacosx-version-min=11.0" ./autogen.sh --with-ikvm-native=no 224 | make -j${nproc} 225 | strip -S support/.libs/libMonoPosixHelper.dylib 226 | 227 | - name: Build MonoKickstart 228 | run: | 229 | cd monokickstart 230 | cmake -B build -D CMAKE_BUILD_TYPE=Release -G Ninja 231 | cmake --build build --parallel 232 | strip -S build/kick.bin.osx 233 | cd .. 234 | mkdir artifact 235 | cp monokickstart/build/kick.bin.osx \ 236 | monokickstart/monoconfig \ 237 | mono/mcs/class/lib/net_4_x-macos/Mono.Posix.dll \ 238 | mono/mcs/class/lib/net_4_x-macos/Mono.Security.dll \ 239 | mono/mcs/class/lib/net_4_x-macos/System.Configuration.dll \ 240 | mono/mcs/class/lib/net_4_x-macos/System.Core.dll \ 241 | mono/mcs/class/lib/net_4_x-macos/System.Data.dll \ 242 | mono/mcs/class/lib/net_4_x-macos/System.Drawing.dll \ 243 | mono/mcs/class/lib/net_4_x-macos/System.Numerics.dll \ 244 | mono/mcs/class/lib/net_4_x-macos/System.Runtime.Serialization.dll \ 245 | mono/mcs/class/lib/net_4_x-macos/System.Security.dll \ 246 | mono/mcs/class/lib/net_4_x-macos/System.Xml.dll \ 247 | mono/mcs/class/lib/net_4_x-macos/System.dll \ 248 | mono/mcs/class/lib/net_4_x-macos/mscorlib.dll \ 249 | artifact 250 | cp mono/runtime/etc/mono/4.5/machine.config artifact/monomachineconfig 251 | 252 | - name: Upload kick.bin.osx 253 | uses: actions/upload-artifact@v4 254 | with: 255 | name: macOS-arm64 256 | path: artifact/* 257 | 258 | - name: Upload MonoPosixHelper 259 | uses: actions/upload-artifact@v4 260 | with: 261 | name: macOS-arm64-MonoPosixHelper 262 | path: mono/support/.libs/libMonoPosixHelper.dylib 263 | 264 | macos-universal: 265 | name: macOS (universal) 266 | runs-on: macos-latest 267 | needs: [macos-intel, macos-arm64] 268 | steps: 269 | - uses: actions/download-artifact@v4 270 | with: 271 | name: macOS-x86_64 272 | path: intel 273 | 274 | - uses: actions/download-artifact@v4 275 | with: 276 | name: macOS-arm64 277 | path: arm 278 | 279 | - uses: actions/download-artifact@v4 280 | with: 281 | name: macOS-x86_64-MonoPosixHelper 282 | path: intel_helper 283 | 284 | - uses: actions/download-artifact@v4 285 | with: 286 | name: macOS-arm64-MonoPosixHelper 287 | path: arm_helper 288 | 289 | - name: Lipo Libraries 290 | run: | 291 | find . 292 | cp -r arm universal 293 | rm universal/kick.bin.osx 294 | lipo -create -output universal/kick.bin.osx intel/kick.bin.osx arm/kick.bin.osx 295 | lipo -create -output libMonoPosixHelper.dylib intel_helper/libMonoPosixHelper.dylib arm_helper/libMonoPosixHelper.dylib 296 | 297 | - name: Upload kick.bin.osx 298 | uses: actions/upload-artifact@v4 299 | with: 300 | name: macOS-universal 301 | path: universal/* 302 | 303 | - name: Upload MonoPosixHelper 304 | uses: actions/upload-artifact@v4 305 | with: 306 | name: macOS-universal-MonoPosixHelper 307 | path: libMonoPosixHelper.dylib -------------------------------------------------------------------------------- /binreloc.c: -------------------------------------------------------------------------------- 1 | 2 | #ifndef __BINRELOC_C__ 3 | #define __BINRELOC_C__ 4 | 5 | #ifdef ENABLE_BINRELOC 6 | #include 7 | #include 8 | #include 9 | #endif /* ENABLE_BINRELOC */ 10 | #include 11 | #include 12 | #include 13 | #include 14 | #if defined(__APPLE__) && defined(__MACH__) 15 | #include 16 | #include 17 | #include 18 | #endif 19 | #include "binreloc.h" 20 | 21 | #ifdef __cplusplus 22 | extern "C" { 23 | #endif /* __cplusplus */ 24 | 25 | 26 | 27 | /** @internal 28 | * Find the canonical filename of the executable. Returns the filename 29 | * (which must be freed) or NULL on error. If the parameter 'error' is 30 | * not NULL, the error code will be stored there, if an error occured. 31 | */ 32 | static char * 33 | _br_find_exe (BrInitError *error) 34 | { 35 | #ifndef ENABLE_BINRELOC 36 | if (error) 37 | *error = BR_INIT_ERROR_DISABLED; 38 | return NULL; 39 | #elif defined(sun) || defined(__sun) 40 | char *path; 41 | path = getexecname(); 42 | return strdup(path); 43 | #elif defined(__APPLE__) && defined(__MACH__) 44 | char path[MAXPATHLEN+1]; 45 | uint32_t path_len = MAXPATHLEN; 46 | // SPI first appeared in Mac OS X 10.2 47 | _NSGetExecutablePath(path, &path_len); 48 | return strdup(path); 49 | #else 50 | char *path, *path2, *line, *result; 51 | size_t buf_size; 52 | ssize_t size; 53 | struct stat stat_buf; 54 | FILE *f; 55 | 56 | /* Read from /proc/self/exe (symlink) */ 57 | if (sizeof (path) > SSIZE_MAX) 58 | buf_size = SSIZE_MAX - 1; 59 | else 60 | buf_size = PATH_MAX - 1; 61 | path = (char *) malloc (buf_size); 62 | if (path == NULL) { 63 | /* Cannot allocate memory. */ 64 | if (error) 65 | *error = BR_INIT_ERROR_NOMEM; 66 | return NULL; 67 | } 68 | path2 = (char *) malloc (buf_size); 69 | if (path2 == NULL) { 70 | /* Cannot allocate memory. */ 71 | if (error) 72 | *error = BR_INIT_ERROR_NOMEM; 73 | free (path); 74 | return NULL; 75 | } 76 | 77 | #ifdef __FreeBSD__ 78 | strncpy (path2, "/proc/self/file", buf_size - 1); 79 | #else 80 | strncpy (path2, "/proc/self/exe", buf_size - 1); 81 | #endif 82 | 83 | while (1) { 84 | int i; 85 | 86 | size = readlink (path2, path, buf_size - 1); 87 | if (size == -1) { 88 | /* Error. */ 89 | free (path2); 90 | break; 91 | } 92 | 93 | /* readlink() success. */ 94 | path[size] = '\0'; 95 | 96 | /* Check whether the symlink's target is also a symlink. 97 | * We want to get the final target. */ 98 | i = stat (path, &stat_buf); 99 | if (i == -1) { 100 | /* Error. */ 101 | free (path2); 102 | break; 103 | } 104 | 105 | /* stat() success. */ 106 | if (!S_ISLNK (stat_buf.st_mode)) { 107 | /* path is not a symlink. Done. */ 108 | free (path2); 109 | return path; 110 | } 111 | 112 | /* path is a symlink. Continue loop and resolve this. */ 113 | strncpy (path, path2, buf_size - 1); 114 | } 115 | 116 | #if defined(__FreeBSD__) 117 | { 118 | char *name, *start, *end; 119 | char *buffer = NULL, *temp; 120 | struct stat finfo; 121 | 122 | name = (char*) getprogname(); 123 | start = end = getenv("PATH"); 124 | 125 | while (*end) { 126 | end = strchr (start, ':'); 127 | if (!end) end = strchr (start, '\0'); 128 | 129 | /* Resize `buffer' for path component, '/', name and a '\0' */ 130 | temp = realloc (buffer, end - start + 1 + strlen (name) + 1); 131 | if (temp) { 132 | buffer = temp; 133 | 134 | strncpy (buffer, start, end - start); 135 | *(buffer + (end - start)) = '/'; 136 | strcpy (buffer + (end - start) + 1, name); 137 | 138 | if ((stat(buffer, &finfo)==0) && (!S_ISDIR (finfo.st_mode))) { 139 | path = strdup(buffer); 140 | free (buffer); 141 | return path; 142 | } 143 | } /* else... ignore the failure; `buffer' is still valid anyway. */ 144 | 145 | start = end + 1; 146 | } 147 | /* Path search failed */ 148 | free (buffer); 149 | 150 | if (error) 151 | *error = BR_INIT_ERROR_DISABLED; 152 | return NULL; 153 | } 154 | #endif 155 | 156 | /* readlink() or stat() failed; this can happen when the program is 157 | * running in Valgrind 2.2. Read from /proc/self/maps as fallback. */ 158 | 159 | buf_size = PATH_MAX + 128; 160 | line = (char *) realloc (path, buf_size); 161 | if (line == NULL) { 162 | /* Cannot allocate memory. */ 163 | free (path); 164 | if (error) 165 | *error = BR_INIT_ERROR_NOMEM; 166 | return NULL; 167 | } 168 | 169 | f = fopen ("/proc/self/maps", "r"); 170 | if (f == NULL) { 171 | free (line); 172 | if (error) 173 | *error = BR_INIT_ERROR_OPEN_MAPS; 174 | return NULL; 175 | } 176 | 177 | /* The first entry should be the executable name. */ 178 | result = fgets (line, (int) buf_size, f); 179 | if (result == NULL) { 180 | fclose (f); 181 | free (line); 182 | if (error) 183 | *error = BR_INIT_ERROR_READ_MAPS; 184 | return NULL; 185 | } 186 | 187 | /* Get rid of newline character. */ 188 | buf_size = strlen (line); 189 | if (buf_size <= 0) { 190 | /* Huh? An empty string? */ 191 | fclose (f); 192 | free (line); 193 | if (error) 194 | *error = BR_INIT_ERROR_INVALID_MAPS; 195 | return NULL; 196 | } 197 | if (line[buf_size - 1] == 10) 198 | line[buf_size - 1] = 0; 199 | 200 | /* Extract the filename; it is always an absolute path. */ 201 | path = strchr (line, '/'); 202 | 203 | /* Sanity check. */ 204 | if (strstr (line, " r-xp ") == NULL || path == NULL) { 205 | fclose (f); 206 | free (line); 207 | if (error) 208 | *error = BR_INIT_ERROR_INVALID_MAPS; 209 | return NULL; 210 | } 211 | 212 | path = strdup (path); 213 | free (line); 214 | fclose (f); 215 | return path; 216 | #endif /* ENABLE_BINRELOC */ 217 | } 218 | 219 | 220 | /** @internal 221 | * Find the canonical filename of the executable which owns symbol. 222 | * Returns a filename which must be freed, or NULL on error. 223 | */ 224 | static char * 225 | _br_find_exe_for_symbol (const void *symbol, BrInitError *error) 226 | { 227 | #ifndef ENABLE_BINRELOC 228 | if (error) 229 | *error = BR_INIT_ERROR_DISABLED; 230 | return (char *) NULL; 231 | #else 232 | #define SIZE PATH_MAX + 100 233 | FILE *f; 234 | size_t address_string_len; 235 | char *address_string, line[SIZE], *found; 236 | 237 | if (symbol == NULL) 238 | return (char *) NULL; 239 | 240 | f = fopen ("/proc/self/maps", "r"); 241 | if (f == NULL) 242 | return (char *) NULL; 243 | 244 | address_string_len = 4; 245 | address_string = (char *) malloc (address_string_len); 246 | /* Handle OOM (Tracker issue #35) */ 247 | if (!address_string) 248 | { 249 | if (error) 250 | *error = BR_INIT_ERROR_NOMEM; 251 | return (char *) NULL; 252 | } 253 | found = (char *) NULL; 254 | 255 | while (!feof (f)) { 256 | char *start_addr, *end_addr, *end_addr_end, *file; 257 | void *start_addr_p, *end_addr_p; 258 | size_t len; 259 | 260 | if (fgets (line, SIZE, f) == NULL) 261 | break; 262 | 263 | /* Sanity check. */ 264 | if (strstr (line, " r-xp ") == NULL || strchr (line, '/') == NULL) 265 | continue; 266 | 267 | /* Parse line. */ 268 | start_addr = line; 269 | end_addr = strchr (line, '-'); 270 | file = strchr (line, '/'); 271 | 272 | /* More sanity check. */ 273 | if (!(file > end_addr && end_addr != NULL && end_addr[0] == '-')) 274 | continue; 275 | 276 | end_addr[0] = '\0'; 277 | end_addr++; 278 | end_addr_end = strchr (end_addr, ' '); 279 | if (end_addr_end == NULL) 280 | continue; 281 | 282 | end_addr_end[0] = '\0'; 283 | len = strlen (file); 284 | if (len == 0) 285 | continue; 286 | if (file[len - 1] == '\n') 287 | file[len - 1] = '\0'; 288 | 289 | /* Get rid of "(deleted)" from the filename. */ 290 | len = strlen (file); 291 | if (len > 10 && strcmp (file + len - 10, " (deleted)") == 0) 292 | file[len - 10] = '\0'; 293 | 294 | /* I don't know whether this can happen but better safe than sorry. */ 295 | len = strlen (start_addr); 296 | if (len != strlen (end_addr)) 297 | continue; 298 | 299 | 300 | /* Transform the addresses into a string in the form of 0xdeadbeef, 301 | * then transform that into a pointer. */ 302 | if (address_string_len < len + 3) { 303 | address_string_len = len + 3; 304 | address_string = (char *) realloc (address_string, address_string_len); 305 | /* Handle OOM (Tracker issue #35) */ 306 | if (!address_string) 307 | { 308 | if (error) 309 | *error = BR_INIT_ERROR_NOMEM; 310 | return (char *) NULL; 311 | } 312 | } 313 | 314 | memcpy (address_string, "0x", 2); 315 | memcpy (address_string + 2, start_addr, len); 316 | address_string[2 + len] = '\0'; 317 | sscanf (address_string, "%p", &start_addr_p); 318 | 319 | memcpy (address_string, "0x", 2); 320 | memcpy (address_string + 2, end_addr, len); 321 | address_string[2 + len] = '\0'; 322 | sscanf (address_string, "%p", &end_addr_p); 323 | 324 | 325 | if (symbol >= start_addr_p && symbol < end_addr_p) { 326 | found = file; 327 | break; 328 | } 329 | } 330 | 331 | free (address_string); 332 | fclose (f); 333 | 334 | if (found == NULL) 335 | return (char *) NULL; 336 | else 337 | return strdup (found); 338 | #endif /* ENABLE_BINRELOC */ 339 | } 340 | 341 | 342 | #ifndef BINRELOC_RUNNING_DOXYGEN 343 | #undef NULL 344 | #define NULL ((char *) 0) /* typecasted as char* for C++ type safeness */ 345 | #endif 346 | 347 | static char *exe = (char *) NULL; 348 | 349 | 350 | /** Initialize the BinReloc library (for applications). 351 | * 352 | * This function must be called before using any other BinReloc functions. 353 | * It attempts to locate the application's canonical filename. 354 | * 355 | * @note If you want to use BinReloc for a library, then you should call 356 | * br_init_lib() instead. 357 | * @note Initialization failure is not fatal. BinReloc functions will just 358 | * fallback to the supplied default path. 359 | * 360 | * @param error If BinReloc failed to initialize, then the error code will 361 | * be stored in this variable. Set to NULL if you want to 362 | * ignore this. See #BrInitError for a list of error codes. 363 | * 364 | * @returns 1 on success, 0 if BinReloc failed to initialize. 365 | */ 366 | int 367 | br_init (BrInitError *error) 368 | { 369 | exe = _br_find_exe (error); 370 | return exe != NULL; 371 | } 372 | 373 | 374 | /** Initialize the BinReloc library (for libraries). 375 | * 376 | * This function must be called before using any other BinReloc functions. 377 | * It attempts to locate the calling library's canonical filename. 378 | * 379 | * @note The BinReloc source code MUST be included in your library, or this 380 | * function won't work correctly. 381 | * @note Initialization failure is not fatal. BinReloc functions will just 382 | * fallback to the supplied default path. 383 | * 384 | * @param error If BinReloc failed to initialize, then the error code will 385 | * be stored in this variable. Set to NULL if you want to 386 | * ignore this. See #BrInitError for a list of error codes. 387 | * 388 | * @returns 1 on success, 0 if a filename cannot be found. 389 | */ 390 | int 391 | br_init_lib (BrInitError *error) 392 | { 393 | exe = _br_find_exe_for_symbol ((const void *) "", error); 394 | return exe != NULL; 395 | } 396 | 397 | 398 | /** Find the canonical filename of the current application. 399 | * 400 | * @param default_exe A default filename which will be used as fallback. 401 | * @returns A string containing the application's canonical filename, 402 | * which must be freed when no longer necessary. If BinReloc is 403 | * not initialized, or if br_init() failed, then a copy of 404 | * default_exe will be returned. If default_exe is NULL, then 405 | * NULL will be returned. 406 | */ 407 | char * 408 | br_find_exe (const char *default_exe) 409 | { 410 | if (exe == (char *) NULL) { 411 | /* BinReloc is not initialized. */ 412 | if (default_exe != (const char *) NULL) 413 | return strdup (default_exe); 414 | else 415 | return (char *) NULL; 416 | } 417 | return strdup (exe); 418 | } 419 | 420 | 421 | /** Locate the directory in which the current application is installed. 422 | * 423 | * The prefix is generated by the following pseudo-code evaluation: 424 | * \code 425 | * dirname(exename) 426 | * \endcode 427 | * 428 | * @param default_dir A default directory which will used as fallback. 429 | * @return A string containing the directory, which must be freed when no 430 | * longer necessary. If BinReloc is not initialized, or if the 431 | * initialization function failed, then a copy of default_dir 432 | * will be returned. If default_dir is NULL, then NULL will be 433 | * returned. 434 | */ 435 | char * 436 | br_find_exe_dir (const char *default_dir) 437 | { 438 | if (exe == NULL) { 439 | /* BinReloc not initialized. */ 440 | if (default_dir != NULL) 441 | return strdup (default_dir); 442 | else 443 | return NULL; 444 | } 445 | 446 | return br_dirname (exe); 447 | } 448 | 449 | 450 | /** Locate the prefix in which the current application is installed. 451 | * 452 | * The prefix is generated by the following pseudo-code evaluation: 453 | * \code 454 | * dirname(dirname(exename)) 455 | * \endcode 456 | * 457 | * @param default_prefix A default prefix which will used as fallback. 458 | * @return A string containing the prefix, which must be freed when no 459 | * longer necessary. If BinReloc is not initialized, or if 460 | * the initialization function failed, then a copy of default_prefix 461 | * will be returned. If default_prefix is NULL, then NULL will be returned. 462 | */ 463 | char * 464 | br_find_prefix (const char *default_prefix) 465 | { 466 | char *dir1, *dir2; 467 | 468 | if (exe == (char *) NULL) { 469 | /* BinReloc not initialized. */ 470 | if (default_prefix != (const char *) NULL) 471 | return strdup (default_prefix); 472 | else 473 | return (char *) NULL; 474 | } 475 | 476 | dir1 = br_dirname (exe); 477 | dir2 = br_dirname (dir1); 478 | free (dir1); 479 | return dir2; 480 | } 481 | 482 | 483 | /** Locate the application's binary folder. 484 | * 485 | * The path is generated by the following pseudo-code evaluation: 486 | * \code 487 | * prefix + "/bin" 488 | * \endcode 489 | * 490 | * @param default_bin_dir A default path which will used as fallback. 491 | * @return A string containing the bin folder's path, which must be freed when 492 | * no longer necessary. If BinReloc is not initialized, or if 493 | * the initialization function failed, then a copy of default_bin_dir will 494 | * be returned. If default_bin_dir is NULL, then NULL will be returned. 495 | */ 496 | char * 497 | br_find_bin_dir (const char *default_bin_dir) 498 | { 499 | char *prefix, *dir; 500 | 501 | prefix = br_find_prefix ((const char *) NULL); 502 | if (prefix == (char *) NULL) { 503 | /* BinReloc not initialized. */ 504 | if (default_bin_dir != (const char *) NULL) 505 | return strdup (default_bin_dir); 506 | else 507 | return (char *) NULL; 508 | } 509 | 510 | dir = br_build_path (prefix, "bin"); 511 | free (prefix); 512 | return dir; 513 | } 514 | 515 | 516 | /** Locate the application's superuser binary folder. 517 | * 518 | * The path is generated by the following pseudo-code evaluation: 519 | * \code 520 | * prefix + "/sbin" 521 | * \endcode 522 | * 523 | * @param default_sbin_dir A default path which will used as fallback. 524 | * @return A string containing the sbin folder's path, which must be freed when 525 | * no longer necessary. If BinReloc is not initialized, or if the 526 | * initialization function failed, then a copy of default_sbin_dir will 527 | * be returned. If default_bin_dir is NULL, then NULL will be returned. 528 | */ 529 | char * 530 | br_find_sbin_dir (const char *default_sbin_dir) 531 | { 532 | char *prefix, *dir; 533 | 534 | prefix = br_find_prefix ((const char *) NULL); 535 | if (prefix == (char *) NULL) { 536 | /* BinReloc not initialized. */ 537 | if (default_sbin_dir != (const char *) NULL) 538 | return strdup (default_sbin_dir); 539 | else 540 | return (char *) NULL; 541 | } 542 | 543 | dir = br_build_path (prefix, "sbin"); 544 | free (prefix); 545 | return dir; 546 | } 547 | 548 | 549 | /** Locate the application's data folder. 550 | * 551 | * The path is generated by the following pseudo-code evaluation: 552 | * \code 553 | * prefix + "/share" 554 | * \endcode 555 | * 556 | * @param default_data_dir A default path which will used as fallback. 557 | * @return A string containing the data folder's path, which must be freed when 558 | * no longer necessary. If BinReloc is not initialized, or if the 559 | * initialization function failed, then a copy of default_data_dir 560 | * will be returned. If default_data_dir is NULL, then NULL will be 561 | * returned. 562 | */ 563 | char * 564 | br_find_data_dir (const char *default_data_dir) 565 | { 566 | #if __APPLE__ 567 | char folder[PATH_MAX]; 568 | CFBundleRef bundle = CFBundleGetMainBundle(); 569 | CFURLRef url = CFBundleCopyResourcesDirectoryURL(bundle); 570 | Boolean success = CFURLGetFileSystemRepresentation( 571 | url, 572 | true, 573 | (UInt8*) folder, 574 | sizeof(folder) 575 | ); 576 | CFRelease(url); 577 | if (!success) 578 | { 579 | return NULL; 580 | } 581 | return strdup(folder); 582 | #else 583 | char *prefix, *dir; 584 | 585 | prefix = br_find_prefix ((const char *) NULL); 586 | if (prefix == (char *) NULL) { 587 | /* BinReloc not initialized. */ 588 | if (default_data_dir != (const char *) NULL) 589 | return strdup (default_data_dir); 590 | else 591 | return (char *) NULL; 592 | } 593 | 594 | dir = br_build_path (prefix, "share"); 595 | free (prefix); 596 | return dir; 597 | #endif 598 | } 599 | 600 | 601 | /** Locate the application's localization folder. 602 | * 603 | * The path is generated by the following pseudo-code evaluation: 604 | * \code 605 | * prefix + "/share/locale" 606 | * \endcode 607 | * 608 | * @param default_locale_dir A default path which will used as fallback. 609 | * @return A string containing the localization folder's path, which must be freed when 610 | * no longer necessary. If BinReloc is not initialized, or if the 611 | * initialization function failed, then a copy of default_locale_dir will be returned. 612 | * If default_locale_dir is NULL, then NULL will be returned. 613 | */ 614 | char * 615 | br_find_locale_dir (const char *default_locale_dir) 616 | { 617 | char *data_dir, *dir; 618 | 619 | data_dir = br_find_data_dir ((const char *) NULL); 620 | if (data_dir == (char *) NULL) { 621 | /* BinReloc not initialized. */ 622 | if (default_locale_dir != (const char *) NULL) 623 | return strdup (default_locale_dir); 624 | else 625 | return (char *) NULL; 626 | } 627 | 628 | dir = br_build_path (data_dir, "locale"); 629 | free (data_dir); 630 | return dir; 631 | } 632 | 633 | 634 | /** Locate the application's library folder. 635 | * 636 | * The path is generated by the following pseudo-code evaluation: 637 | * \code 638 | * prefix + "/lib" 639 | * \endcode 640 | * 641 | * @param default_lib_dir A default path which will used as fallback. 642 | * @return A string containing the library folder's path, which must be freed when 643 | * no longer necessary. If BinReloc is not initialized, or if the initialization 644 | * function failed, then a copy of default_lib_dir will be returned. 645 | * If default_lib_dir is NULL, then NULL will be returned. 646 | */ 647 | char * 648 | br_find_lib_dir (const char *default_lib_dir) 649 | { 650 | char *prefix, *dir; 651 | 652 | prefix = br_find_prefix ((const char *) NULL); 653 | if (prefix == (char *) NULL) { 654 | /* BinReloc not initialized. */ 655 | if (default_lib_dir != (const char *) NULL) 656 | return strdup (default_lib_dir); 657 | else 658 | return (char *) NULL; 659 | } 660 | 661 | dir = br_build_path (prefix, "lib"); 662 | free (prefix); 663 | return dir; 664 | } 665 | 666 | 667 | /** Locate the application's libexec folder. 668 | * 669 | * The path is generated by the following pseudo-code evaluation: 670 | * \code 671 | * prefix + "/libexec" 672 | * \endcode 673 | * 674 | * @param default_libexec_dir A default path which will used as fallback. 675 | * @return A string containing the libexec folder's path, which must be freed when 676 | * no longer necessary. If BinReloc is not initialized, or if the initialization 677 | * function failed, then a copy of default_libexec_dir will be returned. 678 | * If default_libexec_dir is NULL, then NULL will be returned. 679 | */ 680 | char * 681 | br_find_libexec_dir (const char *default_libexec_dir) 682 | { 683 | char *prefix, *dir; 684 | 685 | prefix = br_find_prefix ((const char *) NULL); 686 | if (prefix == (char *) NULL) { 687 | /* BinReloc not initialized. */ 688 | if (default_libexec_dir != (const char *) NULL) 689 | return strdup (default_libexec_dir); 690 | else 691 | return (char *) NULL; 692 | } 693 | 694 | dir = br_build_path (prefix, "libexec"); 695 | free (prefix); 696 | return dir; 697 | } 698 | 699 | 700 | /** Locate the application's configuration files folder. 701 | * 702 | * The path is generated by the following pseudo-code evaluation: 703 | * \code 704 | * prefix + "/etc" 705 | * \endcode 706 | * 707 | * @param default_etc_dir A default path which will used as fallback. 708 | * @return A string containing the etc folder's path, which must be freed when 709 | * no longer necessary. If BinReloc is not initialized, or if the initialization 710 | * function failed, then a copy of default_etc_dir will be returned. 711 | * If default_etc_dir is NULL, then NULL will be returned. 712 | */ 713 | char * 714 | br_find_etc_dir (const char *default_etc_dir) 715 | { 716 | char *prefix, *dir; 717 | 718 | prefix = br_find_prefix ((const char *) NULL); 719 | if (prefix == (char *) NULL) { 720 | /* BinReloc not initialized. */ 721 | if (default_etc_dir != (const char *) NULL) 722 | return strdup (default_etc_dir); 723 | else 724 | return (char *) NULL; 725 | } 726 | 727 | dir = br_build_path (prefix, "etc"); 728 | free (prefix); 729 | return dir; 730 | } 731 | 732 | 733 | /*********************** 734 | * Utility functions 735 | ***********************/ 736 | 737 | /** Concatenate str1 and str2 to a newly allocated string. 738 | * 739 | * @param str1 A string. 740 | * @param str2 Another string. 741 | * @returns A newly-allocated string. This string should be freed when no longer needed. 742 | */ 743 | char * 744 | br_strcat (const char *str1, const char *str2) 745 | { 746 | char *result; 747 | size_t len1, len2; 748 | 749 | if (str1 == NULL) 750 | str1 = ""; 751 | if (str2 == NULL) 752 | str2 = ""; 753 | 754 | len1 = strlen (str1); 755 | len2 = strlen (str2); 756 | 757 | result = (char *) malloc (len1 + len2 + 1); 758 | /* Handle OOM (Tracker issue #35) */ 759 | if (result) 760 | { 761 | memcpy (result, str1, len1); 762 | memcpy (result + len1, str2, len2); 763 | result[len1 + len2] = '\0'; 764 | } 765 | return result; 766 | } 767 | 768 | 769 | char * 770 | br_build_path (const char *dir, const char *file) 771 | { 772 | char *dir2, *result; 773 | size_t len; 774 | int must_free = 0; 775 | 776 | len = strlen (dir); 777 | if (len > 0 && dir[len - 1] != '/') { 778 | dir2 = br_strcat (dir, "/"); 779 | must_free = 1; 780 | } else 781 | dir2 = (char *) dir; 782 | 783 | result = br_strcat (dir2, file); 784 | if (must_free) 785 | free (dir2); 786 | return result; 787 | } 788 | 789 | 790 | /* Emulates glibc's strndup() */ 791 | static char * 792 | br_strndup (const char *str, size_t size) 793 | { 794 | char *result = (char *) NULL; 795 | size_t len; 796 | 797 | if (str == (const char *) NULL) 798 | return (char *) NULL; 799 | 800 | len = strlen (str); 801 | if (len == 0) 802 | return strdup (""); 803 | if (size > len) 804 | size = len; 805 | 806 | result = (char *) malloc (len + 1); 807 | /* Handle OOM (Tracker issue #35) */ 808 | if (result) 809 | { 810 | memcpy (result, str, size); 811 | result[size] = '\0'; 812 | } 813 | return result; 814 | } 815 | 816 | 817 | /** Extracts the directory component of a path. 818 | * 819 | * Similar to g_dirname() or the dirname commandline application. 820 | * 821 | * Example: 822 | * \code 823 | * br_dirname ("/usr/local/foobar"); --> Returns: "/usr/local" 824 | * \endcode 825 | * 826 | * @param path A path. 827 | * @returns A directory name. This string should be freed when no longer needed. 828 | */ 829 | char * 830 | br_dirname (const char *path) 831 | { 832 | char *end, *result; 833 | 834 | if (path == (const char *) NULL) 835 | return (char *) NULL; 836 | 837 | end = strrchr (path, '/'); 838 | if (end == (const char *) NULL) 839 | return strdup ("."); 840 | 841 | while (end > path && *end == '/') 842 | end--; 843 | result = br_strndup (path, end - path + 1); 844 | if (result[0] == 0) { 845 | free (result); 846 | return strdup ("/"); 847 | } else 848 | return result; 849 | } 850 | 851 | 852 | #ifdef __cplusplus 853 | } 854 | #endif /* __cplusplus */ 855 | 856 | #endif /* __BINRELOC_C__ */ 857 | -------------------------------------------------------------------------------- /patches/corefx-MONO_IOMAP.patch: -------------------------------------------------------------------------------- 1 | From 2902d5856985830db1e23f78ed8b83d4c502a261 Mon Sep 17 00:00:00 2001 2 | From: Ethan Lee 3 | Date: Wed, 22 Apr 2020 13:10:16 -0400 4 | Subject: [PATCH] DRAFT: Port MONO_IOMAP to CoreFX. 5 | 6 | The original mono-io-portability code mapped almost 1:1 with the CoreFX I/O 7 | implementation with the exception of g_dir_open, which for non-Win32 is just 8 | opendir with more memory allocations. The tabs/style are fixed in the function 9 | implementations, while the original portability code is preserved in its 10 | entirety, hideous whitespace and all. 11 | 12 | The functions that use a 'const char* path' but were NOT present in 13 | mono-io-portability include the following: 14 | - FnMatch 15 | - ReadLink 16 | - INotifyAddWatch 17 | - RealPath 18 | - LChflags 19 | 20 | Peculiarly, Mono still seems to use the old MonoIO _just_ enough to where we 21 | can't name this something more appropriate like DOTNET_IOMAP, so instead we 22 | have to share the old name to get full coverage for this feature. 23 | --- 24 | src/Native/Unix/System.Native/pal_io.c | 585 ++++++++++++++++++++++- 25 | src/Native/Unix/System.Native/pal_time.c | 72 +++ 26 | 2 files changed, 652 insertions(+), 5 deletions(-) 27 | 28 | diff --git a/src/Native/Unix/System.Native/pal_io.c b/src/Native/Unix/System.Native/pal_io.c 29 | index ddd56b91c93d..447e03fcfde5 100644 30 | --- a/src/Native/Unix/System.Native/pal_io.c 31 | +++ b/src/Native/Unix/System.Native/pal_io.c 32 | @@ -139,6 +139,50 @@ c_static_assert(PAL_IN_EXCL_UNLINK == IN_EXCL_UNLINK); 33 | c_static_assert(PAL_IN_ISDIR == IN_ISDIR); 34 | #endif // HAVE_INOTIFY 35 | 36 | +/* BEGIN MONO_IO_PORTABILITY_H */ 37 | + 38 | +#include 39 | +#include 40 | +#include "config.h" 41 | + 42 | +enum { 43 | + PORTABILITY_NONE = 0x00, 44 | + PORTABILITY_UNKNOWN = 0x01, 45 | + PORTABILITY_DRIVE = 0x02, 46 | + PORTABILITY_CASE = 0x04 47 | +}; 48 | + 49 | +#ifdef DISABLE_PORTABILITY 50 | + 51 | +#define mono_portability_helpers_init() 52 | +#define mono_portability_find_file(pathname,last_exists) NULL 53 | + 54 | +#define IS_PORTABILITY_NONE FALSE 55 | +#define IS_PORTABILITY_UNKNOWN FALSE 56 | +#define IS_PORTABILITY_DRIVE FALSE 57 | +#define IS_PORTABILITY_CASE FALSE 58 | +#define IS_PORTABILITY_SET FALSE 59 | + 60 | +#else 61 | + 62 | +void mono_portability_helpers_init_COREFX (void); 63 | +gchar *mono_portability_find_file_COREFX (const gchar *pathname, gboolean last_exists); 64 | +#define mono_portability_helpers_init() mono_portability_helpers_init_COREFX() 65 | +#define mono_portability_find_file(pathname,last_exists) mono_portability_find_file_COREFX(pathname,last_exists) 66 | + 67 | +extern int mono_io_portability_helpers_COREFX; 68 | +#define mono_io_portability_helpers mono_io_portability_helpers_COREFX 69 | + 70 | +#define IS_PORTABILITY_NONE (mono_io_portability_helpers & PORTABILITY_NONE) 71 | +#define IS_PORTABILITY_UNKNOWN (mono_io_portability_helpers & PORTABILITY_UNKNOWN) 72 | +#define IS_PORTABILITY_DRIVE (mono_io_portability_helpers & PORTABILITY_DRIVE) 73 | +#define IS_PORTABILITY_CASE (mono_io_portability_helpers & PORTABILITY_CASE) 74 | +#define IS_PORTABILITY_SET (mono_io_portability_helpers > 0) 75 | + 76 | +#endif 77 | + 78 | +/* END MONO_IO_PORTABILITY_H */ 79 | + 80 | static void ConvertFileStatus(const struct stat_* src, struct FileStatus* dst) 81 | { 82 | dst->Dev = (int64_t)src->st_dev; 83 | @@ -181,6 +225,20 @@ int32_t SystemNative_Stat2(const char* path, struct FileStatus* output) 84 | struct stat_ result; 85 | int ret; 86 | while ((ret = stat_(path, &result)) < 0 && errno == EINTR); 87 | + if (ret == -1 && (errno == ENOENT || errno == ENOTDIR) && IS_PORTABILITY_SET) 88 | + { 89 | + int32_t saved_errno = errno; 90 | + char* located_filename = mono_portability_find_file(path, TRUE); 91 | + 92 | + if (located_filename == NULL) 93 | + { 94 | + errno = saved_errno; 95 | + return -1; 96 | + } 97 | + 98 | + while ((ret = stat_(located_filename, &result)) < 0 && errno == EINTR); 99 | + g_free(located_filename); 100 | + } 101 | 102 | if (ret == 0) 103 | { 104 | @@ -208,6 +266,20 @@ int32_t SystemNative_LStat2(const char* path, struct FileStatus* output) 105 | { 106 | struct stat_ result; 107 | int ret = lstat_(path, &result); 108 | + if (ret == -1 && (errno == ENOENT || errno == ENOTDIR) && IS_PORTABILITY_SET) 109 | + { 110 | + int32_t saved_errno = errno; 111 | + char* located_filename = mono_portability_find_file(path, TRUE); 112 | + 113 | + if (located_filename == NULL) 114 | + { 115 | + errno = saved_errno; 116 | + return -1; 117 | + } 118 | + 119 | + ret = lstat_(located_filename, &result); 120 | + g_free(located_filename); 121 | + } 122 | 123 | if (ret == 0) 124 | { 125 | @@ -274,7 +346,39 @@ intptr_t SystemNative_Open(const char* path, int32_t flags, int32_t mode) 126 | } 127 | 128 | int result; 129 | - while ((result = open(path, flags, (mode_t)mode)) < 0 && errno == EINTR); 130 | + char* located_filename; 131 | + if (flags & O_CREAT) 132 | + { 133 | + located_filename = mono_portability_find_file(path, FALSE); 134 | + if (located_filename == NULL) 135 | + { 136 | + while ((result = open(path, flags, (mode_t)mode)) < 0 && errno == EINTR); 137 | + } 138 | + else 139 | + { 140 | + while ((result = open(located_filename, flags, (mode_t)mode)) < 0 && errno == EINTR); 141 | + g_free(located_filename); 142 | + } 143 | + } 144 | + else 145 | + { 146 | + while ((result = open(path, flags, (mode_t)mode)) < 0 && errno == EINTR); 147 | + if (result == -1 && (errno == ENOENT || errno == ENOTDIR) && IS_PORTABILITY_SET) 148 | + { 149 | + int32_t saved_errno = errno; 150 | + located_filename = mono_portability_find_file(path, TRUE); 151 | + 152 | + if (located_filename == NULL) 153 | + { 154 | + errno = saved_errno; 155 | + return -1; 156 | + } 157 | + 158 | + while ((result = open(located_filename, flags, (mode_t)mode)) < 0 && errno == EINTR); 159 | + g_free (located_filename); 160 | + } 161 | + } 162 | + 163 | #if !HAVE_O_CLOEXEC 164 | if (old_flags & PAL_O_CLOEXEC) 165 | { 166 | @@ -306,6 +410,20 @@ int32_t SystemNative_Unlink(const char* path) 167 | { 168 | int32_t result; 169 | while ((result = unlink(path)) < 0 && errno == EINTR); 170 | + if (result == -1 && (errno == ENOENT || errno == ENOTDIR || errno == EISDIR) && IS_PORTABILITY_SET) 171 | + { 172 | + int32_t saved_errno = errno; 173 | + char* located_filename = mono_portability_find_file(path, TRUE); 174 | + 175 | + if (located_filename == NULL) 176 | + { 177 | + errno = saved_errno; 178 | + return -1; 179 | + } 180 | + 181 | + while ((result = unlink(located_filename)) < 0 && errno == EINTR); 182 | + g_free(located_filename); 183 | + } 184 | return result; 185 | } 186 | 187 | @@ -467,7 +585,22 @@ int32_t SystemNative_ReadDirR(DIR* dir, uint8_t* buffer, int32_t bufferSize, str 188 | 189 | DIR* SystemNative_OpenDir(const char* path) 190 | { 191 | - return opendir(path); 192 | + DIR* ret = opendir(path); 193 | + if (ret == NULL && (errno == ENOENT || errno == ENOTDIR || errno == ENAMETOOLONG) && IS_PORTABILITY_SET) 194 | + { 195 | + int32_t saved_errno = errno; 196 | + char* located_filename = mono_portability_find_file(path, TRUE); 197 | + 198 | + if (located_filename == NULL) 199 | + { 200 | + errno = saved_errno; 201 | + return NULL; 202 | + } 203 | + 204 | + ret = opendir(located_filename); 205 | + g_free(located_filename); 206 | + } 207 | + return ret; 208 | } 209 | 210 | int32_t SystemNative_CloseDir(DIR* dir) 211 | @@ -592,7 +725,18 @@ int32_t SystemNative_FcntlSetIsNonBlocking(intptr_t fd, int32_t isNonBlocking) 212 | int32_t SystemNative_MkDir(const char* path, int32_t mode) 213 | { 214 | int32_t result; 215 | - while ((result = mkdir(path, (mode_t)mode)) < 0 && errno == EINTR); 216 | + char* located_filename = mono_portability_find_file(path, FALSE); 217 | + 218 | + if (located_filename == NULL) 219 | + { 220 | + while ((result = mkdir(path, (mode_t)mode)) < 0 && errno == EINTR); 221 | + } 222 | + else 223 | + { 224 | + while ((result = mkdir(located_filename, (mode_t)mode)) < 0 && errno == EINTR); 225 | + g_free(located_filename); 226 | + } 227 | + 228 | return result; 229 | } 230 | 231 | @@ -600,6 +744,20 @@ int32_t SystemNative_ChMod(const char* path, int32_t mode) 232 | { 233 | int32_t result; 234 | while ((result = chmod(path, (mode_t)mode)) < 0 && errno == EINTR); 235 | + if (result == -1 && (errno == ENOENT || errno == ENOTDIR) && IS_PORTABILITY_SET) 236 | + { 237 | + int32_t saved_errno = errno; 238 | + char* located_filename = mono_portability_find_file(path, TRUE); 239 | + 240 | + if (located_filename == NULL) 241 | + { 242 | + errno = saved_errno; 243 | + return -1; 244 | + } 245 | + 246 | + while ((result = chmod(located_filename, (mode_t)mode)) < 0 && errno == EINTR); 247 | + g_free(located_filename); 248 | + } 249 | return result; 250 | } 251 | 252 | @@ -628,12 +786,41 @@ int32_t SystemNative_ChDir(const char* path) 253 | { 254 | int32_t result; 255 | while ((result = chdir(path)) < 0 && errno == EINTR); 256 | + if (result == -1 && (errno == ENOENT || errno == ENOTDIR || errno == ENAMETOOLONG) && IS_PORTABILITY_SET) 257 | + { 258 | + int32_t saved_errno = errno; 259 | + char *located_filename = mono_portability_find_file (path, TRUE); 260 | + 261 | + if (located_filename == NULL) 262 | + { 263 | + errno = saved_errno; 264 | + return -1; 265 | + } 266 | + 267 | + while ((result = chdir(located_filename)) < 0 && errno == EINTR); 268 | + g_free(located_filename); 269 | + } 270 | return result; 271 | } 272 | 273 | int32_t SystemNative_Access(const char* path, int32_t mode) 274 | { 275 | - return access(path, mode); 276 | + int32_t result = access(path, mode); 277 | + if (result == -1 && (errno == ENOENT || errno == ENOTDIR) && IS_PORTABILITY_SET) 278 | + { 279 | + int32_t saved_errno = errno; 280 | + char* located_filename = mono_portability_find_file(path, TRUE); 281 | + 282 | + if (located_filename == NULL) 283 | + { 284 | + errno = saved_errno; 285 | + return -1; 286 | + } 287 | + 288 | + result = access(located_filename, mode); 289 | + g_free(located_filename); 290 | + } 291 | + return result; 292 | } 293 | 294 | int32_t SystemNative_FnMatch(const char* pattern, const char* path, int32_t flags) 295 | @@ -1159,7 +1346,36 @@ int32_t SystemNative_ReadLink(const char* path, char* buffer, int32_t bufferSize 296 | int32_t SystemNative_Rename(const char* oldPath, const char* newPath) 297 | { 298 | int32_t result; 299 | - while ((result = rename(oldPath, newPath)) < 0 && errno == EINTR); 300 | + char* located_newpath = mono_portability_find_file(newPath, FALSE); 301 | + 302 | + if (located_newpath == NULL) 303 | + { 304 | + while ((result = rename(oldPath, newPath)) < 0 && errno == EINTR); 305 | + } 306 | + else 307 | + { 308 | + while ((result = rename(oldPath, located_newpath)) < 0 && errno == EINTR); 309 | + 310 | + if (result == -1 && (errno == EISDIR || errno == ENAMETOOLONG || errno == ENOENT || errno == ENOTDIR || errno == EXDEV) && IS_PORTABILITY_SET) 311 | + { 312 | + int32_t saved_errno = errno; 313 | + char* located_oldpath = mono_portability_find_file(oldPath, TRUE); 314 | + 315 | + if (located_oldpath == NULL) 316 | + { 317 | + g_free(located_oldpath); 318 | + g_free(located_newpath); 319 | + 320 | + errno = saved_errno; 321 | + return -1; 322 | + } 323 | + 324 | + while ((result = rename(located_oldpath, located_newpath)) < 0 && errno == EINTR); 325 | + g_free(located_oldpath); 326 | + } 327 | + g_free(located_newpath); 328 | + } 329 | + 330 | return result; 331 | } 332 | 333 | @@ -1167,6 +1383,20 @@ int32_t SystemNative_RmDir(const char* path) 334 | { 335 | int32_t result; 336 | while ((result = rmdir(path)) < 0 && errno == EINTR); 337 | + if (result == -1 && (errno == ENOENT || errno == ENOTDIR || errno == ENAMETOOLONG) && IS_PORTABILITY_SET) 338 | + { 339 | + int32_t saved_errno = errno; 340 | + char* located_filename = mono_portability_find_file(path, TRUE); 341 | + 342 | + if (located_filename == NULL) 343 | + { 344 | + errno = saved_errno; 345 | + return -1; 346 | + } 347 | + 348 | + while ((result = rmdir(located_filename)) < 0 && errno == EINTR); 349 | + g_free(located_filename); 350 | + } 351 | return result; 352 | } 353 | 354 | @@ -1492,3 +1722,348 @@ int32_t SystemNative_Symlink(const char* target, const char* linkPath) 355 | { 356 | return symlink(target, linkPath); 357 | } 358 | + 359 | +/* BEGIN MONO_IO_PORTABILITY_C */ 360 | + 361 | +#ifndef DISABLE_PORTABILITY 362 | + 363 | +#include 364 | +#include 365 | +#include 366 | +#include 367 | + 368 | +int mono_io_portability_helpers_COREFX = PORTABILITY_UNKNOWN; 369 | + 370 | +static gchar *mono_portability_find_file_internal (const gchar *pathname, gboolean last_exists); 371 | + 372 | +void mono_portability_helpers_init_COREFX (void) 373 | +{ 374 | + gchar *env; 375 | + 376 | + if (mono_io_portability_helpers != PORTABILITY_UNKNOWN) 377 | + return; 378 | + 379 | + mono_io_portability_helpers = PORTABILITY_NONE; 380 | + 381 | + env = g_getenv ("MONO_IOMAP"); 382 | + if (env != NULL) { 383 | + /* parse the environment setting and set up some vars 384 | + * here 385 | + */ 386 | + gchar **options = g_strsplit (env, ":", 0); 387 | + int i; 388 | + 389 | + if (options == NULL) { 390 | + /* This shouldn't happen */ 391 | + return; 392 | + } 393 | + 394 | + for (i = 0; options[i] != NULL; i++) { 395 | +#ifdef DEBUG 396 | + g_message ("%s: Setting option [%s]", __func__, 397 | + options[i]); 398 | +#endif 399 | + if (!strncasecmp (options[i], "drive", 5)) { 400 | + mono_io_portability_helpers |= PORTABILITY_DRIVE; 401 | + } else if (!strncasecmp (options[i], "case", 4)) { 402 | + mono_io_portability_helpers |= PORTABILITY_CASE; 403 | + } else if (!strncasecmp (options[i], "all", 3)) { 404 | + mono_io_portability_helpers |= (PORTABILITY_DRIVE | PORTABILITY_CASE); 405 | + } 406 | + } 407 | + g_free (env); 408 | + } 409 | +} 410 | + 411 | +/* Returns newly allocated string, or NULL on failure */ 412 | +static gchar *find_in_dir (DIR *current, const gchar *name) 413 | +{ 414 | + struct dirent *entry; 415 | + 416 | +#ifdef DEBUG 417 | + g_message ("%s: looking for [%s]\n", __func__, name); 418 | +#endif 419 | + 420 | + while((entry = readdir (current)) != NULL) { 421 | +#ifdef DEBUGX 422 | + g_message ("%s: found [%s]\n", __func__, entry->d_name); 423 | +#endif 424 | + 425 | + if (!g_ascii_strcasecmp (name, entry->d_name)) { 426 | + char *ret; 427 | + 428 | +#ifdef DEBUG 429 | + g_message ("%s: matched [%s] to [%s]\n", __func__, 430 | + entry->d_name, name); 431 | +#endif 432 | + 433 | + ret = g_strdup (entry->d_name); 434 | + closedir (current); 435 | + return ret; 436 | + } 437 | + } 438 | + 439 | +#ifdef DEBUG 440 | + g_message ("%s: returning NULL\n", __func__); 441 | +#endif 442 | + 443 | + closedir (current); 444 | + 445 | + return(NULL); 446 | +} 447 | + 448 | +gchar *mono_portability_find_file_COREFX (const gchar *pathname, gboolean last_exists) 449 | +{ 450 | + gchar *ret; 451 | + 452 | + if (!pathname || !pathname [0]) 453 | + return NULL; 454 | + ret = mono_portability_find_file_internal (pathname, last_exists); 455 | + 456 | + return ret; 457 | +} 458 | + 459 | +/* Returns newly-allocated string or NULL on failure */ 460 | +static gchar *mono_portability_find_file_internal (const gchar *pathname, gboolean last_exists) 461 | +{ 462 | + gchar *new_pathname, **components, **new_components; 463 | + int num_components = 0, component = 0; 464 | + DIR *scanning = NULL; 465 | + size_t len; 466 | + 467 | + mono_portability_helpers_init(); /* flibitChange! */ 468 | + 469 | + if (IS_PORTABILITY_NONE) { 470 | + return(NULL); 471 | + } 472 | + 473 | + new_pathname = g_strdup (pathname); 474 | + 475 | +#ifdef DEBUG 476 | + g_message ("%s: Finding [%s] last_exists: %s\n", __func__, pathname, 477 | + last_exists?"TRUE":"FALSE"); 478 | +#endif 479 | + 480 | + if (last_exists && 481 | + access (new_pathname, F_OK) == 0) { 482 | +#ifdef DEBUG 483 | + g_message ("%s: Found it without doing anything\n", __func__); 484 | +#endif 485 | + return(new_pathname); 486 | + } 487 | + 488 | + /* First turn '\' into '/' and strip any drive letters */ 489 | + g_strdelimit (new_pathname, '\\', '/'); 490 | + 491 | +#ifdef DEBUG 492 | + g_message ("%s: Fixed slashes, now have [%s]\n", __func__, 493 | + new_pathname); 494 | +#endif 495 | + 496 | + if (IS_PORTABILITY_DRIVE && 497 | + g_ascii_isalpha (new_pathname[0]) && 498 | + (new_pathname[1] == ':')) { 499 | + int len = strlen (new_pathname); 500 | + 501 | + g_memmove (new_pathname, new_pathname+2, len - 2); 502 | + new_pathname[len - 2] = '\0'; 503 | +#ifdef DEBUG 504 | + g_message ("%s: Stripped drive letter, now looking for [%s]\n", 505 | + __func__, new_pathname); 506 | +#endif 507 | + } 508 | + 509 | + len = strlen (new_pathname); 510 | + if (len > 1 && new_pathname [len - 1] == '/') { 511 | + new_pathname [len - 1] = 0; 512 | +#ifdef DEBUG 513 | + g_message ("%s: requested name had a trailing /, rewritten to '%s'\n", 514 | + __func__, new_pathname); 515 | +#endif 516 | + } 517 | + 518 | + if (last_exists && 519 | + access (new_pathname, F_OK) == 0) { 520 | +#ifdef DEBUG 521 | + g_message ("%s: Found it\n", __func__); 522 | +#endif 523 | + 524 | + return(new_pathname); 525 | + } 526 | + 527 | + /* OK, have to work harder. Take each path component in turn 528 | + * and do a case-insensitive directory scan for it 529 | + */ 530 | + 531 | + if (!(IS_PORTABILITY_CASE)) { 532 | + g_free (new_pathname); 533 | + return(NULL); 534 | + } 535 | + 536 | + components = g_strsplit (new_pathname, "/", 0); 537 | + if (components == NULL) { 538 | + /* This shouldn't happen */ 539 | + g_free (new_pathname); 540 | + return(NULL); 541 | + } 542 | + 543 | + while(components[num_components] != NULL) { 544 | + num_components++; 545 | + } 546 | + g_free (new_pathname); 547 | + 548 | + if (num_components == 0){ 549 | + return NULL; 550 | + } 551 | + 552 | + 553 | + new_components = (gchar **)g_new0 (gchar **, num_components + 1); 554 | + 555 | + if (num_components > 1) { 556 | + if (strcmp (components[0], "") == 0) { 557 | + /* first component blank, so start at / */ 558 | + scanning = opendir ("/"); 559 | + if (scanning == NULL) { 560 | +#ifdef DEBUG 561 | + g_message ("%s: opendir 1 error: %s", __func__, 562 | + g_strerror (errno)); 563 | +#endif 564 | + g_strfreev (new_components); 565 | + g_strfreev (components); 566 | + return(NULL); 567 | + } 568 | + 569 | + new_components[component++] = g_strdup (""); 570 | + } else { 571 | + DIR *current; 572 | + gchar *entry; 573 | + 574 | + current = opendir ("."); 575 | + if (current == NULL) { 576 | +#ifdef DEBUG 577 | + g_message ("%s: opendir 2 error: %s", __func__, 578 | + g_strerror (errno)); 579 | +#endif 580 | + g_strfreev (new_components); 581 | + g_strfreev (components); 582 | + return(NULL); 583 | + } 584 | + 585 | + entry = find_in_dir (current, components[0]); 586 | + if (entry == NULL) { 587 | + g_strfreev (new_components); 588 | + g_strfreev (components); 589 | + return(NULL); 590 | + } 591 | + 592 | + scanning = opendir (entry); 593 | + if (scanning == NULL) { 594 | +#ifdef DEBUG 595 | + g_message ("%s: opendir 3 error: %s", __func__, 596 | + g_strerror (errno)); 597 | +#endif 598 | + g_free (entry); 599 | + g_strfreev (new_components); 600 | + g_strfreev (components); 601 | + return(NULL); 602 | + } 603 | + 604 | + new_components[component++] = entry; 605 | + } 606 | + } else { 607 | + if (last_exists) { 608 | + if (strcmp (components[0], "") == 0) { 609 | + /* First and only component blank */ 610 | + new_components[component++] = g_strdup (""); 611 | + } else { 612 | + DIR *current; 613 | + gchar *entry; 614 | + 615 | + current = opendir ("."); 616 | + if (current == NULL) { 617 | +#ifdef DEBUG 618 | + g_message ("%s: opendir 4 error: %s", 619 | + __func__, 620 | + g_strerror (errno)); 621 | +#endif 622 | + g_strfreev (new_components); 623 | + g_strfreev (components); 624 | + return(NULL); 625 | + } 626 | + 627 | + entry = find_in_dir (current, components[0]); 628 | + if (entry == NULL) { 629 | + g_strfreev (new_components); 630 | + g_strfreev (components); 631 | + return(NULL); 632 | + } 633 | + 634 | + new_components[component++] = entry; 635 | + } 636 | + } else { 637 | + new_components[component++] = g_strdup (components[0]); 638 | + } 639 | + } 640 | + 641 | +#ifdef DEBUG 642 | + g_message ("%s: Got first entry: [%s]\n", __func__, new_components[0]); 643 | +#endif 644 | + 645 | + g_assert (component == 1); 646 | + 647 | + for(; component < num_components; component++) { 648 | + gchar *entry; 649 | + gchar *path_so_far; 650 | + 651 | + if (!last_exists && 652 | + component == num_components -1) { 653 | + entry = g_strdup (components[component]); 654 | + closedir (scanning); 655 | + } else { 656 | + entry = find_in_dir (scanning, components[component]); 657 | + if (entry == NULL) { 658 | + g_strfreev (new_components); 659 | + g_strfreev (components); 660 | + return(NULL); 661 | + } 662 | + } 663 | + 664 | + new_components[component] = entry; 665 | + 666 | + if (component < num_components -1) { 667 | + path_so_far = g_strjoinv ("/", new_components); 668 | + 669 | + scanning = opendir (path_so_far); 670 | + g_free (path_so_far); 671 | + if (scanning == NULL) { 672 | + g_strfreev (new_components); 673 | + g_strfreev (components); 674 | + return(NULL); 675 | + } 676 | + } 677 | + } 678 | + 679 | + g_strfreev (components); 680 | + 681 | + new_pathname = g_strjoinv ("/", new_components); 682 | + 683 | +#ifdef DEBUG 684 | + g_message ("%s: pathname [%s] became [%s]\n", __func__, pathname, 685 | + new_pathname); 686 | +#endif 687 | + 688 | + g_strfreev (new_components); 689 | + 690 | + if ((last_exists && 691 | + access (new_pathname, F_OK) == 0) || 692 | + (!last_exists)) { 693 | + return(new_pathname); 694 | + } 695 | + 696 | + g_free (new_pathname); 697 | + return(NULL); 698 | +} 699 | + 700 | +#endif /* !DISABLE_PORTABILITY */ 701 | + 702 | +/* END MONO_IO_PORTABILITY_C */ 703 | diff --git a/src/Native/Unix/System.Native/pal_time.c b/src/Native/Unix/System.Native/pal_time.c 704 | index 095c44362f24..a6fb3c24bbc9 100644 705 | --- a/src/Native/Unix/System.Native/pal_time.c 706 | +++ b/src/Native/Unix/System.Native/pal_time.c 707 | @@ -14,6 +14,50 @@ 708 | #include 709 | #endif 710 | 711 | +/* BEGIN MONO_IO_PORTABILITY_H */ 712 | + 713 | +#include 714 | +#include 715 | +#include "config.h" 716 | + 717 | +enum { 718 | + PORTABILITY_NONE = 0x00, 719 | + PORTABILITY_UNKNOWN = 0x01, 720 | + PORTABILITY_DRIVE = 0x02, 721 | + PORTABILITY_CASE = 0x04 722 | +}; 723 | + 724 | +#ifdef DISABLE_PORTABILITY 725 | + 726 | +#define mono_portability_helpers_init() 727 | +#define mono_portability_find_file(pathname,last_exists) NULL 728 | + 729 | +#define IS_PORTABILITY_NONE FALSE 730 | +#define IS_PORTABILITY_UNKNOWN FALSE 731 | +#define IS_PORTABILITY_DRIVE FALSE 732 | +#define IS_PORTABILITY_CASE FALSE 733 | +#define IS_PORTABILITY_SET FALSE 734 | + 735 | +#else 736 | + 737 | +void mono_portability_helpers_init_COREFX (void); 738 | +gchar *mono_portability_find_file_COREFX (const gchar *pathname, gboolean last_exists); 739 | +#define mono_portability_helpers_init() mono_portability_helpers_init_COREFX() 740 | +#define mono_portability_find_file(pathname,last_exists) mono_portability_find_file_COREFX(pathname,last_exists) 741 | + 742 | +extern int mono_io_portability_helpers_COREFX; 743 | +#define mono_io_portability_helpers mono_io_portability_helpers_COREFX 744 | + 745 | +#define IS_PORTABILITY_NONE (mono_io_portability_helpers & PORTABILITY_NONE) 746 | +#define IS_PORTABILITY_UNKNOWN (mono_io_portability_helpers & PORTABILITY_UNKNOWN) 747 | +#define IS_PORTABILITY_DRIVE (mono_io_portability_helpers & PORTABILITY_DRIVE) 748 | +#define IS_PORTABILITY_CASE (mono_io_portability_helpers & PORTABILITY_CASE) 749 | +#define IS_PORTABILITY_SET (mono_io_portability_helpers > 0) 750 | + 751 | +#endif 752 | + 753 | +/* END MONO_IO_PORTABILITY_H */ 754 | + 755 | enum 756 | { 757 | SecondsToMicroSeconds = 1000000, // 10^6 758 | @@ -43,6 +87,20 @@ int32_t SystemNative_UTime(const char* path, UTimBuf* times) 759 | 760 | int32_t result; 761 | while (CheckInterrupted(result = utime(path, &temp))); 762 | + if (result == -1 && errno == ENOENT && IS_PORTABILITY_SET) 763 | + { 764 | + int32_t saved_errno = errno; 765 | + char* located_filename = mono_portability_find_file(path, TRUE); 766 | + 767 | + if (located_filename == NULL) 768 | + { 769 | + errno = saved_errno; 770 | + return -1; 771 | + } 772 | + 773 | + while (CheckInterrupted(result = utime(located_filename, &temp))); 774 | + g_free(located_filename); 775 | + } 776 | return result; 777 | } 778 | 779 | @@ -55,6 +113,20 @@ int32_t SystemNative_UTimes(const char* path, TimeValPair* times) 780 | 781 | int32_t result; 782 | while (CheckInterrupted(result = utimes(path, temp))); 783 | + if (result == -1 && errno == ENOENT && IS_PORTABILITY_SET) 784 | + { 785 | + int32_t saved_errno = errno; 786 | + char* located_filename = mono_portability_find_file(path, TRUE); 787 | + 788 | + if (located_filename == NULL) 789 | + { 790 | + errno = saved_errno; 791 | + return -1; 792 | + } 793 | + 794 | + while (CheckInterrupted(result = utimes(located_filename, temp))); 795 | + g_free(located_filename); 796 | + } 797 | return result; 798 | } 799 | 800 | --------------------------------------------------------------------------------