Skip to content

🔗 Linker Errors - Guía de Diagnóstico

Errores de linking son los más frustrantes. Esta guía los desmitifica.


📊 ANATOMÍA DE UN ERROR DE LINKER

Linking CXX executable AudioLab.exe
main.cpp.obj : error LNK2019: unresolved external symbol
"void __cdecl foo(void)" (?foo@@YAXXZ) referenced in function main
AudioLab.exe : fatal error LNK1120: 1 unresolved externals

Desglose: - LNK2019: Undefined reference (symbol declarado pero no definido) - ?foo@@YAXXZ: Name mangling (decoración de C++) - main.cpp.obj: Dónde se usa el symbol - AudioLab.exe: Target que falla


🔍 TIPOS DE ERRORES DE LINKER

1️⃣ UNDEFINED REFERENCE / UNRESOLVED EXTERNAL

Qué significa

El linker encontró una declaración (en header) pero no la definición (en .cpp).

Ejemplo

// AudioProcessor.h
class AudioProcessor {
    void process();  // Declaración
};

// main.cpp
processor.process();  // Uso

// ❌ FALTA AudioProcessor.cpp con la definición

Soluciones

✅ Asegurar que .cpp está en build

target_sources(mi_plugin PRIVATE
    src/AudioProcessor.cpp  # Agregar
)

✅ Verificar que library está linkeada

target_link_libraries(mi_plugin PRIVATE
    audio_processing_lib
)

✅ Revisar name mangling (C vs C++)

// Si linkeas con C library
extern "C" {
    #include "c_library.h"
}

2️⃣ MULTIPLE DEFINITION / DUPLICATE SYMBOL

Qué significa

El mismo símbolo está definido dos veces.

Ejemplo

// ❌ BAD: En header (.h)
void utility() {  // No inline, no static
    // ...
}

// Cada .cpp que incluye este header genera una definición
// → Duplicate symbols al linkear

Soluciones

✅ Opción 1: Inline

// En header
inline void utility() {
    // ...
}

✅ Opción 2: Static

// En header (una copia por translation unit)
static void utility() {
    // ...
}

✅ Opción 3: Declaración + Definición

// Header
void utility();

// Source (.cpp)
void utility() {
    // ...
}

3️⃣ CANNOT FIND LIBRARY

Síntoma

ld: library not found for -ljuce_core

o

LINK : fatal error LNK1104: cannot open file 'juce_core.lib'

Causas

  • Library no compilada
  • Path incorrecto
  • Architecture mismatch (x86 vs x64)
  • Configuration mismatch (Debug vs Release)

Soluciones

✅ Verificar que library existe

# Buscar archivo
find build -name "*.lib" -o -name "*.a"

✅ Agregar library path

target_link_directories(mi_plugin PRIVATE
    ${CMAKE_BINARY_DIR}/lib
)

✅ Verificar architecture

# Forzar x64
set(CMAKE_GENERATOR_PLATFORM x64)

4️⃣ ABI MISMATCH

Síntoma

Compile exitoso, pero crash inmediato al ejecutar, o comportamiento extraño.

Causas

  • Mezclar Debug y Release binaries
  • Mezclar diferentes CRT en Windows (/MD vs /MT)
  • Mezclar diferentes std::lib (libstdc++ vs libc++)
  • Mezclar diferentes compiler versions

Diagnóstico

# Linux: Ver qué ABI usa un binary
readelf -d libMiLibrary.so | grep NEEDED

# Windows: Ver dependencies
dumpbin /DEPENDENTS MiLibrary.dll

Solución

🔥 REGLA DE ORO: TODO debe compilarse con:

  • Mismo compilador
  • Misma versión de compilador
  • Mismo build type (Debug/Release)
  • Mismo CRT runtime
# Asegurar consistencia
set(CMAKE_MSVC_RUNTIME_LIBRARY
    "MultiThreaded$<$<CONFIG:Debug>:Debug>DLL")

🛠️ HERRAMIENTAS DE DIAGNÓSTICO

Windows: dumpbin

# Ver exports de una DLL
dumpbin /EXPORTS MiLibrary.dll

# Ver imports necesarios
dumpbin /IMPORTS MiPlugin.dll

# Ver symbols
dumpbin /SYMBOLS MiObject.obj

Linux: nm, objdump

# Ver symbols definidos
nm -C libMiLibrary.so | grep "T "

# Ver symbols undefined
nm -C libMiLibrary.so | grep "U "

# Ver dependencies
ldd MiPlugin.so

macOS: otool

# Ver dependencies
otool -L MiPlugin.component/Contents/MacOS/MiPlugin

# Ver symbols
nm -gU MiLibrary.dylib

Cross-platform: CMake

# Ver qué libraries linkea un target
cmake --build build --target mi_plugin --verbose 2>&1 | grep "link"

🔧 WORKFLOW DE RESOLUCIÓN

1. Identificar símbolo problemático
2. Demangle el nombre (c++filt)
3. Buscar declaración (grep en headers)
4. Buscar definición (grep en sources)
5. Verificar que .cpp está en CMakeLists.txt
6. Verificar que library está linkeada
7. Verificar orden de linking

Ejemplo Práctico

# Error: undefined reference to `_Z3foov'

# 1. Demangle
c++filt _Z3foov
# Output: foo()

# 2. Buscar declaración
grep -r "void foo()" src/

# 3. Buscar definición
grep -r "void foo() {" src/

# 4. Si existe, verificar CMakeLists.txt
grep -A 10 "target_sources" CMakeLists.txt

💡 TIPS AVANZADOS

# Mostrar link command
set(CMAKE_VERBOSE_MAKEFILE ON)

# O al compilar
cmake --build build --verbose
# Eliminar binario para forzar relink
rm build/mi_plugin.vst3
cmake --build build --target mi_plugin

Static vs Shared

# Forzar static linking
set(BUILD_SHARED_LIBS OFF)

# Forzar shared
set(BUILD_SHARED_LIBS ON)

📚 RECURSOS