Skip to content

ci: move the shipped binaries to AlmaLinux 8, get the test images off EOL Debian - #790

Merged
rkennke merged 9 commits into
mainfrom
ci/bump-eol-base-images
Sep 17, 2026
Merged

rkennke merged 9 commits into
mainfrom
ci/bump-eol-base-images

Conversation

@rkennke

@rkennke rkennke commented Sep 14, 2026

Copy link
Copy Markdown
Contributor

Summary

Spun out of the CI investigation on #786. Gets the GitLab pipeline off end-of-life base images, and moves the shipped binaries onto a toolchain that still receives security rebuilds.

Root cause of the current failures

bullseye-security's pool files have been pruned upstream while the index still advertises them — Debian 11 left LTS on 2026-08-31. So apt-get update succeeds and apt-get install then 404s on the individual .debs:

E: Failed to fetch http://deb.debian.org/debian-security/pool/updates/main/g/glibc/libc6-dev_2.31-13+deb11u14_amd64.deb  404  Not Found
E: Unable to fetch some archives, maybe run apt-get update or try with --fix-missing?

Reproduced outside CI, straight from Fastly with no proxy in the path, so it is neither the fabric egress gateway nor a stale mirror. The +deb11uNN versions and glibc 2.31 also identify the running image as bullseye.

Bullseye cannot be rescued in place: bullseye-security is not published on archive.debian.org under either its own name or the older bullseye/updates name, and the main archive alone leaves held broken packages. A bullseye image can no longer even be rebuilt, since its own apt-get install layer fails. Verified: the job's exact package list installs cleanly on trixie.

Why the shipped-binary image matters more than it looks

Release builds link -static-libstdc++ -static-libgcc (ConfigurationPresets.kt:121-122), so the C++ runtime and unwinder in the shipped libjavaProfiler.so come out of the build image's toolchain. The build image's support status is therefore a property of the artifact, not just of CI.

On CentOS 7 those objects can never get another security rebuild. Worse for consistency: no gcc newer than devtoolset-10 was ever built for aarch64 anywhere in the EL7 ecosystem — the dts-11 collection ships binutils 2.36.1, elfutils, dwz and its own toolchain metapackage for aarch64, but no compiler. Confirmed against CentOS vault altarch, Oracle's SCL repo and the CentOS SIG buildlogs. Oracle Linux 7 is the best-maintained EL7 derivative (live CDN, both arches, working collections) but its public repos sit at the same 7.9 package levels, with later content behind an ELS subscription.

Changes

1. Shipped binaries: AlmaLinux 8 + gcc-toolset-14, both arches — new .gitlab/base/el8/Dockerfile, replacing .gitlab/base/centos7/Dockerfile and its vault.centos.org / Rocky-mirror repository plumbing. Supported until 2029, and the toolset is published for x86_64 and aarch64 alike, so one image definition covers both architectures with one compiler version. almalinux:8 is a multi-arch OCI index (amd64, arm64, ppc64le, s390x), so a single BASE_IMAGE_GLIBC digest serves both.

2. Test/tooling images: Debian 13 trixie, both arches. With the shipped arm64 build moved off the Debian image, that image no longer sets a customer-facing floor, so it moves to current stable alongside amd64 — which is what restores apt on the arm64 sanitizer jobs.

3. The archived buster default is gone with centos7/Dockerfile. It defaulted to openjdk:11-slim-buster; buster's repos are archived (does not have a Release file) and rebuild-images.sh reaches that default whenever the base-image variable is unset, since it only log_warns.

4. .gitlab/base/Dockerfile: no more swallowed install failures. Both the apt-get install and apk add lines ended in || true, so a total package-install failure produced a "successful" image whose tools were simply missing. The apk branch was dead — Alpine/musl has its own Dockerfile.musl.

5. Names describe purpose, not a version that has now changed twice: BUILD_IMAGE_{X64,ARM64}_GLIBC, BASE_IMAGE_GLIBC, tag suffixes {x64,arm64}-glibc-base.

