Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 64 additions & 10 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ on:
- linux-aarch64-gnu
- linux-x86_64-gnu-nts
- linux-aarch64-gnu-nts
- linux-x86_64-gnu-nts-shared
- linux-aarch64-gnu-nts-shared
- macos-aarch64
- windows-x86_64
release_tag_suffix:
Expand Down Expand Up @@ -143,6 +145,14 @@ jobs:
# PHP_EXTENSIONS comment at the top of this file).
nts_x64='{"arch":"x86_64","libc":"gnu","image":"docker.io/library/almalinux:8","suffix":"-gnu-nts","zts":"0","extra":",ffi","variant":"gnu-nts","runner":["self-hosted","linux","x64"]}'
nts_arm='{"arch":"aarch64","libc":"gnu","image":"docker.io/library/almalinux:8","suffix":"-gnu-nts","zts":"0","extra":",ffi","variant":"gnu-nts","runner":["self-hosted","linux","arm64"]}'
# Shared-embed NTS: same toolchain and extension set as -gnu-nts,
# but SPC_CMD_VAR_PHP_EMBED_TYPE=shared (the "embed" field, read by
# the container script) makes spc produce lib/libphp.so with all
# dependencies statically linked inside, instead of lib/libphp.a
# plus dependency archives. For consumers whose build systems only
# detect a shared libphp (dlopen/-lphp), e.g. PAM.
nts_sh_x64='{"arch":"x86_64","libc":"gnu","image":"docker.io/library/almalinux:8","suffix":"-gnu-nts-shared","zts":"0","extra":",ffi","variant":"gnu-nts-shared","embed":"shared","runner":["self-hosted","linux","x64"]}'
nts_sh_arm='{"arch":"aarch64","libc":"gnu","image":"docker.io/library/almalinux:8","suffix":"-gnu-nts-shared","zts":"0","extra":",ffi","variant":"gnu-nts-shared","embed":"shared","runner":["self-hosted","linux","arm64"]}'
case "$PLATFORM" in
all) lm="[$musl_x64,$musl_arm,$gnu_x64,$gnu_arm]"; mac=true; win=true ;;
linux-x86_64) lm="[$musl_x64]"; mac=false; win=false ;;
Expand All @@ -151,6 +161,8 @@ jobs:
linux-aarch64-gnu) lm="[$gnu_arm]"; mac=false; win=false ;;
linux-x86_64-gnu-nts) lm="[$nts_x64]"; mac=false; win=false ;;
linux-aarch64-gnu-nts) lm="[$nts_arm]"; mac=false; win=false ;;
linux-x86_64-gnu-nts-shared) lm="[$nts_sh_x64]"; mac=false; win=false ;;
linux-aarch64-gnu-nts-shared) lm="[$nts_sh_arm]"; mac=false; win=false ;;
macos-aarch64) lm='[]'; mac=true; win=false ;;
windows-x86_64) lm='[]'; mac=false; win=true ;;
*) echo "::error::unknown platform $PLATFORM"; exit 1 ;;
Expand All @@ -172,7 +184,8 @@ jobs:
# Checked out into a subdirectory on purpose: actions/checkout only
# manages the path it is given, so spc's caches and this job's own
# output/ and sdk/ directories in the workspace root are untouched.
# Only tools/mk-sdk-metadata.sh is used from it, in Package SDK.
# Only tools/mk-sdk-metadata.sh and embed/ are used from it, in
# Package SDK.
- name: Checkout build tools
uses: actions/checkout@v4
with:
Expand All @@ -196,6 +209,7 @@ jobs:
-e PHP_EXTENSIONS="${{ env.PHP_EXTENSIONS }}${{ matrix.extra }}" \
-e LIBC="${{ matrix.libc }}" \
-e ENABLE_ZTS="${{ matrix.zts }}" \
-e EMBED_TYPE="${{ matrix.embed }}" \
-e SPC_OPCACHE_EXTRA_SAPIS="${{ env.SPC_OPCACHE_EXTRA_SAPIS }}" \
"${{ matrix.image }}" sh -c '
set -eux
Expand Down Expand Up @@ -372,6 +386,11 @@ jobs:
ZTS_FLAG="--enable-zts"
fi

# static (default) -> lib/libphp.a + dependency archives;
# shared -> lib/libphp.so with dependencies linked in
# (rc18 src/Package/Target/php/unix.php branches on this).
export SPC_CMD_VAR_PHP_EMBED_TYPE="${EMBED_TYPE:-static}"

