diff --git a/.appveyor.yml b/.appveyor.yml deleted file mode 100644 index 48fbb4df2..000000000 --- a/.appveyor.yml +++ /dev/null @@ -1,84 +0,0 @@ -shallow_clone: true - -# We're currently only testing libffi built with Microsoft's -# tools. -# This matrix should be expanded to include at least: -# 32- and 64-bit gcc/cygwin -# 32- and 64-bit gcc/mingw -# 32- and 64-bit clang/mingw -# and perhaps more. - -image: Visual Studio 2017 -platform: - - x64 - - x86 - - arm - - arm64 - -configuration: - - Debug - - Release - -environment: - global: - CYG_ROOT: C:/cygwin64 - CYG_CACHE: C:/cygwin64/var/cache/setup - CYG_MIRROR: http://mirrors.kernel.org/sourceware/cygwin/ - VSVER: 15 - matrix: - - SHARED_ARG: "--enable-shared --disable-static" - - SHARED_ARG: "--enable-static --disable-shared" - -install: - - ps: >- - If ($env:Platform -Match "x86") { - $env:VCVARS_PLATFORM="x86" - $env:BUILD="i686-pc-cygwin" - $env:HOST="i686-pc-cygwin" - $env:MSVCC="/cygdrive/c/projects/libffi/msvcc.sh" - $env:SRC_ARCHITECTURE="x86" - } ElseIf ($env:Platform -Match "arm64") { - $env:VCVARS_PLATFORM="x86_arm64" - $env:BUILD="i686-pc-cygwin" - $env:HOST="aarch64-w64-cygwin" - $env:MSVCC="/cygdrive/c/projects/libffi/msvcc.sh -marm64" - $env:SRC_ARCHITECTURE="aarch64" - } ElseIf ($env:Platform -Match "arm") { - $env:VCVARS_PLATFORM="x86_arm" - $env:BUILD="i686-pc-cygwin" - $env:HOST="arm-w32-cygwin" - $env:MSVCC="/cygdrive/c/projects/libffi/msvcc.sh -marm" - $env:SRC_ARCHITECTURE="arm" - } Else { - $env:VCVARS_PLATFORM="amd64" - $env:BUILD="x86_64-w64-cygwin" - $env:HOST="x86_64-w64-cygwin" - $env:MSVCC="/cygdrive/c/projects/libffi/msvcc.sh -m64" - $env:SRC_ARCHITECTURE="x86" - } - If ($env:Configuration -Match "Debug") { - $env:DEBUG_ARG="--enable-debug" - } Else { - $env:DEBUG_ARG="--disable-debug" - } - - 'appveyor DownloadFile https://cygwin.com/setup-x86_64.exe -FileName setup.exe' - - 'setup.exe -qgnNdO -R "%CYG_ROOT%" -s "%CYG_MIRROR%" -l "%CYG_CACHE%" -P dejagnu -P autoconf -P automake -P libtool' - - '%CYG_ROOT%/bin/bash -lc "cygcheck -dc cygwin"' - - echo call VsDevCmd to set VS150COMNTOOLS - - call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\Common7\Tools\VsDevCmd.bat" - - ps: $env:VSCOMNTOOLS=(Get-Content ("env:VS" + "$env:VSVER" + "0COMNTOOLS")) - - echo "Using Visual Studio %VSVER%.0 at %VSCOMNTOOLS%" - - call "%VSCOMNTOOLS%..\..\vc\Auxiliary\Build\vcvarsall.bat" %VCVARS_PLATFORM% - -build_script: - - c:\cygwin64\bin\sh -lc "(cd $OLDPWD; ./autogen.sh)" - - c:\cygwin64\bin\sh -lc "(cd $OLDPWD; ./configure CC='%MSVCC%' CXX='%MSVCC%' LD='link' CPP='cl -nologo -EP' CXXCPP='cl -nologo -EP' CPPFLAGS='-DFFI_BUILDING_DLL' AR='/cygdrive/c/projects/libffi/.travis/ar-lib lib' NM='dumpbin -symbols' STRIP=':' --build=$BUILD --host=$HOST $DEBUG_ARG $SHARED_ARG)" - - c:\cygwin64\bin\sh -lc "(cd $OLDPWD; cp src/%SRC_ARCHITECTURE%/ffitarget.h include)" - - c:\cygwin64\bin\sh -lc "(cd $OLDPWD; make)" - - c:\cygwin64\bin\sh -lc "(cd $OLDPWD; cp $HOST/.libs/libffi.lib $HOST/testsuite/libffi-8.lib || true)" - - c:\cygwin64\bin\sh -lc "(cd $OLDPWD; cp `find . -name 'libffi-?.dll'` $HOST/testsuite/ || true)" - - c:\cygwin64\bin\sh -lc "(cd $OLDPWD; TERM=none make check RUNTESTFLAGS='-v -v -v -v --target '$HOST DEJAGNU=$PWD/.appveyor/site.exp SITEDIR=$PWD/.appveyor)" - - -on_finish: - - c:\cygwin64\bin\sh -lc "(cd $OLDPWD; cat `find ./ -name libffi.log`)" diff --git a/.appveyor/site.exp b/.appveyor/site.exp deleted file mode 100644 index 93f47731f..000000000 --- a/.appveyor/site.exp +++ /dev/null @@ -1,16 +0,0 @@ -# Copyright (C) 2021 Anthony Green - -lappend boards_dir $::env(SITEDIR) - -verbose "Global Config File: target_triplet is $target_triplet" 1 -global target_list - -case "$target_triplet" in { - { "aarch*cygwin*" } { - set target_list "unix-noexec" - } - { "arm*cygwin*" } { - set target_list "unix-noexec" - } -} - diff --git a/.ci/Containerfile.ppc64le b/.ci/Containerfile.ppc64le new file mode 100644 index 000000000..d771f7bbe --- /dev/null +++ b/.ci/Containerfile.ppc64le @@ -0,0 +1,12 @@ +FROM ppc64le/fedora + +RUN dnf install -y dejagnu automake autoconf dejagnu texinfo gcc libtool diffutils gawk + +# ----------------------------------------------------------------------------- +# Give UID 10000 a name so ‘whoami’ works during DejaGNU tests +# ----------------------------------------------------------------------------- +RUN groupadd -g 10000 builder \ + && useradd -u 10000 -g 10000 -d /home/builder -s /sbin/nologin builder \ + && mkdir -p /home/builder \ + && chown 10000:10000 /home/builder +USER 10000 diff --git a/.ci/build.sh b/.ci/build.sh index b27f49e3d..4eabf568a 100755 --- a/.ci/build.sh +++ b/.ci/build.sh @@ -17,17 +17,22 @@ fi export DOCKER=docker +if ! command -v makeinfo >/dev/null 2>&1; then + CONFIGURE_OPTIONS="--disable-docs ${CONFIGURE_OPTIONS}" +fi + function build_linux() { - ./autogen.sh ./configure ${HOST+--host=$HOST} ${CONFIGURE_OPTIONS} || cat */config.log + ls -l */config.log + cat */config.log make make dist DEJAGNU=$(pwd)/.ci/site.exp BOARDSDIR=$(pwd)/.ci runtest --version DEJAGNU=$(pwd)/.ci/site.exp BOARDSDIR=$(pwd)/.ci make check RUNTESTFLAGS="-a $RUNTESTFLAGS" ./rlgl l --key=${RLGL_KEY} https://rl.gl - ./rlgl e -l project=libffi -l sha=${GITHUB_SHA:0:7} -l CC='$CC' ${HOST+-l host=$HOST} --policy=https://github.com/libffi/rlgl-policy.git */testsuite/libffi.log + ./rlgl e -l project=libffi -l sha=${GITHUB_SHA:0:7} -l CC='${CC}' ${HOST+-l host=$HOST} --policy=https://github.com/libffi/rlgl-policy.git */testsuite/libffi.log exit $? } @@ -62,7 +67,7 @@ function build_cross() function build_ios() { which python -# export PYTHON_BIN=/usr/local/bin/python + # export PYTHON_BIN=/usr/local/bin/python ./generate-darwin-source-and-headers.py --only-ios xcodebuild -showsdks xcodebuild -project libffi.xcodeproj -target "libffi-iOS" -configuration Release -sdk iphoneos11.4 @@ -72,7 +77,7 @@ function build_ios() function build_macosx() { which python -# export PYTHON_BIN=/usr/local/bin/python + # export PYTHON_BIN=/usr/local/bin/python ./generate-darwin-source-and-headers.py --only-osx xcodebuild -showsdks xcodebuild -project libffi.xcodeproj -target "libffi-Mac" -configuration Release -sdk macosx10.13 @@ -82,43 +87,43 @@ function build_macosx() case "$HOST" in arm-apple-darwin*) - ./autogen.sh - build_ios - ;; + ./autogen.sh + build_ios + ;; x86_64-apple-darwin*) - ./autogen.sh - build_macosx - ;; + ./autogen.sh + build_macosx + ;; arm32v7-linux-gnu) - ./autogen.sh + ./autogen.sh build_foreign_linux arm quay.io/moxielogic/arm32v7-ci-build-container:latest - ;; + ;; bfin-elf ) - ./autogen.sh - GCC_OPTIONS=-msim build_cross - ;; + ./autogen.sh + GCC_OPTIONS=-msim build_cross + ;; m32r-elf ) - ./autogen.sh - build_cross - ;; + ./autogen.sh + build_cross + ;; or1k-elf ) - ./autogen.sh - build_cross - ;; + ./autogen.sh + build_cross + ;; powerpc-eabisim ) - ./autogen.sh - build_cross - ;; + ./autogen.sh + build_cross + ;; m68k-linux-gnu ) - ./autogen.sh - GCC_OPTIONS=-mcpu=547x build_cross_linux - ;; + ./autogen.sh + GCC_OPTIONS=-mcpu=547x build_cross_linux + ;; alpha-linux-gnu | sh4-linux-gnu ) - ./autogen.sh - build_cross_linux - ;; + ./autogen.sh + build_cross_linux + ;; *) - ./autogen.sh - build_linux - ;; + ./autogen.sh + build_linux + ;; esac diff --git a/.ci/install.sh b/.ci/install.sh index 446fed0ab..9fa4c4099 100755 --- a/.ci/install.sh +++ b/.ci/install.sh @@ -6,73 +6,69 @@ if [[ $RUNNER_OS != 'Linux' ]]; then # brew update > brew-update.log 2>&1 # fix an issue with libtool on travis by reinstalling it brew uninstall libtool; - brew install automake libtool dejagnu; + brew install automake libtool dejagnu gcc@15; # Download and extract the rlgl client wget -qO - https://rl.gl/cli/rlgl-darwin-amd64.tgz | \ - tar --strip-components=2 -xvzf - ./rlgl/rlgl; + tar --strip-components=2 -xvzf - ./rlgl/rlgl; else # Download and extract the rlgl client case $HOST in - aarch64-linux-gnu) - wget -qO - https://rl.gl/cli/rlgl-linux-arm.tgz | \ - tar --strip-components=2 -xvzf - ./rlgl/rlgl; - ;; - ppc64le-linux-gnu) - wget -qO - https://rl.gl/cli/rlgl-linux-ppc64le.tgz | \ - tar --strip-components=2 -xvzf - ./rlgl/rlgl; - ;; - s390x-linux-gnu) - wget -qO - https://rl.gl/cli/rlgl-linux-s390x.tgz | \ - tar --strip-components=2 -xvzf - ./rlgl/rlgl; - ;; - *) - wget -qO - https://rl.gl/cli/rlgl-linux-amd64.tgz | \ - tar --strip-components=2 -xvzf - ./rlgl/rlgl; - ;; + aarch64-*linux-gnu) + wget -qO - https://rl.gl/cli/rlgl-linux-arm.tgz | \ + tar --strip-components=2 -xvzf - ./rlgl/rlgl; + ;; + ppc64le-linux-gnu) + wget -qO - https://rl.gl/cli/rlgl-linux-ppc64le.tgz | \ + tar --strip-components=2 -xvzf - ./rlgl/rlgl; + ;; + s390x-linux-gnu) + wget -qO - https://rl.gl/cli/rlgl-linux-s390x.tgz | \ + tar --strip-components=2 -xvzf - ./rlgl/rlgl; + ;; + *) + wget -qO - https://rl.gl/cli/rlgl-linux-amd64.tgz | \ + tar --strip-components=2 -xvzf - ./rlgl/rlgl; + ;; esac sudo apt-get clean # clear the cache sudo apt-get update - - set -x - wget --retry-connrefused --waitretry=1 --read-timeout=20 --timeout=15 -t 0 -qO - https://ftpmirror.gnu.org/autoconf/autoconf-2.71.tar.gz | tar -xvzf - - mkdir -p ~/i - (cd autoconf-2.71; ./configure --prefix=$HOME/i; make; make install) + sudo apt install libltdl-dev zip case $HOST in - mips64el-linux-gnu | sparc64-linux-gnu) + mips64el-linux-gnu | sparc64-linux-gnu) ;; - alpha-linux-gnu | arm32v7-linux-gnu | m68k-linux-gnu | sh4-linux-gnu) - sudo apt-get install qemu-user-static - ;; - hppa-linux-gnu ) - sudo apt-get install -y qemu-user-static g++-5-hppa-linux-gnu - ;; - i386-pc-linux-gnu) - sudo apt-get install gcc-multilib g++-multilib; - ;; - moxie-elf) - echo 'deb [trusted=yes] https://repos.moxielogic.org:7114/MoxieLogic moxiedev main' | sudo tee -a /etc/apt/sources.list - sudo apt-get clean # clear the cache - sudo apt-get update ## -qq - sudo apt-get update - sudo apt-get install -y --allow-unauthenticated moxielogic-moxie-elf-gcc moxielogic-moxie-elf-gcc-c++ moxielogic-moxie-elf-gcc-libstdc++ moxielogic-moxie-elf-gdb-sim texinfo sharutils texlive dejagnu - ;; - x86_64-w64-mingw32) - sudo apt-get install gcc-mingw-w64-x86-64 g++-mingw-w64-x86-64 wine; - ;; - i686-w32-mingw32) - sudo apt-get install gcc-mingw-w64-i686 g++-mingw-w64-i686 wine; - ;; + alpha-linux-gnu | arm32v7-linux-gnu | m68k-linux-gnu | sh4-linux-gnu) + sudo apt-get install qemu-user-static + ;; + hppa-linux-gnu ) + sudo apt-get install -y qemu-user-static g++-5-hppa-linux-gnu + ;; + i386-pc-linux-gnu) + sudo apt-get install gcc-multilib g++-multilib; + ;; + moxie-elf) + echo 'deb [trusted=yes] https://repos.moxielogic.org:7114/MoxieLogic moxiedev main' | sudo tee -a /etc/apt/sources.list + sudo apt-get clean # clear the cache + sudo apt-get update ## -qq + sudo apt-get update + sudo apt-get install -y --allow-unauthenticated moxielogic-moxie-elf-gcc moxielogic-moxie-elf-gcc-c++ moxielogic-moxie-elf-gcc-libstdc++ moxielogic-moxie-elf-gdb-sim texinfo sharutils texlive dejagnu + ;; + x86_64-w64-mingw32) + sudo apt-get install gcc-mingw-w64-x86-64 g++-mingw-w64-x86-64 wine; + ;; + i686-w32-mingw32) + sudo apt-get install gcc-mingw-w64-i686 g++-mingw-w64-i686 wine; + ;; esac case $HOST in - arm32v7-linux-gnu) + arm32v7-linux-gnu) # don't install host tools ;; - *) - sudo apt-get install dejagnu texinfo sharutils - ;; + *) + sudo apt-get install dejagnu texinfo sharutils + ;; esac fi diff --git a/.ci/site.exp b/.ci/site.exp index 96c013e08..8b1d92b48 100644 --- a/.ci/site.exp +++ b/.ci/site.exp @@ -26,4 +26,10 @@ case "$target_triplet" in { { "powerpc-eabisim" } { set target_list "powerpc-eabisim" } + { "aarch*cygwin*" } { + set target_list "unix-noexec" + } + { "arm*cygwin*" } { + set target_list "unix-noexec" + } } diff --git a/.appveyor/unix-noexec.exp b/.ci/unix-noexec.exp similarity index 100% rename from .appveyor/unix-noexec.exp rename to .ci/unix-noexec.exp diff --git a/.gail-labels b/.gail-labels new file mode 100644 index 000000000..2090d9fe6 --- /dev/null +++ b/.gail-labels @@ -0,0 +1,44 @@ +# This is a list of labels to be used for automatic tagging +# of github issues using gail: https://github.com/atgreen/gail + +# Main categories +BUILD-ERROR +RUNTIME-ERROR +FEATURE-REQUEST +QUESTION + +# Operating systems +ANDROID +IOS +LINUX +MACOS +SOLARIS +WINDOWS + +# Processor families +AARCH64 +ALPHA +ARC +ARM +AVR32 +BLACKFIN +CSKY +HPPA +IA-64 +KVX +LOONGARCH64 +M68K +M88K +MICROBLAZE +MIPS +MOXIE +OPENRISC +POWERPC +RISC-V +S390 +SPARC +TILE +VAX +WASM +X86 +XTENSA diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 59c3a8425..38d2db98e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -13,84 +13,44 @@ on: # A workflow run is made up of one or more jobs that can run sequentially or in parallel jobs: + build-linux: + name: Linux ${{ matrix.HOST }} ${{ matrix.CONFIGURE_OPTIONS }} \ + ${{ matrix.MEVAL }} ${{ matrix.LIBFFI_TEST_OPTIMIZATION }} - build-warp: - name: ${{ matrix.HOST }} ${{ matrix.CONFIGURE_OPTIONS }} ${{ matrix.MEVAL }} ${{ matrix.LIBFFI_TEST_OPTIMIZATION }} - runs-on: warp-ubuntu-latest-arm64-2x + runs-on: ${{ matrix.runner }} strategy: fail-fast: false matrix: - include: - - HOST: "aarch64-linux-gnu" + include: + # ---------- existing x86-64 variants ---------- + - runner: ubuntu-latest + HOST: x86_64-pc-linux-gnu + MEVAL: 'export CC=clang CXX=clang++' - steps: - - uses: actions/checkout@v3 + - runner: ubuntu-latest + HOST: i386-pc-linux-gnu + MEVAL: 'export CC="gcc -m32" CXX="g++ -m32"' - - env: - MEVAL: ${{ matrix.MEVAL }} - HOST: ${{ matrix.HOST }} - LDFLAGS: ${{ matrix.LDFLAGS }} - RUNTESTFLAGS: ${{ matrix.RUNTESTFLAGS }} - CONFIGURE_OPTIONS: ${{ matrix.CONFIGURE_OPTIONS }} - run: | - if test x"$MEVAL" != x; then eval ${MEVAL}; fi - ./.ci/install.sh - ./.ci/build.sh + - runner: ubuntu-latest + HOST: x86_64-pc-linux-gnu + CONFIGURE_OPTIONS: "--disable-shared" - build-sim: - name: ${{ matrix.HOST }} ${{ matrix.CONFIGURE_OPTIONS }} ${{ matrix.MEVAL }} ${{ matrix.LIBFFI_TEST_OPTIMIZATION }} - runs-on: ubuntu-latest + - runner: ubuntu-latest + HOST: x86_64-pc-linux-gnu + CONFIGURE_OPTIONS: "--enable-shared" - strategy: - fail-fast: false - matrix: - include: - - HOST: "x86_64-pc-linux-gnu" - MEVAL: "export CC=clang CXX=clang" - - HOST: "i386-pc-linux-gnu" - MEVAL: 'export CC="gcc -m32" CXX="g++ -m32"' - - HOST: "x86_64-pc-linux-gnu" - CONFIGURE_OPTIONS: "--disable-shared" - - HOST: "x86_64-pc-linux-gnu" - CONFIGURE_OPTIONS: "--enable-shared" - - HOST: "m68k-linux-gnu" - MEVAL: 'export CC="m68k-linux-gnu-gcc-8 -mcpu=547x" CXX="m68k-linux-gnu-g++-8 -mcpu=547x"' - CONFIGURE_OPTIONS: '--disable-shared' - QEMU_LD_PREFIX: '/usr/m68k-linux-gnu' - QEMU_CPU: 'cfv4e' - - HOST: "sh4-linux-gnu" - CONFIGURE_OPTIONS: "--disable-shared" - QEMU_LD_PREFIX: "/usr/sh4-linux-gnu" - QEMU_CPU: 'sh7785' - - HOST: "alpha-linux-gnu" - CONFIGURE_OPTIONS: "--disable-shared" - QEMU_LD_PREFIX: "/usr/alpha-linux-gnu" - QEMU_CPU: 'ev4-alpha-cpu' - - HOST: "arm32v7-linux-gnu" - LIBFFI_TEST_OPTIMIZATION: "-O0" - QEMU_CPU: 'any' - - HOST: "arm32v7-linux-gnu" - LIBFFI_TEST_OPTIMIZATION: "-O2" - QEMU_CPU: 'any' - - HOST: "arm32v7-linux-gnu" - LIBFFI_TEST_OPTIMIZATION: "-O2 -fomit-frame-pointer" - QEMU_CPU: 'any' - - HOST: "powerpc-eabisim" - RUNTESTFLAGS: "--target_board powerpc-eabisim" - - HOST: "or1k-elf" - RUNTESTFLAGS: "--target_board or1k-sim" - - HOST: "m32r-elf" - RUNTESTFLAGS: "--target_board m32r-sim" - - HOST: "bfin-elf" - RUNTESTFLAGS: "--target_board bfin-sim" - - MEVAL: "export PATH=/opt/moxielogic/bin:$PATH CC=moxie-elf-gcc CXX=moxie-elf-g++" - HOST: "moxie-elf" - LDFLAGS: "-Tsim.ld" - RUNTESTFLAGS: "--target_board moxie-sim" + - runner: ubuntu-latest + HOST: x86_64-pc-linux-gnu + CONFIGURE_OPTIONS: "--disable-exec-static-tramp" + + # ---------- new native arm64 build ---------- + - runner: ubuntu-22.04-arm # or ubuntu-24.04-arm + HOST: aarch64-unknown-linux-gnu + MEVAL: 'export CC=clang CXX=clang++' steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - env: MEVAL: ${{ matrix.MEVAL }} @@ -105,297 +65,119 @@ jobs: ./.ci/install.sh ./.ci/build.sh - build-cfarm: - name: ${{ matrix.CFARM_TRIPLE }} ${{ matrix.CFARM_CC }} - runs-on: ubuntu-latest + build-macos: + name: MacOS ${{ matrix.platform }} ${{ matrix.compilers }} + runs-on: ${{ matrix.platform }} strategy: fail-fast: false matrix: - include: - - CFARM_HOST: cfarm185.cfarm.net - CFARM_PORT: 22 - CFARM_TRIPLE: aarch64-linux-gnu - CFARM_CC: "gcc" - CFARM_CXX: "g++" - - CFARM_HOST: cfarm185.fsffrance.org - CFARM_PORT: 22 - CFARM_TRIPLE: aarch64-lto-linux-gnu - CFARM_CC: "gcc -flto" - CFARM_CXX: "g++ -flto" - - CFARM_HOST: cfarm400.cfarm.net - CFARM_PORT: 25465 - CFARM_TRIPLE: loongarch64-linux-gnu - CFARM_CC: "gcc" - CFARM_CXX: "g++" - - CFARM_HOST: cfarm230.cfarm.net - CFARM_PORT: 22 - CFARM_TRIPLE: mips-linux-gnu - CFARM_CC: "gcc" - CFARM_CXX: "g++" - - CFARM_HOST: cfarm211.cfarm.net - CFARM_PORT: 22 - CFARM_TRIPLE: sparc64-linux-gnu - CFARM_CC: "gcc" - CFARM_CXX: "g++" - - CFARM_HOST: cfarm211.cfarm.net - CFARM_PORT: 22 - CFARM_TRIPLE: sparc64-linux-gnu - CFARM_CC: "gcc -m32" - CFARM_CXX: "g++ -m32" - - CFARM_HOST: cfarm91.cfarm.net - CFARM_PORT: 22 - CFARM_TRIPLE: riscv64-linux-gnu - CFARM_CC: "gcc" - CFARM_CXX: "g++" - - CFARM_HOST: cfarm103.cfarm.net - CFARM_PORT: 22 - CFARM_TRIPLE: aarch64-m1-linux-gnu - CFARM_CC: "gcc" - CFARM_CXX: "g++" - - CFARM_HOST: cfarm112.cfarm.net - CFARM_PORT: 22 - CFARM_TRIPLE: powerpc64le-linux-gnu - CFARM_CC: "gcc" - CFARM_CXX: "g++" - - CFARM_HOST: cfarm111.cfarm.net - CFARM_PORT: 22 - CFARM_TRIPLE: powerpc-ibm-aix7.1.5.0 - CFARM_CC: "gcc" - CFARM_CXX: "g++" + platform: [macos-13, macos-14, macos-15] + compilers: [CC=gcc-15 CXX=g++-15, CC=clang CXX=g++-15] steps: + - run: git config --global core.autocrlf input + - uses: actions/checkout@v4 + - run: ./.ci/install.sh + - run: ${{ matrix.compilers }} ./.ci/build.sh - - uses: actions/checkout@v3 - - - name: Run autogen - run: | - wget --retry-connrefused --waitretry=1 --read-timeout=20 --timeout=15 -t 0 -qO - https://ftpmirror.gnu.org/autoconf/autoconf-2.71.tar.gz | tar -xvzf - - mkdir -p ~/i - (cd autoconf-2.71; ./configure --prefix=$HOME/i; make; make install) - rm -rf autoconf-2.71 - PATH=$HOME/i/bin:$PATH ./autogen.sh - echo "${{ secrets.CFARM_KEY }}" > /tmp/cfk - chmod go-rw /tmp/cfk - - - name: Generate build dir name - run: | - echo BUILD_DIR=t/$GITHUB_RUN_NUMBER-$RANDOM >> $GITHUB_ENV - - - name: Check for host availability - id: check-host - run: | - set +e - ssh -o StrictHostKeyChecking=no -o ConnectTimeout=10 -o ConnectionAttempts=3 -i /tmp/cfk -p ${{ matrix.CFARM_PORT }} ${{ secrets.CFARM_USERNAME }}@${{ matrix.CFARM_HOST }} "mkdir -p ${{ env.BUILD_DIR }}" - if test $? -ne 0; then - echo "Remote host is unavailable." - echo "HOST_OK=NO" >> $GITHUB_OUTPUT - else - echo "Remote host is available." - echo "HOST_OK=YES" >> $GITHUB_OUTPUT - fi - set -e - - - name: Show host availability - run: | - echo ${{ steps.check-host.outputs.HOST_OK }} - - - name: Copy source to remote host - if: ${{ steps.check-host.outputs.HOST_OK == 'YES' }} - run: | - echo ${{ steps.check-host.outputs.HOST_OK }} - scp -o StrictHostKeyChecking=no -o ConnectTimeout=10 -o ConnectionAttempts=3 -i /tmp/cfk -P ${{ matrix.CFARM_PORT }} -r * ${{ secrets.CFARM_USERNAME }}@${{ matrix.CFARM_HOST }}:${{ env.BUILD_DIR }} - - - name: Run configure and make - if: ${{ steps.check-host.outputs.HOST_OK == 'YES' }} - run: | - ssh -o StrictHostKeyChecking=no -o ConnectTimeout=10 -o ConnectionAttempts=3 -i /tmp/cfk -p ${{ matrix.CFARM_PORT }} ${{ secrets.CFARM_USERNAME }}@${{ matrix.CFARM_HOST }} "${{ matrix.CFARM_CC }} --version" - ssh -o StrictHostKeyChecking=no -o ConnectTimeout=10 -o ConnectionAttempts=3 -i /tmp/cfk -p ${{ matrix.CFARM_PORT }} ${{ secrets.CFARM_USERNAME }}@${{ matrix.CFARM_HOST }} "(cd ${{ env.BUILD_DIR }}; if test -f ~/.profile; then source ~/.profile; fi; CC='${{ matrix.CFARM_CC }}' CXX='${{ matrix.CFARM_CXX }}' ./configure --host=${{ matrix.CFARM_TRIPLE }}) || true; exit 0" - ssh -o StrictHostKeyChecking=no -o ConnectTimeout=10 -o ConnectionAttempts=3 -i /tmp/cfk -p ${{ matrix.CFARM_PORT }} ${{ secrets.CFARM_USERNAME }}@${{ matrix.CFARM_HOST }} "(cd ${{ env.BUILD_DIR }}; if test -f ~/.profile; then source ~/.profile; fi; make;) || true; exit 0" - - - name: Run tests - if: ${{ steps.check-host.outputs.HOST_OK == 'YES' }} - run: | - ssh -o StrictHostKeyChecking=no -o ConnectTimeout=10 -o ConnectionAttempts=3 -i /tmp/cfk -p ${{ matrix.CFARM_PORT }} ${{ secrets.CFARM_USERNAME }}@${{ matrix.CFARM_HOST }} "(cd ${{ env.BUILD_DIR }}; if test -f ~/.profile; then source ~/.profile; fi; GCC_COLORS= make check & CHECKPID=\$!; while kill -0 \$CHECKPID 2>/dev/null; do echo 'Waiting for tests to finish'; sleep 5; done;)" - - - name: Copy results and clean up - if: ${{ steps.check-host.outputs.HOST_OK == 'YES' }} - run: | - scp -o StrictHostKeyChecking=no -o ConnectTimeout=10 -o ConnectionAttempts=3 -i /tmp/cfk -P ${{ matrix.CFARM_PORT }} ${{ secrets.CFARM_USERNAME }}@${{ matrix.CFARM_HOST }}:${{ env.BUILD_DIR }}/*/testsuite/*.log . - ssh -o StrictHostKeyChecking=no -o ConnectTimeout=10 -o ConnectionAttempts=3 -i /tmp/cfk -p ${{ matrix.CFARM_PORT }} ${{ secrets.CFARM_USERNAME }}@${{ matrix.CFARM_HOST }} "rm -rf ${{ env.BUILD_DIR }}" - - - name: Install rlgl and run - if: ${{ steps.check-host.outputs.HOST_OK == 'YES' }} - run: | - wget -qO - https://rl.gl/cli/rlgl-linux-amd64.tgz | \ - tar --strip-components=2 -xvzf - ./rlgl/rlgl; - ./rlgl l --key=0LIBFFI-0LIBFFI-0LIBFFI-0LIBFFI https://rl.gl - ./rlgl e -l project=libffi -l sha=${GITHUB_SHA:0:7} -l CC='${{ matrix.CFARM_CC }}' -l build-host=${{ matrix.CFARM_TRIPLE }} --policy=https://github.com/libffi/rlgl-policy.git libffi.log - exit $? - - build: - name: Cygwin ${{ matrix.arch }} + build-non-msvc: + name: Windows ${{ matrix.width }}-bit ${{ matrix.compiler }} runs-on: windows-latest strategy: fail-fast: false matrix: include: + # Cygwin 32-bit + - host: i686-pc-cygwin + width: 32 + arch: x86 + target: i686-pc-cygwin + gcc_prefix: /usr/i686-pc-cygwin + compiler: gcc - host: i686-pc-cygwin + width: 32 arch: x86 - - host: x86_64-pc-cygwin + target: i686-pc-cygwin + gcc_prefix: /usr/i686-pc-cygwin + compiler: clang + # MinGW-w64 64-bit + - host: x86_64-w64-mingw32 + width: 64 arch: x64 + target: x86_64-w64-mingw32 + gcc_prefix: /usr/${{ github.workspace }}/mingw64 # unused but keeps the table homogeneous + compiler: clang + - host: x86_64-w64-mingw32 + width: 64 + arch: x64 + target: x86_64-w64-mingw32 + gcc_prefix: /usr/${{ github.workspace }}/mingw64 # unused but keeps the table homogeneous + compiler: gcc steps: - run: git config --global core.autocrlf input + - uses: actions/checkout@v4 - - uses: actions/checkout@v3 - - - name: Set up Cygwin - uses: egor-tensin/setup-cygwin@v3 + # ──────────────────────────────── Cygwin & tool-chains ──────────────────────────────── + - uses: egor-tensin/setup-cygwin@v4 with: - platform: ${{ matrix.arch }} - packages: wget gcc-core make dejagnu automake autoconf libtool texinfo dos2unix unzip - - - run: | - set -x - cd $(cygpath $RUNNER_WORKSPACE)/libffi - wget https://rl.gl/cli/rlgl-windows-amd64.zip - unzip rlgl-windows-amd64.zip - autoreconf -f -v -i - ./configure - make -j 4 - TERM=none DEJAGNU=$(pwd)/.ci/site.exp BOARDSDIR=$(pwd)/.ci GCC_COLORS= make check || true - ./rlgl/rlgl.exe l --key=0LIBFFI-0LIBFFI-0LIBFFI-0LIBFFI https://rl.gl - ./rlgl/rlgl.exe e \ - -l project=libffi \ - -l sha=${GITHUB_SHA:0:7} \ - -l CC=gcc \ - -l host=${{ matrix.host }} \ - --policy=https://github.com/libffi/rlgl-policy.git $(find . -name libffi.log) + # gcc / g++ are needed so Clang can reuse their start-files & libraries + packages: > + wget make dejagnu automake autoconf libtool texinfo unzip dos2unix + clang gcc-core gcc-g++ + cygwin32-gcc-core cygwin32-gcc-g++ cygwin32-runtime cygwin32-libgcc1 + + # ──────────────────────────────── Common environment ──────────────────────────────── + - name: Export build env + shell: bash + run: | + echo "GCC_PREFIX=${{ matrix.gcc_prefix }}" >> $GITHUB_ENV + echo "CC=${{ matrix.compiler }} --target=${{ matrix.target }} -B${{ matrix.gcc_prefix }}/bin \ + -L${{ matrix.gcc_prefix }}/lib -I${{ matrix.gcc_prefix }}/include" >> $GITHUB_ENV + echo "CXX=${{ matrix.compiler }} --target=${{ matrix.target }} -B${{ matrix.gcc_prefix }}/bin \ + -L${{ matrix.gcc_prefix }}/lib -I${{ matrix.gcc_prefix }}/include" >> $GITHUB_ENV + # make sure the cross-gcc bin dir is found *before* /usr/bin (64-bit) + echo "${{ matrix.gcc_prefix }}/bin" >> $GITHUB_PATH + + # ──────────────────────────────── Pick version from configure.ac ───────────────────── + - id: ver + name: Read libffi version shell: C:\tools\cygwin\bin\bash.exe --login --norc -eo pipefail -o igncr '{0}' + run: | + cd "$(cygpath $RUNNER_WORKSPACE)/libffi" + version=$(sed -nE 's/^AC_INIT\(\[libffi\],[[:space:]]*\[([^]]+)\].*/\1/p' configure.ac) + [[ $version ]] || { echo "Could not parse version"; exit 1; } + echo "version=$version" >> "$GITHUB_OUTPUT" - build-msys2: - - runs-on: windows-latest - - strategy: - fail-fast: false - matrix: - include: - - MSYSTEM: MINGW32 - MSYS2_ARCH: i686 - - MSYSTEM: MINGW64 - MSYS2_ARCH: x86_64 - name: ${{ matrix.MSYSTEM }} - - steps: - - run: git config --global core.autocrlf input - - uses: actions/checkout@v3 - - - uses: msys2/setup-msys2@v2 - with: - msystem: ${{ matrix.MSYSTEM }} - update: true - install: >- - base-devel - autoconf-wrapper - autoconf - automake - libtool - make - dejagnu - mingw-w64-${{ matrix.MSYS2_ARCH }}-gcc - mingw-w64-${{ matrix.MSYS2_ARCH }}-gcc-libs - unzip - - - run: | - set -x - cd $(cygpath $RUNNER_WORKSPACE)/libffi + # ──────────────────────────────── Build & test ─────────────────────────────────────── + - name: Build and test + shell: C:\tools\cygwin\bin\bash.exe --login --norc -eo pipefail -o igncr '{0}' + run: | + set -euo pipefail + cd "$(cygpath $RUNNER_WORKSPACE)/libffi" wget https://rl.gl/cli/rlgl-windows-amd64.zip unzip rlgl-windows-amd64.zip - autoreconf -f -v -i - CC=${{ matrix.MSYS2_ARCH }}-w64-mingw32-gcc CXX=${{ matrix.MSYS2_ARCH }}-w64-mingw32-g++ ./configure - make - TERM=none DEJAGNU=$(pwd)/.ci/site.exp BOARDSDIR=$(pwd)/.ci GCC_COLORS= make check || true - ./rlgl/rlgl.exe l --key=0LIBFFI-0LIBFFI-0LIBFFI-0LIBFFI https://rl.gl - ./rlgl/rlgl.exe e \ - -l project=libffi \ - -l sha=${GITHUB_SHA:0:7} \ - -l CC=${{ matrix.MSYS2_ARCH }}-w64-mingw32-gcc \ - -l host=x86_64-pc-cygwin \ - --policy=https://github.com/libffi/rlgl-policy.git $(find . -name libffi.log) - shell: msys2 {0} - - build-msys2-clang: - - runs-on: windows-latest - - strategy: - fail-fast: false - matrix: - include: - - MSYSTEM: MINGW32 - MSYS2_ARCH: i686 - - MSYSTEM: MINGW64 - MSYS2_ARCH: x86_64 - name: ${{ matrix.MSYSTEM }} - - steps: - - run: git config --global core.autocrlf input - - uses: actions/checkout@v3 + autoreconf -fvi + ./configure \ + --enable-shared \ + --build=${{ matrix.host }} --host=${{ matrix.host }} \ + CPPFLAGS="-DFFI_BUILDING_DLL -DUSE_STATIC_RTL" \ + CFLAGS="-DFFI_BUILDING_DLL -DUSE_STATIC_RTL" - - uses: msys2/setup-msys2@v2 - with: - msystem: ${{ matrix.MSYSTEM }} - update: true - install: >- - base-devel - autoconf-wrapper - autoconf - automake - libtool - make - dejagnu - clang - mingw-w64-${{ matrix.MSYS2_ARCH }}-gcc - mingw-w64-${{ matrix.MSYS2_ARCH }}-gcc-libs - unzip - - - run: | - set -x - cd $(cygpath $RUNNER_WORKSPACE)/libffi - wget https://rl.gl/cli/rlgl-windows-amd64.zip - unzip rlgl-windows-amd64.zip - autoreconf -f -v -i - CC=clang CXX=clang ./configure make - TERM=none DEJAGNU=$(pwd)/.ci/site.exp BOARDSDIR=$(pwd)/.ci make check || true + # sanity-check + file */.libs/*ffi-*.dll + + TERM=none DEJAGNU=$(pwd)/.ci/site.exp BOARDSDIR=$(pwd)/.ci GCC_COLORS= make check || true ./rlgl/rlgl.exe l --key=0LIBFFI-0LIBFFI-0LIBFFI-0LIBFFI https://rl.gl ./rlgl/rlgl.exe e \ -l project=libffi \ -l sha=${GITHUB_SHA:0:7} \ - -l CC=clang \ - -l host=x86_64-pc-cygwin \ + -l CC=${{ matrix.compiler }} \ + -l host=${{ matrix.host }} \ --policy=https://github.com/libffi/rlgl-policy.git $(find . -name libffi.log) - shell: msys2 {0} - - build-macos: - runs-on: ${{ matrix.platform }} - - strategy: - fail-fast: false - matrix: - platform: [macos-11, macos-12] - compilers: [CC=gcc CXX=g++, CC=clang CXX=clang] - - name: ${{ matrix.platform }} ${{ matrix.compilers }} - - steps: - - run: git config --global core.autocrlf input - - uses: actions/checkout@v3 - - run: ./.ci/install.sh - - run: ${{ matrix.compilers }} ./.ci/build.sh build-msvc: name: Windows ${{ matrix.width }}-bit Visual C++ @@ -409,32 +191,61 @@ jobs: width: 32 arch: x86 tools: amd64_x86 - - host: x86_64-pc-cygwin + - host: x86_64-w64-mingw32 width: 64 arch: x64 tools: amd64 steps: - run: git config --global core.autocrlf input - - uses: actions/checkout@v3 - - uses: egor-tensin/setup-cygwin@v3 + - uses: actions/checkout@v4 + - uses: egor-tensin/setup-cygwin@v4 with: platform: x64 packages: wget make dejagnu automake autoconf libtool texinfo unzip dos2unix - - uses: ilammy/msvc-dev-cmd@v1.12.0 + - uses: ilammy/msvc-dev-cmd@v1.13.0 with: arch: ${{ matrix.tools }} + - name: Read libffi version from configure.ac + id: ver + shell: C:\tools\cygwin\bin\bash.exe --login --norc -eo pipefail -o igncr '{0}' + run: | + cd $(cygpath $RUNNER_WORKSPACE)/libffi + # Pull the second bracketed field from the AC_INIT line. + # Example line: AC_INIT([libffi],[3.5.0],[http://…]) + ls -l + grep AC_INIT configure.ac + ver=$(sed -nE 's/^AC_INIT\(\[libffi\],[[:space:]]*\[([^]]+)\].*/\1/p' configure.ac) + [[ -n "$ver" ]] || { echo "Could not parse version!"; exit 1; } + echo "version=$ver" >> "$GITHUB_OUTPUT" + - name: Build and test run: | - # export PATH=$PATH:"/cygdrive/c/Program Files (x86)/Microsoft Visual Studio/2019/Enterprise/VC/Tools/MSVC/14.16.27023/bin/HostX64/x64" cd $(cygpath $RUNNER_WORKSPACE)/libffi wget https://rl.gl/cli/rlgl-windows-amd64.zip unzip rlgl-windows-amd64.zip autoreconf -f -v -i - ./configure --host=${{ matrix.host }} CC="$(pwd)/msvcc.sh -m${{ matrix.width }}" CXX="$(pwd)/msvcc.sh -m${{ matrix.width }}" LD='link' CPP='cl -nologo -EP' CXXCPP='cl -nologo -EP' CPPFLAGS='-DFFI_BUILDING_DLL' AR='$(pwd)/.ci/ar-lib lib' NM='dumpbin -symbols' STRIP=':' $DEBUG_ARG $SHARED_ARG || cat */config.log + ./configure \ + --enable-shared \ + --build="${{ matrix.host }}" --host="${{ matrix.host }}" \ + --disable-docs \ + CC="$(pwd)/msvcc.sh -m${{ matrix.width }}" \ + CXX="$(pwd)/msvcc.sh -m${{ matrix.width }}" \ + LD="link" \ + LDFLAGS="-no-undefined" \ + CPP="cl -nologo -EP" \ + CXXCPP="cl -nologo -EP" \ + CPPFLAGS="-DFFI_BUILDING_DLL -DUSE_STATIC_RTL" \ + CFLAGS="-DFFI_BUILDING_DLL -DUSE_STATIC_RTL" \ + AR='$(pwd)/.ci/ar-lib lib' \ + NM='dumpbin -symbols' STRIP=':' \ + $DEBUG_ARG || cat */config.log make + find ./ -type f -name 'libffi*' + ls -l */.libs cp $(find . -name 'libffi-?.dll') ${{ matrix.host }}/testsuite/ + find ./ -name ffi.h TERM=none DEJAGNU=$(pwd)/.ci/site.exp BOARDSDIR=$(pwd)/.ci GCC_COLORS= make check || true ./rlgl/rlgl.exe l --key=0LIBFFI-0LIBFFI-0LIBFFI-0LIBFFI https://rl.gl ./rlgl/rlgl.exe e \ @@ -445,6 +256,72 @@ jobs: --policy=https://github.com/libffi/rlgl-policy.git $(find . -name libffi.log) shell: C:\tools\cygwin\bin\bash.exe --login --norc -eo pipefail -o igncr '{0}' + - name: Add meta information to DLL + shell: C:\tools\cygwin\bin\bash.exe --login --norc -eo pipefail -o igncr '{0}' + run: | + cd $(cygpath $RUNNER_WORKSPACE)/libffi + + # Deconstruct the libffi version + ver=${{ steps.ver.outputs.version }} + echo "$ver" + + base=${ver%%-*} + rc=${ver#*-} + [[ $rc == "$ver" ]] && rc="" # no rc? rc="" + + maj=$(cut -d. -f1 <<<"$base") + min=$(cut -d. -f2 <<<"$base") + pat=$(cut -d. -f3 <<<"$base") + + # Decide on build number + if [[ $rc =~ ^rc([0-9]+)$ ]]; then + build=${BASH_REMATCH[1]} # rc1 → 1 + else + build=0 + fi + + # Use rcedit to edit the DLLs resources + wget -O rcedit.exe https://github.com/electron/rcedit/releases/download/v2.0.0/rcedit-${{ matrix.arch }}.exe + chmod +x ./rcedit.exe + + dll_filename=libffi-8.dll + ./rcedit.exe "${{ matrix.host }}"/.libs/$dll_filename \ + --set-file-version "$maj.$min.$pat.$build" \ + --set-product-version "$maj.$min.$pat.$build" \ + --set-version-string "CompanyName" "https://github.com/libffi/libffi" \ + --set-version-string "FileDescription" "Portable foreign function interface library (${{ matrix.arch }})" \ + --set-version-string "ProductName" "libffi" \ + --set-version-string "FileVersion" "${{ steps.ver.outputs.version }}" \ + --set-version-string "LegalCopyright" "Copyright (c) 1996-2025 Anthony Green and others" \ + --set-version-string "OriginalFilename" "$dll_filename" + + - name: Create binary distribution + shell: C:\tools\cygwin\bin\bash.exe --login --norc -eo pipefail -o igncr '{0}' + run: | + cd $(cygpath $RUNNER_WORKSPACE)/libffi + set -euxo pipefail + + pkgdir="libffi-${{ steps.ver.outputs.version }}-x86-${{ matrix.width }}bit-msvc-binaries" + mkdir -p "$pkgdir" + + # Copy libraries, headers, and licence into the *same* directory + cp "${{ matrix.host }}"/.libs/libffi-8.* "$pkgdir/" + cp "${{ matrix.host }}"/include/*.h "$pkgdir/" + cp LICENSE "$pkgdir/" + + - name: Upload artefact + uses: actions/upload-artifact@v4 + with: + name: libffi-${{ steps.ver.outputs.version }}-x86-${{ matrix.width }}bit-msvc-binaries + path: libffi-${{ steps.ver.outputs.version }}-x86-${{ matrix.width }}bit-msvc-binaries + if-no-files-found: error + + - name: Upload to GitHub Release + if: github.ref_type == 'tag' + uses: softprops/action-gh-release@v2 + with: + files: libffi-${{ steps.ver.outputs.version }}-x86-${{ matrix.width }}bit-msvc-binaries.zip + build-android: name: Android ${{ matrix.host }} runs-on: ubuntu-latest @@ -456,7 +333,7 @@ jobs: steps: - run: git config --global core.autocrlf input - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - run: ./.ci/install.sh - env: HOST: ${{ matrix.HOST }} @@ -469,6 +346,8 @@ jobs: export TOOLCHAIN="${ANDROID_NDK_ROOT}"/toolchains/llvm/prebuilt/linux-x86_64 export CC="${TOOLCHAIN}"/bin/${HOST}${ANDROID_API_LEVEL}-clang export CXX="${TOOLCHAIN}"/bin/${HOST}${ANDROID_API_LEVEL}-clang++ + export CC_FOR_TARGET="${TOOLCHAIN}"/bin/${HOST}${ANDROID_API_LEVEL}-clang + export CXX_FOR_TARGET="${TOOLCHAIN}"/bin/${HOST}${ANDROID_API_LEVEL}-clang++ export LD="${TOOLCHAIN}"/bin/ld.lld export AR="${TOOLCHAIN}"/bin/llvm-ar export AS="${CC}" diff --git a/.github/workflows/emscripten.yml b/.github/workflows/emscripten.yml index 2be8df404..a8c0a1001 100644 --- a/.github/workflows/emscripten.yml +++ b/.github/workflows/emscripten.yml @@ -19,7 +19,7 @@ env: # "info" field, or in Makefile.envs: # https://github.com/pyodide/pyodide/blob/main/Makefile.envs#L2 PYTHON_VERSION: 3.12.7 - EMSCRIPTEN_VERSION: 3.1.58 + EMSCRIPTEN_VERSION: 4.0.10 EM_CACHE_FOLDER: emsdk-cache jobs: @@ -39,6 +39,21 @@ jobs: actions-cache-folder: ${{ env.EM_CACHE_FOLDER }} test-dejagnu: + strategy: + matrix: + target: + - name: wasm32 + host: wasm32 + configureflags: + testflags: + - name: wasm64 + host: wasm64 + configureflags: + testflags: -sMEMORY64=1 + - name: wasm64-2 + host: wasm64 + configureflags: WASM64_MEMORY64=2 + testflags: -sMEMORY64=2 runs-on: ubuntu-24.04 needs: [setup-emsdk-cache] steps: @@ -62,11 +77,26 @@ jobs: version: ${{ env.EMSCRIPTEN_VERSION }} actions-cache-folder: ${{ env.EM_CACHE_FOLDER }} + - name: Setup node.js + uses: actions/setup-node@v4 + with: + node-version: 24 + + # This step updates emsdk's configuration file ".emscripten" to point to + # nodejs installed in the previous step. + - name: Configure emsdk to use the installed node.js + run: sed -i -E 's|NODE_JS = .*|NODE_JS = '"'$(which node)'"'|g' ${EMSDK}/.emscripten + - name: Install dependencies run: sudo apt-get install dejagnu libltdl-dev - name: Run tests run: testsuite/emscripten/node-tests.sh + env: + TARGET_HOST: ${{ matrix.target.host }} + EXTRA_CONFIGURE_FLAGS: ${{ matrix.target.configureflags }} + EXTRA_CFLAGS: ${{ matrix.target.testflags }} + EXTRA_TEST_LDFLAGS: ${{ matrix.target.testflags }} - name: Install rlgl and run run: | @@ -77,6 +107,21 @@ jobs: exit $? build: + strategy: + matrix: + target: + - name: wasm32 + host: wasm32 + configureflags: + testflags: + - name: wasm64 + host: wasm64 + configureflags: + testflags: -sMEMORY64=1 + - name: wasm64-2 + host: wasm64 + configureflags: WASM64_MEMORY64=2 + testflags: -sMEMORY64=2 runs-on: ubuntu-24.04 needs: [setup-emsdk-cache] steps: @@ -100,6 +145,9 @@ jobs: - name: Build run: ./testsuite/emscripten/build.sh + env: + TARGET_HOST: ${{ matrix.target.host }} + EXTRA_CONFIGURE_FLAGS: ${{ matrix.target.configureflags }} - name: Build tests run: | @@ -107,16 +155,23 @@ jobs: cp -r testsuite/libffi.closures testsuite/libffi.closures.test ./testsuite/emscripten/build-tests.sh testsuite/libffi.call.test ./testsuite/emscripten/build-tests.sh testsuite/libffi.closures.test + env: + EXTRA_CFLAGS: ${{ matrix.target.testflags }} + EXTRA_LD_FLAGS: ${{ matrix.target.testflags }} - name: Store artifacts uses: actions/upload-artifact@v4 with: - name: built-tests + name: built-tests-${{ matrix.target.name }} path: ./testsuite/libffi.c*/ test: strategy: matrix: + target: + - name: wasm32 + - name: wasm64 + - name: wasm64-2 browser: ["chrome"] # FIXME: selenium can't find gecko driver for "firefox" runs-on: ubuntu-24.04 @@ -128,7 +183,7 @@ jobs: - name: Download build artifact uses: actions/download-artifact@v4 with: - name: built-tests + name: built-tests-${{ matrix.target.name }} path: ./testsuite/ - uses: conda-incubator/setup-miniconda@v3 diff --git a/.github/workflows/label-new-issue.yaml b/.github/workflows/label-new-issue.yaml new file mode 100644 index 000000000..9a73973f0 --- /dev/null +++ b/.github/workflows/label-new-issue.yaml @@ -0,0 +1,15 @@ +on: + issues: + types: [opened] + +jobs: + label_issue_job: + permissions: write-all + runs-on: ubuntu-latest + name: Label new issue + steps: + - id: label-new-issue + uses: atgreen/gail-issue-labeler-action@master + with: + llm_api_key: ${{ secrets.OPENAI_KEY }} + github_token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/tarball.yml b/.github/workflows/tarball.yml new file mode 100644 index 000000000..c83222e7c --- /dev/null +++ b/.github/workflows/tarball.yml @@ -0,0 +1,55 @@ +name: snapshot-dist-tarball +on: + push: + branches: [ master ] + workflow_dispatch: + +jobs: + dist: + runs-on: ubuntu-latest + steps: + - name: Install tools + run: | + sudo apt update + sudo apt install texlive texinfo autoconf automake libtool libltdl-dev + + - uses: actions/checkout@v4 + with: {fetch-depth: 0} + + - name: Compute snapshot VERSION + id: ver + run: | + DESC=$(git describe --long --tags --match 'v[0-9]*' 2>/dev/null || echo "") + VERSION=$(echo "$DESC" | sed -E 's/^v//; s/-([0-9]+)-g/\.\1+g/') + if [[ -z "$VERSION" || "$VERSION" == "$DESC" ]]; then + VERSION="3.5.1-dev.0+g$(git rev-parse --short HEAD)" + fi + echo "VERSION=$VERSION" >> "$GITHUB_ENV" + echo "version=$VERSION" >> "$GITHUB_OUTPUT" + + - name: Patch configure.ac + run: | + sed -Ei "s/^(AC_INIT\(\[libffi\],\s*\[)[^]]+/\1${VERSION}/" configure.ac + sed -Ei "s/^(FFI_VERSION_STRING=\")[^\"]+/\1${VERSION}/" configure.ac + + - run: autoreconf -fi + - run: ./configure + - run: make dist # produces libffi-${VERSION}.tar.gz + + - name: Wipe old snapshot assets + uses: mknejp/delete-release-assets@v1 + with: + token: ${{ secrets.GITHUB_TOKEN }} + tag: snapshots # ← whatever tag your nightly release uses + fail-if-no-assets: false + assets: | + libffi-*.tar.* + libffi-*.zip + + - name: Create (or update) “snapshots” release + uses: softprops/action-gh-release@v2 + with: + tag_name: snapshots + prerelease: true + body: "Snapshot built from ${{ github.sha }}" + files: libffi-${{ env.VERSION }}.tar.* diff --git a/LICENSE b/LICENSE index 7282e798c..12b4970e0 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -libffi - Copyright (c) 1996-2024 Anthony Green, Red Hat, Inc and others. +libffi - Copyright (c) 1996-2025 Anthony Green, Red Hat, Inc and others. See source files for details. Permission is hereby granted, free of charge, to any person obtaining diff --git a/Makefile.am b/Makefile.am index 1f8aa9cce..c3bcf00b3 100644 --- a/Makefile.am +++ b/Makefile.am @@ -4,6 +4,10 @@ AUTOMAKE_OPTIONS = foreign subdir-objects ACLOCAL_AMFLAGS = -I m4 +# Alias required by AX_ENABLE_BUILDDIR / config-ml +.PHONY: all-configured +all-configured: all + SUBDIRS = include testsuite man if BUILD_DOCS ## This hack is needed because it doesn't seem possible to make a @@ -14,15 +18,15 @@ if BUILD_DOCS SUBDIRS += doc endif -EXTRA_DIST = LICENSE ChangeLog.old \ - m4/libtool.m4 m4/lt~obsolete.m4 \ - m4/ltoptions.m4 m4/ltsugar.m4 m4/ltversion.m4 \ - m4/ltversion.m4 src/debug.c msvcc.sh \ - generate-darwin-source-and-headers.py \ - libffi.xcodeproj/project.pbxproj \ - src/powerpc/t-aix \ - libtool-ldflags libtool-version configure.host README.md \ - libffi.map.in LICENSE-BUILDTOOLS msvc_build make_sunver.pl +EXTRA_DIST = LICENSE ChangeLog.old \ + m4/libtool.m4 m4/lt~obsolete.m4 \ + m4/ltoptions.m4 m4/ltsugar.m4 m4/ltversion.m4 \ + m4/ltversion.m4 src/debug.c msvcc.sh \ + generate-darwin-source-and-headers.py \ + libffi.xcodeproj/project.pbxproj \ + src/powerpc/t-aix \ + libtool-ldflags libtool-version configure.host README.md \ + libffi.map.in LICENSE-BUILDTOOLS msvc_build make_sunver.pl # local.exp is generated by configure DISTCLEANFILES = local.exp @@ -58,11 +62,12 @@ noinst_HEADERS = src/aarch64/ffitarget.h src/aarch64/internal.h \ src/moxie/ffitarget.h \ src/or1k/ffitarget.h src/pa/ffitarget.h \ src/powerpc/ffitarget.h src/powerpc/asm.h \ - src/powerpc/ffi_powerpc.h src/riscv/ffitarget.h \ + src/powerpc/ffi_powerpc.h src/powerpc/internal.h \ + src/riscv/ffitarget.h \ src/s390/ffitarget.h src/s390/internal.h src/sh/ffitarget.h \ src/sh64/ffitarget.h src/sparc/ffitarget.h \ src/sparc/internal.h src/tile/ffitarget.h src/vax/ffitarget.h \ - src/wasm32/ffitarget.h \ + src/wasm/ffitarget.h \ src/x86/ffitarget.h src/x86/internal.h src/x86/internal64.h \ src/x86/asmnames.h src/xtensa/ffitarget.h src/dlmalloc.c \ src/kvx/ffitarget.h src/kvx/asm.h \ @@ -93,7 +98,7 @@ EXTRA_libffi_la_SOURCES = src/aarch64/ffi.c src/aarch64/sysv.S \ src/sh64/sysv.S src/sparc/ffi.c src/sparc/ffi64.c \ src/sparc/v8.S src/sparc/v9.S src/tile/ffi.c src/tile/tile.S \ src/vax/ffi.c src/vax/elfbsd.S src/x86/ffi.c src/x86/sysv.S \ - src/wasm32/ffi.c \ + src/wasm/ffi.c \ src/x86/ffiw64.c src/x86/win64.S src/x86/ffi64.c \ src/x86/unix64.S src/x86/sysv_intel.S src/x86/win64_intel.S \ src/xtensa/ffi.c src/xtensa/sysv.S src/kvx/ffi.c \ @@ -146,7 +151,7 @@ libffi_la_LDFLAGS = -no-undefined $(libffi_version_info) $(libffi_version_script libffi_la_DEPENDENCIES = $(libffi_la_LIBADD) $(libffi_version_dep) AM_CPPFLAGS = -I. -I$(top_srcdir)/include -Iinclude -I$(top_srcdir)/src -AM_CCASFLAGS = $(AM_CPPFLAGS) +AM_CCASFLAGS = '$(AM_CPPFLAGS)' dist-hook: d=`(cd $(distdir); pwd)`; (cd doc; make pdf; cp *.pdf $$d/doc) diff --git a/README.md b/README.md index ba1ad08d1..2370c312e 100644 --- a/README.md +++ b/README.md @@ -7,8 +7,7 @@ Status ====== -libffi-3.4.7 was released on February 8, 2025. Check the libffi web -page for updates: . +libffi-3.5.2 was released on August 2, 2025. What is libffi? @@ -97,6 +96,9 @@ tested: | RISC-V 64-bit | Linux | GCC | | S390 | Linux | GCC | | S390X | Linux | GCC | +| SH3 | Linux | GCC | +| SH4 | Linux | GCC | +| SH5/SH64 | Linux | GCC | | SPARC | Linux | GCC | | SPARC | Solaris | GCC | | SPARC | Solaris | Oracle Solaris Studio C | @@ -106,6 +108,7 @@ tested: | TILE-Gx/TILEPro | Linux | GCC | | VAX | OpenBSD/vax | GCC | | WASM32 | Emscripten | EMCC | +| WASM64 | Emscripten | EMCC | | X86 | FreeBSD | GCC | | X86 | GNU HURD | GCC | | X86 | Interix | GCC | @@ -117,6 +120,7 @@ tested: | X86 | Solaris | Oracle Solaris Studio C | | X86 | Windows/Cygwin | GCC | | X86 | Windows/MinGW | GCC | +| X86-64 | DragonFly BSD | GCC | | X86-64 | FreeBSD | GCC | | X86-64 | Linux | GCC | | X86-64 | Linux/x32 | GCC | @@ -141,7 +145,7 @@ compiler. If you're building libffi directly from git hosted sources, configure won't exist yet; run ./autogen.sh first. This will require that you -install autoconf, automake and libtool. +install autoconf, automake, libtool and texinfo. You may want to tell configure where to install the libffi library and header files. To do that, use the ``--prefix`` configure switch. Libffi @@ -206,7 +210,30 @@ History See the git log for details at http://github.com/libffi/libffi. - 3.4.7 Feb-8-2024 + 3.5.2 Aug-2-2025 + Add wasm64 support. + Add DragonFly BSD support. + Ensure trampoline file descriptors are closed on exec. + + 3.5.1 Jun-10-2025 + Fix symbol versioning error. + + 3.5.0 Jun-8-2025 + Add FFI_VERSION_STRING and FFI_VERSION_NUMBER macros, as well + as ffi_get_version() and ffi_get_version_number() functions. + Add ffi_get_default_abi() and ffi_get_closure_size() functions. + Fix closures on powerpc64-linux when statically linking. + Mark the PA stack as non-executable. + + 3.4.8 Apr-9-2025 + Add static trampoline support for powerpc-linux (32-bit SYSV BE), + powerpc64-linux (64-bit ELFv1 BE) and + powerpc64le-linux (64-bit ELFv2 LE) + Various x86-64 bug fixes (x32 ABI and improper memory access for + small argument calls). + Fix to enable pointer authentication for aarch64. + + 3.4.7 Feb-8-2025 Add static trampoline support for Linux on s390x. Fix BTI support for ARM64. Support pointer authentication for ARM64. diff --git a/configure.ac b/configure.ac index a1d02b803..dcfc7b242 100644 --- a/configure.ac +++ b/configure.ac @@ -1,10 +1,15 @@ dnl Process this with autoconf to create configure -AC_PREREQ([2.71]) +AC_PREREQ([2.68]) -AC_INIT([libffi],[3.4.7],[http://github.com/libffi/libffi/issues]) +AC_INIT([libffi],[3.5.2],[http://github.com/libffi/libffi/issues]) AC_CONFIG_HEADERS([fficonfig.h]) +FFI_VERSION_STRING="3.5.2" +FFI_VERSION_NUMBER=30502 +AC_SUBST(FFI_VERSION_STRING) +AC_SUBST(FFI_VERSION_NUMBER) + AC_CANONICAL_TARGET target_alias=${target_alias-$host_alias} @@ -89,10 +94,6 @@ m4_warn([obsolete], [The preprocessor macro `STDC_HEADERS' is obsolete. Except in unusual embedded environments, you can safely include all ISO C90 headers unconditionally.])dnl -# Autoupdate added the next two lines to ensure that your configure -# script's behavior did not change. They are probably safe to remove. -AC_CHECK_INCLUDES_DEFAULT -AC_PROG_EGREP AC_CHECK_FUNCS(memcpy) AC_CHECK_HEADERS(alloca.h) @@ -122,6 +123,8 @@ AC_C_BIGENDIAN GCC_AS_CFI_PSEUDO_OP +AC_ARG_VAR([WASM64_MEMORY64], [Used only for the wasm64 target. Set to 1 (default) or 2 for Emscripten's -sMEMORY64 mode]) + case "$TARGET" in SPARC) AC_CACHE_CHECK([assembler and linker support unaligned pc related relocs], @@ -181,6 +184,12 @@ case "$TARGET" in [Define if the compiler uses zarch features.]) fi ;; + wasm64) + if test -z "$WASM64_MEMORY64"; then + WASM64_MEMORY64=1 + fi + CFLAGS="$CFLAGS -sMEMORY64=$WASM64_MEMORY64" + ;; esac AC_CACHE_CHECK([whether compiler supports pointer authentication], @@ -229,7 +238,7 @@ case "$target" in [Cannot use PROT_EXEC on this target, so, we revert to alternative means]) ;; - *-apple-* | *-*-freebsd* | *-*-kfreebsd* | *-*-openbsd* | *-pc-solaris* | *-linux-android*) + *-apple-* | *-*-dragonfly* | *-*-freebsd* | *-*-kfreebsd* | *-*-openbsd* | *-pc-solaris* | *-linux-android*) AC_DEFINE(FFI_MMAP_EXEC_WRIT, 1, [Cannot use malloc on this target, so, we revert to alternative means]) @@ -382,7 +391,8 @@ case "$target" in [Define this if you want statically defined trampolines]) fi ;; - *arm*-*-linux-* | aarch64*-*-linux-* | i*86-*-linux-* | x86_64-*-linux-* | loongarch*-*-linux-* | s390x*-linux-*) + *arm*-*-linux-* | aarch64*-*-linux-* | i*86-*-linux-* | x86_64-*-linux-* \ + | loongarch*-*-linux-* | s390x*-linux-* | powerpc*-linux-*) AC_DEFINE(FFI_EXEC_STATIC_TRAMP, 1, [Define this if you want statically defined trampolines]) ;; diff --git a/configure.host b/configure.host index c69a49cef..4e10c3edb 100644 --- a/configure.host +++ b/configure.host @@ -261,7 +261,12 @@ case "${host}" in ;; wasm32-*-*) - TARGET=wasm32; TARGETDIR=wasm32 + TARGET=wasm32; TARGETDIR=wasm + SOURCES="ffi.c" + ;; + + wasm64-*-*) + TARGET=wasm64; TARGETDIR=wasm SOURCES="ffi.c" ;; diff --git a/doc/libffi.texi b/doc/libffi.texi index e82f94402..165c2fb1f 100644 --- a/doc/libffi.texi +++ b/doc/libffi.texi @@ -18,7 +18,7 @@ This manual is for libffi, a portable foreign function interface library. -Copyright @copyright{} 2008--2024 Anthony Green and Red Hat, Inc. +Copyright @copyright{} 2008--2025 Anthony Green and Red Hat, Inc. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the @@ -236,6 +236,29 @@ object declared as @code{short}; but if the return type is a larger type -- usually @code{ffi_arg}. @end defun +@findex ffi_get_version +@defun {const char *} ffi_get_version (void) +Returns the library version as a string. This string is also +available at build time as the macro @code{FFI_VERSION_STRING}. +@end defun + +@findex ffi_get_version_number +@defun {unsigned long} ffi_get_version_number (void) +Returns the library version as an unsigned long value where +version ``x.y.z'' is represented as the number x*10000+y*100+z. +This number is also available at build time as the macro +@code{FFI_VERSION_NUMBER}. +@end defun + +@findex ffi_get_default_abi +@defun {unsigned int} ffi_get_default_abi (void) +Return the value of @code{FFI_DEFAULT_ABI}. +@end defun + +@findex ffi_get_closure_size +@defun {size_t} ffi_get_closure_size (void) +Return @code{sizeof(ffi_closure)}. +@end defun @node Simple Example @section Simple Example @@ -263,14 +286,14 @@ int main() &ffi_type_sint, args) == FFI_OK) @{ s = "Hello World!"; - ffi_call(&cif, puts, &rc, values); + ffi_call(&cif, (void(*)())puts, &rc, values); /* rc now holds the result of the call to puts */ /* values holds a pointer to the function's arg, so to call puts() again all we need to do is change the value of s */ s = "This is cool!"; - ffi_call(&cif, puts, &rc, values); + ffi_call(&cif, (void(*)())puts, &rc, values); @} return 0; diff --git a/doc/version.texi b/doc/version.texi index 73e9e6f4c..fc180c12d 100644 --- a/doc/version.texi +++ b/doc/version.texi @@ -1,4 +1,4 @@ -@set UPDATED 1 June 2024 -@set UPDATED-MONTH June 2024 -@set EDITION 3.4.7 -@set VERSION 3.4.7 +@set UPDATED 2 August 2025 +@set UPDATED-MONTH August 2025 +@set EDITION 3.5.2 +@set VERSION 3.5.2 diff --git a/generate-darwin-source-and-headers.py b/generate-darwin-source-and-headers.py index c801dc065..f6e915284 100755 --- a/generate-darwin-source-and-headers.py +++ b/generate-darwin-source-and-headers.py @@ -11,15 +11,6 @@ class Platform(object): pass -class i386_platform(Platform): - arch = 'i386' - - prefix = "#ifdef __i386__\n\n" - suffix = "\n\n#endif" - src_dir = 'x86' - src_files = ['sysv.S', 'ffi.c', 'internal.h'] - - class x86_64_platform(Platform): arch = 'x86_64' @@ -47,13 +38,6 @@ class armv7_platform(Platform): src_files = ['sysv.S', 'ffi.c', 'internal.h'] -class ios_simulator_i386_platform(i386_platform): - target = 'i386-apple-ios-simulator' - directory = 'darwin_ios' - sdk = 'iphonesimulator' - version_min = '-miphoneos-version-min=7.0' - - class ios_simulator_x86_64_platform(x86_64_platform): target = 'x86_64-apple-ios-simulator' directory = 'darwin_ios' @@ -117,13 +101,6 @@ class tvos_device_arm64_platform(arm64_platform): version_min = '-mtvos-version-min=9.0' -class watchos_simulator_i386_platform(i386_platform): - target = 'i386-apple-watchos-simulator' - directory = 'darwin_watchos' - sdk = 'watchsimulator' - version_min = '-mwatchos-version-min=4.0' - - class watchos_simulator_x86_64_platform(x86_64_platform): target = 'x86_64-apple-watchos-simulator' directory = 'darwin_watchos' @@ -210,7 +187,7 @@ def xcrun_cmd(cmd): mkdir_p(build_dir) env = dict(CC=xcrun_cmd('clang'), LD=xcrun_cmd('ld'), - CFLAGS='%s -fembed-bitcode' % (platform.version_min)) + CFLAGS='%s' % (platform.version_min)) working_dir = os.getcwd() try: os.chdir(build_dir) @@ -248,7 +225,6 @@ def generate_source_and_headers( copy_files('include', 'darwin_common/include', pattern='*.h') if generate_ios: - copy_src_platform_files(ios_simulator_i386_platform) copy_src_platform_files(ios_simulator_x86_64_platform) copy_src_platform_files(ios_simulator_arm64_platform) copy_src_platform_files(ios_device_armv7_platform) @@ -261,7 +237,6 @@ def generate_source_and_headers( copy_src_platform_files(tvos_simulator_arm64_platform) copy_src_platform_files(tvos_device_arm64_platform) if generate_watchos: - copy_src_platform_files(watchos_simulator_i386_platform) copy_src_platform_files(watchos_simulator_x86_64_platform) copy_src_platform_files(watchos_simulator_arm64_platform) copy_src_platform_files(watchos_device_armv7k_platform) @@ -270,7 +245,6 @@ def generate_source_and_headers( platform_headers = collections.defaultdict(set) if generate_ios: - build_target(ios_simulator_i386_platform, platform_headers) build_target(ios_simulator_x86_64_platform, platform_headers) build_target(ios_simulator_arm64_platform, platform_headers) build_target(ios_device_armv7_platform, platform_headers) @@ -283,7 +257,6 @@ def generate_source_and_headers( build_target(tvos_simulator_arm64_platform, platform_headers) build_target(tvos_device_arm64_platform, platform_headers) if generate_watchos: - build_target(watchos_simulator_i386_platform, platform_headers) build_target(watchos_simulator_x86_64_platform, platform_headers) build_target(watchos_simulator_arm64_platform, platform_headers) build_target(watchos_device_armv7k_platform, platform_headers) diff --git a/include/ffi.h.in b/include/ffi.h.in index e5c1daef3..6ce7ac55e 100644 --- a/include/ffi.h.in +++ b/include/ffi.h.in @@ -1,6 +1,6 @@ /* -----------------------------------------------------------------*-C-*- libffi @VERSION@ - - Copyright (c) 2011, 2014, 2019, 2021, 2022, 2024 Anthony Green + - Copyright (c) 2011, 2014, 2019, 2021, 2022, 2024, 2025 Anthony Green - Copyright (c) 1996-2003, 2007, 2008 Red Hat, Inc. Permission is hereby granted, free of charge, to any person @@ -314,6 +314,24 @@ void ffi_java_raw_to_ptrarray (ffi_cif *cif, ffi_java_raw *raw, void **args) __a FFI_API size_t ffi_java_raw_size (ffi_cif *cif) __attribute__((deprecated)); +/* ---- Version API ------------------------------------------------------ */ + +#define FFI_VERSION_STRING "@FFI_VERSION_STRING@" +#define FFI_VERSION_NUMBER @FFI_VERSION_NUMBER@ + +#ifndef LIBFFI_ASM +/* Return a version string. */ +FFI_API const char *ffi_get_version (void); + +/* Return the version as an unsigned long value: (x * 10000 + y * 100 + z) */ +FFI_API unsigned long ffi_get_version_number (void); +#endif + +/* ---- Internals API ---------------------------------------------------- */ + +FFI_API unsigned int ffi_get_default_abi (void); +FFI_API size_t ffi_get_closure_size (void); + /* ---- Definitions for closures ----------------------------------------- */ #if FFI_CLOSURES diff --git a/libffi.map.in b/libffi.map.in index 08c50b22d..cc135ba8e 100644 --- a/libffi.map.in +++ b/libffi.map.in @@ -23,7 +23,6 @@ LIBFFI_BASE_8.0 { ffi_type_longdouble; ffi_type_pointer; - /* Exported functions. */ ffi_call; ffi_prep_cif; ffi_prep_cif_var; @@ -46,6 +45,19 @@ LIBFFI_BASE_8.0 { *; }; +/* ---------------------------------------------------------------------- + Symbols **added in libffi 3.5.0**. + Give them a fresh namespace so that package managers notice when + code requires a newer libffi than 3.4.x. + -------------------------------------------------------------------- */ +LIBFFI_BASE_8.1 { + global: + ffi_get_version; + ffi_get_version_number; + ffi_get_default_abi; + ffi_get_closure_size; +} LIBFFI_BASE_8.0; + #ifdef FFI_TARGET_HAS_COMPLEX_TYPE LIBFFI_COMPLEX_8.0 { global: diff --git a/libtool-version b/libtool-version index 3b32cb76a..37c2bc903 100644 --- a/libtool-version +++ b/libtool-version @@ -26,4 +26,4 @@ # release, then set age to 0. # # CURRENT:REVISION:AGE -9:4:1 +10:0:2 diff --git a/m4/asmcfi.m4 b/m4/asmcfi.m4 index 3e2860227..6be634b44 100644 --- a/m4/asmcfi.m4 +++ b/m4/asmcfi.m4 @@ -1,13 +1,30 @@ +dnl ------------------------------------------------------------------ +dnl Check whether the assembler understands .cfi_* pseudo-ops. +dnl ------------------------------------------------------------------ AC_DEFUN([GCC_AS_CFI_PSEUDO_OP], -[AC_CACHE_CHECK([assembler .cfi pseudo-op support], - gcc_cv_as_cfi_pseudo_op, [ - gcc_cv_as_cfi_pseudo_op=unknown - AC_TRY_COMPILE([asm (".cfi_sections\n\t.cfi_startproc\n\t.cfi_endproc");],, - [gcc_cv_as_cfi_pseudo_op=yes], - [gcc_cv_as_cfi_pseudo_op=no]) - ]) - if test "x$gcc_cv_as_cfi_pseudo_op" = xyes; then - AC_DEFINE(HAVE_AS_CFI_PSEUDO_OP, 1, - [Define if your assembler supports .cfi_* directives.]) - fi +[ + AC_CACHE_CHECK([assembler .cfi pseudo-op support], + [gcc_cv_as_cfi_pseudo_op], + [ AC_LANG_PUSH([C]) + AC_COMPILE_IFELSE( + [AC_LANG_SOURCE([[ + #ifdef _MSC_VER + Nope. + #endif + int foo (void) + { + __asm__ (".cfi_remember_state\n\t" + ".cfi_restore_state\n\t"); + return 0; + } + ]])], + [gcc_cv_as_cfi_pseudo_op=yes], + [gcc_cv_as_cfi_pseudo_op=no]) + AC_LANG_POP([C]) + ]) + + if test "x$gcc_cv_as_cfi_pseudo_op" = xyes; then + AC_DEFINE([HAVE_AS_CFI_PSEUDO_OP], [1], + [Define if your assembler supports .cfi_* directives.]) + fi ]) diff --git a/m4/ax_check_compile_flag.m4 b/m4/ax_check_compile_flag.m4 index bd753b34d..54191c553 100644 --- a/m4/ax_check_compile_flag.m4 +++ b/m4/ax_check_compile_flag.m4 @@ -34,14 +34,24 @@ # and this notice are preserved. This file is offered as-is, without any # warranty. -#serial 6 +#serial 11 AC_DEFUN([AX_CHECK_COMPILE_FLAG], [AC_PREREQ(2.64)dnl for _AC_LANG_PREFIX and AS_VAR_IF AS_VAR_PUSHDEF([CACHEVAR],[ax_cv_check_[]_AC_LANG_ABBREV[]flags_$4_$1])dnl -AC_CACHE_CHECK([whether _AC_LANG compiler accepts $1], CACHEVAR, [ +AC_CACHE_CHECK([whether the _AC_LANG compiler accepts $1], CACHEVAR, [ ax_check_save_flags=$[]_AC_LANG_PREFIX[]FLAGS - _AC_LANG_PREFIX[]FLAGS="$[]_AC_LANG_PREFIX[]FLAGS $4 $1" + if test x"m4_case(_AC_LANG, + [C], [$GCC], + [C++], [$GXX], + [Fortran], [$GFC], + [Fortran 77], [$G77], + [Objective C], [$GOBJC], + [Objective C++], [$GOBJCXX], + [no])" = xyes ; then + add_gnu_werror="-Werror" + fi + _AC_LANG_PREFIX[]FLAGS="$[]_AC_LANG_PREFIX[]FLAGS $4 $1 $add_gnu_werror" AC_COMPILE_IFELSE([m4_default([$5],[AC_LANG_PROGRAM()])], [AS_VAR_SET(CACHEVAR,[yes])], [AS_VAR_SET(CACHEVAR,[no])]) diff --git a/src/aarch64/internal.h b/src/aarch64/internal.h index 50fa5c139..591b9f5c0 100644 --- a/src/aarch64/internal.h +++ b/src/aarch64/internal.h @@ -91,6 +91,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #define SIGN_LR_LINUX_ONLY #define BRANCH_TO_REG braaz #define PAC_CFI_WINDOW_SAVE + #define GNU_PROPERTY_AARCH64_POINTER_AUTH 0 /* Linux PAC Support */ #elif defined(__ARM_FEATURE_PAC_DEFAULT) #define GNU_PROPERTY_AARCH64_POINTER_AUTH (1 << 1) @@ -140,5 +141,6 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #define SIGN_LR_LINUX_ONLY #define BRANCH_TO_REG br #define PAC_CFI_WINDOW_SAVE + #define GNU_PROPERTY_AARCH64_POINTER_AUTH 0 #endif /* HAVE_ARM64E_PTRAUTH */ #endif /* LIBFFI_ASM */ diff --git a/src/aarch64/sysv.S b/src/aarch64/sysv.S index e83bc65de..81d33f2ef 100644 --- a/src/aarch64/sysv.S +++ b/src/aarch64/sysv.S @@ -692,7 +692,7 @@ CNAME(ffi_go_closure_SYSV): .asciz "GNU"; .long 0xc0000000; /* GNU_PROPERTY_AARCH64_FEATURE_1_AND */ .long 4; - .long GNU_PROPERTY_AARCH64_BTI; + .long GNU_PROPERTY_AARCH64_BTI | GNU_PROPERTY_AARCH64_POINTER_AUTH; .long 0; .popsection; #endif diff --git a/src/mips/ffitarget.h b/src/mips/ffitarget.h index 294c3ba1a..cc7250cc1 100644 --- a/src/mips/ffitarget.h +++ b/src/mips/ffitarget.h @@ -32,16 +32,14 @@ #error "Please do not include ffitarget.h directly into your source. Use ffi.h instead." #endif -#ifdef __linux__ -# include -#elif defined(__rtems__) +#ifdef __rtems__ /* * Subprogram calling convention - copied from sgidefs.h */ #define _MIPS_SIM_ABI32 1 #define _MIPS_SIM_NABI32 2 #define _MIPS_SIM_ABI64 3 -#elif !defined(__OpenBSD__) && !defined(__FreeBSD__) +#elif !defined(__OpenBSD__) && !defined(__FreeBSD__) && !defined(__linux__) # include #endif diff --git a/src/pa/linux.S b/src/pa/linux.S index 2d3b03604..fdd433247 100644 --- a/src/pa/linux.S +++ b/src/pa/linux.S @@ -425,3 +425,7 @@ ffi_closure_pa32: .align 4 .LEFDE2: + +#if defined(__ELF__) && defined(__linux__) +.section .note.GNU-stack,"",%progbits +#endif diff --git a/src/powerpc/ffi.c b/src/powerpc/ffi.c index a19bcbbfc..3601cc4ab 100644 --- a/src/powerpc/ffi.c +++ b/src/powerpc/ffi.c @@ -31,6 +31,8 @@ #include "ffi.h" #include "ffi_common.h" #include "ffi_powerpc.h" +#include "internal.h" +#include #if HAVE_LONG_DOUBLE_VARIANT /* Adjust ffi_type_longdouble. */ @@ -173,3 +175,20 @@ ffi_prep_go_closure (ffi_go_closure *closure, closure->fun = fun; return FFI_OK; } + +#ifdef FFI_EXEC_STATIC_TRAMP +void * +ffi_tramp_arch (size_t *tramp_size, size_t *map_size) +{ + extern void *trampoline_code_table; + *tramp_size = PPC_TRAMP_SIZE; + *map_size = PPC_TRAMP_MAP_SIZE; +#if defined (_CALL_AIX) || _CALL_ELF == 1 + /* The caller wants the entry point address of the trampoline code, + not the address of the function descriptor. */ + return *(void **)trampoline_code_table; +#else + return &trampoline_code_table; +#endif +} +#endif diff --git a/src/powerpc/ffi_linux64.c b/src/powerpc/ffi_linux64.c index 3454dacd3..90100a4d8 100644 --- a/src/powerpc/ffi_linux64.c +++ b/src/powerpc/ffi_linux64.c @@ -29,6 +29,7 @@ ----------------------------------------------------------------------- */ #include "ffi.h" +#include #ifdef POWERPC64 #include "ffi_common.h" @@ -820,32 +821,38 @@ ffi_prep_closure_loc_linux64 (ffi_closure *closure, void *user_data, void *codeloc) { -#if _CALL_ELF == 2 - unsigned int *tramp = (unsigned int *) &closure->tramp[0]; - if (cif->abi < FFI_LINUX || cif->abi >= FFI_LAST_ABI) return FFI_BAD_ABI; - tramp[0] = 0xe96c0018; /* 0: ld 11,2f-0b(12) */ - tramp[1] = 0xe98c0010; /* ld 12,1f-0b(12) */ - tramp[2] = 0x7d8903a6; /* mtctr 12 */ - tramp[3] = 0x4e800420; /* bctr */ +#ifdef FFI_EXEC_STATIC_TRAMP + if (ffi_tramp_is_present(closure)) + { + /* Initialize the static trampoline's parameters. */ + void (*dest)(void) = ffi_closure_LINUX64; + ffi_tramp_set_parms (closure->ftramp, dest, closure); + } + else +#endif + { +#if _CALL_ELF == 2 + unsigned int *tramp = (unsigned int *) &closure->tramp[0]; + tramp[0] = 0xe96c0018; /* 0: ld 11,2f-0b(12) */ + tramp[1] = 0xe98c0010; /* ld 12,1f-0b(12) */ + tramp[2] = 0x7d8903a6; /* mtctr 12 */ + tramp[3] = 0x4e800420; /* bctr */ /* 1: .quad function_addr */ /* 2: .quad context */ - *(void **) &tramp[4] = (void *) ffi_closure_LINUX64; - *(void **) &tramp[6] = codeloc; - flush_icache ((char *) tramp, (char *) codeloc, 4 * 4); + *(void **) &tramp[4] = (void *) ffi_closure_LINUX64; + *(void **) &tramp[6] = codeloc; + flush_icache ((char *) tramp, (char *) codeloc, 4 * 4); #else - void **tramp = (void **) &closure->tramp[0]; - - if (cif->abi < FFI_LINUX || cif->abi >= FFI_LAST_ABI) - return FFI_BAD_ABI; - - /* Copy function address and TOC from ffi_closure_LINUX64 OPD. */ - memcpy (&tramp[0], (void **) ffi_closure_LINUX64, sizeof (void *)); - tramp[1] = codeloc; - memcpy (&tramp[2], (void **) ffi_closure_LINUX64 + 1, sizeof (void *)); + /* Copy function address and TOC from ffi_closure_LINUX64 OPD. */ + void **tramp = (void **) &closure->tramp[0]; + memcpy (&tramp[0], (void **) ffi_closure_LINUX64, sizeof (void *)); + tramp[1] = codeloc; + memcpy (&tramp[2], (void **) ffi_closure_LINUX64 + 1, sizeof (void *)); #endif + } closure->cif = cif; closure->fun = fun; diff --git a/src/powerpc/ffi_sysv.c b/src/powerpc/ffi_sysv.c index 4078e7511..5d7c5c823 100644 --- a/src/powerpc/ffi_sysv.c +++ b/src/powerpc/ffi_sysv.c @@ -29,6 +29,7 @@ ----------------------------------------------------------------------- */ #include "ffi.h" +#include #ifndef POWERPC64 #include "ffi_common.h" @@ -636,25 +637,34 @@ ffi_prep_closure_loc_sysv (ffi_closure *closure, void *user_data, void *codeloc) { - unsigned int *tramp; - if (cif->abi < FFI_SYSV || cif->abi >= FFI_LAST_ABI) return FFI_BAD_ABI; - tramp = (unsigned int *) &closure->tramp[0]; - tramp[0] = 0x7c0802a6; /* mflr r0 */ - tramp[1] = 0x429f0005; /* bcl 20,31,.+4 */ - tramp[2] = 0x7d6802a6; /* mflr r11 */ - tramp[3] = 0x7c0803a6; /* mtlr r0 */ - tramp[4] = 0x800b0018; /* lwz r0,24(r11) */ - tramp[5] = 0x816b001c; /* lwz r11,28(r11) */ - tramp[6] = 0x7c0903a6; /* mtctr r0 */ - tramp[7] = 0x4e800420; /* bctr */ - *(void **) &tramp[8] = (void *) ffi_closure_SYSV; /* function */ - *(void **) &tramp[9] = codeloc; /* context */ - - /* Flush the icache. */ - flush_icache ((char *)tramp, (char *)codeloc, 8 * 4); +#ifdef FFI_EXEC_STATIC_TRAMP + if (ffi_tramp_is_present(closure)) + { + /* Initialize the static trampoline's parameters. */ + void (*dest)(void) = ffi_closure_SYSV; + ffi_tramp_set_parms (closure->ftramp, dest, closure); + } + else +#endif + { + unsigned int *tramp = (unsigned int *) &closure->tramp[0]; + tramp[0] = 0x7c0802a6; /* mflr r0 */ + tramp[1] = 0x429f0005; /* bcl 20,31,.+4 */ + tramp[2] = 0x7d6802a6; /* mflr r11 */ + tramp[3] = 0x7c0803a6; /* mtlr r0 */ + tramp[4] = 0x800b0018; /* lwz r0,24(r11) */ + tramp[5] = 0x816b001c; /* lwz r11,28(r11) */ + tramp[6] = 0x7c0903a6; /* mtctr r0 */ + tramp[7] = 0x4e800420; /* bctr */ + *(void **) &tramp[8] = (void *) ffi_closure_SYSV; /* function */ + *(void **) &tramp[9] = codeloc; /* context */ + + /* Flush the icache. */ + flush_icache ((char *)tramp, (char *)codeloc, 8 * 4); + } closure->cif = cif; closure->fun = fun; diff --git a/src/powerpc/internal.h b/src/powerpc/internal.h new file mode 100644 index 000000000..b3db5f014 --- /dev/null +++ b/src/powerpc/internal.h @@ -0,0 +1,10 @@ +#ifdef FFI_EXEC_STATIC_TRAMP +/* For the trampoline code table mapping, a mapping size of 64K is chosen. */ +#define PPC_TRAMP_MAP_SHIFT 16 +#define PPC_TRAMP_MAP_SIZE (1 << PPC_TRAMP_MAP_SHIFT) +# ifdef __PCREL__ +# define PPC_TRAMP_SIZE 24 +# else +# define PPC_TRAMP_SIZE 40 +# endif /* __PCREL__ */ +#endif /* FFI_EXEC_STATIC_TRAMP */ diff --git a/src/powerpc/linux64_closure.S b/src/powerpc/linux64_closure.S index 199981db3..d9e5cff64 100644 --- a/src/powerpc/linux64_closure.S +++ b/src/powerpc/linux64_closure.S @@ -27,6 +27,7 @@ #define LIBFFI_ASM #include #include +#include "internal.h" .file "linux64_closure.S" @@ -559,7 +560,53 @@ ffi_go_closure_linux64: .size .ffi_go_closure_linux64,.-.ffi_go_closure_linux64 # endif # endif + +#ifdef FFI_EXEC_STATIC_TRAMP + .text + .align PPC_TRAMP_MAP_SHIFT + FFI_HIDDEN (trampoline_code_table) + .globl trampoline_code_table +# if _CALL_ELF == 2 + .type trampoline_code_table,@function +trampoline_code_table: + .localentry trampoline_code_table,.-trampoline_code_table +# else + .section ".opd","aw" + .align 3 +trampoline_code_table: + .quad .L.trampoline_code_table,.TOC.@tocbase,0 + .type trampoline_code_table,@function + .text +.L.trampoline_code_table: +# endif + .rept PPC_TRAMP_MAP_SIZE / PPC_TRAMP_SIZE +#ifdef __PCREL__ + pla %r2,PPC_TRAMP_MAP_SIZE + ld %r11,0(%r2) + ld %r12,8(%r2) + mtctr %r12 + bctr +#else + mflr %r0 + bcl 20,31,$+4 + mflr %r11 + addis %r11,%r11,PPC_TRAMP_MAP_SIZE@ha + mtlr %r0 + ld %r12,(PPC_TRAMP_MAP_SIZE+0)@l(%r11) + mtctr %r12 + ld %r11,(PPC_TRAMP_MAP_SIZE-8)@l(%r11) + bctr + nop +#endif + .endr + .align PPC_TRAMP_MAP_SHIFT +#if _CALL_ELF == 2 + .size trampoline_code_table,.-trampoline_code_table +#else + .size trampoline_code_table,.-.L.trampoline_code_table #endif +#endif /* FFI_EXEC_STATIC_TRAMP */ +#endif /* POWERPC64 */ #if (defined __ELF__ && defined __linux__) || _CALL_ELF == 2 .section .note.GNU-stack,"",@progbits diff --git a/src/powerpc/ppc_closure.S b/src/powerpc/ppc_closure.S index b6d209de8..d3556ebad 100644 --- a/src/powerpc/ppc_closure.S +++ b/src/powerpc/ppc_closure.S @@ -27,6 +27,7 @@ #define LIBFFI_ASM #include #include +#include "internal.h" #include .file "ppc_closure.S" @@ -391,6 +392,29 @@ ENTRY(ffi_go_closure_sysv) .cfi_endproc END(ffi_go_closure_sysv) +#ifdef FFI_EXEC_STATIC_TRAMP + .text + .align PPC_TRAMP_MAP_SHIFT + FFI_HIDDEN (trampoline_code_table) + .globl trampoline_code_table + .type trampoline_code_table,@function +trampoline_code_table: + .rept PPC_TRAMP_MAP_SIZE / PPC_TRAMP_SIZE + mflr %r0 + bcl 20,31,$+4 + mflr %r11 + addis %r11,%r11,PPC_TRAMP_MAP_SIZE@ha + mtlr %r0 + lwz %r0,(PPC_TRAMP_MAP_SIZE-4)@l(%r11) + mtctr %r0 + lwz %r11,(PPC_TRAMP_MAP_SIZE-8)@l(%r11) + bctr + nop + .endr + .size trampoline_code_table,.-trampoline_code_table + .align PPC_TRAMP_MAP_SHIFT +#endif /* FFI_EXEC_STATIC_TRAMP */ + #if defined __ELF__ && defined __linux__ .section .note.GNU-stack,"",@progbits #endif diff --git a/src/tramp.c b/src/tramp.c index 8ec084868..76a9dfe75 100644 --- a/src/tramp.c +++ b/src/tramp.c @@ -209,6 +209,11 @@ ffi_tramp_get_libffi (void) unsigned long start, end, offset, inode; uintptr_t addr = (uintptr_t) tramp_globals.text; int nfields, found; + int open_flags = O_RDONLY; + +#ifdef O_CLOEXEC + open_flags |= O_CLOEXEC; +#endif snprintf (file, PATH_MAX, "/proc/%d/maps", getpid()); fp = fopen (file, "r"); @@ -236,7 +241,7 @@ ffi_tramp_get_libffi (void) if (!found) return 0; - tramp_globals.fd = open (file, O_RDONLY); + tramp_globals.fd = open (file, open_flags); if (tramp_globals.fd == -1) return 0; diff --git a/src/types.c b/src/types.c index c1c27f38d..8b2dec98e 100644 --- a/src/types.c +++ b/src/types.c @@ -1,5 +1,5 @@ /* ----------------------------------------------------------------------- - types.c - Copyright (c) 1996, 1998, 2024 Red Hat, Inc. + types.c - Copyright (c) 1996, 1998, 2024, 2025 Red Hat, Inc. Predefined ffi_types needed by libffi. @@ -31,6 +31,28 @@ #include #include +/* Return a version string. */ +const char *ffi_get_version (void) +{ + return FFI_VERSION_STRING; +} + +/* Return the version as an unsigned long value: (x * 10000 + y * 100 + z) */ +unsigned long ffi_get_version_number (void) +{ + return FFI_VERSION_NUMBER; +} + +unsigned int ffi_get_default_abi (void) +{ + return FFI_DEFAULT_ABI; +} + +size_t ffi_get_closure_size (void) +{ + return sizeof(ffi_closure); +} + /* Type definitions */ #define FFI_TYPEDEF(name, type, id, maybe_const)\ diff --git a/src/wasm32/ffi.c b/src/wasm/ffi.c similarity index 79% rename from src/wasm32/ffi.c rename to src/wasm/ffi.c index 632848712..486ffa785 100644 --- a/src/wasm32/ffi.c +++ b/src/wasm/ffi.c @@ -59,16 +59,29 @@ EM_JS_DEPS(libffi, "$getWasmTableEntry,$setWasmTableEntry,$getEmptyTableSlot,$co offsetof(struct, field) == offset, \ "Memory layout of '" #struct "' has changed: '" #field "' is in an unexpected location"); +#if __SIZEOF_POINTER__ == 4 + +#define FFI_EMSCRIPTEN_ABI FFI_WASM32_EMSCRIPTEN +#define PTR_SIG 'i' + +#define DEC_PTR(p) p +#define ENC_PTR(p) p + +#define DEREF_PTR(addr, offset) DEREF_U32(addr, offset) +#define DEREF_PTR_NUMBER(addr, offset) DEREF_PTR(addr, offset) + CHECK_FIELD_OFFSET(ffi_cif, abi, 4*0); CHECK_FIELD_OFFSET(ffi_cif, nargs, 4*1); CHECK_FIELD_OFFSET(ffi_cif, arg_types, 4*2); CHECK_FIELD_OFFSET(ffi_cif, rtype, 4*3); +CHECK_FIELD_OFFSET(ffi_cif, flags, 4*5); CHECK_FIELD_OFFSET(ffi_cif, nfixedargs, 4*6); #define CIF__ABI(addr) DEREF_U32(addr, 0) #define CIF__NARGS(addr) DEREF_U32(addr, 1) #define CIF__ARGTYPES(addr) DEREF_U32(addr, 2) #define CIF__RTYPE(addr) DEREF_U32(addr, 3) +#define CIF__FLAGS(addr) DEREF_U32(addr, 5) #define CIF__NFIXEDARGS(addr) DEREF_U32(addr, 6) CHECK_FIELD_OFFSET(ffi_type, size, 0); @@ -81,6 +94,49 @@ CHECK_FIELD_OFFSET(ffi_type, elements, 8); #define FFI_TYPE__TYPEID(addr) DEREF_U16(addr + 6, 0) #define FFI_TYPE__ELEMENTS(addr) DEREF_U32(addr + 8, 0) +#elif __SIZEOF_POINTER__ == 8 + +#define FFI_EMSCRIPTEN_ABI FFI_WASM64_EMSCRIPTEN +#define PTR_SIG 'j' + +// DEC_PTR casts a pointer value (comming from Wasm) represented as BigInt (i64) to Number (i53). +// This should be used for a pointer that is expected to be within the i53 range. If the pointer +// value is outside the Number's range, the value will become NaN. +#define DEC_PTR(p) bigintToI53Checked(p) +// ENC_PTR casts a pointer value represented as Number to BigInt (i64) +#define ENC_PTR(p) BigInt(p) + +#define DEREF_PTR(addr, offset) DEREF_U64(addr, offset) +#define DEREF_PTR_NUMBER(addr, offset) DEC_PTR(DEREF_PTR(addr, offset)) + +CHECK_FIELD_OFFSET(ffi_cif, abi, 0); +CHECK_FIELD_OFFSET(ffi_cif, nargs, 4); +CHECK_FIELD_OFFSET(ffi_cif, arg_types, 8); +CHECK_FIELD_OFFSET(ffi_cif, rtype, 16); +CHECK_FIELD_OFFSET(ffi_cif, flags, 28); +CHECK_FIELD_OFFSET(ffi_cif, nfixedargs, 32); + +#define CIF__ABI(addr) DEREF_U32(addr, 0) +#define CIF__NARGS(addr) DEREF_U32(addr + 4, 0) +#define CIF__ARGTYPES(addr) DEREF_U64(addr + 8, 0) +#define CIF__RTYPE(addr) DEREF_U64(addr + 16, 0) +#define CIF__FLAGS(addr) DEREF_U32(addr + 28, 0) +#define CIF__NFIXEDARGS(addr) DEREF_U32(addr + 32, 0) + +CHECK_FIELD_OFFSET(ffi_type, size, 0); +CHECK_FIELD_OFFSET(ffi_type, alignment, 8); +CHECK_FIELD_OFFSET(ffi_type, type, 10); +CHECK_FIELD_OFFSET(ffi_type, elements, 16); + +#define FFI_TYPE__SIZE(addr) DEREF_U64(addr, 0) +#define FFI_TYPE__ALIGN(addr) DEREF_U16(addr + 8, 0) +#define FFI_TYPE__TYPEID(addr) DEREF_U16(addr + 10, 0) +#define FFI_TYPE__ELEMENTS(addr) DEREF_U64(addr + 16, 0) + +#else +#error "Unknown pointer size" +#endif + #define ALIGN_ADDRESS(addr, align) (addr &= (~((align) - 1))) #define STACK_ALLOC(stack, size, align) ((stack -= (size)), ALIGN_ADDRESS(stack, align)) @@ -100,7 +156,7 @@ _Static_assert(FFI_BAD_TYPEDEF_MACRO == FFI_BAD_TYPEDEF, "FFI_BAD_TYPEDEF must b ffi_status FFI_HIDDEN ffi_prep_cif_machdep(ffi_cif *cif) { - if (cif->abi != FFI_WASM32_EMSCRIPTEN) + if (cif->abi != FFI_EMSCRIPTEN_ABI) return FFI_BAD_ABI; // This is called after ffi_prep_cif_machdep_var so we need to avoid // overwriting cif->nfixedargs. @@ -144,6 +200,7 @@ ffi_prep_cif_machdep_var(ffi_cif *cif, unsigned nfixedargs, unsigned ntotalargs) EM_JS_MACROS( void, unbox_small_structs, (ffi_type type_ptr), { + type_ptr = DEC_PTR(type_ptr); var type_id = FFI_TYPE__TYPEID(type_ptr); while (type_id === FFI_TYPE_STRUCT) { // Don't unbox single element structs if they are bigger than 16 bytes. This @@ -156,15 +213,15 @@ unbox_small_structs, (ffi_type type_ptr), { // // See the Python comment here: // https://github.com/python/cpython/blob/a16a9f978f42b8a09297c1efbf33877f6388c403/Modules/_ctypes/stgdict.c#L718-L779 - if (FFI_TYPE__SIZE(type_ptr) > 16) { + if (DEC_PTR(FFI_TYPE__SIZE(type_ptr)) > 16) { break; } - var elements = FFI_TYPE__ELEMENTS(type_ptr); - var first_element = DEREF_U32(elements, 0); + var elements = DEC_PTR(FFI_TYPE__ELEMENTS(type_ptr)); + var first_element = DEREF_PTR_NUMBER(elements, 0); if (first_element === 0) { type_id = FFI_TYPE_VOID; break; - } else if (DEREF_U32(elements, 1) === 0) { + } else if (DEREF_PTR_NUMBER(elements, 1) === 0) { type_ptr = first_element; type_id = FFI_TYPE__TYPEID(first_element); } else { @@ -178,10 +235,15 @@ EM_JS_MACROS( void, ffi_call_js, (ffi_cif *cif, ffi_fp fn, void *rvalue, void **avalue), { + cif = DEC_PTR(cif); + fn = DEC_PTR(fn); + rvalue = DEC_PTR(rvalue); + avalue = DEC_PTR(avalue); var abi = CIF__ABI(cif); var nargs = CIF__NARGS(cif); var nfixedargs = CIF__NFIXEDARGS(cif); - var arg_types_ptr = CIF__ARGTYPES(cif); + var arg_types_ptr = DEC_PTR(CIF__ARGTYPES(cif)); + var flags = CIF__FLAGS(cif); var rtype_unboxed = unbox_small_structs(CIF__RTYPE(cif)); var rtype_ptr = rtype_unboxed[0]; var rtype_id = rtype_unboxed[1]; @@ -205,7 +267,7 @@ ffi_call_js, (ffi_cif *cif, ffi_fp fn, void *rvalue, void **avalue), // just use this. We also mark a flag that we don't need to convert the return // value of the dynamic call back to C. if (rtype_id === FFI_TYPE_LONGDOUBLE || rtype_id === FFI_TYPE_STRUCT) { - args.push(rvalue); + args.push(ENC_PTR(rvalue)); ret_by_arg = true; } @@ -214,8 +276,8 @@ ffi_call_js, (ffi_cif *cif, ffi_fp fn, void *rvalue, void **avalue), // Javascript to C automatically, here we manually do the inverse conversion // from C to Javascript. for (var i = 0; i < nfixedargs; i++) { - var arg_ptr = DEREF_U32(avalue, i); - var arg_unboxed = unbox_small_structs(DEREF_U32(arg_types_ptr, i)); + var arg_ptr = DEREF_PTR_NUMBER(avalue, i); + var arg_unboxed = unbox_small_structs(DEREF_PTR(arg_types_ptr, i)); var arg_type_ptr = arg_unboxed[0]; var arg_type_id = arg_unboxed[1]; @@ -226,7 +288,6 @@ ffi_call_js, (ffi_cif *cif, ffi_fp fn, void *rvalue, void **avalue), case FFI_TYPE_INT: case FFI_TYPE_SINT32: case FFI_TYPE_UINT32: - case FFI_TYPE_POINTER: args.push(DEREF_U32(arg_ptr, 0)); break; case FFI_TYPE_FLOAT: @@ -260,11 +321,14 @@ ffi_call_js, (ffi_cif *cif, ffi_fp fn, void *rvalue, void **avalue), // Nontrivial structs are passed by pointer. // Have to copy the struct onto the stack though because C ABI says it's // call by value. - var size = FFI_TYPE__SIZE(arg_type_ptr); + var size = DEC_PTR(FFI_TYPE__SIZE(arg_type_ptr)); var align = FFI_TYPE__ALIGN(arg_type_ptr); STACK_ALLOC(cur_stack_ptr, size, align); HEAP8.subarray(cur_stack_ptr, cur_stack_ptr+size).set(HEAP8.subarray(arg_ptr, arg_ptr + size)); - args.push(cur_stack_ptr); + args.push(ENC_PTR(cur_stack_ptr)); + break; + case FFI_TYPE_POINTER: + args.push(DEREF_PTR(arg_ptr, 0)); break; case FFI_TYPE_COMPLEX: throw new Error('complex marshalling nyi'); @@ -282,11 +346,11 @@ ffi_call_js, (ffi_cif *cif, ffi_fp fn, void *rvalue, void **avalue), // We don't have any way of knowing how many args were actually passed, so we // just always copy extra nonsense past the end. The ownwards call will know // not to look at it. - if (nfixedargs != nargs) { + if (flags & VARARGS_FLAG) { var struct_arg_info = []; for (var i = nargs - 1; i >= nfixedargs; i--) { - var arg_ptr = DEREF_U32(avalue, i); - var arg_unboxed = unbox_small_structs(DEREF_U32(arg_types_ptr, i)); + var arg_ptr = DEREF_PTR_NUMBER(avalue, i); + var arg_unboxed = unbox_small_structs(DEREF_PTR(arg_types_ptr, i)); var arg_type_ptr = arg_unboxed[0]; var arg_type_id = arg_unboxed[1]; switch (arg_type_id) { @@ -303,7 +367,6 @@ ffi_call_js, (ffi_cif *cif, ffi_fp fn, void *rvalue, void **avalue), case FFI_TYPE_INT: case FFI_TYPE_UINT32: case FFI_TYPE_SINT32: - case FFI_TYPE_POINTER: case FFI_TYPE_FLOAT: STACK_ALLOC(cur_stack_ptr, 4, 4); DEREF_U32(cur_stack_ptr, 0) = DEREF_U32(arg_ptr, 0); @@ -326,8 +389,12 @@ ffi_call_js, (ffi_cif *cif, ffi_fp fn, void *rvalue, void **avalue), // Again, struct must be passed by pointer. // But ABI is by value, so have to copy struct onto stack. // Currently arguments are going onto stack so we can't put it there now. Come back for this. - STACK_ALLOC(cur_stack_ptr, 4, 4); - struct_arg_info.push([cur_stack_ptr, arg_ptr, FFI_TYPE__SIZE(arg_type_ptr), FFI_TYPE__ALIGN(arg_type_ptr)]); + STACK_ALLOC(cur_stack_ptr, __SIZEOF_POINTER__, __SIZEOF_POINTER__); + struct_arg_info.push([cur_stack_ptr, arg_ptr, DEC_PTR(FFI_TYPE__SIZE(arg_type_ptr)), FFI_TYPE__ALIGN(arg_type_ptr)]); + break; + case FFI_TYPE_POINTER: + STACK_ALLOC(cur_stack_ptr, __SIZEOF_POINTER__, __SIZEOF_POINTER__); + DEREF_PTR(cur_stack_ptr, 0) = DEREF_PTR(arg_ptr, 0); break; case FFI_TYPE_COMPLEX: throw new Error('complex arg marshalling nyi'); @@ -336,7 +403,7 @@ ffi_call_js, (ffi_cif *cif, ffi_fp fn, void *rvalue, void **avalue), } } // extra normal argument which is the pointer to the varargs. - args.push(cur_stack_ptr); + args.push(ENC_PTR(cur_stack_ptr)); // Now allocate variable struct args on stack too. for (var i = 0; i < struct_arg_info.length; i++) { var struct_info = struct_arg_info[i]; @@ -346,7 +413,7 @@ ffi_call_js, (ffi_cif *cif, ffi_fp fn, void *rvalue, void **avalue), var align = struct_info[3]; STACK_ALLOC(cur_stack_ptr, size, align); HEAP8.subarray(cur_stack_ptr, cur_stack_ptr+size).set(HEAP8.subarray(arg_ptr, arg_ptr + size)); - DEREF_U32(arg_target, 0) = cur_stack_ptr; + DEREF_PTR(arg_target, 0) = ENC_PTR(cur_stack_ptr); } } stackRestore(cur_stack_ptr); @@ -371,7 +438,6 @@ ffi_call_js, (ffi_cif *cif, ffi_fp fn, void *rvalue, void **avalue), case FFI_TYPE_INT: case FFI_TYPE_UINT32: case FFI_TYPE_SINT32: - case FFI_TYPE_POINTER: DEREF_U32(rvalue, 0) = result; break; case FFI_TYPE_FLOAT: @@ -392,6 +458,9 @@ ffi_call_js, (ffi_cif *cif, ffi_fp fn, void *rvalue, void **avalue), case FFI_TYPE_SINT64: DEREF_U64(rvalue, 0) = result; break; + case FFI_TYPE_POINTER: + DEREF_PTR(rvalue, 0) = result; + break; case FFI_TYPE_COMPLEX: throw new Error('complex ret marshalling nyi'); default: @@ -403,6 +472,8 @@ void ffi_call(ffi_cif *cif, void (*fn)(void), void *rvalue, void **avalue) { ffi_call_js(cif, fn, rvalue, avalue); } +#if __SIZEOF_POINTER__ == 4 + CHECK_FIELD_OFFSET(ffi_closure, ftramp, 4*0); CHECK_FIELD_OFFSET(ffi_closure, cif, 4*1); CHECK_FIELD_OFFSET(ffi_closure, fun, 4*2); @@ -413,12 +484,30 @@ CHECK_FIELD_OFFSET(ffi_closure, user_data, 4*3); #define CLOSURE__fun(addr) DEREF_U32(addr, 2) #define CLOSURE__user_data(addr) DEREF_U32(addr, 3) +#elif __SIZEOF_POINTER__ == 8 + +CHECK_FIELD_OFFSET(ffi_closure, ftramp, 0); +CHECK_FIELD_OFFSET(ffi_closure, cif, 8); +CHECK_FIELD_OFFSET(ffi_closure, fun, 16); +CHECK_FIELD_OFFSET(ffi_closure, user_data, 24); + +#define CLOSURE__wrapper(addr) DEREF_U64(addr, 0) +#define CLOSURE__cif(addr) DEREF_U64(addr, 1) +#define CLOSURE__fun(addr) DEREF_U64(addr, 2) +#define CLOSURE__user_data(addr) DEREF_U64(addr, 3) + +#else +#error "Unknown pointer size" +#endif + EM_JS_MACROS(void *, ffi_closure_alloc_js, (size_t size, void **code), { + size = DEC_PTR(size); + code = DEC_PTR(code); var closure = _malloc(size); var index = getEmptyTableSlot(); - DEREF_U32(code, 0) = index; - CLOSURE__wrapper(closure) = index; - return closure; + DEREF_PTR(code, 0) = ENC_PTR(index); + CLOSURE__wrapper(closure) = ENC_PTR(index); + return ENC_PTR(closure); }) void * __attribute__ ((visibility ("default"))) @@ -427,7 +516,8 @@ ffi_closure_alloc(size_t size, void **code) { } EM_JS_MACROS(void, ffi_closure_free_js, (void *closure), { - var index = CLOSURE__wrapper(closure); + closure = DEC_PTR(closure); + var index = DEC_PTR(CLOSURE__wrapper(closure)); freeTableIndexes.push(index); _free(closure); }) @@ -442,10 +532,15 @@ ffi_status, ffi_prep_closure_loc_js, (ffi_closure *closure, ffi_cif *cif, void *fun, void *user_data, void *codeloc), { + closure = DEC_PTR(closure); + cif = DEC_PTR(cif); + fun = DEC_PTR(fun); + user_data = DEC_PTR(user_data); + codeloc = DEC_PTR(codeloc); var abi = CIF__ABI(cif); var nargs = CIF__NARGS(cif); var nfixedargs = CIF__NFIXEDARGS(cif); - var arg_types_ptr = CIF__ARGTYPES(cif); + var arg_types_ptr = DEC_PTR(CIF__ARGTYPES(cif)); var rtype_unboxed = unbox_small_structs(CIF__RTYPE(cif)); var rtype_ptr = rtype_unboxed[0]; var rtype_id = rtype_unboxed[1]; @@ -461,7 +556,7 @@ ffi_prep_closure_loc_js, case FFI_TYPE_STRUCT: case FFI_TYPE_LONGDOUBLE: // Return via a first pointer argument. - sig = 'vi'; + sig = 'v' + PTR_SIG; ret_by_arg = true; break; case FFI_TYPE_INT: @@ -471,7 +566,6 @@ ffi_prep_closure_loc_js, case FFI_TYPE_SINT16: case FFI_TYPE_UINT32: case FFI_TYPE_SINT32: - case FFI_TYPE_POINTER: sig = 'i'; break; case FFI_TYPE_FLOAT: @@ -484,6 +578,9 @@ ffi_prep_closure_loc_js, case FFI_TYPE_SINT64: sig = 'j'; break; + case FFI_TYPE_POINTER: + sig = PTR_SIG; + break; case FFI_TYPE_COMPLEX: throw new Error('complex ret marshalling nyi'); default: @@ -492,11 +589,11 @@ ffi_prep_closure_loc_js, var unboxed_arg_type_id_list = []; var unboxed_arg_type_info_list = []; for (var i = 0; i < nargs; i++) { - var arg_unboxed = unbox_small_structs(DEREF_U32(arg_types_ptr, i)); + var arg_unboxed = unbox_small_structs(DEREF_PTR(arg_types_ptr, i)); var arg_type_ptr = arg_unboxed[0]; var arg_type_id = arg_unboxed[1]; unboxed_arg_type_id_list.push(arg_type_id); - unboxed_arg_type_info_list.push([FFI_TYPE__SIZE(arg_type_ptr), FFI_TYPE__ALIGN(arg_type_ptr)]); + unboxed_arg_type_info_list.push([DEC_PTR(FFI_TYPE__SIZE(arg_type_ptr)), FFI_TYPE__ALIGN(arg_type_ptr)]); } for (var i = 0; i < nfixedargs; i++) { switch (unboxed_arg_type_id_list[i]) { @@ -507,8 +604,6 @@ ffi_prep_closure_loc_js, case FFI_TYPE_SINT16: case FFI_TYPE_UINT32: case FFI_TYPE_SINT32: - case FFI_TYPE_POINTER: - case FFI_TYPE_STRUCT: sig += 'i'; break; case FFI_TYPE_FLOAT: @@ -524,6 +619,10 @@ ffi_prep_closure_loc_js, case FFI_TYPE_SINT64: sig += 'j'; break; + case FFI_TYPE_STRUCT: + case FFI_TYPE_POINTER: + sig += PTR_SIG; + break; case FFI_TYPE_COMPLEX: throw new Error('complex marshalling nyi'); default: @@ -532,7 +631,7 @@ ffi_prep_closure_loc_js, } if (nfixedargs < nargs) { // extra pointer to varargs stack - sig += 'i'; + sig += PTR_SIG; } LOG_DEBUG("CREATE_CLOSURE", "sig:", sig); function trampoline() { @@ -551,7 +650,7 @@ ffi_prep_closure_loc_js, STACK_ALLOC(cur_ptr, 8, 8); ret_ptr = cur_ptr; } - cur_ptr -= 4 * nargs; + cur_ptr -= __SIZEOF_POINTER__ * nargs; var args_ptr = cur_ptr; var carg_idx = 0; // Here we either have the actual argument, or a pair of BigInts for long @@ -572,58 +671,62 @@ ffi_prep_closure_loc_js, case FFI_TYPE_SINT8: // Bad things happen if we don't align to 4 here STACK_ALLOC(cur_ptr, 1, 4); - DEREF_U32(args_ptr, carg_idx) = cur_ptr; + DEREF_PTR(args_ptr, carg_idx) = ENC_PTR(cur_ptr); DEREF_U8(cur_ptr, 0) = cur_arg; break; case FFI_TYPE_UINT16: case FFI_TYPE_SINT16: // Bad things happen if we don't align to 4 here STACK_ALLOC(cur_ptr, 2, 4); - DEREF_U32(args_ptr, carg_idx) = cur_ptr; + DEREF_PTR(args_ptr, carg_idx) = ENC_PTR(cur_ptr); DEREF_U16(cur_ptr, 0) = cur_arg; break; case FFI_TYPE_INT: case FFI_TYPE_UINT32: case FFI_TYPE_SINT32: - case FFI_TYPE_POINTER: STACK_ALLOC(cur_ptr, 4, 4); - DEREF_U32(args_ptr, carg_idx) = cur_ptr; + DEREF_PTR(args_ptr, carg_idx) = ENC_PTR(cur_ptr); DEREF_U32(cur_ptr, 0) = cur_arg; break; case FFI_TYPE_STRUCT: // cur_arg is already a pointer to struct // copy it onto stack to pass by value STACK_ALLOC(cur_ptr, arg_size, arg_align); - HEAP8.subarray(cur_ptr, cur_ptr + arg_size).set(HEAP8.subarray(cur_arg, cur_arg + arg_size)); - DEREF_U32(args_ptr, carg_idx) = cur_ptr; + HEAP8.subarray(cur_ptr, cur_ptr + arg_size).set(HEAP8.subarray(DEC_PTR(cur_arg), DEC_PTR(cur_arg) + arg_size)); + DEREF_PTR(args_ptr, carg_idx) = ENC_PTR(cur_ptr); break; case FFI_TYPE_FLOAT: STACK_ALLOC(cur_ptr, 4, 4); - DEREF_U32(args_ptr, carg_idx) = cur_ptr; + DEREF_PTR(args_ptr, carg_idx) = ENC_PTR(cur_ptr); DEREF_F32(cur_ptr, 0) = cur_arg; break; case FFI_TYPE_DOUBLE: STACK_ALLOC(cur_ptr, 8, 8); - DEREF_U32(args_ptr, carg_idx) = cur_ptr; + DEREF_PTR(args_ptr, carg_idx) = ENC_PTR(cur_ptr); DEREF_F64(cur_ptr, 0) = cur_arg; break; case FFI_TYPE_UINT64: case FFI_TYPE_SINT64: STACK_ALLOC(cur_ptr, 8, 8); - DEREF_U32(args_ptr, carg_idx) = cur_ptr; + DEREF_PTR(args_ptr, carg_idx) = ENC_PTR(cur_ptr); DEREF_U64(cur_ptr, 0) = cur_arg; break; case FFI_TYPE_LONGDOUBLE: STACK_ALLOC(cur_ptr, 16, 8); - DEREF_U32(args_ptr, carg_idx) = cur_ptr; + DEREF_PTR(args_ptr, carg_idx) = ENC_PTR(cur_ptr); DEREF_U64(cur_ptr, 0) = cur_arg; cur_arg = args[jsarg_idx++]; DEREF_U64(cur_ptr, 1) = cur_arg; break; + case FFI_TYPE_POINTER: + STACK_ALLOC(cur_ptr, __SIZEOF_POINTER__, __SIZEOF_POINTER__); + DEREF_PTR(args_ptr, carg_idx) = ENC_PTR(cur_ptr); + DEREF_PTR(cur_ptr, 0) = cur_arg; + break; } } // If its a varargs call, last js argument is a pointer to the varargs. - var varargs = args[args.length - 1]; + var varargs = DEC_PTR(args[args.length - 1]); // We have no way of knowing how many varargs were actually provided, this // fills the rest of the stack space allocated with nonsense. The onward // call will know to ignore the nonsense. @@ -639,20 +742,20 @@ ffi_prep_closure_loc_js, if (arg_type_id === FFI_TYPE_STRUCT) { // In this case varargs is a pointer to pointer to struct so we need to // deref once - var struct_ptr = DEREF_U32(varargs, 0); + var struct_ptr = DEREF_PTR_NUMBER(varargs, 0); STACK_ALLOC(cur_ptr, arg_size, arg_align); HEAP8.subarray(cur_ptr, cur_ptr + arg_size).set(HEAP8.subarray(struct_ptr, struct_ptr + arg_size)); - DEREF_U32(args_ptr, carg_idx) = cur_ptr; + DEREF_PTR(args_ptr, carg_idx) = ENC_PTR(cur_ptr); } else { - DEREF_U32(args_ptr, carg_idx) = varargs; + DEREF_PTR(args_ptr, carg_idx) = ENC_PTR(varargs); } - varargs += 4; + varargs += __SIZEOF_POINTER__; } stackRestore(cur_ptr); stackAlloc(0); // stackAlloc enforces alignment invariants on the stack pointer LOG_DEBUG("CALL_CLOSURE", "closure:", closure, "fptr", CLOSURE__fun(closure), "cif", CLOSURE__cif(closure)); getWasmTableEntry(CLOSURE__fun(closure))( - CLOSURE__cif(closure), ret_ptr, args_ptr, + CLOSURE__cif(closure), ENC_PTR(ret_ptr), ENC_PTR(args_ptr), CLOSURE__user_data(closure) ); stackRestore(orig_stack_ptr); @@ -677,9 +780,9 @@ ffi_prep_closure_loc_js, return FFI_BAD_TYPEDEF_MACRO; } setWasmTableEntry(codeloc, wasm_trampoline); - CLOSURE__cif(closure) = cif; - CLOSURE__fun(closure) = fun; - CLOSURE__user_data(closure) = user_data; + CLOSURE__cif(closure) = ENC_PTR(cif); + CLOSURE__fun(closure) = ENC_PTR(fun); + CLOSURE__user_data(closure) = ENC_PTR(user_data); return FFI_OK_MACRO; }) @@ -688,7 +791,7 @@ ffi_prep_closure_loc_js, ffi_status ffi_prep_closure_loc(ffi_closure *closure, ffi_cif *cif, void (*fun)(ffi_cif *, void *, void **, void *), void *user_data, void *codeloc) { - if (cif->abi != FFI_WASM32_EMSCRIPTEN) + if (cif->abi != FFI_EMSCRIPTEN_ABI) return FFI_BAD_ABI; return ffi_prep_closure_loc_js(closure, cif, (void *)fun, user_data, codeloc); diff --git a/src/wasm32/ffitarget.h b/src/wasm/ffitarget.h similarity index 86% rename from src/wasm32/ffitarget.h rename to src/wasm/ffitarget.h index ac78b7433..10041c00d 100644 --- a/src/wasm32/ffitarget.h +++ b/src/wasm/ffitarget.h @@ -42,14 +42,31 @@ typedef void (*ffi_fp)(void); typedef enum ffi_abi { FFI_FIRST_ABI = 0, +#if __SIZEOF_POINTER__ == 4 FFI_WASM32, // "raw", no structures, varargs, or closures (not implemented!) FFI_WASM32_EMSCRIPTEN, // structures, varargs, and split 64-bit params +#elif __SIZEOF_POINTER__ == 8 + FFI_WASM64, + FFI_WASM64_EMSCRIPTEN, +#else +#error "Unknown pointer size" +#endif FFI_LAST_ABI, +#if __SIZEOF_POINTER__ == 4 #ifdef __EMSCRIPTEN__ FFI_DEFAULT_ABI = FFI_WASM32_EMSCRIPTEN #else FFI_DEFAULT_ABI = FFI_WASM32 #endif +#elif __SIZEOF_POINTER__ == 8 +#ifdef __EMSCRIPTEN__ + FFI_DEFAULT_ABI = FFI_WASM64_EMSCRIPTEN +#else + FFI_DEFAULT_ABI = FFI_WASM64 +#endif +#else +#error "Unknown pointer size" +#endif } ffi_abi; #define FFI_CLOSURES 1 diff --git a/src/x86/ffi64.c b/src/x86/ffi64.c index 982b144cb..1d1b88e04 100644 --- a/src/x86/ffi64.c +++ b/src/x86/ffi64.c @@ -654,7 +654,7 @@ ffi_call_int (ffi_cif *cif, void (*fn)(void), void *rvalue, break; default: reg_args->gpr[gprcount] = 0; - memcpy (®_args->gpr[gprcount], a, sizeof(UINT64)); + memcpy (®_args->gpr[gprcount], a, size <= 8 ? size : 8); } gprcount++; break; diff --git a/testsuite/Makefile.am b/testsuite/Makefile.am index 92ae1aaa2..2e422b9a4 100644 --- a/testsuite/Makefile.am +++ b/testsuite/Makefile.am @@ -17,8 +17,8 @@ EXTRA_DIST = config/default.exp emscripten/build.sh emscripten/conftest.py \ libffi.call/float1.c libffi.call/float2.c libffi.call/float3.c \ libffi.call/float4.c libffi.call/float_va.c libffi.call/many.c \ libffi.call/many2.c libffi.call/many_double.c libffi.call/many_mixed.c \ - libffi.call/negint.c libffi.call/offsets.c libffi.call/pr1172638.c \ - libffi.call/promotion.c libffi.call/pyobjc_tc.c libffi.call/return_dbl.c \ + libffi.call/negint.c libffi.call/offsets.c libffi.call/overread.c \ + libffi.call/pr1172638.c libffi.call/promotion.c libffi.call/pyobjc_tc.c libffi.call/return_dbl.c \ libffi.call/return_dbl1.c libffi.call/return_dbl2.c libffi.call/return_fl.c \ libffi.call/return_fl1.c libffi.call/return_fl2.c libffi.call/return_fl3.c \ libffi.call/return_ldl.c libffi.call/return_ll.c libffi.call/return_ll1.c \ @@ -31,11 +31,11 @@ EXTRA_DIST = config/default.exp emscripten/build.sh emscripten/conftest.py \ libffi.call/struct9.c libffi.call/struct_by_value_2.c libffi.call/struct_by_value_3.c \ libffi.call/struct_by_value_3f.c libffi.call/struct_by_value_4.c libffi.call/struct_by_value_4f.c \ libffi.call/struct_by_value_big.c libffi.call/struct_by_value_small.c libffi.call/struct_return_2H.c \ - libffi.call/struct_int_float.c \ + libffi.call/struct_int_float.c libffi.call/longjmp.c \ libffi.call/struct_return_8H.c libffi.call/uninitialized.c libffi.call/va_1.c \ libffi.call/va_2.c libffi.call/va_3.c libffi.call/va_struct1.c \ libffi.call/va_struct2.c libffi.call/va_struct3.c libffi.call/callback.c \ - libffi.call/callback2.c libffi.call/callback3.c libffi.call/callback4.c \ + libffi.call/callback2.c libffi.call/callback3.c libffi.call/callback4.c libffi.call/x32.c \ libffi.closures/closure.exp libffi.closures/closure_fn0.c libffi.closures/closure_fn1.c \ libffi.closures/closure_fn2.c libffi.closures/closure_fn3.c libffi.closures/closure_fn4.c \ libffi.closures/closure_fn5.c libffi.closures/closure_fn6.c libffi.closures/closure_loc_fn0.c \ @@ -85,4 +85,5 @@ EXTRA_DIST = config/default.exp emscripten/build.sh emscripten/conftest.py \ libffi.complex/return_complex2_float.c libffi.complex/return_complex2_longdouble.c libffi.complex/return_complex_double.c \ libffi.complex/return_complex_float.c libffi.complex/return_complex_longdouble.c libffi.go/aa-direct.c \ libffi.go/closure1.c libffi.go/ffitest.h libffi.go/go.exp \ - libffi.go/static-chain.h Makefile.am Makefile.in + libffi.go/static-chain.h Makefile.am Makefile.in \ + libffi.threads/ffitest.h libffi.threads/threads.exp libffi.threads/tsan.c diff --git a/testsuite/emscripten/build.sh b/testsuite/emscripten/build.sh index 83ece7bcf..a332c1d53 100755 --- a/testsuite/emscripten/build.sh +++ b/testsuite/emscripten/build.sh @@ -36,11 +36,11 @@ export PKG_CONFIG_PATH="$TARGET/lib/pkgconfig" export EM_PKG_CONFIG_PATH="$PKG_CONFIG_PATH" # Specific variables for cross-compilation -export CHOST="wasm32-unknown-linux" # wasm32-unknown-emscripten +export CHOST="${TARGET_HOST}-unknown-linux" # wasm32-unknown-emscripten autoreconf -fiv emconfigure ./configure --host=$CHOST --prefix="$TARGET" --enable-static --disable-shared --disable-dependency-tracking \ - --disable-builddir --disable-multi-os-directory --disable-raw-api --disable-docs + --disable-builddir --disable-multi-os-directory --disable-raw-api --disable-docs ${EXTRA_CONFIGURE_FLAGS} make install cp fficonfig.h target/include/ cp include/ffi_common.h target/include/ diff --git a/testsuite/emscripten/node-tests.sh b/testsuite/emscripten/node-tests.sh index 016e99c4b..2710947c0 100755 --- a/testsuite/emscripten/node-tests.sh +++ b/testsuite/emscripten/node-tests.sh @@ -8,16 +8,16 @@ fi # Common compiler flags export CFLAGS="-fPIC $EXTRA_CFLAGS" export CXXFLAGS="$CFLAGS -sNO_DISABLE_EXCEPTION_CATCHING $EXTRA_CXXFLAGS" -export LDFLAGS="-sEXPORTED_FUNCTIONS=_main,_malloc,_free -sALLOW_TABLE_GROWTH -sASSERTIONS -sNO_DISABLE_EXCEPTION_CATCHING -sWASM_BIGINT" +export LDFLAGS="-sEXPORTED_FUNCTIONS=_main,_malloc,_free -sALLOW_TABLE_GROWTH -sASSERTIONS -sNO_DISABLE_EXCEPTION_CATCHING -sWASM_BIGINT -Wno-main" # Specific variables for cross-compilation -export CHOST="wasm32-unknown-linux" # wasm32-unknown-emscripten +export CHOST="${TARGET_HOST}-unknown-linux" # wasm32-unknown-emscripten autoreconf -fiv emconfigure ./configure --prefix="$(pwd)/target" --host=$CHOST --enable-static --disable-shared \ - --disable-builddir --disable-multi-os-directory --disable-raw-api --disable-docs || + --disable-builddir --disable-multi-os-directory --disable-raw-api --disable-docs ${EXTRA_CONFIGURE_FLAGS} || (cat config.log && exit 1) make EMMAKEN_JUST_CONFIGURE=1 emmake make check \ - RUNTESTFLAGS="LDFLAGS_FOR_TARGET='$LDFLAGS'" || (cat testsuite/libffi.log && exit 1) + RUNTESTFLAGS="LDFLAGS_FOR_TARGET='$LDFLAGS $EXTRA_TEST_LDFLAGS'" || (cat testsuite/libffi.log && exit 1) diff --git a/testsuite/lib/libffi.exp b/testsuite/lib/libffi.exp index b5731dbc9..81eff7752 100644 --- a/testsuite/lib/libffi.exp +++ b/testsuite/lib/libffi.exp @@ -1,4 +1,4 @@ -# Copyright (C) 2003, 2005, 2008, 2009, 2010, 2011, 2014, 2019, 2022 Free Software Foundation, Inc. +# Copyright (C) 2003, 2005, 2008, 2009, 2010, 2011, 2014, 2019, 2022, 2025 Free Software Foundation, Inc. # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -38,14 +38,14 @@ proc is-effective-target { arg } { global et_index set selected 0 if { ![info exists et_index] } { - # Initialize the effective target index that is used in some - # check_effective_target_* procs. - set et_index 0 + # Initialize the effective target index that is used in some + # check_effective_target_* procs. + set et_index 0 } if { [info procs check_effective_target_${arg}] != [list] } { - set selected [check_effective_target_${arg}] + set selected [check_effective_target_${arg}] } else { - error "unknown effective target keyword `$arg'" + error "unknown effective target keyword `$arg'" } verbose "is-effective-target: $arg $selected" 2 return $selected @@ -53,9 +53,9 @@ proc is-effective-target { arg } { proc is-effective-target-keyword { arg } { if { [info procs check_effective_target_${arg}] != [list] } { - return 1 + return 1 } else { - return 0 + return 0 } } @@ -87,93 +87,93 @@ if { [info procs saved-dg-process-target] == [list] } { # Evaluate an operand within a selector expression. proc selector_opd { op } { - set selector "target" - lappend selector $op - set answer [ expr { [dg-process-target $selector] == "S" } ] - verbose "selector_opd: `$op' $answer" 2 - return $answer + set selector "target" + lappend selector $op + set answer [ expr { [dg-process-target $selector] == "S" } ] + verbose "selector_opd: `$op' $answer" 2 + return $answer } # Evaluate a target triplet list within a selector expression. # Unlike other operands, this needs to be expanded from a list to # the same string as "target". proc selector_list { op } { - set selector "target [join $op]" - set answer [ expr { [dg-process-target $selector] == "S" } ] - verbose "selector_list: `$op' $answer" 2 - return $answer + set selector "target [join $op]" + set answer [ expr { [dg-process-target $selector] == "S" } ] + verbose "selector_list: `$op' $answer" 2 + return $answer } # Evaluate a selector expression. proc selector_expression { exp } { - if { [llength $exp] == 2 } { - if [string match "!" [lindex $exp 0]] { - set op1 [lindex $exp 1] - set answer [expr { ! [selector_opd $op1] }] - } else { - # Assume it's a list of target triplets. - set answer [selector_list $exp] - } - } elseif { [llength $exp] == 3 } { - set op1 [lindex $exp 0] - set opr [lindex $exp 1] - set op2 [lindex $exp 2] - if [string match "&&" $opr] { - set answer [expr { [selector_opd $op1] && [selector_opd $op2] }] - } elseif [string match "||" $opr] { - set answer [expr { [selector_opd $op1] || [selector_opd $op2] }] - } else { - # Assume it's a list of target triplets. - set answer [selector_list $exp] - } - } else { - # Assume it's a list of target triplets. - set answer [selector_list $exp] - } - - verbose "selector_expression: `$exp' $answer" 2 - return $answer + if { [llength $exp] == 2 } { + if [string match "!" [lindex $exp 0]] { + set op1 [lindex $exp 1] + set answer [expr { ! [selector_opd $op1] }] + } else { + # Assume it's a list of target triplets. + set answer [selector_list $exp] + } + } elseif { [llength $exp] == 3 } { + set op1 [lindex $exp 0] + set opr [lindex $exp 1] + set op2 [lindex $exp 2] + if [string match "&&" $opr] { + set answer [expr { [selector_opd $op1] && [selector_opd $op2] }] + } elseif [string match "||" $opr] { + set answer [expr { [selector_opd $op1] || [selector_opd $op2] }] + } else { + # Assume it's a list of target triplets. + set answer [selector_list $exp] + } + } else { + # Assume it's a list of target triplets. + set answer [selector_list $exp] + } + + verbose "selector_expression: `$exp' $answer" 2 + return $answer } # Evaluate "target selector" or "xfail selector". proc dg-process-target-1 { args } { - verbose "dg-process-target-1: `$args'" 2 - - # Extract the 'what' keyword from the argument list. - set selector [string trim [lindex $args 0]] - if [regexp "^xfail " $selector] { - set what "xfail" - } elseif [regexp "^target " $selector] { - set what "target" - } else { - error "syntax error in target selector \"$selector\"" - } - - # Extract the rest of the list, which might be a keyword. - regsub "^${what}" $selector "" rest - set rest [string trim $rest] - - if [is-effective-target-keyword $rest] { - # The selector is an effective target keyword. - if [is-effective-target $rest] { - return [expr { $what == "xfail" ? "F" : "S" }] - } else { - return [expr { $what == "xfail" ? "P" : "N" }] - } - } - - if [string match "{*}" $rest] { - if [selector_expression [lindex $rest 0]] { - return [expr { $what == "xfail" ? "F" : "S" }] - } else { - return [expr { $what == "xfail" ? "P" : "N" }] - } - } - - # The selector is not an effective-target keyword, so process - # the list of target triplets. - return [saved-dg-process-target $selector] + verbose "dg-process-target-1: `$args'" 2 + + # Extract the 'what' keyword from the argument list. + set selector [string trim [lindex $args 0]] + if [regexp "^xfail " $selector] { + set what "xfail" + } elseif [regexp "^target " $selector] { + set what "target" + } else { + error "syntax error in target selector \"$selector\"" + } + + # Extract the rest of the list, which might be a keyword. + regsub "^${what}" $selector "" rest + set rest [string trim $rest] + + if [is-effective-target-keyword $rest] { + # The selector is an effective target keyword. + if [is-effective-target $rest] { + return [expr { $what == "xfail" ? "F" : "S" }] + } else { + return [expr { $what == "xfail" ? "P" : "N" }] + } + } + + if [string match "{*}" $rest] { + if [selector_expression [lindex $rest 0]] { + return [expr { $what == "xfail" ? "F" : "S" }] + } else { + return [expr { $what == "xfail" ? "P" : "N" }] + } + } + + # The selector is not an effective-target keyword, so process + # the list of target triplets. + return [saved-dg-process-target $selector] } # Intercept calls to the DejaGnu function. In addition to @@ -181,24 +181,24 @@ if { [info procs saved-dg-process-target] == [list] } { # "target selector1 xfail selector2". proc dg-process-target { args } { - verbose "replacement dg-process-target: `$args'" 2 - - set selector [string trim [lindex $args 0]] - - # If the argument list contains both 'target' and 'xfail', - # process 'target' and, if that succeeds, process 'xfail'. - if [regexp "^target .* xfail .*" $selector] { - set xfail_index [string first "xfail" $selector] - set xfail_selector [string range $selector $xfail_index end] - set target_selector [string range $selector 0 [expr $xfail_index-1]] - set target_selector [string trim $target_selector] - if { [dg-process-target-1 $target_selector] == "N" } { - return "N" - } - return [dg-process-target-1 $xfail_selector] + verbose "replacement dg-process-target: `$args'" 2 + + set selector [string trim [lindex $args 0]] + + # If the argument list contains both 'target' and 'xfail', + # process 'target' and, if that succeeds, process 'xfail'. + if [regexp "^target .* xfail .*" $selector] { + set xfail_index [string first "xfail" $selector] + set xfail_selector [string range $selector $xfail_index end] + set target_selector [string range $selector 0 [expr $xfail_index-1]] + set target_selector [string trim $target_selector] + if { [dg-process-target-1 $target_selector] == "N" } { + return "N" + } + return [dg-process-target-1 $xfail_selector] - } - return [dg-process-target-1 $selector] + } + return [dg-process-target-1 $selector] } } @@ -217,8 +217,8 @@ proc libffi-dg-test-1 { target_compile prog do_what extra_tool_flags } { upvar 2 dg-output-text output_match if { [llength $output_match] > 1 } { - regsub -all "\n" [lindex $output_match 1] "\r?\n" x - set output_match [lreplace $output_match 1 1 $x] + regsub -all "\n" [lindex $output_match 1] "\r?\n" x + set output_match [lreplace $output_match 1 1 $x] } if { [ istarget "wasm32-*-*" ] } { @@ -232,34 +232,34 @@ proc libffi-dg-test-1 { target_compile prog do_what extra_tool_flags } { set options [list] switch $do_what { - "compile" { - set compile_type "assembly" - set output_file "[file rootname [file tail $prog]].s" - } - "link" { - set compile_type "executable" - set output_file "[file rootname [file tail $prog]]$exec_suffix" - # The following line is needed for targets like the i960 where - # the default output file is b.out. Sigh. - } - "run" { - set compile_type "executable" - # FIXME: "./" is to cope with "." not being in $PATH. - # Should this be handled elsewhere? - # YES. - set output_file "./[file rootname [file tail $prog]]$exec_suffix" - # This is the only place where we care if an executable was - # created or not. If it was, dg.exp will try to run it. - remote_file build delete $output_file; - } - default { - perror "$do_what: not a valid dg-do keyword" - return "" - } + "compile" { + set compile_type "assembly" + set output_file "[file rootname [file tail $prog]].s" + } + "link" { + set compile_type "executable" + set output_file "[file rootname [file tail $prog]]$exec_suffix" + # The following line is needed for targets like the i960 where + # the default output file is b.out. Sigh. + } + "run" { + set compile_type "executable" + # FIXME: "./" is to cope with "." not being in $PATH. + # Should this be handled elsewhere? + # YES. + set output_file "./[file rootname [file tail $prog]]$exec_suffix" + # This is the only place where we care if an executable was + # created or not. If it was, dg.exp will try to run it. + remote_file build delete $output_file; + } + default { + perror "$do_what: not a valid dg-do keyword" + return "" + } } if { $extra_tool_flags != "" } { - lappend options "additional_flags=$extra_tool_flags" + lappend options "additional_flags=$extra_tool_flags" } set comp_output [libffi_target_compile "$prog" "$output_file" "$compile_type" $options]; @@ -297,7 +297,7 @@ proc libffi-init { args } { global compiler_vendor if ![info exists blddirffi] { - set blddirffi [pwd]/.. + set blddirffi [pwd]/.. } verbose "libffi $blddirffi" @@ -306,7 +306,7 @@ proc libffi-init { args } { if { [string match $compiler_vendor "gnu"] } { set gccdir [lookfor_file $tool_root_dir gcc/libgcc.a] if {$gccdir != ""} { - set gccdir [file dirname $gccdir] + set gccdir [file dirname $gccdir] } verbose "gccdir $gccdir" @@ -315,17 +315,17 @@ proc libffi-init { args } { set compiler "${gccdir}/xgcc" if { [is_remote host] == 0 && [which $compiler] != 0 } { - foreach i "[exec $compiler --print-multi-lib]" { - set mldir "" - regexp -- "\[a-z0-9=_/\.-\]*;" $i mldir - set mldir [string trimright $mldir "\;@"] - if { "$mldir" == "." } { - continue - } - if { [llength [glob -nocomplain ${gccdir}/${mldir}/libgcc_s*.so.*]] >= 1 } { - append ld_library_path ":${gccdir}/${mldir}" - } - } + foreach i "[exec $compiler --print-multi-lib]" { + set mldir "" + regexp -- "\[a-z0-9=_/\.-\]*;" $i mldir + set mldir [string trimright $mldir "\;@"] + if { "$mldir" == "." } { + continue + } + if { [llength [glob -nocomplain ${gccdir}/${mldir}/libgcc_s*.so.*]] >= 1 } { + append ld_library_path ":${gccdir}/${mldir}" + } + } } } @@ -341,8 +341,8 @@ proc libffi-init { args } { set libffi_dir "${blddirffi}/.libs" verbose "libffi_dir $libffi_dir" if { $libffi_dir != "" } { - set libffi_dir [file dirname ${libffi_dir}] - set libffi_link_flags "-L${libffi_dir}/.libs" + set libffi_dir [file dirname ${libffi_dir}] + set libffi_link_flags "-L ../.libs" } set_ld_library_path_env_vars @@ -353,8 +353,8 @@ proc libffi_exit { } { global gluefile; if [info exists gluefile] { - file_on_build delete $gluefile; - unset gluefile; + file_on_build delete $gluefile; + unset gluefile; } } @@ -369,54 +369,60 @@ proc libffi_target_compile { source dest type options } { global compiler_vendor if { [target_info needs_status_wrapper]!="" && [info exists gluefile] } { - lappend options "libs=${gluefile}" - lappend options "ldflags=$wrap_flags" + lappend options "libs=${gluefile}" + lappend options "ldflags=$wrap_flags" } # TOOL_OPTIONS must come first, so that it doesn't override testcase # specific options. if [info exists TOOL_OPTIONS] { - lappend options "additional_flags=$TOOL_OPTIONS" + lappend options "additional_flags=$TOOL_OPTIONS" } # search for ffi_mips.h in srcdir, too - lappend options "additional_flags=-I${libffi_include} -I${srcdir}/../include -I${libffi_include}/.." - lappend options "additional_flags=${libffi_link_flags}" - - # Darwin needs a stack execution allowed flag. - - if { [istarget "*-*-darwin9*"] || [istarget "*-*-darwin1*"] - || [istarget "*-*-darwin2*"] } { - # lappend options "additional_flags=-Wl,-allow_stack_execute" - lappend options "additional_flags=-Wno-unused-command-line-argument" - lappend options "additional_flags=-Wl,-search_paths_first" - } - - # If you're building the compiler with --prefix set to a place - # where it's not yet installed, then the linker won't be able to - # find the libgcc used by libffi.dylib. We could pass the - # -dylib_file option, but that's complicated, and it's much easier - # to just make the linker find libgcc using -L options. - if { [string match "*-*-darwin*" $target_triplet] } { - lappend options "libs= -shared-libgcc" - } + # lappend options "additional_flags=-I${libffi_include} -I${srcdir}/../include -I${libffi_include}/.." + lappend options "additional_flags=-I ../include -I ${srcdir}/../include -I .." + if { [string match $type "executable"] } { + + lappend options "additional_flags=${libffi_link_flags}" + + # Darwin needs a stack execution allowed flag. + if { [istarget "*-*-darwin9*"] || [istarget "*-*-darwin1*"] + || [istarget "*-*-darwin2*"] } { + # lappend options "additional_flags=-Wl,-allow_stack_execute" + lappend options "additional_flags=-Wno-unused-command-line-argument" + lappend options "additional_flags=-Wl,-search_paths_first" + } - if { [string match "*-*-openbsd*" $target_triplet] } { - lappend options "libs= -lpthread" - } + # If you're building the compiler with --prefix set to a place + # where it's not yet installed, then the linker won't be able to + # find the libgcc used by libffi.dylib. We could pass the + # -dylib_file option, but that's complicated, and it's much easier + # to just make the linker find libgcc using -L options. + if { [string match "*-*-darwin*" $target_triplet] } { + lappend options "libs= -shared-libgcc" + } - lappend options "libs= -lffi" + if { [string match "*-*-openbsd*" $target_triplet] } { + lappend options "libs= -lpthread" + } - if { ![string match "*android*" $target_triplet] } { + lappend options "libs= -lffi" if { [string match "aarch64*-*-linux*" $target_triplet] } { - lappend options "libs= -lpthread" + if { ! [string match "*android*" $target_triplet] } { + lappend options "libs= -lpthread" + } } # this may be required for g++, but just confused clang. if { [string match "*.cc" $source] } { lappend options "c++" if { [string match "*-*-darwin*" $target_triplet] } { + if { [string match $compiler_vendor "gnu"] } { + lappend options "libs= -lc++" + } + } elseif { [string match "*android*" $target_triplet] } { lappend options "libs= -lc++" } } @@ -430,6 +436,7 @@ proc libffi_target_compile { source dest type options } { # which causes it to be seen as unsupported. if { [string match "wasm32-*" $target_triplet] } { lappend options "additional_flags=-Wno-unused-command-line-argument" + lappend options "libs= -lpthread" } verbose "options: $options" @@ -447,10 +454,12 @@ proc libffi_feature_test { test } { puts $f "#else" puts $f "# error Failed $test" puts $f "#endif" + puts $f "int main() {return 0;}" close $f - set lines [libffi_target_compile $src /dev/null assembly ""] + set lines [libffi_target_compile $src $src.i preprocess ""] file delete $src + file delete $src.i return [string match "" $lines] } @@ -463,10 +472,10 @@ proc libffi_feature_test { test } { proc search_for { file pattern } { set fd [open $file r] while { [gets $fd cur_line]>=0 } { - if [string match "*$pattern*" $cur_line] then { - close $fd - return 1 - } + if [string match "*$pattern*" $cur_line] then { + close $fd + return 1 + } } close $fd return 0 @@ -478,27 +487,27 @@ proc libffi-dg-runtest { testcases default-extra-flags } { global runtests foreach test $testcases { - # If we're only testing specific files and this isn't one of - # them, skip it. - if ![runtest_file_p $runtests $test] { - continue - } - - # Look for a loop within the source code - if we don't find one, - # don't pass -funroll[-all]-loops. - global torture_with_loops torture_without_loops - if [expr [search_for $test "for*("]+[search_for $test "while*("]] { - set option_list $torture_with_loops - } else { - set option_list $torture_without_loops - } - - set nshort [file tail [file dirname $test]]/[file tail $test] - - foreach flags $option_list { - verbose "Testing $nshort, $flags" 1 - dg-test $test $flags ${default-extra-flags} - } + # If we're only testing specific files and this isn't one of + # them, skip it. + if ![runtest_file_p $runtests $test] { + continue + } + + # Look for a loop within the source code - if we don't find one, + # don't pass -funroll[-all]-loops. + global torture_with_loops torture_without_loops + if [expr [search_for $test "for*("]+[search_for $test "while*("]] { + set option_list $torture_with_loops + } else { + set option_list $torture_without_loops + } + + set nshort [file tail [file dirname $test]]/[file tail $test] + + foreach flags $option_list { + verbose "Testing $nshort, $flags" 1 + dg-test $test $flags ${default-extra-flags} + } } } @@ -507,31 +516,31 @@ proc run-many-tests { testcases extra_flags } { global has_gccbug global env switch $compiler_vendor { - "clang" { - set common "-W -Wall" - if [info exists env(LIBFFI_TEST_OPTIMIZATION)] { - set optimizations [ list $env(LIBFFI_TEST_OPTIMIZATION) ] - } else { - set optimizations { "-O0" "-O2" } + "clang" { + set common "-W -Wall" + if [info exists env(LIBFFI_TEST_OPTIMIZATION)] { + set optimizations [ list $env(LIBFFI_TEST_OPTIMIZATION) ] + } else { + set optimizations { "-O0" "-O2" } + } } - } - "gnu" { - set common "-W -Wall -Wno-psabi" - if [info exists env(LIBFFI_TEST_OPTIMIZATION)] { - set optimizations [ list $env(LIBFFI_TEST_OPTIMIZATION) ] - } else { - set optimizations { "-O0" "-O2" } + "gnu" { + set common "-W -Wall -Wno-psabi -fno-diagnostics-color" + if [info exists env(LIBFFI_TEST_OPTIMIZATION)] { + set optimizations [ list $env(LIBFFI_TEST_OPTIMIZATION) ] + } else { + set optimizations { "-O0" "-O2" } + } } - } - default { - # Assume we are using the vendor compiler. - set common "" - if [info exists env(LIBFFI_TEST_OPTIMIZATION)] { - set optimizations [ list $env(LIBFFI_TEST_OPTIMIZATION) ] - } else { - set optimizations { "" } + default { + # Assume we are using the vendor compiler. + set common "" + if [info exists env(LIBFFI_TEST_OPTIMIZATION)] { + set optimizations [ list $env(LIBFFI_TEST_OPTIMIZATION) ] + } else { + set optimizations { "" } + } } - } } info exists env(LD_LIBRARY_PATH) @@ -546,7 +555,7 @@ proc run-many-tests { testcases extra_flags } { "-DABI_NUM=FFI_FASTCALL -DABI_ATTR=__FASTCALL__" } } elseif { [istarget "x86_64-*-*"] \ - && [libffi_feature_test "#if !defined __ILP32__ \ + && [libffi_feature_test "#if !defined __ILP32__ \ && !defined __i386__"] } { set targetabis { "" @@ -566,22 +575,22 @@ proc run-many-tests { testcases extra_flags } { foreach opt $optimizations { foreach abi $abis { set options [concat $common $opt $abi] - set has_gccbug false; - if { [string match $compiler_vendor "gnu"] \ - && [string match "*MSABI*" $abi] \ - && ( ( [string match "*DGTEST=57 *" $common] \ - && [string match "*call.c*" $testname] ) \ - || ( [string match "*DGTEST=54 *" $common] \ - && [string match "*callback*" $testname] ) \ - || [string match "*DGTEST=55 *" $common] \ - || [string match "*DGTEST=56 *" $common] ) } then { - if [libffi_feature_test "#if (__GNUC__ < 9) || ((__GNUC__ == 9) && (__GNUC_MINOR__ < 3))"] { - set has_gccbug true; - } - } - verbose "Testing $testname, $options" 1 - verbose "has_gccbug = $has_gccbug" 1 - dg-test $test $options "" + set has_gccbug false; + if { [string match $compiler_vendor "gnu"] \ + && [string match "*MSABI*" $abi] \ + && ( ( [string match "*DGTEST=57 *" $common] \ + && [string match "*call.c*" $testname] ) \ + || ( [string match "*DGTEST=54 *" $common] \ + && [string match "*callback*" $testname] ) \ + || [string match "*DGTEST=55 *" $common] \ + || [string match "*DGTEST=56 *" $common] ) } then { + if [libffi_feature_test "#if (__GNUC__ < 9) || ((__GNUC__ == 9) && (__GNUC_MINOR__ < 3))"] { + set has_gccbug true; + } + } + verbose "Testing $testname, $options" 1 + verbose "has_gccbug = $has_gccbug" 1 + dg-test $test $options "" } } } @@ -593,8 +602,8 @@ proc dg-xfail-if { args } { set args [lreplace $args 0 0] set selector "target [join [lindex $args 1]]" if { [dg-process-target $selector] == "S" } { - global compiler_conditional_xfail_data - set compiler_conditional_xfail_data $args + global compiler_conditional_xfail_data + set compiler_conditional_xfail_data $args } } @@ -606,22 +615,22 @@ proc check-flags { args } { # The next two arguments are optional. If they were not specified, # use the defaults. if { [llength $args] == 2 } { - lappend $args [list "*"] + lappend $args [list "*"] } if { [llength $args] == 3 } { - lappend $args [list ""] + lappend $args [list ""] } # If the option strings are the defaults, or the same as the # defaults, there is no need to call check_conditional_xfail to # compare them to the actual options. if { [string compare [lindex $args 2] "*"] == 0 - && [string compare [lindex $args 3] "" ] == 0 } { - set result 1 + && [string compare [lindex $args 3] "" ] == 0 } { + set result 1 } else { - # The target list might be an effective-target keyword, so replace - # the original list with "*-*-*", since we already know it matches. - set result [check_conditional_xfail [lreplace $args 1 1 "*-*-*"]] + # The target list might be an effective-target keyword, so replace + # the original list with "*-*-*", since we already know it matches. + set result [check_conditional_xfail [lreplace $args 1 1 "*-*-*"]] } return $result @@ -637,7 +646,7 @@ proc dg-skip-if { args } { # Don't bother if we're already skipping the test. upvar dg-do-what dg-do-what if { [lindex ${dg-do-what} 1] == "N" } { - return + return } set selector [list target [lindex $args 1]] @@ -662,18 +671,18 @@ if { [info procs saved-dg-test] == [list] } { rename dg-test saved-dg-test proc dg-test { args } { - global additional_files - global additional_sources - global errorInfo - - if { [ catch { eval saved-dg-test $args } errmsg ] } { - set saved_info $errorInfo - set additional_files "" - set additional_sources "" - error $errmsg $saved_info - } - set additional_files "" - set additional_sources "" + global additional_files + global additional_sources + global errorInfo + + if { [ catch { eval saved-dg-test $args } errmsg ] } { + set saved_info $errorInfo + set additional_files "" + set additional_sources "" + error $errmsg $saved_info + } + set additional_files "" + set additional_sources "" } } diff --git a/testsuite/libffi.bhaible/testcases.c b/testsuite/libffi.bhaible/testcases.c index 23a6f4660..22cf1ebb5 100644 --- a/testsuite/libffi.bhaible/testcases.c +++ b/testsuite/libffi.bhaible/testcases.c @@ -21,7 +21,7 @@ #include -FILE* out; +FILE* out = NULL; #define uchar unsigned char #define ushort unsigned short diff --git a/testsuite/libffi.call/ffitest.h b/testsuite/libffi.call/ffitest.h index 8bd8a3cb6..a7c634b4c 100644 --- a/testsuite/libffi.call/ffitest.h +++ b/testsuite/libffi.call/ffitest.h @@ -1,3 +1,6 @@ +#undef __USE_MINGW_ANSI_STDIO +#define __USE_MINGW_ANSI_STDIO 1 + #include #include #include diff --git a/testsuite/libffi.call/longjmp.c b/testsuite/libffi.call/longjmp.c new file mode 100644 index 000000000..fc0c7ffd9 --- /dev/null +++ b/testsuite/libffi.call/longjmp.c @@ -0,0 +1,60 @@ +/* Area: ffi_call + Purpose: Test longjmp over ffi_call frames */ + +/* Test code adapted from Lars Kanis' bug report: + https://github.com/libffi/libffi/issues/905 */ + +/* { dg-do run } */ + +#include "ffitest.h" +#include "ffi_common.h" + +#include + +static jmp_buf buf; + +static void ABI_ATTR lev2(const char *str) { + printf("lev2 %s\n", str); + // jumps back to where setjmp was called - making setjmp now return 1 + longjmp(buf, 1); +} + +static void ABI_ATTR lev1(const char *str) { + lev2(str); + + // will not be reached + printf("lev1 %s\n", str); +} + +int main() +{ + ffi_cif cif; + ffi_type *args[1]; + void *values[1]; + char *s; + ffi_arg rc; + /* Initialize the argument info vectors */ + args[0] = &ffi_type_pointer; + values[0] = &s; + /* Initialize the cif */ + if (ffi_prep_cif(&cif, FFI_DEFAULT_ABI, 1, + &ffi_type_sint, args) == FFI_OK) + { + s = "direct call"; + if (!setjmp(buf)){ + // works on x64 and arm64 + lev1(s); + } else { + printf("back to main\n"); + } + + s = "through libffi"; + if (!setjmp(buf)){ + // works on x64 but segfaults on arm64 + ffi_call(&cif, (void (*)(void))lev1, &rc, values); + } else { + printf("back to main\n"); + } + } + return 0; +} diff --git a/testsuite/libffi.call/overread.c b/testsuite/libffi.call/overread.c new file mode 100644 index 000000000..5ba10774c --- /dev/null +++ b/testsuite/libffi.call/overread.c @@ -0,0 +1,54 @@ +/* Area: ffi_call + Purpose: Tests if ffi_call reads data beyond end. + Limitations: needs mmap. + PR: 887 + Originator: Mikulas Patocka */ + +/* { dg-do run } */ + +#include "ffitest.h" + +#ifdef __linux__ +#include +#include + +static int ABI_ATTR fn(unsigned char a, unsigned short b, unsigned int c, unsigned long d) +{ + return (int)(a + b + c + d); +} +#endif + +int main(void) +{ +#ifdef __linux__ + ffi_cif cif; + ffi_type *args[MAX_ARGS]; + void *values[MAX_ARGS]; + ffi_arg rint; + char *m; + int ps; + args[0] = &ffi_type_uchar; + args[1] = &ffi_type_ushort; + args[2] = &ffi_type_uint; + args[3] = &ffi_type_ulong; + CHECK(ffi_prep_cif(&cif, ABI_NUM, 4, &ffi_type_sint, args) == FFI_OK); + ps = getpagesize(); + m = mmap(NULL, ps * 3, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); + CHECK(m != MAP_FAILED); + CHECK(mprotect(m, ps, PROT_NONE) == 0); + CHECK(mprotect(m + ps * 2, ps, PROT_NONE) == 0); + values[0] = m + ps * 2 - sizeof(unsigned char); + values[1] = m + ps * 2 - sizeof(unsigned short); + values[2] = m + ps * 2 - sizeof(unsigned int); + values[3] = m + ps * 2 - sizeof(unsigned long); + ffi_call(&cif, FFI_FN(fn), &rint, values); + CHECK((int)rint == 0); + values[0] = m + ps; + values[1] = m + ps; + values[2] = m + ps; + values[3] = m + ps; + ffi_call(&cif, FFI_FN(fn), &rint, values); + CHECK((int)rint == 0); +#endif + exit(0); +} diff --git a/testsuite/libffi.call/x32.c b/testsuite/libffi.call/x32.c new file mode 100644 index 000000000..9c380a529 --- /dev/null +++ b/testsuite/libffi.call/x32.c @@ -0,0 +1,31 @@ +/* Area: ffi_call + Purpose: Check zero-extension of pointers on x32. + Limitations: none. + PR: 887 + Originator: Mikulas Patocka */ + +/* { dg-do run } */ + +#include "ffitest.h" + +static int ABI_ATTR fn(int *a) +{ + if (a) + return *a; + return -1; +} + +int main(void) +{ + ffi_cif cif; + ffi_type *args[MAX_ARGS]; + void *values[MAX_ARGS]; + void *z[2] = { (void *)0, (void *)1 }; + ffi_arg rint; + args[0] = &ffi_type_pointer; + values[0] = z; + CHECK(ffi_prep_cif(&cif, ABI_NUM, 1, &ffi_type_sint, args) == FFI_OK); + ffi_call(&cif, FFI_FN(fn), &rint, values); + CHECK((int)rint == -1); + exit(0); +} diff --git a/testsuite/libffi.closures/cls_dbls_struct.c b/testsuite/libffi.closures/cls_dbls_struct.c index 00e247e7e..d0c206c04 100644 --- a/testsuite/libffi.closures/cls_dbls_struct.c +++ b/testsuite/libffi.closures/cls_dbls_struct.c @@ -28,7 +28,7 @@ closure_test_gn(ffi_cif* cif __UNUSED__, void* resp __UNUSED__, closure_test_fn(*(Dbls*)args[0]); } -int main(int argc __UNUSED__, char** argv __UNUSED__) +int main(void) { ffi_cif cif; diff --git a/testsuite/libffi.closures/huge_struct.c b/testsuite/libffi.closures/huge_struct.c index 572a0c8fb..b3488304c 100644 --- a/testsuite/libffi.closures/huge_struct.c +++ b/testsuite/libffi.closures/huge_struct.c @@ -216,8 +216,7 @@ cls_large_fn(ffi_cif* cif __UNUSED__, void* resp, void** args, void* userdata __ ui8_5, si8_5); } -int -main(int argc __UNUSED__, const char** argv __UNUSED__) +int main (void) { void *code; ffi_closure *pcl = ffi_closure_alloc(sizeof(ffi_closure), &code); diff --git a/testsuite/libffi.closures/unwindtest.cc b/testsuite/libffi.closures/unwindtest.cc index b643c48e4..634fb716b 100644 --- a/testsuite/libffi.closures/unwindtest.cc +++ b/testsuite/libffi.closures/unwindtest.cc @@ -48,7 +48,9 @@ typedef int (*closure_test_type1)(float, float, float, float, signed short, float, float, int, double, int, int, float, int, int, int, int); +#ifdef __EMSCRIPTEN__ extern "C" +#endif int main (void) { ffi_cif cif; diff --git a/testsuite/libffi.closures/unwindtest_ffi_call.cc b/testsuite/libffi.closures/unwindtest_ffi_call.cc index 6feaa5752..1b86cc071 100644 --- a/testsuite/libffi.closures/unwindtest_ffi_call.cc +++ b/testsuite/libffi.closures/unwindtest_ffi_call.cc @@ -14,7 +14,9 @@ static int checking(int a __UNUSED__, short b __UNUSED__, throw 9; } +#ifdef __EMSCRIPTEN__ extern "C" +#endif int main (void) { ffi_cif cif; diff --git a/testsuite/libffi.threads/ffitest.h b/testsuite/libffi.threads/ffitest.h new file mode 100644 index 000000000..d27d362d6 --- /dev/null +++ b/testsuite/libffi.threads/ffitest.h @@ -0,0 +1 @@ +#include "../libffi.call/ffitest.h" diff --git a/testsuite/libffi.threads/threads.exp b/testsuite/libffi.threads/threads.exp new file mode 100644 index 000000000..ca0375091 --- /dev/null +++ b/testsuite/libffi.threads/threads.exp @@ -0,0 +1,50 @@ +# Copyright (C) 2003, 2006, 2009, 2010, 2014 Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; see the file COPYING3. If not see +# . + +dg-init +libffi-init + +global srcdir subdir + +if { [string match $compiler_vendor "microsoft"] } { + # -wd4005 macro redefinition + # -wd4244 implicit conversion to type of smaller size + # -wd4305 truncation to smaller type + # -wd4477 printf %lu of uintptr_t + # -wd4312 implicit conversion to type of greater size + # -wd4311 pointer truncation to unsigned long + # -EHsc C++ Exception Handling (no SEH exceptions) + set additional_options "-wd4005 -wd4244 -wd4305 -wd4477 -wd4312 -wd4311 -EHsc"; +} else { + set additional_options ""; +} + +set tlist [lsort [glob -nocomplain -- $srcdir/$subdir/*.c]] + +# No pthreads for windows or wasm +if { [string match $compiler_vendor "microsoft"] || [istarget "wasm*-*-*"] } { + foreach test $tlist { + unsupported "$test" + } +} else { + run-many-tests $tlist $additional_options +} + +dg-finish + +# Local Variables: +# tcl-indent-level:4 +# End: diff --git a/testsuite/libffi.threads/tsan.c b/testsuite/libffi.threads/tsan.c new file mode 100644 index 000000000..aeff60c1f --- /dev/null +++ b/testsuite/libffi.threads/tsan.c @@ -0,0 +1,74 @@ +/* { dg-do run } */ + +#include "ffitest.h" + +#include +#include +#include + +#define NUM_THREADS 20 + +#ifdef _POSIX_BARRIERS +pthread_barrier_t barrier; +#endif + +typedef float (*callback_fn)(float, float); + +void callback(ffi_cif *cif __UNUSED__, void *ret, void **args, void *userdata __UNUSED__) { + float a = *(float *)args[0]; + float b = *(float *)args[1]; + *(float *)ret = a / 2 + b / 2; +} + +void *thread_func(void *arg) { +#ifdef _POSIX_BARRIERS + pthread_barrier_wait(&barrier); +#endif + + ffi_cif cif; + ffi_type *args[2] = { &ffi_type_float, &ffi_type_float }; + + if (ffi_prep_cif(&cif, FFI_DEFAULT_ABI, 2, &ffi_type_float, args) != FFI_OK) { + fprintf(stderr, "ffi_prep_cif failed\n"); + return NULL; + } + + ffi_closure *closure = ffi_closure_alloc(sizeof(ffi_closure), (void **)&arg); + + if (ffi_prep_closure_loc(closure, &cif, callback, NULL, arg) != FFI_OK) { + fprintf(stderr, "ffi_prep_closure_loc failed\n"); + return NULL; + } + + callback_fn fn = (callback_fn)arg; + (void) fn(4.0f, 6.0f); + + ffi_closure_free(closure); + return NULL; +} + +int main() { + pthread_t threads[NUM_THREADS]; + +#ifdef _POSIX_BARRIERS + pthread_barrier_init(&barrier, NULL, NUM_THREADS); +#endif + + for (int i = 0; i < NUM_THREADS; ++i) { + if (pthread_create(&threads[i], NULL, thread_func, NULL) != 0) { + perror("pthread_create"); + exit(EXIT_FAILURE); + } + } + + for (int i = 0; i < NUM_THREADS; ++i) { + pthread_join(threads[i], NULL); + } + +#ifdef _POSIX_BARRIERS + pthread_barrier_destroy(&barrier); +#endif + + printf("Completed\n"); + return 0; +}