|
- Error: Linking a runtime binary failed
- ######################################
-
- .. contents
-
- .. highlight
-
- This error indicates that the final phases of the build pipeline, the link
- phases, failed.
-
- The end result of the software development process is to produce applications
- and programs that can be executed by users. In the traditional compilation and
- linking model, which we still use to this day, multiple *translation units*
- (which can be thought of as "source files") are combined together in a process
- known as *linking*. The result of linking is an actual executable.
-
- .. note
- Linking is also used to generate executables that are used as tests.
- Refer: :ref:`pkgs.apps-tests`.
-
-
- What is "Linking"?
- ******************
-
- The *phases of translation* define the steps taken by a compiler (and linker)
- to map from the input human-readable source code to the code that can be
- executed by a machine. The first few phases are collected bundled together in
- a phase known as "compilation," while the later phases are known as "linking."
-
- The output of the compilation phases is often generated by a *compiler* and
- then written to the filesystem. The resulting files, known as *object files*
- are then fed into another tool known as a *linker*.
-
- .. note
- "Object files" are just one possible intermediate product. Compilers and
- linkers may deal with different intermediate products depending on compiler
- and linker options.
-
-
- Symbol Reference Resolution
- ***************************
-
- When code within a translation unit uses a function, variable, or member of a
- class that is declared **but not defined** in that translation unit, that
- usage is stored as an "unresolved" reference to an external symbol within the
- resulting object file.
-
- Each translation unit may also contain the *definitions* of any number of
- symbols. It is possible that other translation units may contain unresolved
- references to the symbol that the translation unit defines.
-
- It is the job of the linker to fill in these unresolved references when it
- combines translation units together.
-
-
- Failure Modes
- *************
-
- There are two very common types of linker errors that may be seen:
-
-
- Multiple Definitions Found
- ==========================
-
- When translation units are combined together, the symbols within them are
- combined together into a final binary. If two or more translation units contain
- the *definition* of a single symbol, then the linker must make a decision:
-
- #. If the symbol is marked properly, then the linker can discard all except one
- of the definitions and choose one to keep in the final binary. For example:
- This is allowed if the associated symbol has been declared with the
- ``inline`` keyword, or is a symbol in any context that is "implicitly
- ``inline``," which includes member functions and static variables which are
- defined within their class's body, and any function template.
- #. **Fail**
-
- If the linker is not allowed to discard all-but-one of the multiple
- definitions, this is a hard-error. This can happen if multiple translation
- units defined the same variable or function at the same namespace.
-
-
- Issue: A non-``inline`` function is defined in a header file
- ------------------------------------------------------------
-
- A likely case is that of defining a function in a header file without
- marking it as ``inline``:
-
- .. code-block
- :caption: ``hello.hpp``
-
- #ifndef MY_HEADER_INC
- #define MY_HEADER_INC
-
- #include <cstdio>
-
- void say_hello() {
- std
- }
-
- #endif
-
- and then that header is ``#include``-ed in multiple source files:
-
- .. code-block
- :caption: ``a.cpp``
-
- #include "hello.hpp"
-
- // ... stuff ...
-
- .. code-block
- :caption: ``b.cpp``
-
- #include "hello.hpp"
-
- // .. different stuff ...
-
- .. note
- ``template`` functions and member functions *defined within the class body*
- are implicitly ``inline``, and using the ``inline`` keyword is then
- redundant.
-
- In the above configuration, the linker will generate an error about multiple
- definitions of the ``say_hello`` function. Possibly confusingly, it will point
- to ``a.cpp`` and ``b.cpp`` as the "definers" of ``say_hello``, even though it
- is actually defined in the header. The issue is that no tools are currently
- able to understand this structure in a way that they can clearly issue
- appropriate instruction on how to fix this. There are two ways to fix this:
-
- #. Add the ``inline`` keyword to the definition of ``say_hello``
-
- #ifndef MY_HEADER_INC
- #define MY_HEADER_INC
-
- #include <cstdio>
-
- inline void say_hello() {
- std
- }
-
- #endif
-
- This activates the rule that permits the linker to disregard the multiple
- definitions and choose one to keep arbitrarily.
-
- .. note
- Only use ``inline`` in headers!
-
- #. Change the definition of ``say_hello`` to be a *declaration*, and move the
- *definition* to a separate source file:
-
- .. code-block
- :caption: ``hello.hpp``
-
- #ifndef MY_HEADER_INC
- #define MY_HEADER_INC
-
- #include <cstdio>
-
- void say_hello() {
- std
- }
-
- #endif
-
- .. code-block
- :caption: ``hello.cpp``
-
- #include "hello.hpp"
-
- void say_hello() {
- std
- }
-
- This will place the sole location of the ``say_hello`` definition within
- ``hello.cpp``.
-
-
- Issue: There are two colliding and distinct definitions
- -------------------------------------------------------
-
- Suppose you have two different source files:
-
- .. code-block
- :caption: ``a.cpp``
-
- #include "a.hpp"
-
- void error(string message) {
- cerr << "An error occured: " << msg << '\n';
- }
-
- void a_func() {
- bool had_error = first_a();
- if (err) {
- error(*err);
- }
- err = second_a();
- if (err) {
- error(*err);
- }
- }
-
- .. code-block
- :caption: ``b.cpp``
-
- void error(string message) {
- throw runtime_error(msg);
- }
-
- void b_func() {
- bool had_error = first_b();
- if (had_error) {
- error("The first step failed!");
- }
- had_error = second_b();
- if (had_error) {
- error("The second step failed!");
- }
- }
-
- The two functions, ``a_func`` and ``b_func``, despite having a similar
- structure, are *completely different* because of the behavior of ``error``:
-
- - In ``a.cpp``:
-
- - ``error()`` will simply log a message but let execution continue.
- - If ``first_a()`` fails, execution will continue into ``second_a()``.
-
- - In ``b.cpp``:
-
- - ``error()`` will throw an exception.
- - If ``first_b()`` fails, execution will never reach ``second_b()``
-
- Nevertheless, the linker will produce an error that there are multiple visible
- definitions of ``error()``, even though the translation units individually have
- no ambiguity.
-
- The issue is that both of the definitions have *external linkage* and must be
- visible to all other translation units.
-
- It may be tempting to fix this issue in the same way that we did in the prior
- example: to declare them ``inline``, and it will *seem* to have worked, but
- **this will not work correctly!!**
-
- Remember what the linker does in the presence of ``inline`` on multiple
- definitions between different translation units: It will *pick one* and
- *discard the others*. This means that either ``error`` function may replace the
- other across translation units, and the resulting code will have wildly
- different behavior.
-
- The *correct* solution is to give the ``error`` function *internal linkage*,
- which means that its definition is not visible across translation units. This
- will allow both definitions of ``error`` to live together in the linked binary
- without ambiguity. The classic way of doing this is through the usage of the
- global-scope ``static`` keyword which is present in C
-
- static void error(string s) {
- // ...
- }
-
- C++ presents another way it can be done: via an *unnamed namespace*
-
- namespace {
-
- void error(string s) {
- // ...
- }
-
- } // close namespace
-
- The benefit of the unnamed namespace is it can be used to mark an entire
- section of declarations to be *internal*, and it can also be used to mark a
- class definition to have *internal linkage* (There is no way to declare a
- "``static class``").
-
-
- Unresolved External Symbol / Undefined Reference
- ================================================
-
- Another common error seen while linking is that of the *unresolved external
- symbol* (Visual C++) or *undefined reference* (GCC and Clang). Both have the
- same underlying cause, and both have the same solutions.
-
- When a translation unit makes use of a symbol which has been declared *but not
- defined within that translation unit*, it is up to the linker to resolve that
- reference to another translation unit that contains the definition.
-
- If the linker is unable to find the definition of the referenced entity, it
- will emit this error.
-
-
- Issue: An external library is not being included in the link
- ------------------------------------------------------------
-
- If the unresolved reference is to an entity belonging to an external library,
- you may be missing the linker inputs to actually use that library.
-
- If your project makes use of a declared entity from a third party (even if that
- usage is transitive through a dependency), it is required that the definitions
- from that third party library are included in the link step. This usually comes
- in the form of a static library, shared library/DLL, or even plain object
- files.
-
- If the external library containing the definition in question is managed by
- ``dds``, this issue should never occur. If the library exists outside of
- ``dds`` (e.g. a system library), then that library will need to be manually
- added as a linker input using a toolchain file using the ``Link-Flags`` option.
- See: :ref:`toolchains.opt-ref`.
-
- If the name of the unresolved symbol appears unfamiliar or you do not believe
- that you are making use of it, it is possible that one of your dependencies is
- making use of a system library symbol that needs to be part of the link. The
- link error will refer to the object/source file that is actually making the
- unresolvable reference. Seeing this filepath will be a reliable way to discover
- who would be making the reference, and therefore a good way to track down the
- dependency that needs an additional linker input. Refer to the documentation
- of the dependency in question to see if it requires additional linker inputs
- in order to be used.
-
- If the library that should contain the unresolved reference is a dependency
- managed by ``dds``, it is possible that the library author has mistakenly
- declared a symbol without providing a definition. If the definition *is*
- present in the ``dds``-provided dependency library, then the failure to resolve
- the reference would be a ``dds`` bug.
-
-
- Issue: The definition is simply missing
- ---------------------------------------
-
- C and C++ allow for an entity to be *declared* and *defined* separately. If you
- *declare* and entity but do not *define* that entity, your code will work as
- long as no one attempts to refer to that entity.
-
- Ensure that the entity that is "missing" exists.
-
-
- Issue: Missing ``virtual`` method implementations
- -------------------------------------------------
-
- If the error refers to a missing ``vtable for class``, or if the error refers
- to a missing definition of a ``virtual`` function, it means that one or more
- ``virtual`` functions are not defined.
-
- Note that ``virtual`` functions are slightly different in this regard: It is
- not required that someone actually make a call to the ``virtual`` function for
- the definition to be required. The metadata that the compiler generates for
- the class containing the ``virtual`` functions will implicitly form a reference
- to every ``virtual`` function, so they must all be defined if someone attempts
- to instantiate the class, as instantiating the class will form a reference to
- that metadata.
-
-
- Issue: Mismatched declarations and definitions
- ----------------------------------------------
-
- Suppose you have a header file and a corresponding source file:
-
- .. code-block
- :caption: ``a.hpp``
-
- namespace foo {
-
- size_t string_length(const string& str);
-
- }
-
- .. code-block
- :caption: ``a.cpp``
-
- #include "a.hpp"
-
- using namespace foo;
-
- size_t string_length(const string& str) {
- // ... implementation goes here ...
- }
-
- The above code will link correctly, as the definition of ``foo
- is available from ``a.cpp``, while the declaration exists in ``a.hpp``.
-
- However, if we modify *only the declaration* to use ``string_view`` instead of
- ``const string&``, something different occurs
-
- namespace foo {
-
- size_t string_length(string_view str);
-
- }
-
- It may be tempting to say that "our declaration and definition do not match,"
- but that is semantically incorrect: We have declared a function
- ``size_t foo
- a **completely different function** ``size_t string_length(const string&)``!
- The compiler will not warn about this: There is nothing semantically incorrect
- about this code.
-
- The linker, however, will not find any definition of ``foo
- The function ``
- ``namespace``: It was declared and defined at the global scope within
- ``a.cpp``.
-
- If you are seeing an error about an unresolved reference to a function that is
- declared and defined separately, and you are *sure* is being compiled, check
- that the signature (and name) of the definition and declaration match
- *exactly*.
-
- .. tip
- In essence, the error originates from relying on the
- ``using namespace foo`` directive to cause the definition of
- ``string_length`` to incidentally hit the name lookup of the prior
- declaration.
-
- In C++, using a *qualified name* at the definition site can prevent this
- error from slipping through
-
- #include "a.hpp"
-
- using namespace foo;
-
- size_t foo
- // ... implementation goes here ...
- }
-
- By using the qualified name ``foo
- the compiler will validate that the function being defined has a prior
- declaration that matches *exactly* to the signature of the definition.
-
- Note that this *is not* the same as defining the function within a
- ``namespace`` block
-
- #include "a.hpp"
-
- // NOT HELPFUL!
-
- namespace foo {
-
- size_t string_length(const string& str) {
- // ... implementation goes here ...
- }
-
- }
-
- This will suffer the same potential mistake as defining it with an
- unqualified name.
-
- Note that within the scope of a function that has been declared within the
- namespace, that namespace is currently within scope even if the definition
- itself is not wrapped in a ``namespace`` block. It may be a good option to
- simply remove the ``using namespace`` directive altogether.
-
- .. note
- This trick cannot be applied to names that are declared at the global
- scope, since you cannot use the global-namespace qualifier at a
- function definition (it is not valid syntax)
-
- // Declaration at global scope
- void some_function();
-
- // Definition? No: Invalid syntax!
- void
- // ... stuff ...
- }
-
-
- Issue: The source file containing definition is not being included in the link
- ------------------------------------------------------------------------------
-
- If the translation unit that contains the definition of an entity is not being
- passed to the linker, the linker will not be able to find it!
-
- If you are using ``dds`` correctly, and the compiled source file containing the
- definition is placed as a (direct or indirect) descendent of the ``src/``
- directory, then ``dds`` will always include that source file as part of the
- link for the enclosing library.
-
- Build systems that require you to enumerate your source files explicitly will
- not automatically see a source file unless it has been added to the source
- list. Even build systems that allow directory-globbing (like CMake) will need
- to have the globbing pattern match the path to the source file.
|