├── .gitignore ├── Test.hs ├── .clang-format ├── README.md └── macos11ghcwa.c /.gitignore: -------------------------------------------------------------------------------- 1 | *.dylib 2 | -------------------------------------------------------------------------------- /Test.hs: -------------------------------------------------------------------------------- 1 | module Main where 2 | 3 | main = putStrLn "Hi." 4 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | Language: Cpp 3 | BasedOnStyle: LLVM 4 | AccessModifierOffset: -4 5 | AlignAfterOpenBracket: AlwaysBreak 6 | AlignConsecutiveMacros: true 7 | AlignEscapedNewlines: Left 8 | AllowShortFunctionsOnASingleLine: Inline 9 | AlwaysBreakTemplateDeclarations: Yes 10 | BreakBeforeBinaryOperators: NonAssignment 11 | BreakBeforeBraces: Allman 12 | ColumnLimit: 120 13 | IndentWidth: 4 14 | KeepEmptyLinesAtTheStartOfBlocks: false 15 | PointerAlignment: Left 16 | SpaceAfterCStyleCast: true 17 | SpaceAfterLogicalNot: true 18 | SpaceBeforeCpp11BracedList: true 19 | SpaceBeforeParens: NonEmptyParentheses 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MacOS 11 Haskell Work-around 2 | 3 | A work around for a bug in older GHCs (<= 8.10.3) on macOS 11 "Big Sur" (bug #18446). 4 | 5 | Details at: https://gitlab.haskell.org/ghc/ghc/-/issues/18446 6 | 7 | This work-around should allow using affected GHC versions in macOS 11. 8 | 9 | Build: 10 | 11 | clang -target x86_64-darwin -dynamiclib macos11ghcwa.c -o macos11ghcwa.dylib 12 | 13 | Use: 14 | 15 | DYLD_INSERT_LIBRARIES=/macos11ghcwa.dylib stack args 16 | 17 | To test one can use (with and without the fix): 18 | 19 | stack exec -- runghc --ghc-arg="-framework OpenGL" Test.hs 20 | 21 | DYLD_INSERT_LIBRARIES=`pwd`/macos11ghcwa.dylib stack exec -- runghc --ghc-arg="-framework OpenGL" Test.hs 22 | -------------------------------------------------------------------------------- /macos11ghcwa.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | // From https://opensource.apple.com/source/dyld/dyld-97.1/include/mach-o/dyld-interposing.h.auto.html 8 | #define DYLD_INTERPOSE(_replacment, _replacee) \ 9 | __attribute__ ((used)) static struct \ 10 | { \ 11 | const void* replacment; \ 12 | const void* replacee; \ 13 | } _interpose_##_replacee __attribute__ ((section ("__DATA,__interpose"))) = { \ 14 | (const void*) (unsigned long) &_replacment, (const void*) (unsigned long) &_replacee}; 15 | 16 | #define STARTS_WITH(prefix, str) (0 == strncmp (str, prefix, sizeof (prefix) - 1)) 17 | 18 | int my_stat (const char* restrict path, struct stat* restrict buf) 19 | { 20 | if (STARTS_WITH ("/System/Library/Frameworks/", path)) 21 | { 22 | // Make believe that system framework files exists to work-around GHC bug 23 | #if VERBOSE 24 | printf ("macos11ghcwa pretending that file exists: %s\n", path); 25 | #endif 26 | return 0; 27 | } 28 | return stat (path, buf); 29 | } 30 | 31 | DYLD_INTERPOSE (my_stat, stat) 32 | 33 | // Intercept ghc and runghc scripts. 34 | // macOS removes the DYLD injection for scripts, 35 | // so we emulate their behaviour ourselves instead of executing them. 36 | 37 | // From https://stackoverflow.com/a/37188697/40916 38 | static int string_ends_with (const char* str, const char* suffix) 39 | { 40 | const int str_len = strlen (str), suffix_len = strlen (suffix); 41 | return str_len >= suffix_len && 0 == strcmp (str + (str_len - suffix_len), suffix); 42 | } 43 | 44 | static char* append (const char* x, const char* y) 45 | { 46 | const int len_x = strlen (x), len_y = strlen (y); 47 | char* result = malloc (len_x + len_y + 1); 48 | memcpy (result, x, len_x); 49 | memcpy (result + len_x, y, len_y); 50 | result[len_x + len_y] = '\0'; 51 | return result; 52 | } 53 | 54 | static char* remove_suffix (const char* s, const char* suffix) 55 | { 56 | if (! string_ends_with (s, suffix)) 57 | return NULL; 58 | char* prefix = strdup (s); 59 | prefix[strlen (s) - strlen (suffix) + 1] = '\0'; 60 | return prefix; 61 | } 62 | 63 | static int num_elems (char* const elems[]) 64 | { 65 | int result = 0; 66 | for (; *elems; ++elems) 67 | ++result; 68 | return result; 69 | } 70 | 71 | static char** fix_env (const char* folder, const char* exeprog, const char* topdir, char* const envp[]) 72 | { 73 | const int env_size = num_elems (envp); 74 | char** new_env = malloc ((env_size + 7) * sizeof (char* const)); 75 | memcpy (new_env, envp, env_size * sizeof (char*)); 76 | const char* exedir = append (topdir, "/bin"); 77 | new_env[env_size] = append ("exedir=", exedir); 78 | new_env[env_size + 1] = append ("exeprog=", exeprog); 79 | new_env[env_size + 2] = append ("datadir=", append (folder, "share")); 80 | new_env[env_size + 3] = append ("bindir=", append (folder, "bin")); 81 | new_env[env_size + 4] = append ("topdir=", topdir); 82 | new_env[env_size + 5] = append ("executablename=", append (exedir, "/ghc")); 83 | new_env[env_size + 6] = NULL; 84 | return new_env; 85 | } 86 | 87 | static char** fix_ghc_argv (const char* topdir, char* const argv[]) 88 | { 89 | const int argc = num_elems (argv); 90 | char** new_argv = malloc ((argc + 2) * sizeof (char* const)); 91 | new_argv[0] = argv[0]; 92 | new_argv[1] = append ("-B", topdir); 93 | memcpy (new_argv + 2, argv + 1, argc * sizeof (char*)); 94 | return new_argv; 95 | } 96 | 97 | static char* get_ghc_ver (const char* folder) 98 | { 99 | const int len = strlen (folder); 100 | int i = len - 2; 101 | while (i > 1 && folder[i - 1] != '/') 102 | --i; 103 | char* result = strdup (folder + i); 104 | result[len - i - 1] = '\0'; 105 | return result; 106 | } 107 | 108 | static int exec_ghc (const char* folder, char* const argv[], char* const envp[]) 109 | { 110 | const char* ghc_ver = get_ghc_ver (folder); 111 | const char* executable = append (folder, append ("lib/", append (ghc_ver, "/bin/ghc"))); 112 | const char* topdir = append (folder, append ("lib/", ghc_ver)); 113 | #if VERBOSE 114 | printf ("macos11ghcwa calling ghc at %s\n", executable); 115 | #endif 116 | return execve (executable, fix_ghc_argv (topdir, argv), fix_env (folder, "ghc-stage2", topdir, envp)); 117 | } 118 | 119 | static char** fix_runghc_argv (const char* folder, char* const argv[]) 120 | { 121 | const int argc = num_elems (argv); 122 | char** new_argv = malloc ((argc + 3) * sizeof (char* const)); 123 | new_argv[0] = argv[0]; 124 | new_argv[1] = "-f"; 125 | new_argv[2] = append (folder, "bin/ghc"); 126 | memcpy (new_argv + 3, argv + 1, argc * sizeof (char*)); 127 | return new_argv; 128 | } 129 | 130 | static int exec_runghc (const char* folder, char* const argv[], char* const envp[]) 131 | { 132 | const char* ghc_ver = get_ghc_ver (folder); 133 | const char* executable = append (folder, append ("lib/", append (ghc_ver, "/bin/runghc"))); 134 | #if VERBOSE 135 | printf ("macos11ghcwa calling runghc at %s\n", executable); 136 | #endif 137 | return execve ( 138 | executable, fix_runghc_argv (folder, argv), 139 | fix_env (folder, "runghc", append (folder, append ("lib/", ghc_ver)), envp)); 140 | } 141 | 142 | int my_execve (const char* file, char* const argv[], char* const envp[]) 143 | { 144 | const char* folder = remove_suffix (file, "/bin/ghc"); 145 | if (folder != NULL) 146 | return exec_ghc (folder, argv, envp); 147 | folder = remove_suffix (file, "/bin/runghc"); 148 | if (folder != NULL) 149 | return exec_runghc (folder, argv, envp); 150 | return execve (file, argv, envp); 151 | } 152 | 153 | DYLD_INTERPOSE (my_execve, execve) 154 | --------------------------------------------------------------------------------