6. The shipped artifact's ABI floor is now asserted in CI rather than being a property of whichever image the pipeline pulled — see below.

7. ddprof-lib/src/main/cpp/glibcCompat.h holds the one .symver pin needed to keep the floor at 2.17, guarded to glibc (musl has no symbol versioning, and a GLIBC_* .symver fails to link there) and carrying its own deletion instructions for when the oldest supported host moves.

The glibc floor is held at 2.17

The floor does not move. Building on a newer glibc does not by itself raise what
the artifact requires — symbol versions are per-function — but two references
did creep above 2.17, and both are now closed:

symbol why it appeared how it is held down
expf@GLIBC_2.27 glibc 2.27 added a faster expf under a new version and made it the default, so a build on 2.28 picks it up from unchanged source glibcCompat.h pins it to each architecture's oldest interface — GLIBC_2.2.5 on x86_64, GLIBC_2.17 on aarch64
getentropy@GLIBC_2.25 libstdc++ (GCC ≥ 12) seeds std::random_device with getentropy, and -static-libstdc++ copies that object into the library. Not referenced by anything in this source removed in #794, which dropped the std::random_device uses. glibc exports only one version of getentropy, so no pin could have reached it

The version to pin is per-architecture because a symbol's oldest version is the
baseline of the glibc release that introduced that port. expf@GLIBC_2.17 does
not exist on x86_64, where the symbol predates 2.17 by a decade.

Measured on the real artifact, built in this image on both architectures:

x86_64 aarch64
max required symbol version GLIBC_2.17 GLIBC_2.17
references above 2.17 none none
dynamic dependencies libc, libdl, libm, libpthread, librt same
libstdc++ / GLIBCXX / CXXABI none none

And loaded on a real EL7 host rather than inferred from symbol tables — Oracle
Linux 7.9, glibc 2.17, both architectures:

unresolved version requirements: NONE — all symbol versions resolve on glibc 2.17

So RHEL 7.9 and its derivatives keep working, and this PR has no
customer-facing consequence. Everything in it is CI-internal.

An ABI floor check, so this cannot drift again

The 2.27 regression above reached a green pipeline and was found by accident
while chasing an unrelated failure. Nothing checked what the shipped library
requires, so the floor was whatever the current base image happened to produce.

.gitlab/scripts/check-abi-floor.sh now asserts two properties of every glibc
artifact, at the end of the build job that produces it:

  1. the highest versioned glibc symbol it references is at or below
    SHIPPED_GLIBC_FLOOR (.gitlab/config.env, declared next to the image pin
    that determines it), failing with the offending symbols named;
  2. its DT_NEEDED set is within an allowlist — which catches a dropped
    -static-libstdc++, the more dangerous regression of the two, since the
    library would then need a C++ runtime from the host JVM's environment.

Three conditions fail loudly rather than reporting a pass: an artifact with no
versioned glibc symbols, one with no NEEDED entries, and a sort that cannot
order versions. Each would otherwise leave the check silently inoperative,
which is worse than not having it — and one of them earned its place during
development, when a parser that read only bare (non-parenthesised) symbol
versions saw a .symver-pinned artifact as having no versioned references at
all.

The checker reads through $OBJDUMP, so its tests drive it with canned output
and need no compiler or shared object: 25 assertions in
.gitlab/scripts/tests/check_abi_floor_test.sh, run by a new ci-script-tests
job alongside shellcheck. Every guard was mutation-checked — disabling it
individually turns a named assertion red.

Images rebuilt and repinned

The images were rebuilt from this branch's definitions in pipeline 137281788 and the digests are pinned here. Each was verified by pulling it:

pin contents
x64-glibc-base, arm64-glibc-base AlmaLinux 8.10, glibc 2.28, gcc 14.2.1
x64-base, arm64-base Debian 13 trixie, glibc 2.41, clang 19.1.7

