From 7a4ab56ecb49e05612cbaaa37ffe22cc0fe71ac2 Mon Sep 17 00:00:00 2001 From: termux-pacman-bot Date: Tue, 8 Apr 2025 16:42:00 +0000 Subject: [PATCH] bump(main/ghc): 9.12.2 Patched `rts` heap reservation logic on Android: * Need for patch - There seems to be a bug on Android. Sometimes simple commands like `ghc --help` usage 90% - 100% cpu and hangs. - It doesn't happen every time, but ~25% of the times (at least in my testing). * Cause of the bug - The function `osReserveHeapMemory` tries to allocate virtual space starting from `0x4200000000`. The allocated space has to start at an address >= this address. - If the kernel doesn't allocate inside this space, it keeps on repeating the `mmap` call with increasing starting point. - Now, on Android the kernel sometimes return an address above (as in counting) this `hint` address. It repeatedly returns the same address for subsequent calls. - Thus, an infinite loop occurs. References: - https://gitlab.haskell.org/ghc/ghc/blob/383be28ffdddf65b57b7b111bfc89808b4229ebc/rts/posix/OSMem.c#L461 - https://github.com/termux/termux-packages/pull/22991#issuecomment-2759137291 * Solution (proposed by Robert Kirkman): - It introduces a new helper function `osTryReserveHeapMemoryRecursive`. This transforms the heap reservation logic into a recursive one. - `osTryReserveHeapMemory()` is run multiple times without unmapping the undesired addresses. Thus, forcing the kernel to map subsequent calls of `mmap` to a new, unique address until an address above the `0x4200000000` mark is obtained. - After which each recursive call unmaps its undesired address before returning the desired address (in order from last mapped to first mapped). References: - https://gitlab.haskell.org/ghc/ghc/-/merge_requests/14164 - https://github.com/termux/termux-packages/pull/22991#issuecomment-2761325484 Co-authored-by: Robert Kirkman Signed-off-by: Aditya Alok --- packages/ghc/always-use-pic.patch | 11 ++ packages/ghc/build.sh | 112 +++++++++++++++ packages/ghc/cabal-set-default-prefix.patch | 11 ++ packages/ghc/disable-no-pie.patch | 18 +++ packages/ghc/elf-types.patch | 14 ++ packages/ghc/fix-arch-check.patch | 11 ++ packages/ghc/hadrian-bindist-config.patch | 13 ++ .../hadrian-bindist-use-host-ghc-pkg.patch | 22 +++ packages/ghc/hadrian-disable-speed-hack.patch | 13 ++ .../ghc/hadrian-do-not-add-cross-prefix.patch | 11 ++ .../hadrian-enable-disabled-packages.patch | 18 +++ .../ghc/hadrian-fix-packages-settings.patch | 46 ++++++ packages/ghc/hadrian-fix-program-rule.patch | 12 ++ packages/ghc/rts-heap-reservation.patch | 132 ++++++++++++++++++ 14 files changed, 444 insertions(+) create mode 100644 packages/ghc/always-use-pic.patch create mode 100644 packages/ghc/build.sh create mode 100644 packages/ghc/cabal-set-default-prefix.patch create mode 100644 packages/ghc/disable-no-pie.patch create mode 100644 packages/ghc/elf-types.patch create mode 100644 packages/ghc/fix-arch-check.patch create mode 100644 packages/ghc/hadrian-bindist-config.patch create mode 100644 packages/ghc/hadrian-bindist-use-host-ghc-pkg.patch create mode 100644 packages/ghc/hadrian-disable-speed-hack.patch create mode 100644 packages/ghc/hadrian-do-not-add-cross-prefix.patch create mode 100644 packages/ghc/hadrian-enable-disabled-packages.patch create mode 100644 packages/ghc/hadrian-fix-packages-settings.patch create mode 100644 packages/ghc/hadrian-fix-program-rule.patch create mode 100644 packages/ghc/rts-heap-reservation.patch diff --git a/packages/ghc/always-use-pic.patch b/packages/ghc/always-use-pic.patch new file mode 100644 index 0000000000..e1270f5bbc --- /dev/null +++ b/packages/ghc/always-use-pic.patch @@ -0,0 +1,11 @@ +--- ghc-9.12.1/compiler/GHC/Driver/DynFlags.hs 2024-12-16 00:59:45.000000000 +0530 ++++ ghc-9.12.1.mod/compiler/GHC/Driver/DynFlags.hs 2025-01-04 19:52:30.600705081 +0530 +@@ -1311,6 +1311,8 @@ + (OSDarwin, ArchAArch64) -> [Opt_PIC] + (OSLinux, ArchAArch64) -> [Opt_PIC, Opt_ExternalDynamicRefs] + (OSLinux, ArchARM {}) -> [Opt_PIC, Opt_ExternalDynamicRefs] ++ (OSLinux, ArchX86_64) -> [Opt_PIC, Opt_ExternalDynamicRefs] ++ (OSLinux, ArchX86) -> [Opt_PIC, Opt_ExternalDynamicRefs] + (OSLinux, ArchRISCV64 {}) -> [Opt_PIC, Opt_ExternalDynamicRefs] + (OSOpenBSD, ArchX86_64) -> [Opt_PIC] -- Due to PIE support in + -- OpenBSD since 5.3 release diff --git a/packages/ghc/build.sh b/packages/ghc/build.sh new file mode 100644 index 0000000000..19ac0ad71e --- /dev/null +++ b/packages/ghc/build.sh @@ -0,0 +1,112 @@ +TERMUX_PKG_HOMEPAGE=https://www.haskell.org/ghc/ +TERMUX_PKG_DESCRIPTION="The Glasgow Haskell Compiler" +TERMUX_PKG_LICENSE="custom" +TERMUX_PKG_MAINTAINER="Aditya Alok " +TERMUX_PKG_VERSION=9.12.2 +TERMUX_PKG_SRCURL="https://downloads.haskell.org/~ghc/$TERMUX_PKG_VERSION/ghc-$TERMUX_PKG_VERSION-src.tar.xz" +TERMUX_PKG_SHA256=0e49cd5dde43f348c5716e5de9a5d7a0f8d68d945dc41cf75dfdefe65084f933 +TERMUX_PKG_DEPENDS="libiconv, libffi, libgmp, libandroid-posix-semaphore, ncurses" +TERMUX_PKG_BUILD_IN_SRC=true +TERMUX_PKG_EXTRA_CONFIGURE_ARGS=" +--host=$TERMUX_BUILD_TUPLE +--with-system-libffi +--disable-ld-override" +TERMUX_PKG_NO_STATICSPLIT=true +TERMUX_PKG_LICENSE_FILE="LICENSE" +TERMUX_PKG_REPLACES="ghc-libs-static, ghc-libs" +TERMUX_PKG_PROVIDES="ghc-libs, ghc-libs-static" + +__setup_bootstrap_compiler() { + local version=9.10.1 + local temp_folder="$TERMUX_PKG_CACHEDIR/ghc-bootstrap-$version" + local tarball="$temp_folder.tar.xz" + local runtime_folder="$temp_folder-runtime" + + export PATH="$runtime_folder/bin:$PATH" + + [[ -d "$runtime_folder" ]] && return + + termux_download "https://downloads.haskell.org/~ghc/$version/ghc-$version-x86_64-ubuntu20_04-linux.tar.xz" \ + "$tarball" \ + ae3be406fdb73bd2b0c22baada77a8ff2f8cde6220dd591dc24541cfe9d895eb + + mkdir -p "$temp_folder" "$runtime_folder" + tar xf "$tarball" --strip-components=1 -C "$temp_folder" + ( + set -e + unset CC CXX CFLAGS CXXFLAGS CPPFLAGS LDFLAGS AR AS CPP LD RANLIB READELF STRIP + cd "$temp_folder" + ./configure --prefix="$runtime_folder" + make install + ) &>/dev/null + + rm -Rf "$temp_folder" "$tarball" +} + +termux_step_pre_configure() { + __setup_bootstrap_compiler && termux_setup_cabal + + export CONF_CC_OPTS_STAGE2="$CFLAGS $CPPFLAGS" + export CONF_GCC_LINKER_OPTS_STAGE2="$LDFLAGS" + export CONF_CXX_OPTS_STAGE2="$CXXFLAGS" + + export target="$TERMUX_HOST_PLATFORM" + export no_profiled_libs="" + + if [[ "$TERMUX_ARCH" == "arm" ]]; then + target="armv7a-linux-androideabi" + # NOTE: We do not build profiled libs for arm. It exceeds the 6 hours usage + # limit of github CI. + no_profiled_libs="+no_profiled_libs" + fi + + TERMUX_PKG_EXTRA_CONFIGURE_ARGS="$TERMUX_PKG_EXTRA_CONFIGURE_ARGS --target=$target" + ./boot.source +} + +termux_step_make() { + ( + unset CFLAGS LDFLAGS CPPFLAGS # For hadrian compilation + + ./hadrian/build binary-dist-dir \ + -j"$TERMUX_PKG_MAKE_PROCESSES" \ + --flavour="release$no_profiled_libs" --docs=none \ + "stage1.unix.ghc.link.opts += -optl-landroid-posix-semaphore" + ) +} + +termux_step_make_install() { + cd _build/bindist/ghc-"$TERMUX_PKG_VERSION"-"$target" || exit 1 + + # Remove unnecessary flags. They would get written to settings file otherwise: + unset CONF_CC_OPTS_STAGE2 CONF_GCC_LINKER_OPTS_STAGE2 CONF_CXX_OPTS_STAGE2 + + # We need to re-run configure: + # See: https://gitlab.haskell.org/ghc/ghc/-/issues/22058 + ./configure \ + --prefix="$TERMUX_PREFIX" \ + --host="$target" + + HOST_GHC_PKG="$(realpath ../../stage0/bin/ghc-pkg)" make install +} + +termux_step_post_massage() { + local ghclibs_dir="lib/ghc-$TERMUX_PKG_VERSION" + + if ! [[ -d "$ghclibs_dir" ]]; then + echo "ERROR: GHC lib directory is not at expected place. Please verify before continuing." + exit 1 + fi + + # Remove version suffix from `llc` and `opt`: + sed -i -E 's|("LLVM llc command",) "llc.*"|\1 "llc"|' "$ghclibs_dir"/lib/settings + sed -i -E 's|("LLVM opt command",) "opt.*"|\1 "opt"|' "$ghclibs_dir"/lib/settings + + # Remove cross-prefix from tools: + sed -i "s|$CC|${CC/${target}-/}|g" "$ghclibs_dir"/lib/settings + sed -i "s|$CXX|${CXX/${target}-/}|g" "$ghclibs_dir"/lib/settings + + # Strip unneeded symbols: + find . -type f \( -name "*.so" -o -name "*.a" \) -exec "$STRIP" --strip-unneeded {} \; + find "$ghclibs_dir"/bin -type f -exec "$STRIP" {} \; +} diff --git a/packages/ghc/cabal-set-default-prefix.patch b/packages/ghc/cabal-set-default-prefix.patch new file mode 100644 index 0000000000..ba34c6c02e --- /dev/null +++ b/packages/ghc/cabal-set-default-prefix.patch @@ -0,0 +1,11 @@ +--- ghc-9.12.1/libraries/Cabal/Cabal/src/Distribution/Simple/InstallDirs.hs 2024-12-16 01:00:10.000000000 +0530 ++++ ghc-9.12.1.mod/libraries/Cabal/Cabal/src/Distribution/Simple/InstallDirs.hs 2025-01-04 20:05:06.846734461 +0530 +@@ -205,7 +205,7 @@ + windowsProgramFilesDir <- getWindowsProgramFilesDir + return (windowsProgramFilesDir "Haskell") + Haiku -> return "/boot/system/non-packaged" +- _ -> return "/usr/local" ++ _ -> return "@TERMUX_PREFIX@" + installLibDir <- + case buildOS of + Windows -> return "$prefix" diff --git a/packages/ghc/disable-no-pie.patch b/packages/ghc/disable-no-pie.patch new file mode 100644 index 0000000000..870576bc6f --- /dev/null +++ b/packages/ghc/disable-no-pie.patch @@ -0,0 +1,18 @@ +--- ghc-9.12.1/m4/fp_gcc_supports_no_pie.m4 2025-02-24 22:46:43.263794157 +0530 ++++ ghc-9.12.1.mod/m4/fp_gcc_supports_no_pie.m4 2025-03-25 14:39:14.331731419 +0530 +@@ -6,15 +6,6 @@ + [ + AC_REQUIRE([AC_PROG_CC]) + AC_MSG_CHECKING([whether CC supports -no-pie]) +- echo 'int main() { return 0; }' > conftest.c +- "$CC" $CONF_GCC_CC_OPTS_STAGE2 -c conftest.c +- # Some GCC versions only warn when passed an unrecognized flag. +- if "$CC" $CONF_GCC_LINKER_OPTS_STAGE2 -no-pie -Werror conftest.o -o conftest > conftest.txt 2>&1 && ! grep -i unrecognized conftest.txt > /dev/null 2>&1; then +- CONF_GCC_SUPPORTS_NO_PIE=YES +- AC_MSG_RESULT([yes]) +- else + CONF_GCC_SUPPORTS_NO_PIE=NO + AC_MSG_RESULT([no]) +- fi +- rm -f conftest.c conftest.o conftest + ]) diff --git a/packages/ghc/elf-types.patch b/packages/ghc/elf-types.patch new file mode 100644 index 0000000000..b8308aaa8d --- /dev/null +++ b/packages/ghc/elf-types.patch @@ -0,0 +1,14 @@ +--- ghc-9.12.1/rts/linker/ElfTypes.h 2024-12-16 00:59:46.000000000 +0530 ++++ ghc-9.12.1.mod/rts/linker/ElfTypes.h 2025-01-04 21:31:55.550538431 +0530 +@@ -8,6 +8,11 @@ + #include + #include "linker/InitFini.h" + ++#define ELF32_ST_VISIBILITY(o) ((o) & 0x03) ++ ++/* For ELF64 the definitions are the same. */ ++#define ELF64_ST_VISIBILITY(o) ELF32_ST_VISIBILITY(o) ++ + /* + * Define a set of types which can be used for both ELF32 and ELF64 + */ diff --git a/packages/ghc/fix-arch-check.patch b/packages/ghc/fix-arch-check.patch new file mode 100644 index 0000000000..f187348935 --- /dev/null +++ b/packages/ghc/fix-arch-check.patch @@ -0,0 +1,11 @@ +--- ghc-9.12.1/m4/fptools_set_haskell_platform_vars.m4 2024-12-16 00:59:46.000000000 +0530 ++++ ghc-9.12.1.mod/m4/fptools_set_haskell_platform_vars.m4 2025-01-24 18:18:00.513871875 +0530 +@@ -21,7 +21,7 @@ + s390x) + test -z "[$]2" || eval "[$]2=ArchS390X" + ;; +- arm) ++ arm*) + GET_ARM_ISA() + test -z "[$]2" || eval "[$]2=\"ArchARM \$ARM_ISA \$ARM_ISA_EXT \$ARM_ABI\"" + ;; diff --git a/packages/ghc/hadrian-bindist-config.patch b/packages/ghc/hadrian-bindist-config.patch new file mode 100644 index 0000000000..ef64754f54 --- /dev/null +++ b/packages/ghc/hadrian-bindist-config.patch @@ -0,0 +1,13 @@ +--- ghc-9.12.1/hadrian/bindist/config.mk.in 2025-02-24 22:46:43.665077469 +0530 ++++ ghc-9.12.1.mod/hadrian/bindist/config.mk.in 2025-02-26 02:09:44.451291933 +0530 +@@ -130,8 +130,8 @@ + #----------------------------------------------------------------------------- + # Build configuration + +-CrossCompiling = @CrossCompiling@ +-CrossCompilePrefix = @CrossCompilePrefix@ ++CrossCompiling = NO ++CrossCompilePrefix = + GhcUnregisterised = @Unregisterised@ + EnableDistroToolchain = @SettingsUseDistroMINGW@ + diff --git a/packages/ghc/hadrian-bindist-use-host-ghc-pkg.patch b/packages/ghc/hadrian-bindist-use-host-ghc-pkg.patch new file mode 100644 index 0000000000..96fef177a4 --- /dev/null +++ b/packages/ghc/hadrian-bindist-use-host-ghc-pkg.patch @@ -0,0 +1,22 @@ +--- ghc-9.12.1/hadrian/src/Rules/BinaryDist.hs 2025-02-24 22:46:43.666788988 +0530 ++++ ghc-9.12.1.mod/hadrian/src/Rules/BinaryDist.hs 2025-02-25 16:56:59.718827035 +0530 +@@ -231,6 +231,6 @@ + -- + -- N.B. the ghc-pkg executable may be prefixed with a target triple + -- (c.f. #20267). +- ghcPkgName <- programName (vanillaContext Stage1 ghcPkg) +- cmd_ (bindistFilesDir -/- "bin" -/- ghcPkgName) ["recache"] ++ stage0ghcPkgPath <- programPath =<< programContext (Stage0 InTreeLibs) ghcPkg ++ cmd_ stage0ghcPkgPath ["recache"] + +--- ghc-9.12.1/hadrian/bindist/Makefile 2025-02-24 22:46:43.665077469 +0530 ++++ ghc-9.12.1.mod/hadrian/bindist/Makefile 2025-02-24 22:46:44.140454443 +0530 +@@ -253,7 +253,7 @@ + @echo "Updating the package DB" + $(foreach p, $(PKG_CONFS),\ + $(call patchpackageconf,$(shell echo $(notdir $p) | sed 's/-[0-9.]*-[0-9a-zA-Z]*\.conf//g'),$(shell echo "$p" | sed 's:\0xxx\0: :g'),$(docdir),$(shell mk/relpath.sh "$(ActualLibsDir)" "$(docdir)"),$(shell echo $(notdir $p) | sed 's/.conf//g'))) +- '$(DESTDIR)$(ActualBinsDir)/$(CrossCompilePrefix)ghc-pkg' --global-package-db "$(DESTDIR)$(ActualLibsDir)/package.conf.d" recache ++ '$(HOST_GHC_PKG)' --global-package-db "$(DESTDIR)$(ActualLibsDir)/package.conf.d" recache + + .PHONY: install_mingw + install_mingw: diff --git a/packages/ghc/hadrian-disable-speed-hack.patch b/packages/ghc/hadrian-disable-speed-hack.patch new file mode 100644 index 0000000000..9da1d08546 --- /dev/null +++ b/packages/ghc/hadrian-disable-speed-hack.patch @@ -0,0 +1,13 @@ +# This is the culprit that removes `-fPIC` flag for i686 build. + +--- ghc-9.12.1/hadrian/src/Settings/Packages.hs 2025-02-24 22:46:43.668323779 +0530 ++++ ghc-9.12.1.mod/hadrian/src/Settings/Packages.hs 2025-02-25 23:06:51.153991428 +0530 +@@ -510,7 +510,7 @@ + speedHack :: Action Bool + speedHack = do + i386 <- anyTargetArch [ArchX86] +- goodOS <- not <$> anyTargetOs [OSSolaris2] ++ goodOS <- not <$> anyTargetOs [OSSolaris2, OSLinux] + return $ i386 && goodOS + + -- See @rts/ghc.mk@. diff --git a/packages/ghc/hadrian-do-not-add-cross-prefix.patch b/packages/ghc/hadrian-do-not-add-cross-prefix.patch new file mode 100644 index 0000000000..1391de1d04 --- /dev/null +++ b/packages/ghc/hadrian-do-not-add-cross-prefix.patch @@ -0,0 +1,11 @@ +--- ghc-9.12.1/hadrian/src/Packages.hs 2025-02-24 22:46:43.665442228 +0530 ++++ ghc-9.12.1.mod/hadrian/src/Packages.hs 2025-02-26 14:41:51.865132206 +0530 +@@ -171,7 +171,7 @@ + crossPrefix = do + cross <- flag CrossCompiling + targetPlatform <- setting TargetPlatformFull +- return $ if cross then targetPlatform ++ "-" else "" ++ return "" + + -- | Given a 'Context', compute the name of the program that is built in it + -- assuming that the corresponding package's type is 'Program'. For example, GHC diff --git a/packages/ghc/hadrian-enable-disabled-packages.patch b/packages/ghc/hadrian-enable-disabled-packages.patch new file mode 100644 index 0000000000..217faf49c2 --- /dev/null +++ b/packages/ghc/hadrian-enable-disabled-packages.patch @@ -0,0 +1,18 @@ +--- ghc-9.12.2/hadrian/src/Settings/Default.hs 2025-03-29 15:32:47.000000000 +0530 ++++ ghc-9.12.2.mod/hadrian/src/Settings/Default.hs 2025-04-07 15:04:48.724844187 +0530 +@@ -172,13 +172,11 @@ + , unlit + , xhtml + , if winTarget then win32 else unix +- ] +- , when (not cross) +- [ haddock ++ , haddock + , hpcBin + , iserv + , runGhc +- , ghcToolchainBin ++ , terminfo + ] + , when (winTarget && not cross) + [ -- See Note [Hadrian's ghci-wrapper package] diff --git a/packages/ghc/hadrian-fix-packages-settings.patch b/packages/ghc/hadrian-fix-packages-settings.patch new file mode 100644 index 0000000000..e3e3522916 --- /dev/null +++ b/packages/ghc/hadrian-fix-packages-settings.patch @@ -0,0 +1,46 @@ +--- ghc-9.12.2/hadrian/src/Settings/Packages.hs 2025-03-29 15:32:47.000000000 +0530 ++++ ghc-9.12.2.mod/hadrian/src/Settings/Packages.hs 2025-04-07 15:06:41.398290889 +0530 +@@ -85,8 +85,8 @@ + -- (#14335) and completely untested in CI for cross + -- backends at the moment, so we might as well disable it + -- for cross GHC. +- [ andM [expr ghcWithInterpreter, notStage0, notCross] `cabalFlag` "internal-interpreter" +- , notM cross `cabalFlag` "terminfo" ++ [ andM [expr ghcWithInterpreter, notStage0] `cabalFlag` "internal-interpreter" ++ , notStage0 `cabalFlag` "terminfo" + , arg "-build-tool-depends" + , flag UseLibzstd `cabalFlag` "with-libzstd" + -- ROMES: While the boot compiler is not updated wrt -this-unit-id +@@ -121,7 +121,7 @@ + + -------------------------------- ghcPkg -------------------------------- + , package ghcPkg ? +- builder (Cabal Flags) ? notM cross `cabalFlag` "terminfo" ++ builder (Cabal Flags) ? notStage0 `cabalFlag` "terminfo" + + -------------------------------- ghcPrim ------------------------------- + , package ghcPrim ? mconcat +@@ -213,7 +213,7 @@ + -- dependencies. + -- TODO: Perhaps the user should rather be responsible for this? + , package haskeline ? +- builder (Cabal Flags) ? notM cross `cabalFlag` "terminfo" ++ builder (Cabal Flags) ? notStage0 `cabalFlag` "terminfo" + + -------------------------------- terminfo ------------------------------ + , package terminfo ? +@@ -374,10 +374,10 @@ + + , input "**/RtsUtils.c" ? pure + [ "-DProjectVersion=" ++ show projectVersion +- , "-DHostPlatform=" ++ show hostPlatform +- , "-DHostArch=" ++ show hostArch +- , "-DHostOS=" ++ show hostOs +- , "-DHostVendor=" ++ show hostVendor ++ , "-DHostPlatform=" ++ show targetPlatform ++ , "-DHostArch=" ++ show targetArch ++ , "-DHostOS=" ++ show targetOs ++ , "-DHostVendor=" ++ show targetVendor + , "-DBuildPlatform=" ++ show buildPlatform + , "-DBuildArch=" ++ show buildArch + , "-DBuildOS=" ++ show buildOs diff --git a/packages/ghc/hadrian-fix-program-rule.patch b/packages/ghc/hadrian-fix-program-rule.patch new file mode 100644 index 0000000000..23b3a164f6 --- /dev/null +++ b/packages/ghc/hadrian-fix-program-rule.patch @@ -0,0 +1,12 @@ +--- ghc-9.12.2/hadrian/src/Rules/Program.hs 2025-03-29 15:32:47.000000000 +0530 ++++ ghc-9.12.2.mod/hadrian/src/Rules/Program.hs 2025-04-07 23:55:57.642949426 +0530 +@@ -102,9 +102,6 @@ + cross <- flag CrossCompiling + -- For cross compiler, copy @stage0/bin/@ to @stage1/bin/@. + case (cross, stage) of +- (True, s) | s > stage0InTree -> do +- srcDir <- buildRoot <&> (-/- (stageString stage0InTree -/- "bin")) +- copyFile (srcDir -/- takeFileName bin) bin + _ -> buildBinary rs bin ctx + + buildBinary :: [(Resource, Int)] -> FilePath -> Context -> Action () diff --git a/packages/ghc/rts-heap-reservation.patch b/packages/ghc/rts-heap-reservation.patch new file mode 100644 index 0000000000..77aa3fdc08 --- /dev/null +++ b/packages/ghc/rts-heap-reservation.patch @@ -0,0 +1,132 @@ +From fdd0c67a38ef8435b9aa46c0d3c9d4460191cdcf Mon Sep 17 00:00:00 2001 +From: Robert Kirkman +Date: Fri, 28 Mar 2025 16:53:16 -0500 +Subject: [PATCH] rts: reattempt heap reservation recursively before unmapping + addresses below the 8GB mark + +This patch works around Android's mmap() occasionally repeatedly mapping the exact +same block of memory at an address below the 8GB mark that was just +unmapped, which would cause the 'ghc --help' command to fall into an infinite loop while +osTryReserveHeapMemory() repeatedly returned the same unwanted address. This +moves the heap reservation attempt logic into a recursive function that +runs osTryReserveHeapMemory() multiple times without unmapping the +undesired addresses, to force the mapping of new, unique addresses +until an address above the 8GB mark is obtained, +after which each recursive call unmaps its undesired address before +returning the desired address, in order from last mapped to first mapped. + +First discussed here: https://github.com/termux/termux-packages/pull/22991 +--- + rts/posix/OSMem.c | 88 +++++++++++++++++++++++++---------------------- + 1 file changed, 46 insertions(+), 42 deletions(-) + +diff --git a/rts/posix/OSMem.c b/rts/posix/OSMem.c +index 94c60f441ac9..2f1638bb5123 100644 +--- a/rts/posix/OSMem.c ++++ b/rts/posix/OSMem.c +@@ -493,11 +493,53 @@ osTryReserveHeapMemory (W_ len, void *hint) + return start; + } + +-void *osReserveHeapMemory(void *startAddressPtr, W_ *len) ++static void * ++osTryReserveHeapMemoryRecursive(W_ minimumAddress, W_ startAddress, int attempt, W_ *len) + { +- int attempt; +- void *at; ++ *len &= ~MBLOCK_MASK; ++ ++ if (*len < MBLOCK_SIZE) { ++ // Give up if the system won't even give us 16 blocks worth of heap ++ barf("osReserveHeapMemory: Failed to allocate heap storage"); ++ } ++ ++ void *hint = (void*)(startAddress + attempt * BLOCK_SIZE); ++ void *at = osTryReserveHeapMemory(*len, hint); ++ attempt++; ++ ++ if (at == NULL) { ++ // This means that mmap failed which we take to mean that we asked ++ // for too much memory. This can happen due to POSIX resource ++ // limits. In this case we reduce our allocation request by a ++ // fraction of the current size and try again. ++ // ++ // Note that the previously would instead decrease the request size ++ // by a factor of two; however, this meant that significant amounts ++ // of memory will be wasted (e.g. imagine a machine with 512GB of ++ // physical memory but a 511GB ulimit). See #14492. ++ *len -= *len / 8; ++ // debugBelch("Limit hit, reduced len: %zu\n", *len); ++ return osTryReserveHeapMemoryRecursive(minimumAddress, startAddress, attempt, len); ++ } else if ((W_)at >= minimumAddress) { ++ // Success! We were given a block of memory starting above the 8 GB ++ // mark, which is what we were looking for. ++ ++ return at; ++ } else { ++ // We got addressing space but it wasn't above the 8GB mark. ++ // Try again recursively first, unmap after, because on aarch64 Android, ++ // sometimes mmap() will continuously map the same address regardless of ++ // the hint changing, if that address has already been unmapped. ++ void *next_at = osTryReserveHeapMemoryRecursive(minimumAddress, startAddress, attempt, len); ++ if (munmap(at, *len) < 0) { ++ sysErrorBelch("unable to release reserved heap"); ++ } ++ return next_at; ++ } ++} + ++void *osReserveHeapMemory(void *startAddressPtr, W_ *len) ++{ + /* We want to ensure the heap starts at least 8 GB inside the address space, + since we want to reserve the address space below that address for code. + Specifically, we need to make sure that any dynamically loaded code will +@@ -585,45 +627,7 @@ void *osReserveHeapMemory(void *startAddressPtr, W_ *len) + } + #endif + +- attempt = 0; +- while (1) { +- *len &= ~MBLOCK_MASK; +- +- if (*len < MBLOCK_SIZE) { +- // Give up if the system won't even give us 16 blocks worth of heap +- barf("osReserveHeapMemory: Failed to allocate heap storage"); +- } +- +- void *hint = (void*)(startAddress + attempt * BLOCK_SIZE); +- at = osTryReserveHeapMemory(*len, hint); +- if (at == NULL) { +- // This means that mmap failed which we take to mean that we asked +- // for too much memory. This can happen due to POSIX resource +- // limits. In this case we reduce our allocation request by a +- // fraction of the current size and try again. +- // +- // Note that the previously would instead decrease the request size +- // by a factor of two; however, this meant that significant amounts +- // of memory will be wasted (e.g. imagine a machine with 512GB of +- // physical memory but a 511GB ulimit). See #14492. +- *len -= *len / 8; +- // debugBelch("Limit hit, reduced len: %zu\n", *len); +- } else if ((W_)at >= minimumAddress) { +- // Success! We were given a block of memory starting above the 8 GB +- // mark, which is what we were looking for. +- +- break; +- } else { +- // We got addressing space but it wasn't above the 8GB mark. +- // Try again. +- if (munmap(at, *len) < 0) { +- sysErrorBelch("unable to release reserved heap"); +- } +- } +- attempt++; +- } +- +- return at; ++ return osTryReserveHeapMemoryRecursive(minimumAddress, startAddress, 0, len); + } + + void osCommitMemory(void *at, W_ size) +-- +GitLab