You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
cargo-pgrx: inject -Wl,--no-gc-sections to keep .pgrxsc sections on aarch64 Linux (pgcentralfoundation#2280)
Closespgcentralfoundation#2279
(hopefully)
Background
----------
The "one compile" schema pipeline embeds SQL entity metadata in a
`.pgrxsc` ELF section made of `#[used]` statics scattered across every
codegen unit. Rust lowers `#[used]` to `@llvm.used`, which is supposed
to translate to `SHF_GNU_RETAIN` so `ld --gc-sections` (which rustc
passes by default on ELF cdylibs) leaves the section alone. That
contract has three load-bearing links: LLVM must emit the flag, binutils
must honor it, and LTO must not mask the difference.
The chain breaks on aarch64 Linux under non-LTO release builds. When it
breaks, every `.pgrxsc` input section is dropped, the output section
vanishes, and `cargo pgrx schema` fails with:
no embedded pgrx schema section found; expected `.pgrxsc` on ELF/PE
or `__DATA,__pgrxsc` on Mach-O
pglinter 0.18.0 hit exactly this on their arm64 `release-lto-off` build
while their amd64 `release` (lto = "fat") build kept working — fat LTO
preserves `@llvm.used` globals via the linker plugin, sidestepping the
`SHF_GNU_RETAIN` path entirely. The asymmetry is profile-driven, not
architecture-driven; arm64 just happens to be where we see it first.
Fix
---
cargo-pgrx now injects a top-level cargo `--config` on every cargo
invocation it spawns:
target.'cfg(all(target_family = "unix",
not(target_os = "macos")))'.rustflags =
["-C", "link-arg=-Wl,--no-gc-sections"]
That disables section GC for the cdylib link step on Linux/BSDs (where
the retention contract is fragile) and leaves macOS (Mach-O, `ld64`,
uses `-dead_strip` and doesn't speak `-Wl,...`) and Windows MSVC
(`link.exe`, also doesn't speak `-Wl,...`) alone. The injection is wired
into the `cargo()` helper in `cargo-pgrx/src/cargo.rs`, which is the
single chokepoint that every cargo subprocess in the crate goes through
— both `Cargo::into_command()` (schema, test, run, pgrx_target) and the
hand-rolled `Command` in `install.rs::build_extension`.
The cost is a few KB of unreferenced code surviving in the final .so,
which is a trade we'll happily make for reliable schema generation
across the whole LLVM/binutils matrix.
Users who set `RUSTFLAGS`/`CARGO_ENCODED_RUSTFLAGS` continue to override
per cargo's rustflags precedence. A new env var
`CARGO_PGRX_DISABLE_GC_SECTIONS_WORKAROUND` provides an explicit escape
hatch.
Why inject instead of touch `pgrx`'s own build.rs
-------------------------------------------------
`cargo:rustc-link-arg-cdylib` from a build script only affects the
package the build script belongs to — it doesn't propagate to
dependents. `pgrx` is an rlib, so emitting link args from its build.rs
is effectively a no-op at extension-link time. Injecting from cargo-pgrx
at the cargo invocation boundary is the one place where we can fix this
for every extension without requiring a source change in each one.
CI regression coverage
----------------------
New `pgrx_tests_arm64` job in `.github/workflows/tests.yml` runs on
`ubuntu-24.04-arm` (free native arm64 runner, same as pglinter uses) and
executes:
cd pgrx-unit-tests && cargo pgrx schema pg17 --release
with `CARGO_PROFILE_RELEASE_LTO=false` and
`CARGO_PROFILE_RELEASE_CODEGEN_UNITS=16` pinned so the regression test
stays faithful to the pglinter failure shape even if the workspace
`[profile.release]` is edited later. pgrx-unit-tests has dozens of
`#[pg_extern]` / `#[pg_trigger]` / `#[pg_aggregate]` / etc. entries, so
every schema entity kind contributes to `.pgrxsc` and the test exercises
every producer path.
If the `--no-gc-sections` injection ever regresses, this job fails
loudly with the exact error string users would see.
Changes
-------
- cargo-pgrx/src/cargo.rs: `pgrx_injected_config_args()` helper + unit
test guarding the `--no-gc-sections` and non-macOS scope
- cargo-pgrx/README.md: document
`CARGO_PGRX_DISABLE_GC_SECTIONS_WORKAROUND` alongside existing `PGRX_*`
env vars
- .github/workflows/tests.yml: `pgrx_tests_arm64` job pinned to the
non-LTO release profile shape
Copy file name to clipboardExpand all lines: cargo-pgrx/README.md
+1Lines changed: 1 addition & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -61,6 +61,7 @@ Options:
61
61
-`PGRX_BUILD_VERBOSE` - Set to true to enable verbose "build.rs" output -- useful for debugging build issues
62
62
-`HTTPS_PROXY` - If set during `cargo pgrx init`, it will download the Postgres sources using these proxy settings. For more details refer to the [env_proxy crate documentation](https://docs.rs/env_proxy/*/env_proxy/fn.for_url.html).
63
63
-`PGRX_IGNORE_RUST_VERSIONS` - Set to true to disable the `rustc` version check we have when performing schema generation (schema generation requires the same version of `rustc` be used to build `cargo-pgrx` as the crate in question).
64
+
-`CARGO_PGRX_DISABLE_GC_SECTIONS_WORKAROUND` - Set to any value to stop `cargo-pgrx` from injecting `-C link-arg=-Wl,--no-gc-sections` on non-macOS Unix cdylib builds. By default, `cargo-pgrx` passes this flag (scoped to `cfg(all(target_family = "unix", not(target_os = "macos")))`) to cargo as `--config target.<cfg>.rustflags=[...]` only for cdylib-producing invocations — `cargo pgrx install`, `cargo pgrx package`, and the non-`--test` path of `cargo pgrx schema`. This prevents GNU `ld --gc-sections` from dropping the `.pgrxsc` section where the embedded SQL schema metadata lives — a problem that surfaces on non-LTO builds (notably aarch64 Linux) as `no embedded pgrx schema section found`. The workaround is intentionally not applied to `cargo pgrx test` / `cargo pgrx schema --test` because those also link a test binary whose undefined Postgres `extern "C"` references are expected to be GC'd away. Disable this only if you are explicitly managing `rustflags` yourself and understand the retention contract.
0 commit comments