Weak Symbols in Cross-Compiled Libraries: Verify Which Implementation Wins
A strong replacement can override a weak fallback only if the replacement participates in the relevant link. Putting the replacement into an archive does not guarantee that its member is extracted. Diagnose symbol binding and object selection separately, then prove which implementation the final executable calls.
This guide uses ordinary ELF objects with GCC 14.2 and GNU BFD Binutils 2.44. An executed host fixture compared a weak fallback, a directly linked strong definition, an archive-only definition and an explicit extraction anchor. It did not test LTO or dynamic-library interposition.
Define the override contract before the flags
Suppose a library offers a weak hook returning a conservative default. The product supplies a strong function with the same interface. Write down whether overriding is optional, which component owns the replacement and how a release proves the replacement is present.
The GCC 14.2 weak-attribute documentation describes weak definitions as an override mechanism and requires a matching type. For variables it also requires matching size and alignment. An identical symbol spelling cannot repair an incompatible prototype or data layout.
Keep C++ linkage names, visibility and calling convention in the inventory when applicable. A function accidentally declared static is local, while a differently mangled name is a different symbol. Neither is a valid strong replacement merely because the source-level function names look alike.

Inspect the fallback and replacement independently
Use nm or readelf -sW on the actual objects. For the tested ELF functions, nm displayed W for the weak fallback and T for the ordinary strong text definition. Preserve raw names and object paths; a demangled listing is a convenience rather than the canonical comparison key.
If the replacement lives in an archive, inspect the member that defines it and the archive used by the final command. A same-named archive in another directory can invalidate the diagnosis. Capture the complete compiler-driver link command and expanded response files before changing the build.
The Binutils 2.44 symbol-tool manual provides archive/member labels and binding indicators. A definition visible inside an archive proves availability as an input candidate. It does not prove that the member entered the executable.
Compare four controlled outcomes
The fixture’s main object called hook and printed its return value. The fallback returned 1. A separate strong object returned 2 and also defined a unique override_anchor symbol. The build used separate translation units without LTO so the experiment focused on ordinary symbol resolution.
| Selected inputs | Observed executable |
|---|---|
| Main plus weak fallback | Printed 1; final hook binding was weak. |
| Main, weak fallback and direct strong object | Printed 2; final hook binding was strong. |
| Main, weak fallback and replacement archive | Printed 1; replacement member was not selected. |
| Same archive case with a unique anchor request | Printed 2; anchor selected the strong member. |
In the archive-only case, the existing fallback satisfied the hook reference before the archive search needed that member. The replacement’s stronger binding did not make every archive member enter the link automatically. This is the important distinction from an ordinary unresolved-symbol ordering error.
Prove selection with the map and trace
Use a map file and –trace-symbol=hook for the controlled comparison. The trace shows the participating references and definitions; the map identifies extracted archive members. Read those alongside the final symbol binding and the program’s behavioral result.
A final T binding alone may not identify which of several strong providers won or whether the wrong artifact was inspected. Tie the map and executable hashes to the same build. A runtime result alone can also be misleading when two implementations happen to return the same value in the chosen test.
The BFD 2.44 linker manual documents explicit undefined-symbol requests and archive inclusion controls. In this fixture, -u override_anchor introduced a request for a unique symbol in the replacement member. Using -u hook instead is not the same proof when hook already has a satisfying weak definition.
Choose a maintainable inclusion rule
If a product requires the replacement unconditionally, linking its object directly can make that requirement obvious. If the build needs an archive, a deliberate unique anchor can connect selection to the required member. Put the anchor in the same member or in an explicit dependency chain, then verify the resulting map.
A narrowly scoped –whole-archive can include all members of a dedicated override archive, but it changes selection more broadly. Bound it with –no-whole-archive and examine duplicate definitions, constructors and added code. Do not make every dependency whole-archive to hide one missing override.
When the fallback is forbidden for a particular product configuration, consider a separate build-time contract that fails if only the fallback remains. A weak definition is intentionally permissive; it should not be the sole enforcement mechanism for a mandatory hardware or safety-specific implementation.
Keep optional references and dynamic lookup separate
An undefined weak reference has different behavior from a weak definition that supplies a callable fallback. Code using an optional weak reference must follow the platform’s supported presence check before calling it. The executed fixture had a real fallback definition and does not qualify an absent-symbol call path.
Likewise, ELF shared-library interposition introduces runtime scope and lookup rules beyond this static-link experiment. Do not use these four outcomes as a complete explanation of LD_PRELOAD, protected visibility or symbol binding inside a shared object. Investigate those cases with their own loader-specific evidence.
Retain a replacement proof in the release
Build both the intentional fallback configuration and the mandatory replacement configuration. Preserve the input bindings, extraction evidence, final binding and distinguishable behavior for each. Repeat after moving source files between archives, enabling LTO or changing the linker.
The useful result is a short resolution map answering where the fallback came from, why the replacement entered the link and how its behavior was demonstrated. That is stronger than saying that a strong symbol “should win” while leaving object selection unexamined.