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;
+}