CGold
latest
  • 1. Overview
  • 2. First step
  • 3. Tutorials
    • 3.1. CMake stages
    • 3.2. Out-of-source build
    • 3.3. Workflow
    • 3.4. Version and policies
    • 3.5. Project declaration
    • 3.6. Variables
    • 3.7. CMake listfiles
    • 3.8. Control structures
    • 3.9. Executables
    • 3.10. Tests
    • 3.11. Libraries
      • 3.11.1. Static
      • 3.11.2. Shared
      • 3.11.3. Static + shared
      • 3.11.4. Symbols
        • 3.11.4.1. Tools
        • 3.11.4.2. Simple error
        • 3.11.4.3. ODR violation (local)
        • 3.11.4.4. ODR violation (global)
        • 3.11.4.5. Link order
    • 3.12. Pseudo targets
    • 3.13. Collecting sources
    • 3.14. Usage requirements
    • 3.15. Build types
    • 3.16. configure_file
    • 3.17. Install
    • 3.18. Toolchain
    • 3.19. Generator expressions
    • 3.20. Properties
    • 3.21. Packing
    • 3.22. Continuous integration
  • 4. Platforms
  • 5. Generators
  • 6. Compilers
  • Contacts
  • Rejected
  • Glossary
CGold
  • 3. Tutorials
  • 3.11. Libraries
  • 3.11.4. Symbols
  • 3.11.4.5. Link order
  • Edit on GitHub

3.11.4.5. Link order¶

3.11.4.5.1. GNU linker¶

This problem occurs only when you’re using GNU linker. From man ld on Linux:

The linker will search an archive only once, at the location where it is
specified on the command line.  If the archive defines a symbol which was
undefined in some object which appeared before the archive on the command
line, the linker will include the appropriate file(s) from the archive.
However, an undefined symbol in an object appearing later on the command
line will not cause the linker to search the archive again.

There is no such issue on OSX for example, quote from man ld:

ld will only pull .o files out of a static library if needed to resolve
some symbol reference.  Unlike traditional linkers, ld will continually
search a static library while linking. There is no need to specify a
static library multiple times on the command line.

Example tested on Linux with GCC compiler and standard ld linker:

> ld --version
GNU ld (GNU Binutils for Ubuntu) 2.26.1
Copyright (C) 2015 Free Software Foundation, Inc.
This program is free software; you may redistribute it under the terms of
the GNU General Public License version 3 or (at your option) a later version.
This program has absolutely no warranty.

> gcc --version
gcc (Ubuntu 5.4.1-2ubuntu1~16.04) 5.4.1 20160904
Copyright (C) 2015 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

3.11.4.5.2. Problem¶

Example with two libraries bar, boo and executable foo:

# Top-level CMakeLists.txt

cmake_minimum_required(VERSION 2.8)
project(foo)

add_library(bar bar.cpp)
add_library(boo boo.cpp)

add_executable(foo foo.cpp)
target_link_libraries(foo PUBLIC bar boo)

Library bar doesn’t depend on anything and define function int bar():

// bar.cpp

int bar() {
  return 0x42;
}

Library boo depends on bar and define function int boo():

// boo.cpp

int bar();

int boo() {
  return bar();
}

Executable foo depends on boo:

// foo.cpp

int boo();

int main() {
  return boo();
}

Build will fail with linker error:

[examples]> rm -rf _builds
[examples]> cmake -Hlibrary-examples/link-order-bad -B_builds -DCMAKE_VERBOSE_MAKEFILE=ON
-- The C compiler identification is GNU 5.4.1
-- The CXX compiler identification is GNU 5.4.1
-- Check for working C compiler: /usr/bin/cc
-- Check for working C compiler: /usr/bin/cc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Detecting C compile features
-- Detecting C compile features - done
-- Check for working CXX compiler: /usr/bin/c++
-- Check for working CXX compiler: /usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done
-- Generating done
-- Build files have been written to: /.../examples/_builds
[examples]> cmake --build _builds
...
[ 16%] Building CXX object CMakeFiles/bar.dir/bar.cpp.o
/usr/bin/c++     -o CMakeFiles/bar.dir/bar.cpp.o -c /.../examples/library-examples/link-order-bad/bar.cpp
[ 33%] Linking CXX static library libbar.a
...
/usr/bin/ar qc libbar.a  CMakeFiles/bar.dir/bar.cpp.o
/usr/bin/ranlib libbar.a
[ 33%] Built target bar
...
[ 50%] Building CXX object CMakeFiles/boo.dir/boo.cpp.o
/usr/bin/c++     -o CMakeFiles/boo.dir/boo.cpp.o -c /.../examples/library-examples/link-order-bad/boo.cpp
[ 66%] Linking CXX static library libboo.a
...
/usr/bin/ar qc libboo.a  CMakeFiles/boo.dir/boo.cpp.o
/usr/bin/ranlib libboo.a
[ 66%] Built target boo
...
[ 83%] Building CXX object CMakeFiles/foo.dir/foo.cpp.o
/usr/bin/c++     -o CMakeFiles/foo.dir/foo.cpp.o -c /.../examples/library-examples/link-order-bad/foo.cpp
[100%] Linking CXX executable foo
...
/usr/bin/c++    -rdynamic CMakeFiles/foo.dir/foo.cpp.o  -o foo libbar.a libboo.a
libboo.a(boo.cpp.o): In function `boo()':
boo.cpp:(.text+0x5): undefined reference to `bar()'
collect2: error: ld returned 1 exit status
...

