Position-Independent Objects: Fix Relocation Errors When Static Libraries Enter Shared Objects
Adding -fPIC to a final shared-library link does not rewrite a dependency archive’s existing machine instructions. When a static library moves into a shared object, identify the specific member and relocation that the linker rejects, then rebuild the producer objects with the appropriate position-independent code policy.
This procedure is pinned to GCC 14.2 and GNU BFD Binutils 2.44. The executed failure was an x86-64 reference to externally visible data. Its relocation name is architecture-specific; no ARM, AArch64 or embedded target link was performed.
Separate the artifact roles
An archive is a container of object files, not a guarantee that those files can participate in every output type. The same ordinary archive may link into a tested executable yet contain relocations unsuitable for a shared object’s loading and symbol-binding requirements.
Record whether the requested output is a normal executable, a position-independent executable or a shared object. Also record each input’s producer flags. A build system can correctly enable PIC for its own wrapper while importing a dependency compiled under an unrelated policy.
The GCC 14.2 code-generation reference distinguishes -fpic/-fPIC from the executable-oriented -fpie/-fPIE options. Target support and code-model details matter. Do not use the presence of “position independent” in a flag description as permission to substitute every variant.

Capture the exact failing relocation
Preserve the full linker diagnostic, including the object or archive member, relocation type and referenced symbol. Confirm the ELF machine and class before interpreting the code. A wrong-architecture object is a different failure from a relocation that is invalid in the intended output.
Use the target-capable readelf -rW to inspect relocations in the implicated member. Pair that with its symbol table to establish whether the referenced data is local, hidden or externally visible. The Binutils 2.44 readelf documentation separates headers, symbols and relocations so the evidence can be preserved independently.
Do not search for a single relocation name across every architecture and label all occurrences invalid. Even on one architecture, a relocation’s acceptability can depend on the referenced symbol and the final link context. Diagnose the actual rejected relationship.
Reproduce one producer-object boundary
The host fixture defined global_value as an externally visible integer initialized to 7 and a function returning that value. A PIC wrapper called that function. The dependency was first compiled explicitly with -fno-pic and -fno-pie, then placed in a static archive.
On the tested x86-64 toolchain, the dependency object contained an R_X86_64_PC32 relocation for the data reference. Linking the PIC wrapper and that archive into a shared library failed, with the diagnostic identifying that relocation and asking for a PIC rebuild.
The same dependency source was then recompiled with -fPIC and placed in a fresh archive. The unchanged wrapper and shared-library link succeeded. A trusted synthetic consumer loaded the resulting library through its local runtime path and confirmed the expected value of 7.
| Changed input | Observed result |
|---|---|
| Non-PIC dependency archive plus PIC wrapper | Shared link rejected the x86-64 data relocation. |
| Dependency rebuilt with -fPIC; wrapper unchanged | Shared link and consumer test passed. |
This is evidence for one compiler, linker, architecture and reference pattern. A non-PIC function with no problematic address references can sometimes link successfully; that does not prove the whole archive follows a suitable shared-library policy.
Trace the rebuild to its real owner
Find the maintained build target that produced the rejected member. Change the compile policy there, then verify the verbose compile command for that source. Adding a consumer-side option cannot repair a prebuilt archive whose producer never sees the option.
Invalidate stale objects in a controlled fresh build directory. Archive updates can leave confusing members when a recipe changes names or source membership. List the new archive, compare member hashes and confirm the final link selects the new path rather than an older sysroot copy.
If the archive is vendor-supplied, request a compatible PIC build or keep the supported linkage arrangement. Do not promise that an object-copy utility can convert arbitrary non-PIC machine code into correct PIC. Such conversion is not equivalent to recompiling the source under the required model.
Audit the accepted shared object
After the repaired link, inspect dynamic tags, runtime relocations and segment permissions. Investigate text-relocation requirements or writable/executable segment warnings rather than weakening the linker’s checks to suppress them. The BFD 2.44 linker reference describes controls and diagnostics whose applicability must be checked for the selected target.
Keep any dependency or undefined-symbol policy explicit. A shared object can link while retaining expected runtime imports, so “no linker error” does not mean the complete deployment is ready. Verify the exported interface and the runtime provider closure separately from the PIC repair.
Do not change symbol visibility merely to make a relocation disappear unless hiding that symbol is correct for the public ABI. Visibility affects binding semantics and consumers. A code-generation repair should not silently remove an interface the product promised.
Qualify the target without importing host assumptions
For the actual target, repeat the producer/member audit with its compiler, linker and ABI documentation. Use read-only artifact inspection first, then run the consumer in an approved image. Include calls that exercise data references and address-taking paths, not just a function that returns a constant.
Preserve the original rejected member, the replacement compile command, the new relocation inventory and the successful consumer result. If only part of an archive is rebuilt, explain why every remaining selected member is still acceptable.
The deliverable is a producer-to-output relocation map. It should identify which object needed rebuilding and why, while keeping architecture-specific evidence separate from the general rule that PIC must be generated at the object-producing stage.