├── README.md ├── compile.sh ├── packages └── ghc-cross │ ├── fix-arch-check.patch │ ├── elf-types.patch │ ├── hadrian-disable-speed-hack.patch │ ├── hadrian-enable-iserv.patch │ ├── cabal-set-default-prefix.patch │ ├── always-use-pic.patch │ ├── disable-no-pie.patch │ ├── hadrian-do-not-add-cross-prefix.patch │ ├── build.sh │ ├── hadrian-bindist-enable-stage2.patch │ └── rts-heap-reservation.patch ├── utils.sh ├── run-docker.sh ├── free-space.sh └── .github └── workflows └── build.yml /README.md: -------------------------------------------------------------------------------- 1 | # About 2 | 3 | Glasgow Haskell Cross-Compiler (ghc) targeting Android. 4 | 5 | Build arch: x86_64 6 | 7 | ## Target archs: 8 | 9 | - aarch64 10 | - arm32 11 | - x86_64 12 | - i686 13 | -------------------------------------------------------------------------------- /compile.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/bash 2 | 3 | set -e -u 4 | 5 | export TAR_OUTPUT_DIR="$(realpath "$1")" 6 | ARCH="$2" 7 | 8 | source ./utils.sh 9 | 10 | mkdir -p "$TAR_OUTPUT_DIR" 11 | 12 | clone_termux_packages 13 | cp -r ./packages/ghc-cross termux-packages/packages/ 14 | 15 | cd termux-packages 16 | ./build-package.sh -I -a "$ARCH" ghc-cross 17 | -------------------------------------------------------------------------------- /packages/ghc-cross/fix-arch-check.patch: -------------------------------------------------------------------------------- 1 | --- ghc-9.12.1/m4/fptools_set_haskell_platform_vars.m4 2024-12-16 00:59:46.000000000 +0530 2 | +++ ghc-9.12.1.mod/m4/fptools_set_haskell_platform_vars.m4 2025-01-24 18:18:00.513871875 +0530 3 | @@ -21,7 +21,7 @@ 4 | s390x) 5 | test -z "[$]2" || eval "[$]2=ArchS390X" 6 | ;; 7 | - arm) 8 | + arm*) 9 | GET_ARM_ISA() 10 | test -z "[$]2" || eval "[$]2=\"ArchARM \$ARM_ISA \$ARM_ISA_EXT \$ARM_ABI\"" 11 | ;; 12 | -------------------------------------------------------------------------------- /packages/ghc-cross/elf-types.patch: -------------------------------------------------------------------------------- 1 | --- ghc-9.12.1/rts/linker/ElfTypes.h 2024-12-16 00:59:46.000000000 +0530 2 | +++ ghc-9.12.1.mod/rts/linker/ElfTypes.h 2025-01-04 21:31:55.550538431 +0530 3 | @@ -8,6 +8,11 @@ 4 | #include 5 | #include "linker/InitFini.h" 6 | 7 | +#define ELF32_ST_VISIBILITY(o) ((o) & 0x03) 8 | + 9 | +/* For ELF64 the definitions are the same. */ 10 | +#define ELF64_ST_VISIBILITY(o) ELF32_ST_VISIBILITY(o) 11 | + 12 | /* 13 | * Define a set of types which can be used for both ELF32 and ELF64 14 | */ 15 | -------------------------------------------------------------------------------- /packages/ghc-cross/hadrian-disable-speed-hack.patch: -------------------------------------------------------------------------------- 1 | # This is the culprit that removes `-fPIC` flag for i686 build. 2 | 3 | --- ghc-9.12.1/hadrian/src/Settings/Packages.hs 2025-02-24 22:46:43.668323779 +0530 4 | +++ ghc-9.12.1.mod/hadrian/src/Settings/Packages.hs 2025-02-25 23:06:51.153991428 +0530 5 | @@ -510,7 +510,7 @@ 6 | speedHack :: Action Bool 7 | speedHack = do 8 | i386 <- anyTargetArch [ArchX86] 9 | - goodOS <- not <$> anyTargetOs [OSSolaris2] 10 | + goodOS <- not <$> anyTargetOs [OSSolaris2, OSLinux] 11 | return $ i386 && goodOS 12 | 13 | -- See @rts/ghc.mk@. 14 | -------------------------------------------------------------------------------- /packages/ghc-cross/hadrian-enable-iserv.patch: -------------------------------------------------------------------------------- 1 | # This Stage2 iserv will run under qemu emulated proot. 2 | 3 | --- ghc-9.12.1/hadrian/src/Settings/Default.hs 2025-02-24 22:46:43.668323779 +0530 4 | +++ ghc-9.12.1.mod/hadrian/src/Settings/Default.hs 2025-03-19 22:06:35.337966131 +0530 5 | @@ -188,7 +188,7 @@ 6 | 7 | -- | Packages built in 'Stage2' by default. You can change this in "UserSettings". 8 | stage2Packages :: Action [Package] 9 | -stage2Packages = stage1Packages 10 | +stage2Packages = (<> [iserv]) <$> stage1Packages 11 | 12 | -- | Packages that are built only for the testsuite. 13 | testsuitePackages :: Action [Package] 14 | -------------------------------------------------------------------------------- /packages/ghc-cross/cabal-set-default-prefix.patch: -------------------------------------------------------------------------------- 1 | --- ghc-9.12.1/libraries/Cabal/Cabal/src/Distribution/Simple/InstallDirs.hs 2024-12-16 01:00:10.000000000 +0530 2 | +++ ghc-9.12.1.mod/libraries/Cabal/Cabal/src/Distribution/Simple/InstallDirs.hs 2025-01-04 20:05:06.846734461 +0530 3 | @@ -205,7 +205,7 @@ 4 | windowsProgramFilesDir <- getWindowsProgramFilesDir 5 | return (windowsProgramFilesDir "Haskell") 6 | Haiku -> return "/boot/system/non-packaged" 7 | - _ -> return "/usr/local" 8 | + _ -> return "@TERMUX_PREFIX@" 9 | installLibDir <- 10 | case buildOS of 11 | Windows -> return "$prefix" 12 | -------------------------------------------------------------------------------- /packages/ghc-cross/always-use-pic.patch: -------------------------------------------------------------------------------- 1 | --- ghc-9.12.1/compiler/GHC/Driver/DynFlags.hs 2024-12-16 00:59:45.000000000 +0530 2 | +++ ghc-9.12.1.mod/compiler/GHC/Driver/DynFlags.hs 2025-01-04 19:52:30.600705081 +0530 3 | @@ -1311,6 +1311,8 @@ 4 | (OSDarwin, ArchAArch64) -> [Opt_PIC] 5 | (OSLinux, ArchAArch64) -> [Opt_PIC, Opt_ExternalDynamicRefs] 6 | (OSLinux, ArchARM {}) -> [Opt_PIC, Opt_ExternalDynamicRefs] 7 | + (OSLinux, ArchX86_64) -> [Opt_PIC, Opt_ExternalDynamicRefs] 8 | + (OSLinux, ArchX86) -> [Opt_PIC, Opt_ExternalDynamicRefs] 9 | (OSLinux, ArchRISCV64 {}) -> [Opt_PIC, Opt_ExternalDynamicRefs] 10 | (OSOpenBSD, ArchX86_64) -> [Opt_PIC] -- Due to PIE support in 11 | -- OpenBSD since 5.3 release 12 | -------------------------------------------------------------------------------- /packages/ghc-cross/disable-no-pie.patch: -------------------------------------------------------------------------------- 1 | --- ghc-9.12.1/m4/fp_gcc_supports_no_pie.m4 2025-02-24 22:46:43.263794157 +0530 2 | +++ ghc-9.12.1.mod/m4/fp_gcc_supports_no_pie.m4 2025-03-25 14:39:14.331731419 +0530 3 | @@ -6,15 +6,6 @@ 4 | [ 5 | AC_REQUIRE([AC_PROG_CC]) 6 | AC_MSG_CHECKING([whether CC supports -no-pie]) 7 | - echo 'int main() { return 0; }' > conftest.c 8 | - "$CC" $CONF_GCC_CC_OPTS_STAGE2 -c conftest.c 9 | - # Some GCC versions only warn when passed an unrecognized flag. 10 | - 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 11 | - CONF_GCC_SUPPORTS_NO_PIE=YES 12 | - AC_MSG_RESULT([yes]) 13 | - else 14 | CONF_GCC_SUPPORTS_NO_PIE=NO 15 | AC_MSG_RESULT([no]) 16 | - fi 17 | - rm -f conftest.c conftest.o conftest 18 | ]) 19 | -------------------------------------------------------------------------------- /utils.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | clone_termux_packages() { 4 | # clone termux-packages into container 5 | #TODO: Remove test branch after merger of https://github.com/termux/termux-packages/pull/22991 6 | git clone -b test-latest-ghc --single-branch https://github.com/termux/termux-packages.git 7 | } 8 | 9 | download() { 10 | url="$1" 11 | destination="$2" 12 | checksum="$3" 13 | 14 | curl --fail --retry 20 --retry-connrefused --retry-delay 30 --location -o "${destination}" "${url}" || { 15 | echo "Failed to download '${url}'." 16 | exit 1 17 | } 18 | 19 | if [ "${checksum}" != "SKIP" ]; then 20 | actual_checksum=$(sha256sum "${destination}" | cut -f 1 -d ' ') 21 | if [ "${checksum}" != "${actual_checksum}" ]; then 22 | printf >&2 "Wrong checksum for %s:\nExpected: %s\nActual: %s\n" \ 23 | "${url}" "${checksum}" "${actual_checksum}" 24 | return 1 25 | fi 26 | fi 27 | } 28 | -------------------------------------------------------------------------------- /packages/ghc-cross/hadrian-do-not-add-cross-prefix.patch: -------------------------------------------------------------------------------- 1 | --- ghc-9.12.1/hadrian/bindist/config.mk.in 2025-02-24 22:46:43.665077469 +0530 2 | +++ ghc-9.12.1.mod/hadrian/bindist/config.mk.in 2025-02-26 02:09:44.451291933 +0530 3 | @@ -130,8 +130,8 @@ 4 | #----------------------------------------------------------------------------- 5 | # Build configuration 6 | 7 | CrossCompiling = @CrossCompiling@ 8 | -CrossCompilePrefix = @CrossCompilePrefix@ 9 | +CrossCompilePrefix = 10 | GhcUnregisterised = @Unregisterised@ 11 | EnableDistroToolchain = @SettingsUseDistroMINGW@ 12 | 13 | --- ghc-9.12.1/hadrian/src/Packages.hs 2025-02-24 22:46:43.665442228 +0530 14 | +++ ghc-9.12.1.mod/hadrian/src/Packages.hs 2025-02-26 14:41:51.865132206 +0530 15 | @@ -171,7 +171,7 @@ 16 | crossPrefix = do 17 | cross <- flag CrossCompiling 18 | targetPlatform <- setting TargetPlatformFull 19 | - return $ if cross then targetPlatform ++ "-" else "" 20 | + return "" 21 | 22 | -- | Given a 'Context', compute the name of the program that is built in it 23 | -- assuming that the corresponding package's type is 'Program'. For example, GHC 24 | -------------------------------------------------------------------------------- /run-docker.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e -u 3 | 4 | CONTAINER_HOME_DIR=/home/builder 5 | 6 | UNAME=$(uname) 7 | REPOROOT="$(dirname $(readlink -f $0))" 8 | 9 | TERMUX_BUILDER_IMAGE_NAME="ghcr.io/termux/package-builder" 10 | CONTAINER_NAME="termux-ghc-builder" 11 | 12 | USER=builder 13 | 14 | echo "Running container '$CONTAINER_NAME' from image '$TERMUX_BUILDER_IMAGE_NAME'..." 15 | 16 | docker start $CONTAINER_NAME >/dev/null 2>&1 || { 17 | echo "Creating new container..." 18 | docker run \ 19 | --detach \ 20 | --name $CONTAINER_NAME \ 21 | --volume $REPOROOT:$CONTAINER_HOME_DIR/termux-packages \ 22 | --tty \ 23 | $TERMUX_BUILDER_IMAGE_NAME 24 | 25 | if [ $(id -u) -ne 1000 -a $(id -u) -ne 0 ]; then 26 | echo "Changed builder uid/gid... (this may take a while)" 27 | docker exec $CONTAINER_NAME sudo chown -R $(id -u) $CONTAINER_HOME_DIR 28 | docker exec $CONTAINER_NAME sudo chown -R $(id -u) /data 29 | docker exec $CONTAINER_NAME sudo usermod -u $(id -u) builder 30 | docker exec $CONTAINER_NAME sudo groupmod -g $(id -g) builder 31 | fi 32 | } 33 | 34 | docker exec --interactive $CONTAINER_NAME "$@" 35 | -------------------------------------------------------------------------------- /free-space.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # df -h 4 | 5 | sudo apt purge -yq $(dpkg -l | grep '^ii' | awk '{ print $2 }' | grep -P '(aspnetcore|cabal-|dotnet-|ghc-|libmono|mongodb-|mysql-|llvm-|liblldb-|php)') \ 6 | firefox google-chrome-stable microsoft-edge-stable mono-devel mono-runtime-common monodoc-manual ruby \ 7 | azure-cli powershell libgl1-mesa-dri shellcheck mercurial-common humanity-icon-theme google-cloud-cli 8 | 9 | # echo "Listing 100 largest packages after" 10 | # dpkg-query -Wf '${Installed-Size}\t${Package}\n' | sort -n | tail -n 100 11 | 12 | # Directories 13 | sudo rm -fr /opt/ghc /opt/hostedtoolcache /usr/share/dotnet /usr/share/swift 14 | sudo rm -rf /usr/local/graalvm/ 15 | sudo rm -rf /usr/local/.ghcup/ 16 | sudo rm -rf /usr/local/share/powershell 17 | sudo rm -rf /usr/local/share/chromium 18 | sudo rm -rf /usr/local/lib/android 19 | sudo rm -rf /usr/local/lib/node_modules 20 | 21 | # https://github.com/actions/runner-images/issues/709#issuecomment-612569242 22 | sudo rm -rf "/usr/local/share/boost" 23 | sudo rm -rf "$AGENT_TOOLSDIRECTORY" 24 | 25 | sudo docker image prune --all --force 26 | sudo docker builder prune -a 27 | 28 | sudo apt autoremove -yq 29 | sudo apt clean 30 | 31 | # df -h 32 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: build 2 | 3 | on: 4 | push: 5 | tags: 6 | - ghc-v[0-9]+.[0-9]+.[0-9]+ 7 | pull_request: 8 | paths: 9 | - "packages/**" 10 | workflow_dispatch: 11 | inputs: 12 | release_tag: 13 | description: Tag to be used for github release 14 | required: true 15 | 16 | jobs: 17 | build: 18 | runs-on: ubuntu-latest 19 | env: 20 | ANDROID_HOME: "/opt/termux/android-sdk" 21 | NDK: "/opt/termux/android-ndk" 22 | strategy: 23 | matrix: 24 | target_arch: [aarch64, arm, x86_64, i686] 25 | fail-fast: false 26 | steps: 27 | - name: Clone repository 28 | uses: actions/checkout@v4 29 | with: 30 | fetch-depth: 1000 31 | - name: Build 32 | run: | 33 | mkdir ./out-dir 34 | 35 | # Process tag '%ci:no-build' that may be added as line to commit message. 36 | # Forces CI to cancel current build with status 'passed'. 37 | if grep -qiP '^\s*%ci:no-build\s*$' <(git log --format="%B" -n 1 "HEAD"); then 38 | echo "[!] Force exiting as tag '%ci:no-build' was applied to HEAD commit message." 39 | touch ./out-dir/placeholder.zip 40 | exit 0 41 | fi 42 | 43 | ./free-space.sh 44 | ./run-docker.sh \ 45 | ./compile.sh ./out-dir ${{ matrix.target_arch }} 46 | 47 | - name: Store files 48 | uses: actions/upload-artifact@v4 49 | with: 50 | name: ghc-${{ matrix.target_arch }} 51 | path: ./out-dir/* 52 | 53 | create-release: 54 | if: github.event_name != 'pull_request' 55 | needs: 56 | - build 57 | runs-on: "ubuntu-latest" 58 | permissions: 59 | contents: write 60 | steps: 61 | # Must perform checkout first, since it deletes the target directory 62 | # before running, and would therefore delete the downloaded artifacts 63 | - uses: actions/checkout@v4 64 | 65 | - name: Get files 66 | uses: actions/download-artifact@v4 67 | 68 | - name: Create new release 69 | env: 70 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 71 | run: | 72 | prerelease="" 73 | release_tag="${GITHUB_REF#refs/*/}" 74 | if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then 75 | release_tag="${{ github.event.inputs.release_tag }}" 76 | fi 77 | if [[ "${release_tag/+test/}" != "$release_tag" ]]; then 78 | prerelease="--prerelease" 79 | fi 80 | 81 | if gh release view "$release_tag"; then 82 | echo "Updating release '$release_tag' ..." 83 | gh release upload "$release_tag" ./ghc-*/*.xz --clobber 84 | else 85 | echo "Creating release '$release_tag' ..." 86 | gh release create "$release_tag" ./ghc-*/*.xz $prerelease --notes-from-tag 87 | fi 88 | -------------------------------------------------------------------------------- /packages/ghc-cross/build.sh: -------------------------------------------------------------------------------- 1 | TERMUX_PKG_HOMEPAGE=https://www.haskell.org/ghc/ 2 | TERMUX_PKG_DESCRIPTION="The Glasgow Haskell Compiler" 3 | TERMUX_PKG_LICENSE="custom" 4 | TERMUX_PKG_MAINTAINER="Aditya Alok " 5 | TERMUX_PKG_VERSION=9.12.2 6 | TERMUX_PKG_SRCURL="https://downloads.haskell.org/~ghc/$TERMUX_PKG_VERSION/ghc-$TERMUX_PKG_VERSION-src.tar.xz" 7 | TERMUX_PKG_SHA256=0e49cd5dde43f348c5716e5de9a5d7a0f8d68d945dc41cf75dfdefe65084f933 8 | TERMUX_PKG_DEPENDS="libiconv, libffi, libgmp, libandroid-posix-semaphore" 9 | TERMUX_PKG_BUILD_IN_SRC=true 10 | TERMUX_PKG_EXTRA_CONFIGURE_ARGS=" 11 | --host=$TERMUX_BUILD_TUPLE 12 | --with-system-libffi 13 | --disable-ld-override" 14 | 15 | __setup_bootstrap_compiler() { 16 | local version=9.10.1 17 | local temp_folder="$TERMUX_PKG_CACHEDIR/ghc-bootstrap-$version" 18 | local tarball="$temp_folder.tar.xz" 19 | local runtime_folder="$temp_folder-runtime" 20 | 21 | export PATH="$runtime_folder/bin:$PATH" 22 | 23 | [[ -d "$runtime_folder" ]] && return 24 | 25 | termux_download "https://downloads.haskell.org/~ghc/$version/ghc-$version-x86_64-ubuntu20_04-linux.tar.xz" \ 26 | "$tarball" \ 27 | ae3be406fdb73bd2b0c22baada77a8ff2f8cde6220dd591dc24541cfe9d895eb 28 | 29 | mkdir -p "$temp_folder" "$runtime_folder" 30 | tar xf "$tarball" --strip-components=1 -C "$temp_folder" 31 | ( 32 | set -e 33 | unset CC CXX CFLAGS CXXFLAGS CPPFLAGS LDFLAGS AR AS CPP LD RANLIB READELF STRIP 34 | cd "$temp_folder" 35 | ./configure --prefix="$runtime_folder" 36 | make install 37 | ) >/dev/null 38 | 39 | rm -Rf "$temp_folder" "$tarball" 40 | } 41 | 42 | termux_step_pre_configure() { 43 | __setup_bootstrap_compiler && termux_setup_cabal 44 | 45 | export CONF_CC_OPTS_STAGE1="$CFLAGS $CPPFLAGS" 46 | export CONF_GCC_LINKER_OPTS_STAGE1="$LDFLAGS" 47 | export CONF_CXX_OPTS_STAGE1="$CXXFLAGS" 48 | 49 | export CONF_CC_OPTS_STAGE2="$CFLAGS $CPPFLAGS" 50 | export CONF_GCC_LINKER_OPTS_STAGE2="$LDFLAGS" 51 | export CONF_CXX_OPTS_STAGE2="$CXXFLAGS" 52 | 53 | export target="$TERMUX_HOST_PLATFORM" 54 | export profiled_libs="" 55 | 56 | if [[ "$TERMUX_ARCH" == "arm" ]]; then 57 | target="armv7a-linux-androideabi" 58 | profiled_libs="+no_profiled_libs" # NOTE: We do not build profiled libs for arm. It exceeds the 6 hours usage limit of github CI. 59 | fi 60 | 61 | TERMUX_PKG_EXTRA_CONFIGURE_ARGS="$TERMUX_PKG_EXTRA_CONFIGURE_ARGS --target=$target" 62 | ./boot.source 63 | } 64 | 65 | termux_step_make() { 66 | ( 67 | unset CFLAGS CPPFLAGS LDFLAGS # For stage0 compilation. 68 | 69 | ./hadrian/build binary-dist-dir \ 70 | -j"$TERMUX_PKG_MAKE_PROCESSES" \ 71 | --flavour="release+split_sections$profiled_libs" \ 72 | --docs=none \ 73 | "stage1.unix.ghc.link.opts += -optl-landroid-posix-semaphore" \ 74 | "stage2.unix.ghc.link.opts += -optl-landroid-posix-semaphore" 75 | ) 76 | } 77 | 78 | termux_step_make_install() { 79 | tar cJf "$TAR_OUTPUT_DIR"/ghc-"$TERMUX_PKG_VERSION"-"$target".tar.xz -C _build/bindist ghc-"$TERMUX_PKG_VERSION"-"$target" 80 | exit 81 | } 82 | -------------------------------------------------------------------------------- /packages/ghc-cross/hadrian-bindist-enable-stage2.patch: -------------------------------------------------------------------------------- 1 | # Enable stage2 build: 2 | 3 | --- ghc-9.12.1/hadrian/src/Rules/Program.hs 2024-12-16 00:59:46.000000000 +0530 4 | +++ ghc-9.12.1.mod/hadrian/src/Rules/Program.hs 2025-01-08 14:17:07.763051325 +0530 5 | @@ -102,7 +102,7 @@ 6 | cross <- flag CrossCompiling 7 | -- For cross compiler, copy @stage0/bin/@ to @stage1/bin/@. 8 | case (cross, stage) of 9 | - (True, s) | s > stage0InTree -> do 10 | + (True, s) | s > stage0InTree && s < Stage2 -> do 11 | srcDir <- buildRoot <&> (-/- (stageString stage0InTree -/- "bin")) 12 | copyFile (srcDir -/- takeFileName bin) bin 13 | _ -> buildBinary rs bin ctx 14 | 15 | --- ghc-9.12.1/hadrian/src/Rules/BinaryDist.hs 2025-02-24 22:46:43.666788988 +0530 16 | +++ ghc-9.12.1.mod/hadrian/src/Rules/BinaryDist.hs 2025-03-19 23:03:26.892308889 +0530 17 | @@ -144,9 +144,9 @@ 18 | phony "binary-dist-dir" $ do 19 | version <- setting ProjectVersion 20 | targetPlatform <- setting TargetPlatformFull 21 | - distDir <- Context.distDir (vanillaContext Stage1 rts) 22 | + distDir <- Context.distDir (vanillaContext Stage2 rts) 23 | 24 | - let ghcBuildDir = root -/- stageString Stage1 25 | + let ghcBuildDir = root -/- stageString Stage2 26 | bindistFilesDir = root -/- "bindist" -/- ghcVersionPretty 27 | ghcVersionPretty = "ghc-" ++ version ++ "-" ++ targetPlatform 28 | rtsIncludeDir = distDir -/- "include" 29 | @@ -155,7 +155,7 @@ 30 | all_pkgs <- stagePackages Stage1 31 | (lib_targets, bin_targets) <- partitionEithers <$> mapM pkgTarget all_pkgs 32 | cross <- flag CrossCompiling 33 | - iserv_targets <- if cross then pure [] else iservBins 34 | + iserv_targets <- iservBins 35 | 36 | let lib_exe_targets = (lib_targets ++ (map (\(_, p) -> p) (bin_targets ++ iserv_targets))) 37 | 38 | @@ -335,7 +335,7 @@ 39 | copyFile (ghcRoot -/- "aclocal.m4") (ghcRoot -/- "distrib" -/- "aclocal.m4") 40 | copyDirectory (ghcRoot -/- "m4") (ghcRoot -/- "distrib") 41 | buildWithCmdOptions [] $ 42 | - target (vanillaContext Stage1 ghc) (Autoreconf $ ghcRoot -/- "distrib") [] [] 43 | + target (vanillaContext Stage2 ghc) (Autoreconf $ ghcRoot -/- "distrib") [] [] 44 | -- We clean after ourselves, moving the configure script we generated in 45 | -- our bindist dir 46 | removeFile (ghcRoot -/- "distrib" -/- "aclocal.m4") 47 | @@ -368,7 +368,7 @@ 48 | generateBuildMk :: Action String 49 | generateBuildMk = do 50 | dynamicGhc <- askDynGhcPrograms 51 | - rtsWays <- unwords . map show . Set.toList <$> interpretInContext (vanillaContext Stage1 rts) getRtsWays 52 | + rtsWays <- unwords . map show . Set.toList <$> interpretInContext (vanillaContext Stage2 rts) getRtsWays 53 | return $ unlines [ "GhcRTSWays" =. rtsWays 54 | , "DYNAMIC_GHC_PROGRAMS" =. yesNo dynamicGhc ] 55 | 56 | @@ -408,7 +408,7 @@ 57 | -- database. For programs, it returns the path to the compiled executable. 58 | pkgTarget :: Package -> Action (Either FilePath (Package, FilePath)) 59 | pkgTarget pkg 60 | - | isLibrary pkg = Left <$> pkgConfFile (vanillaContext Stage1 pkg) 61 | + | isLibrary pkg = Left <$> pkgConfFile (vanillaContext Stage2 pkg) 62 | | otherwise = do 63 | path <- programPath =<< programContext Stage1 pkg 64 | return (Right (pkg, path)) 65 | @@ -487,9 +487,9 @@ 66 | -- explicitly and 'need' the result of building them. 67 | iservBins :: Action [(Package, FilePath)] 68 | iservBins = do 69 | - rtsways <- interpretInContext (vanillaContext Stage1 ghc) getRtsWays 70 | + rtsways <- interpretInContext (vanillaContext Stage2 ghc) getRtsWays 71 | traverse (fmap (\p -> (iserv, p)) . programPath) 72 | - [ Context Stage1 iserv w Final 73 | + [ Context Stage2 iserv w Final 74 | | w <- [vanilla, profiling, dynamic] 75 | , w `elem` rtsways 76 | ] 77 | 78 | -------------------------------------------------------------------------------- /packages/ghc-cross/rts-heap-reservation.patch: -------------------------------------------------------------------------------- 1 | From fdd0c67a38ef8435b9aa46c0d3c9d4460191cdcf Mon Sep 17 00:00:00 2001 2 | From: Robert Kirkman 3 | Date: Fri, 28 Mar 2025 16:53:16 -0500 4 | Subject: [PATCH] rts: reattempt heap reservation recursively before unmapping 5 | addresses below the 8GB mark 6 | 7 | This patch works around Android's mmap() occasionally repeatedly mapping the exact 8 | same block of memory at an address below the 8GB mark that was just 9 | unmapped, which would cause the 'ghc --help' command to fall into an infinite loop while 10 | osTryReserveHeapMemory() repeatedly returned the same unwanted address. This 11 | moves the heap reservation attempt logic into a recursive function that 12 | runs osTryReserveHeapMemory() multiple times without unmapping the 13 | undesired addresses, to force the mapping of new, unique addresses 14 | until an address above the 8GB mark is obtained, 15 | after which each recursive call unmaps its undesired address before 16 | returning the desired address, in order from last mapped to first mapped. 17 | 18 | First discussed here: https://github.com/termux/termux-packages/pull/22991 19 | --- 20 | rts/posix/OSMem.c | 88 +++++++++++++++++++++++++---------------------- 21 | 1 file changed, 46 insertions(+), 42 deletions(-) 22 | 23 | diff --git a/rts/posix/OSMem.c b/rts/posix/OSMem.c 24 | index 94c60f441ac9..2f1638bb5123 100644 25 | --- a/rts/posix/OSMem.c 26 | +++ b/rts/posix/OSMem.c 27 | @@ -493,11 +493,53 @@ osTryReserveHeapMemory (W_ len, void *hint) 28 | return start; 29 | } 30 | 31 | -void *osReserveHeapMemory(void *startAddressPtr, W_ *len) 32 | +static void * 33 | +osTryReserveHeapMemoryRecursive(W_ minimumAddress, W_ startAddress, int attempt, W_ *len) 34 | { 35 | - int attempt; 36 | - void *at; 37 | + *len &= ~MBLOCK_MASK; 38 | + 39 | + if (*len < MBLOCK_SIZE) { 40 | + // Give up if the system won't even give us 16 blocks worth of heap 41 | + barf("osReserveHeapMemory: Failed to allocate heap storage"); 42 | + } 43 | + 44 | + void *hint = (void*)(startAddress + attempt * BLOCK_SIZE); 45 | + void *at = osTryReserveHeapMemory(*len, hint); 46 | + attempt++; 47 | + 48 | + if (at == NULL) { 49 | + // This means that mmap failed which we take to mean that we asked 50 | + // for too much memory. This can happen due to POSIX resource 51 | + // limits. In this case we reduce our allocation request by a 52 | + // fraction of the current size and try again. 53 | + // 54 | + // Note that the previously would instead decrease the request size 55 | + // by a factor of two; however, this meant that significant amounts 56 | + // of memory will be wasted (e.g. imagine a machine with 512GB of 57 | + // physical memory but a 511GB ulimit). See #14492. 58 | + *len -= *len / 8; 59 | + // debugBelch("Limit hit, reduced len: %zu\n", *len); 60 | + return osTryReserveHeapMemoryRecursive(minimumAddress, startAddress, attempt, len); 61 | + } else if ((W_)at >= minimumAddress) { 62 | + // Success! We were given a block of memory starting above the 8 GB 63 | + // mark, which is what we were looking for. 64 | + 65 | + return at; 66 | + } else { 67 | + // We got addressing space but it wasn't above the 8GB mark. 68 | + // Try again recursively first, unmap after, because on aarch64 Android, 69 | + // sometimes mmap() will continuously map the same address regardless of 70 | + // the hint changing, if that address has already been unmapped. 71 | + void *next_at = osTryReserveHeapMemoryRecursive(minimumAddress, startAddress, attempt, len); 72 | + if (munmap(at, *len) < 0) { 73 | + sysErrorBelch("unable to release reserved heap"); 74 | + } 75 | + return next_at; 76 | + } 77 | +} 78 | 79 | +void *osReserveHeapMemory(void *startAddressPtr, W_ *len) 80 | +{ 81 | /* We want to ensure the heap starts at least 8 GB inside the address space, 82 | since we want to reserve the address space below that address for code. 83 | Specifically, we need to make sure that any dynamically loaded code will 84 | @@ -585,45 +627,7 @@ void *osReserveHeapMemory(void *startAddressPtr, W_ *len) 85 | } 86 | #endif 87 | 88 | - attempt = 0; 89 | - while (1) { 90 | - *len &= ~MBLOCK_MASK; 91 | - 92 | - if (*len < MBLOCK_SIZE) { 93 | - // Give up if the system won't even give us 16 blocks worth of heap 94 | - barf("osReserveHeapMemory: Failed to allocate heap storage"); 95 | - } 96 | - 97 | - void *hint = (void*)(startAddress + attempt * BLOCK_SIZE); 98 | - at = osTryReserveHeapMemory(*len, hint); 99 | - if (at == NULL) { 100 | - // This means that mmap failed which we take to mean that we asked 101 | - // for too much memory. This can happen due to POSIX resource 102 | - // limits. In this case we reduce our allocation request by a 103 | - // fraction of the current size and try again. 104 | - // 105 | - // Note that the previously would instead decrease the request size 106 | - // by a factor of two; however, this meant that significant amounts 107 | - // of memory will be wasted (e.g. imagine a machine with 512GB of 108 | - // physical memory but a 511GB ulimit). See #14492. 109 | - *len -= *len / 8; 110 | - // debugBelch("Limit hit, reduced len: %zu\n", *len); 111 | - } else if ((W_)at >= minimumAddress) { 112 | - // Success! We were given a block of memory starting above the 8 GB 113 | - // mark, which is what we were looking for. 114 | - 115 | - break; 116 | - } else { 117 | - // We got addressing space but it wasn't above the 8GB mark. 118 | - // Try again. 119 | - if (munmap(at, *len) < 0) { 120 | - sysErrorBelch("unable to release reserved heap"); 121 | - } 122 | - } 123 | - attempt++; 124 | - } 125 | - 126 | - return at; 127 | + return osTryReserveHeapMemoryRecursive(minimumAddress, startAddress, 0, len); 128 | } 129 | 130 | void osCommitMemory(void *at, W_ size) 131 | -- 132 | GitLab 133 | --------------------------------------------------------------------------------