Note that linker can’t find symbol int bar() from bar library even if libbar.a is present in command line.

To understand the reason of error you have to understand how linker works:

  • All files passed to linker processed from left to right

  • Linker collects undefined symbols from files to the pool of undefined symbols

  • If object from archive doesn’t resolve any symbols from pool of undefined symbols, then it dropped

Next thing happens in example above:

  • 3 files passed to linker to create final foo executable:

    • object CMakeFiles/foo.dir/foo.cpp.o

    • archive libbar.a

    • archive libboo.a

  • CMakeFiles/foo.dir/foo.cpp.o has undefined symbol int boo(). Current pool of undefined symbols is int boo()

  • Archive libbar.a defines int bar(), doesn’t have any undefined symbols and doesn’t resolve any symbols from pool. Hence we drop it. Current pool of undefined symbols is int boo()

  • Archive libboo.a defines int boo() and has undefined symbol int bar(). int boo() removed from pool and int bar() added. Current pool of undefined symbols is int bar()

  • No files left. Pool of undefined symbols is not empty and error about unresolved int bar() symbol reported.

3.11.4.5.3. Fix¶

To fix this you should declare dependency between boo and bar:

--- /home/docs/checkouts/readthedocs.org/user_builds/cgold/checkouts/latest/docs/examples/library-examples/link-order-bad/CMakeLists.txt
+++ /home/docs/checkouts/readthedocs.org/user_builds/cgold/checkouts/latest/docs/examples/library-examples/link-order-fix/CMakeLists.txt
@@ -5,6 +5,7 @@
 
 add_library(bar bar.cpp)
 add_library(boo boo.cpp)
+target_link_libraries(boo PUBLIC bar)
 
 add_executable(foo foo.cpp)
-target_link_libraries(foo PUBLIC bar boo)
+target_link_libraries(foo PUBLIC boo)

This approach both clean (foo doesn’t explicitly depends on bar, why target_link_libraries(foo PUBLIC bar) used?) and correct - CMake will control the right order of files:

[examples]> rm -rf _builds
[examples]> cmake -Hlibrary-examples/link-order-fix -B_builds -DCMAKE_VERBOSE_MAKEFILE=ON
-- The C compiler identification is GNU 5.4.1
-- The CXX compiler identification is GNU 5.4.1
-- Check for working C compiler: /usr/bin/cc
-- Check for working C compiler: /usr/bin/cc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Detecting C compile features
-- Detecting C compile features - done
-- Check for working CXX compiler: /usr/bin/c++
-- Check for working CXX compiler: /usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done
-- Generating done
-- Build files have been written to: /.../examples/_builds
[examples]> cmake --build _builds
...
/usr/bin/c++    -rdynamic CMakeFiles/foo.dir/foo.cpp.o  -o foo libboo.a libbar.a
make[2]: Leaving directory '/.../examples/_builds'
[100%] Built target foo
make[1]: Leaving directory '/.../examples/_builds'
/.../bin/cmake -E cmake_progress_start /.../examples/_builds/CMakeFiles 0

3.11.4.5.4. Summary¶

  • If one library depends on symbols from other library you have to express it by target_link_libraries command. Even if you may not have problems in current setup they may appear later or on another platform.

  • If you have “undefined reference” error even if library with missing symbols is present in command line, then it may means that the order is not correct. Fix it by adding target_link_libraries(boo PUBLIC bar), where boo is library with unresolved symbols and bar is library which defines those symbols.

Previous Next

© Copyright 2015-2019, Ruslan Baratov. Revision 89dee651.

Built with Sphinx using a theme provided by Read the Docs.