The musl, datadog-ci and benchmark images come from the same rebuild; their definitions are unchanged on this branch.

For the record on why the pins are here rather than in the image-update PR that rebuild opened against main (#791, closed): main has no BUILD_IMAGE_*_GLIBC variables, so the two shipped-binary pins silently did nothing there, while BUILD_IMAGE_ARM64 — which main still uses for build:arm64 and stresstest:arm64 — would have moved the shipped arm64 binary onto a trixie image and its glibc 2.41 floor.

Worth knowing for the future: the pins live in this repo, and every rebuild creates new pipeline-ID-prefixed tags rather than overwriting old ones, so rebuilding affects no branch until a repin is merged.

Verified locally

  • .gitlab/base/el8/Dockerfile builds for both platforms; gcc-toolset-14 provides libstdc++.a/libgcc.a, the profiler's exact link flags succeed, and jq needs no EPEL.
  • ./gradlew :ddprof-lib:buildRelease succeeds inside the image on both architectures, and the measurements above come from those artifacts.
  • buster's apt repos fail as described; bullseye's install fails as described; trixie's succeeds.
  • The floor check passes at 2.17 on both architectures' release artifacts, and both load on Oracle Linux 7.9 (glibc 2.17) with no unresolved symbol versions.
  • The expf pin links under clang on trixie — the sanitizer jobs' toolchain — on both architectures, and under musl, where the guard correctly skips it.

One coverage regression this introduces, and how to undo it

The stress tests run in the same image as the build they consume, so moving the
build host moved them too:

before after
stresstest:x64 CentOS 7 — glibc 2.17 AlmaLinux 8 — glibc 2.28
stresstest:arm64 Debian bullseye — glibc 2.31 AlmaLinux 8 — glibc 2.28

arm64 moves toward the floor. x64 moves away from it, and that was the only
place anything executed against glibc 2.17 — the functional matrix runs on
ubuntu-latest and alpine, and the sanitizer jobs on trixie, so nothing else
comes near it.

Net: ABI coverage at the floor is much better than before (the check above, plus
loading the artifact on a real EL7 host), while runtime coverage at the floor
went from thin to zero. For a profiler that hooks PLT entries, walks
dl_iterate_phdr, and unwinds through libc's own frames, that is the half worth
having.

It is separable from this PR, which is why it is not fixed here: build:x64
genuinely cannot stay on CentOS 7 — that is the point of the change — but
stresstest:x64 has no such constraint. It compiles nothing, consuming the
prebuilt library through -Pskip-native -Pwith-libs (stresstests.sh:36-37),
so it can run in an EL7 container while the build stays on EL8. That is
better than the old arrangement, because it would exercise the el8-built
artifact on 2.17 rather than a 2.17-built one.

Proposed follow-up, happy to open it alongside this:

  1. give .stresstest_job its own image variable so the runtime environment stops
    being tied to the build environment, and point the glibc targets at an EL7
    image;
  2. add a small functional job on the same image, consuming the same artifact.
    Temurin 8, 11, 17, 21 and 25 all start on glibc 2.17 (verified), so JDK
    support is not a constraint — a two- or three-JDK subset on a nightly cadence
    would cover the libc-sensitive suites without multiplying the matrix.

Worth being precise about what that would and would not test: a container gives
EL7 userspace on the host kernel, so it covers the glibc surface (linking,
PLT layout, dl_iterate_phdr, TLS, unwinding through libc) but not
perf_events, timer_create or signal delivery, which are the host kernel's.
Real EL7-kernel coverage needs a VM and is a separate question.

Also noticed, not changed

  • DOCKER_IMAGE: …/images/docker:24.0.4-gbi-focal (images.yml) is Ubuntu 20.04, past standard support since April 2025. Left alone because I cannot enumerate replacement tags in that ECR registry.
  • .gitlab/fuzzing/.gitlab-ci.yml:20 and .gitlab/jdk-integration/.gitlab-ci.yml:32 also apt-get install at job time, so they depend on a live archive on every run, exactly as the sanitizer jobs do. Moving those packages into the images would make the pipeline independent of archive availability.
  • GitHub Actions is unaffected: ubuntu-latest/ubuntu-22.04 runners and alpine:3.23 containers are all still supported.

🤖 Generated with Claude Code

@datadog-datadog-prod-us1

datadog-datadog-prod-us1 Bot commented Sep 14, 2026

Copy link
Copy Markdown
Contributor

Pipelines

Unblock PR with BitsAI

❌ Errors

Your PR has failed checks. Please review the issues below and take necessary action before merging.

🚦 2 Pipeline jobs failed

DataDog/java-profiler | post-pr-comment — 🔧 Needs a code fix, caused by this PR

View more details · View in GitLab

DataDog/java-profiler | integration-test-x64-musl: [hotspot, 25]

View more details · View in GitLab

Useful? React with 👍 / 👎

This comment will be updated automatically if new data arrives.
🔗 Commit SHA: 68cf4d8 | Docs | View more details | Give us feedback!

@dd-octo-sts

dd-octo-sts Bot commented Sep 14, 2026

Copy link
Copy Markdown
Contributor

Scan-Build Report

User:runner@runnervmlun5p
Working Directory:/home/runner/work/java-profiler/java-profiler/ddprof-lib/src/test/make
Command Line:make -j4 all
Clang Version:Ubuntu clang version 18.1.3 (1ubuntu1)
Date:Wed Sep 16 15:45:13 2026

Bug Summary

Bug TypeQuantityDisplay?
All Bugs1
Logic error
Dereference of null pointer1

Reports

Bug Group Bug Type ▾ File Function/Method Line Path Length
Logic errorDereference of null pointerfaultInjection.cppcrashNow242

@rkennke
rkennke force-pushed the ci/bump-eol-base-images branch from bd98bb8 to 5386860 Compare September 14, 2026 10:15
@dd-octo-sts

dd-octo-sts Bot commented Sep 14, 2026

Copy link
Copy Markdown
Contributor

CI Test Results

Run: #35117227796 | Commit: adb3048 | Duration: 15m 27s (longest job)

All 32 test jobs passed

Status Overview

JDK glibc-aarch64/debug glibc-amd64/debug musl-aarch64/debug musl-amd64/debug
8 - - -
8-ibm - - -
8-j9 - -
8-librca - -
8-orcl - - -
11 - - -
11-j9 - -
11-librca - -
17 - -
17-graal - -
17-j9 - -
17-librca - -
21 - -
21-graal - -
21-librca - -
25 - -
25-graal - -
25-librca - -

Legend: ✅ passed | ❌ failed | ⚪ skipped | 🚫 cancelled

Summary: Total: 32 | Passed: 32 | Failed: 0


Updated: 2026-09-16 16:01:52 UTC

@rkennke rkennke changed the title ci: drop the archived buster base default, bump the amd64 build image ci: move the shipped binaries to AlmaLinux 8, get the test images off EOL Debian Sep 14, 2026
@dd-octo-sts

dd-octo-sts Bot commented Sep 14, 2026

Copy link
Copy Markdown
Contributor

All 40 integration tests passed

📊 Dashboard · 👷 Pipeline · 📦 68cf4d85

@rkennke
rkennke marked this pull request as ready for review September 14, 2026 17:16
@rkennke
rkennke requested a review from a team as a code owner September 14, 2026 17:16

@datadog-datadog-prod-us1 datadog-datadog-prod-us1 Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Datadog Autotest: PASS

More details

The CI jobs and rebuild scripts use consistent new image names. The Bullseye fallback already fails on the base branch, so it is not a regression from this PR.

Was this helpful? React 👍 or 👎

Open Bits AI session

🤖 Datadog Autotest · Commit 7bfc401 · What is Autotest? · @DataDog review to ask questions · Any feedback? Reach out in #autotest

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 7bfc40165c

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread .gitlab/base/Dockerfile Outdated
Comment thread .gitlab/base/el8/Dockerfile
rkennke and others added 9 commits September 16, 2026 16:54
The x64-2.17 image's Dockerfile defaulted to openjdk:11-slim-buster. Debian 10
buster left even LTS support in June 2024 and its repositories are archived, so
`apt-get update` against them now fails with "does not have a Release file" --
and rebuild-images.sh builds without --build-arg BASE_IMAGE whenever
BASE_IMAGE_LIBC_2_17 is unset, which reaches that default. The body of the file
is yum-based, so a Debian base cannot work there at all; the default now names
what CI actually passes (centos:7), with the glibc 2.17 rationale for staying
on an EOL distro recorded next to the vault mirror URLs it already needs.

The amd64 build image moves to Debian 13 trixie, the current stable release.
It compiles no shipped native code -- build:x64 uses the glibc 2.17 image -- so
its glibc is not a customer-facing floor. The arm64 image stays on bullseye: it
is where build:arm64 compiles the shipped linux-arm64 libjavaProfiler.so, so
that glibc (2.31) is the runtime floor for arm64 customers and trixie would
raise it to 2.41. Both variables now say which of the two they are and why.

Also drop the `|| true` from the base image's package install, and with it the
dead `apk` branch -- Alpine has its own Dockerfile.musl. As written, a total
install failure produced a "successful" image whose tools were simply missing,
surfacing later as an unrelated job failure.

Verified against the real archives: buster's apt repositories fail as
described, bullseye's still resolve, and the full package list installs on
trixie with hexdump present (bsdmainutils is still a real package there).

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Release builds link -static-libstdc++ -static-libgcc, so the C++ runtime and
unwinder in the shipped libjavaProfiler.so come out of the build image's
toolchain. That makes the build image's support status a property of the
artifact, not just of CI: on CentOS 7 those objects can never receive another
security rebuild, and no gcc newer than devtoolset-10 was ever built for
aarch64 there (the dts-11 collection ships binutils, elfutils and its own
metapackage for aarch64, but no compiler).

AlmaLinux 8 with gcc-toolset-14 is supported until 2029 and publishes the
toolset for x86_64 and aarch64 alike, so one image definition now covers both
architectures with one compiler version, replacing the hand-rolled CentOS 7
image and its vault/Rocky mirror repositories. almalinux:8 is a multi-arch
manifest, so a single BASE_IMAGE_GLIBC serves both.

Measured on the real artifact, built in this image for both architectures:

  max required symbol version   GLIBC_2.27  (both x86_64 and aarch64)
  symbols above 2.17            expf (2.27), getentropy (2.25)
  dynamic dependencies          libc, libdl, libm, libpthread, librt
  GLIBCXX/CXXABI references     none

So the runtime floor moves 2.17 -> 2.27, not to the base image's 2.28: symbol
versions are per-function. 2.27 keeps Ubuntu 18.04 (glibc 2.27) in support and
drops RHEL 7 (2.17), Amazon Linux 2 (2.26) and SLES 15 SP1 (2.26). Both
symbols are addressable with a .symver pin if a lower floor is needed later.

With the shipped arm64 build moved off the Debian image, that image no longer
sets a customer-facing floor, so it moves to trixie alongside amd64 -- which
also restores apt on the arm64 sanitizer jobs.

Names now describe the purpose rather than a version that has changed twice:
BUILD_IMAGE_{X64,ARM64}_GLIBC and BASE_IMAGE_GLIBC, with tag suffixes
{x64,arm64}-glibc-base. The variables still hold the current images until
rebuild-images.sh publishes the new tags.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
The sanitizer jobs installed cmake, libgtest-dev, libgmock-dev, binutils,
libc6-dbg and llvm in a before_script, which made every run depend on the
distro archive still serving them, and which does not survive the move to
trixie: sanitizer/asan_interface.h is no longer part of the llvm package
there, so compiling linearAllocator.cpp fails with

  fatal error: 'sanitizer/asan_interface.h' file not found

The header comes from libclang-rt-<n>-dev. The packages now live in the image
instead, using the unversioned libclang-rt-dev metapackage so the right clang
runtime is picked up on whatever release the image is built from.

Dropping the before_script override also stops these jobs bypassing the
pipeline default, so they get the CANCELLED guard and the maven proxy export
that every other job already had.

Verified on a locally built trixie image (clang 19.1.7, gtest 1.16.0),
aarch64: buildGtestAsan and buildGtestTsan both compile, and all 62 asan and
62 tsan binaries pass.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Pins from the rebuild in pipeline 137281788, which built every image from the
definitions on this branch. Verified by pulling each digest:

  x64-glibc-base    AlmaLinux 8.10, glibc 2.28, gcc 14.2.1
  arm64-glibc-base  AlmaLinux 8.10, glibc 2.28, gcc 14.2.1
  x64-base          Debian 13 trixie, glibc 2.41, clang 19.1.7
  arm64-base        Debian 13 trixie

The musl, datadog-ci and benchmark images come from the same rebuild; their
definitions did not change on this branch.

These pins belong here rather than in the image-update PR the rebuild opened
against main: main has no BUILD_IMAGE_*_GLIBC variables, so the two
shipped-binary pins silently did nothing there, while BUILD_IMAGE_ARM64 -- which
main still uses for build:arm64 and stresstest:arm64 -- would have moved the
shipped arm64 binary onto a trixie image and its glibc 2.41 floor.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
The ARG default was still the bullseye digest while both OPENJDK_BASE_IMAGE
variables moved to trixie, and the comment justified it as "the more
conservative of the two" for an arm64 glibc floor that no longer exists here:
the shipped binaries come from .gitlab/base/el8/Dockerfile, so neither arch of
this image sets a customer floor.

It also stopped being a harmless default. With the package install no longer
ending in `|| true`, a direct `docker build` of this file -- or
rebuild-images.sh reaching its documented unset-variable fallback -- hit
bullseye's pruned security pool and failed on exactly the 404s this PR exists
to fix. Verified: building with no BASE_IMAGE argument now produces a trixie
image.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
The oldest host the shipped library can load on is decided entirely by the
build image: the highest versioned glibc symbol it references sets the glibc
floor, and the static-link flags decide whether it needs a C++ runtime from
the host at all. Nothing checked either, so both could move without a source
change and without a failing job.

check-abi-floor.sh asserts both against SHIPPED_GLIBC_FLOOR, declared in
config.env next to the image pin that determines it, and build.sh runs it on
each glibc artifact before the job ends. musl targets carry no glibc symbol
versions and are skipped. Failures name the offending symbols or libraries,
since the point of the check is to say what needs fixing.

Three cases fail loudly rather than reporting a pass: an artifact with no
versioned glibc symbols, one with no NEEDED entries, and a sort that cannot
order versions. Each would otherwise leave the check quietly inoperative,
which is worse than not having it.

The checker reads the artifact through $OBJDUMP, so its tests drive it with
canned output and need no compiler or real shared object; they run in a new
ci-script-tests job alongside shellcheck. Every guard was mutation-checked:
disabling it individually turns a named assertion red.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
glibc 2.27 added a faster expf under a new symbol version and made it the
default, so building on AlmaLinux 8 made the shipped library require
expf@GLIBC_2.27 from source that has not changed. glibcCompat.h selects the
GLIBC_2.17 interface explicitly, which is still exported and still maintained;
.symver picks which of the versions already in the host's libc is called and
bundles nothing, so a host on a newer glibc still runs its own implementation.

Measured on the AlmaLinux 8 + gcc-toolset-14 build: expf moves from
GLIBC_2.27 to GLIBC_2.17 and the artifact's requirement drops from 2.27 to
2.25, so the floor follows to 2.25. The remaining above-2.17 symbol is
getentropy, which libstdc++ references from its own std::random_device and
which glibc exports in only one version -- no pin can reach it, so 2.17 waits
on removing the std::random_device uses.

The pin is guarded to glibc: musl has no symbol versioning and a .symver
naming a GLIBC_* version fails to link there, which would have broken both
musl targets. Verified by linking and running a TU that reaches expf through
poissonSampler.h under musl, where it emits no GLIBC_ references at all.

The floor check could not read the artifact the pin produces. objdump
parenthesises a binding to a non-default version, which is exactly what
.symver creates, and the parser matched only the bare form -- so it found no
versioned references on a pinned library. The empty-result guard turned that
into a loud failure rather than the silent pass it would otherwise have been,
which is what it exists for. The parser now accepts both forms, with fixtures
for a pinned artifact at and above the floor.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
The expf pin named a version that does not exist on x86_64, so every link
against it failed there:

  nativeSocketSampler.o: undefined reference to `expf@GLIBC_2.17'
  no symbol version section for versioned symbol `expf@GLIBC_2.17'

A symbol's oldest version is the baseline of the glibc release that introduced
the port, which differs per architecture: x86_64 has expf@GLIBC_2.2.5 and
aarch64 expf@GLIBC_2.17, both alongside the GLIBC_2.27 default added in 2.27.
Naming a version the architecture does not export is a link error rather than a
fallback, so the pin now selects per architecture and an unlisted one gets no
pin at all, leaving the ABI floor check to report what it requires.

This took out every x86_64 job that links the library: the two sanitizer gtest
builds, and the CodeQL and Analyze jobs, which build linkRelease.

Verified by linking and running a translation unit that reaches expf through
poissonSampler.h under clang on Debian trixie -- the toolchain the sanitizer
jobs use -- on both architectures, and by building the release artifact on
AlmaLinux 8 for both: expf binds to each architecture's oldest version,
getentropy@GLIBC_2.25 is the only remaining reference above 2.17, and the floor
check passes at the declared 2.25 on both.

Separately, ci-script-tests invoked the test suites directly. includes_test.sh
is not executable and documents being run through bash, so the job failed with
exit 126 after its own assertions had all passed. Both suites now run through
bash, which leaves the existing file's mode alone.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
#794 removed the std::random_device uses, so libstdc++'s getentropy reference
no longer enters the link and the last symbol above 2.17 is gone. With expf
already pinned per architecture, the artifact requires nothing newer than the
glibc that EL7 ships.

Measured on release builds in the AlmaLinux 8 image on both architectures: no
references above 2.17 at all, and the floor check passes at 2.17 where it
previously named getentropy@GLIBC_2.25. Both artifacts also load on Oracle
Linux 7.9 (glibc 2.17) with no unresolved symbol versions, which tests the
dynamic linker rather than the symbol table.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
@rkennke
rkennke force-pushed the ci/bump-eol-base-images branch 2 times, most recently from ab651e5 to 68cf4d8 Compare September 16, 2026 15:43
@dd-octo-sts

dd-octo-sts Bot commented Sep 16, 2026

Copy link
Copy Markdown
Contributor

Reliability & Chaos Results

All reliability & chaos checks passed Pipeline: https://gitlab.ddbuild.io/DataDog/java-profiler/-/pipelines/137918131

Comment thread ddprof-lib/src/main/cpp/glibcCompat.h

@jbachorik jbachorik left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice. LGTM!

@rkennke
rkennke merged commit a5a613a into main Sep 17, 2026
158 of 170 checks passed
@rkennke
rkennke deleted the ci/bump-eol-base-images branch September 17, 2026 09:32
@github-actions github-actions Bot added this to the 1.51.0 milestone Sep 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants