Search topics...
Build SystemsLinker Errors & Diagnosticsfoundational

How do you debug a "multiple definition" or "undefined reference" linker error?

0 upvotes
Practice with AISoon
Study the fundamentals first — Build Systems topic page

Both are about cross-file consistency, but they have different fixes.

undefined reference to 'foo' — the linker couldn't find a definition for foo in any of the .o files or libraries you linked. Common causes:

  1. Missing library. For printf and friends, make sure libc is being linked — check for --specs=nano.specs or explicit -lc in the link command. Vendor SDKs often need explicit -lvendor_lib.
  2. Source file not in the build. The file containing foo's definition isn't being compiled, so no .o exists with the symbol. Check the build system's source list.
  3. Wrong symbol name (C/C++ boundary). C++ does name mangling. If a C++ caller references a C function declared without extern "C", the C++ side calls _Z3foov (mangled) but the C definition provides foo (unmangled). Wrap the declaration in extern "C" { ... }.
  4. Typo or wrong prototype. Check spelling and signature.

Diagnostic: arm-none-eabi-nm on each candidate .o shows whether foo is defined (T) or only referenced (U).

multiple definition of 'foo' — the same symbol is defined in two .o files. Common causes:

  1. Non-static function or global in a header. Every .c that includes the header gets its own copy. Fix: mark it static inline (per-TU copy is allowed) or move the body to a single .c file with only a declaration in the header.
  2. A .c file is being compiled and linked twice. Check the source list for duplicates.
  3. Two libraries provide the same symbol. Less common; usually the fix is to remove one or use --allow-multiple-definition (not recommended — usually masks a real problem).

Diagnostic: arm-none-eabi-nm on each .o to find where the symbol is defined.

The general workflow: run the build, copy the failing symbol name, run nm on every .o in the link to see who defines it (or doesn't). Both errors are 1-minute fixes once you know which .o is at fault.

Source: Build Systems Q&A