spc build ${PHP_EXTENSIONS} \
--dl-with-php=${PHP_VERSION} \
--build-embed \
Expand All @@ -386,7 +405,19 @@ jobs:
# flat layout. cp -L dereferences symlinks
# (e.g. libcurses.a → libncurses.a).
mkdir -p /output/lib /output/include
find /build/buildroot/lib -name "*.a" -type f -exec cp -Lf {} /output/lib/ \;
if [ "$SPC_CMD_VAR_PHP_EMBED_TYPE" = "shared" ]; then
# The shared embed installs a versioned libphp-<ver>.so plus
# a libphp.so symlink; copy both preserving the link (-P via
# cp -a on the find results). Dependency archives stay out --
# they are already linked into the .so.
find /build/buildroot/lib -name "libphp*.so*" -exec cp -a {} /output/lib/ \;
if [ ! -e /output/lib/libphp.so ]; then
echo "ERROR: shared embed build produced no libphp.so under buildroot/lib" >&2
exit 1
fi
else
find /build/buildroot/lib -name "*.a" -type f -exec cp -Lf {} /output/lib/ \;
fi
PHP_INC_DIR=$(find /build/buildroot/include -name php -type d | head -n 1)
if [ -z "$PHP_INC_DIR" ]; then
echo "ERROR: no php header dir found under buildroot/include" >&2
Expand Down Expand Up @@ -419,8 +450,15 @@ jobs:
# equivalent intrinsics.
if [ "$(uname -m)" = "x86_64" ]; then
grep -q "define HAVE_FUNC_ATTRIBUTE_TARGET 1" /output/include/php/main/php_config.h
nm /output/lib/libphp.a | grep -q "SHA256_Transform_shani"
echo "==> intrinsics guard passed: SHA-NI transform present in libphp.a"
if [ "$SPC_CMD_VAR_PHP_EMBED_TYPE" = "shared" ]; then
# --no-strip keeps the full symtab, so nm sees the
# static-scope transform in the .so as well.
nm /output/lib/libphp.so | grep -q "SHA256_Transform_shani"
echo "==> intrinsics guard passed: SHA-NI transform present in libphp.so"
else
nm /output/lib/libphp.a | grep -q "SHA256_Transform_shani"
echo "==> intrinsics guard passed: SHA-NI transform present in libphp.a"
fi
fi

# Bundle the container libstdc++.a (musl-flavored on Alpine,
Expand Down Expand Up @@ -477,6 +515,20 @@ jobs:
# consumer.
sh sdk-tools/tools/mk-sdk-metadata.sh sdk linux

# NTS tarballs ship embed/resolver_shim.c + EMBEDDING.md: libphp.a
# is built against glibc 2.28 and references unversioned
# __dn_expand/__res_nsearch/__dn_skipname, which glibc >= 2.34
# (Ubuntu 22.04+) no longer exports -- embedders must compile the
# shim into their build or the link fails. EMBEDDING.md documents
# this and the shared-variant usage.
case "${{ matrix.suffix }}" in
*-nts*)
mkdir -p sdk/embed
cp sdk-tools/embed/resolver_shim.c sdk/embed/
cp sdk-tools/embed/EMBEDDING.md sdk/EMBEDDING.md
;;
esac

tar czf php-sdk-${{ inputs.php_version }}-linux-${{ matrix.arch }}${{ matrix.suffix }}.tar.gz -C sdk .

- name: Upload artifact
Expand Down Expand Up @@ -1113,12 +1165,14 @@ jobs:
| `php-sdk-${{ inputs.php_version }}-macos-aarch64.tar.gz` | macOS Apple Silicon |
| `php-sdk-${{ inputs.php_version }}-windows-x86_64.tar.gz` | Windows x86_64 (ZTS) |

All of the above are thread-safe (ZTS). A non-thread-safe variant
(`-gnu-nts`, Linux glibc only, extension set plus `ffi`) is built
on demand for consumers that need one; it is not part of every
release and is not required for a release to be considered
complete. Check the asset list above for whether this release has
one.
All of the above are thread-safe (ZTS). Non-thread-safe variants
(Linux glibc only, extension set plus `ffi`) are built on demand
for consumers that need one: `-gnu-nts` ships static `libphp.a`
plus `embed/resolver_shim.c` and `EMBEDDING.md`, and
`-gnu-nts-shared` ships `libphp.so` with dependencies linked in.
They are not part of every release and are not required for a
release to be considered complete. Check the asset list above for
whether this release has them.

