Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Weak symbols let a library or platform layer provide a default function or object that an application can replace with a strong definition of the same name at link time. They are useful for optional callbacks and embedded exception handlers, but they are a compiler-and-linker feature—not portable C or C++ behavior. The key is to distinguish a weak default definition from an undefined weak reference: the first supplies fallback behavior; the second may resolve to zero under ELF rules and must be checked before use.

What weak symbols are used for

A weak symbol is a definition or reference with special linker binding. Under ELF, a weak definition has lower precedence than a global definition. If both define the same symbol, the strong global definition wins without a duplicate-definition error. GCC describes its weak attribute as emitting an external declaration as a weak symbol rather than a global one.

This makes weak symbols useful when one implementation should be the default and an application may replace it without editing the library or startup code. The choice is made at link time, not each time the program runs.

  • Overridable library defaults: a library provides a conservative implementation of a callback, and an application supplies a strong implementation when it needs different behavior.
  • Embedded exception and interrupt handlers: startup code provides placeholder handlers, while a project defines strong handlers for the interrupts it uses. Arm documents this as a common use of weak function definitions.
  • Optional platform hooks: a component can support optional logging, tracing, board identification, or a platform service without requiring every application to implement it.

Override a weak function in C

Provide a weak default

With GCC on a supported target, define the default with __attribute__((weak)). Keep its declaration and definition consistent with the public interface, and make the fallback behavior deliberate rather than silently masking a missing integration.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
/* library.c */
__attribute__((weak)) void platform_log(const char *message)
{
    (void)message; /* Deliberate fallback: discard the message. */
}

Provide the application’s strong definition

In a separate application source file, define the same external symbol normally, with the exact same function type and calling convention:

/* app.c */
void platform_log(const char *message)
{
    /* Send the message to the application's logging system. */
}

When both object files participate in the link and the toolchain implements these semantics, the application’s strong platform_log definition is selected. The fallback remains available when no strong definition is linked. Do not rely on this mechanism to reconcile incompatible declarations: a mismatched function type is an ABI defect, not a valid override.

Check that the intended definition won

Inspect the final executable or link map rather than assuming the override was included. GNU binutils users can examine symbols with nm or readelf; embedded toolchains typically provide equivalent symbol-table or map-file views. Confirm that the final symbol is the application definition, then test both builds: one with the override and one without it.

Weak default definitions and undefined weak references are different

Weak default definition

A weak default has a body. If no strong definition replaces it, that body is the implementation used by the program. This is generally the straightforward pattern for a default callback or placeholder handler.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Undefined weak reference

An undefined weak symbol has no definition in the current link. ELF permits an unresolved weak symbol to have a zero value. Therefore, an optional function or object referenced this way must be checked before dereferencing or calling it; do not treat an absent hook as though it were a callable no-op.

extern void optional_trace(const char *message)
    __attribute__((weak));

void report_event(const char *message)
{
    if (optional_trace != 0)
        optional_trace(message);
}

Document what happens when the hook is absent, and apply the appropriate presence check before using optional data symbols as well. GCC’s weakref facility describes a weak reference as an alias that does not itself require the target symbol to be defined.

Use weak aliases and weakref deliberately

An alias or weak reference can make one symbol name refer weakly to another. This is useful when a component wants a local or alternate name for a hook without requiring the target definition to exist. It does not remove the need to understand the target toolchain’s alias rules.

  • Keep the alias or reference type-compatible with its target.
  • Make the target relationship visible in the relevant header or linker documentation.
  • Check whether the selected compiler requires an alias target to be defined in the same translation unit, and follow that compiler’s documented syntax.
  • Treat a missing weak target as potentially zero under ELF; guard its use where applicable.

Why a weak symbol may not be pulled from a static library

ELF linkers do not extract an object file from a static archive merely to satisfy an undefined weak reference. This differs from the ordinary expectation that a referenced archive member will be brought into the link. As a result, a hook implementation can exist in a library archive and still be absent from the final executable if nothing else causes its member to be selected.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Best Value

Diagnose archive extraction

  1. Check the link map or symbol table to see whether the archive member containing the hook was included.
  2. Check whether another strong unresolved reference causes that same member to be extracted.
  3. Review static-library order and the linker’s documented archive-search behavior for the target toolchain.
  4. If inclusion is intentional and no ordinary strong reference pulls in the object, use the linker’s force-inclusion option narrowly.

GNU ld provides --whole-archive to force inclusion of every object from an archive. Pair it with --no-whole-archive so the behavior does not unintentionally extend to later libraries:

cc main.o -Wl,--whole-archive -lplatform_hooks 
  -Wl,--no-whole-archive -o app

Forcing every archive member into a link can increase included code and introduce duplicate definitions or otherwise unnecessary objects. Apply it only to the archive that needs it, and inspect the result.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Weak symbols versus explicit registration

Weak symbols are concise when one link-time default should be replaceable by one application implementation. An explicit function-pointer configuration or registration API is often clearer when selection must happen at runtime, several providers may coexist, or missing configuration should be obvious.

Decision point Weak symbol Explicit registration or function pointer
Portability Depends on compiler, object format, and linker support. Uses ordinary language-level interfaces, though API details still vary.
When selection happens At link time. Can be configured at runtime or during initialization.
Missing implementation A weak default can hide absence unless its fallback is observable; an undefined weak reference may be zero under ELF. Can make unconfigured state explicit and report it through the API.
Multiple providers Best suited to one selected strong definition replacing one default. Can support multiple registrations or selectable providers by design.
Testing and diagnosis Requires checking which symbol the linker selected; archive extraction can affect availability. Provider choice is explicit in configuration and can be exercised through ordinary API tests.

Portability and review checklist

GCC documents weak-symbol support for ELF and GNU a.out environments. That does not make __attribute__((weak)) portable C or C++, nor does it guarantee identical behavior from every compiler, object format, or linker. For other toolchains, use the vendor’s documented mechanism and verify its semantics.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
  • Confirm the target object format and compiler/linker support.
  • Match function type, calling convention, and—for variable overrides—size and alignment.
  • Choose intentionally between a weak default definition and an undefined weak reference.
  • Guard undefined weak functions or objects before use where the target format permits a zero value.
  • Check whether a static archive member is actually extracted; use whole-archive behavior only when justified.
  • Inspect the map file or symbol table and test both the fallback and override paths.
  • Document the hook’s ABI, ownership, thread-safety expectations, and behavior when no override is present.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.