Feature-Test Macros in Cross Packages: Expose the Intended libc Interface Consistently
Choose libc feature-test macros before any system header is processed, and apply the policy consistently to the translation units that need it. A function can exist in libc while its declaration is hidden under the selected language and feature mode. Adding a guessed prototype or suppressing the diagnostic can turn that configuration error into a type mismatch.
This article pins glibc 2.41 headers and reports compile-only GCC 14.2.0 checks on x86-64 Linux. It examines declaration visibility and the selected strerror_r interface. It does not migrate libc, change time/file-width ABIs or execute calls through mismatched function types.
Inventory the effective preprocessor context
For each failing source, record the compiler driver, language mode, target/sysroot and all -D/-U options. Include definitions supplied by generated configuration headers, forced includes and transitive build-target requirements. A macro visible in one source file is not necessarily visible in another.
The glibc 2.41 feature-selection header describes user-facing selectors such as _POSIX_C_SOURCE, _XOPEN_SOURCE, _DEFAULT_SOURCE and _GNU_SOURCE. It derives internal __USE_* and related conditions used by other headers.
Do not define those internal conditions directly. They are implementation machinery, not a supported application policy. Select the documented public macro and verify the resulting declaration through the actual target headers.
Strict language modes matter. The fixture used -std=c11 without GNU/default feature overrides. A project compiled in a GNU dialect may expose additional defaults, so reproducing its failure with a different language mode can answer the wrong question.

Use getline as a declaration-visibility control
The tagged stdio header exposes getline under its POSIX.1-2008-related condition or another documented extension condition. The fixture deliberately isolated the POSIX selector rather than claiming it is the only possible route.
/* Compile-only probe; never calls the function. */
#include <stdio.h>
#include <sys/types.h>
ssize_t (*probe)(char **, size_t *, FILE *) = getline;
Under the fixture’s strict C11 defaults, compilation failed because getline was undeclared. Adding -D_POSIX_C_SOURCE=200809L before preprocessing made the same declaration compile successfully with warnings treated as errors.
The third case placed the macro definition after including stdio.h. It still failed: the header and its feature-selection machinery had already been processed. Defining a macro later does not retroactively regenerate declarations from earlier includes.
| Executed compile-only case | Result | Meaning |
|---|---|---|
| Strict C11, no selected POSIX extension | getline undeclared. | This header context did not expose the declaration. |
| Early _POSIX_C_SOURCE=200809L | Pointer declaration compiled. | The selected header interface was visible and type-compatible. |
| Same definition after stdio.h | getline remained undeclared. | The feature choice arrived too late. |
None of these results tests allocation, line reading or runtime availability on an embedded target. They isolate the preprocessing and declaration boundary so the subsequent link and functional tests can start from a valid type contract.
Check the selected type, not merely the name
The glibc 2.41 string header documents two strerror_r interfaces. The POSIX form returns int, while the GNU form returns char *. In the scoped header, selecting the POSIX environment without _GNU_SOURCE chooses the former, including its glibc redirection machinery.
The executed positive probes assigned strerror_r to an int-returning function pointer under _POSIX_C_SOURCE=200809L and to a char-pointer-returning function pointer under _GNU_SOURCE. Both compiled. Assigning the GNU-selected declaration to the int-returning pointer failed with an incompatible-pointer-types diagnostic treated as an error.
That is an interface-selection failure, not a missing library file. Do not repair it with a cast or a handwritten declaration. Decide which contract the source actually implements, then make its feature policy and error handling agree.
A shared utility header that assumes one form can become inconsistent when included by targets with different definitions. Test that header under every supported target policy, or provide a deliberately reviewed wrapper with one application-facing contract.
Put the policy at a stable build boundary
Prefer an early, consistent definition in the build configuration or a configuration header guaranteed to precede all system headers. Audit forced includes and precompiled headers, because either can process libc headers before a source-local definition is reached.
Apply the narrowest policy that supplies the required interfaces. _GNU_SOURCE is a broad GNU extension selection, not a universal cure for every undeclared function. Enabling it can select different interfaces, as the strerror_r control demonstrates.
When a public library header requires a feature policy, decide how consumers receive that requirement and whether the header remains self-contained under supported configurations. Do not let only the library’s own source files receive definitions needed by its callers.
Inspect macros and declarations with the target compiler
A proposed inventory command is the target compiler’s -dM -E preprocessing pass over a representative source. Preserve the resulting relevant selectors together with the actual command. The host fixture verified _POSIX_C_SOURCE=200809L and identified its headers as glibc 2.41 in this output.
Use compile-only pointer or type assertions to validate the intended declaration without invoking it. Treat missing declarations and incompatible pointer types as errors. Then perform a separate target link check and bounded functional test for the selected interface.
A successful workstation probe does not prove another sysroot exposes the same API. Other libc implementations can interpret feature policies differently or support different extensions. Pin their own documentation and headers rather than applying this glibc-specific matrix unchanged.
Close with a reproducible visibility contract
Retain the macro inventory, include ordering, selected libc/header identity and positive/negative compile probes. Review all affected translation units after changing a feature definition; a clean compile of one file is not evidence of project-wide consistency.
The executed cases establish early versus late declaration visibility and two distinct strerror_r type selections on glibc 2.41. They provide a focused regression gate while leaving runtime behavior and target compatibility explicitly separate.