Each tarball contains `lib/`, `include/php/`,
`THIRD-PARTY-NOTICES.txt`, and — on Linux and macOS — a
Expand Down
55 changes: 55 additions & 0 deletions embed/EMBEDDING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# Embedding this SDK

This file ships in the `-gnu-nts` (static, `lib/libphp.a`) and
`-gnu-nts-shared` (shared, `lib/libphp.so`) Linux tarballs.

## Static (`-gnu-nts`): linking libphp.a

`bin/php-config` is relocatable (`--includes` for the `-I` set, `--libs`
for the archive group plus `-lm -ldl -lpthread`). Minimal embed build:

```sh
cc $(bin/php-config --includes) \
your_embed.c embed/resolver_shim.c \
-L lib $(bin/php-config --libs)
```

### resolver_shim.c — required on glibc >= 2.34

`libphp.a` is compiled against glibc 2.28 and references the underscored
resolver names (`__dn_expand`, `__res_nsearch`, `__dn_skipname`). glibc
2.34 moved libresolv into libc and stripped the unversioned compatibility
aliases, so linking on Ubuntu 22.04+ / Debian 12+ / RHEL 9+ fails with
undefined references to those symbols. Compile `embed/resolver_shim.c`
(MIT, from the ephpm project) into your build as shown above; it maps the
underscored names to the public `dn_expand`/`res_nsearch`/`dn_skipname`.
On glibc < 2.34 it is harmless.

### C++ runtime

`lib/libstdc++.a` is the archive the SDK's own C++ dependencies (ICU for
intl, etc.) were compiled against. `php-config --libs` includes it via the
group; do not substitute the host's copy across a libc flavor boundary.

## Shared (`-gnu-nts-shared`): linking or dlopening libphp.so

`lib/libphp.so` has the SDK's dependencies statically linked in and
exports the embed API (`php_embed_init` / `php_embed_shutdown`).

```sh
cc $(bin/php-config --includes) \
your_embed.c -L lib -lphp -Wl,-rpath,'$ORIGIN/../lib'
```

The resolver shim is not needed here: the library's resolver references
are versioned (`__dn_expand@GLIBC_2.2.5` etc. — check with
`readelf --dyn-syms`), which every later glibc still provides.

The library keeps ~15 undefined C++ runtime references (`__cxa_*`,
`_ZSt*`): the loading process must have `libstdc++.so.6` available at
runtime. It is present on any system with a C++ toolchain or runtime
installed; minimal containers may need `libstdc++6` (deb) /
`libstdc++` (rpm).

Both variants are non-thread-safe (NTS): one PHP engine per process, no
parallel requests in-process.
35 changes: 35 additions & 0 deletions embed/resolver_shim.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/* Resolver compat shim.
*
* From ephpm (https://github.com/ephpm/ephpm, crates/ephpm-php/resolver_shim.c),
* MIT license.
*
* PHP's ext/standard/dns.c references the underscored resolver names
* (`__dn_expand`, `__dn_skipname`, `__res_nsearch`) which glibc historically
* exported as unversioned compatibility aliases alongside the public
* `dn_expand` / `dn_skipname` / `res_nsearch`. Ubuntu 22.04+ (glibc 2.35+)
* strips the unversioned aliases: only versioned `__dn_expand@GLIBC_2.2.5`
* remains, and rust-lld / newer GNU ld will not silently match an
* unversioned reference to a versioned definition.
*
* This SDK's libphp.a is compiled against glibc 2.28, so linking it on a
* glibc >= 2.34 host hits exactly that: compile this file into your embed
* build to provide the underscored names as thin wrappers over the
* still-exported public entry points. Runtime cost is a single tail call.
* No behavior change.
*/

#include <resolv.h>

int __dn_expand(const unsigned char *msg, const unsigned char *eomorig,
const unsigned char *comp_dn, char *exp_dn, int length) {
return dn_expand(msg, eomorig, comp_dn, exp_dn, length);
}

int __dn_skipname(const unsigned char *comp_dn, const unsigned char *eom) {
return dn_skipname(comp_dn, eom);
}

int __res_nsearch(res_state statp, const char *dname, int class, int type,
unsigned char *answer, int anslen) {
return res_nsearch(statp, dname, class, type, answer, anslen);
}
4 changes: 3 additions & 1 deletion versions.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@
],
"platforms_nts": [
"linux-x86_64-gnu-nts",
"linux-aarch64-gnu-nts"
"linux-aarch64-gnu-nts",
"linux-x86_64-gnu-nts-shared",
"linux-aarch64-gnu-nts-shared"
],
"minors_nts": ["8.4"]
}