Nm Reports Symbol Is Defined But Ldd Reports Symbol Is Undefined

nm reports symbol is defined but ldd reports symbol is undefined

First, defining a function called 'read' is a bad idea(TM), because it is a standard libc function on all UNIXen. The behavior of your program is undefined when you do this.

Second, the read function you defined in libbaz.so is marked with a 't' in nm output. This means that this function is local (not visible outside libbaz.so). Global functions are marked with 'T' by nm.

Did you use 'static int read(...)' when you defined it in read.c?
If not, did you use a linker script, or attribute((visibility(hidden))), or perhaps -fvisibility=hidden on command line when you compiled and linked libbaz.so?

ld reports missing symbols, but symbols seem to exist

Those symbols are unmangled C symbols. As you have tagged this as C++, I assume you are compiling with C++. If you do that you may need to wrap your libraries header files in an extern block in your code:

extern "C" {
#include "library.h"
}

where library.h is the name of the library's header file(s), to prevent them being mangled in the calling code.

Do libraries reported by ldd resolve all undefined references of an input library?

Does ldd print only libraries from DT_NEEDED structures of .dynamic section?

No, that is what readelf --dynamic does.

what is the use of ldd at all?

ldd shows what libraries the runtime linker ld.so loads when starting your executable or loading a shared library. This is a recursive process, e.g. an executable needs a shared library (DT_NEEDED), so that library gets loaded. Then it proceeds to load the dependencies of the loaded library (DT_NEEDED) and so on.

You don't necessarily need ldd, you can just set LD_DEBUG=all environment variable to make ld.so print that information and more. See man ld.so for more information.

Each loaded executable or shared library expose their defined exported dynamic symbols as a lookup scope (a hash table). Lookup scopes form a list. When resolving an undefined symbol ld.so walks the lookup scopes and finds the first one that defines the symbol and resolve the symbol reference. If ld.so reaches the end of lookup scopes it reports the symbol as unresolved.

There is no correspondence between the unresolved symbol name and an executable/shared library it is supposed to come from. ld.so loads all shared libraries from DT_NEEDED sections recursively, builds that list of lookup scopes and then looks for unresolved symbols in there.

How To Write Shared Libraries by U. Drepper explains this in full detail.

undefined symbols remain, but shared libraries compile and seem to function properly

You have undefined symbol with ldd because the so files were created without the -lm option. However it will not be a problem as long as the final executable is linked with -lm option. It is what is done for tests like test_nvector_serial which is linked with -lm -lrt.

You can see all this by running the make in a verbose mode with make VERBOSE=1.
If you try to generate the executable without -lm you will have collect2: error: ld returned 1 exit status and the linker complaining about exp, pow and sqrt.
if you add -lm when creating the so files ( I did it ). you will see :

ldd -r ./src/nvec_ser/libsundials_nvecserial.so
linux-vdso.so.1 (0x00007ffe1a769000)
libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007fe4a5c19000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fe4a5828000)
/lib64/ld-linux-x86-64.so.2 (0x00007fe4a61bd000)

And in this case you can link your tests ( only to do some test for yourself if you want) without -lm.

To compile your so files with -lm. You can do it by running the command line with -lm option after entering the right folder (just for test purpose). The other way is by modifying the CMakeLists.txt which generate the so files. For libsundials_nvecserial.so for example, you modify sundials-2.7.0/src/nvec_ser/CMakeLists.txt by adding target_link_libraries(sundials_nvecserial_shared -lm) after the ADD_LIBRARY(sundials_nvecserial_shared SHARED ${nvecserial_SOURCES} ${shared_SOURCES}).

For the LD_LIBRARY_PATH, it does not have to contain something necessarily if you are looking in standard places. The linker use also /etc/ld.so.cache and search also in default path unless you use -z nodeflib. you can find more details in https://man7.org/linux/man-pages/man8/ld.so.8.html

For the nm -D. It is normal , try to compile a hello world gcc -o exec hello.c you will see that printf is undefined. Take a look at dynamic linking, and also to the -rdynamic option used to build the so files.

Symbol lookup error at runtime even though nm reports symbol present

Did you verify, e.g., using ldd myprog, that you looked at the correct shared object? There is a chance a different shared object is found.

How do I list the symbols in a .so file

The standard tool for listing symbols is nm, you can use it simply like this:

nm -gD yourLib.so

If you want to see symbols of a C++ library, add the "-C" option which demangle the symbols (it's far more readable demangled).

nm -gDC yourLib.so

If your .so file is in elf format, you have two options:

Either objdump (-C is also useful for demangling C++):

$ objdump -TC libz.so

libz.so: file format elf64-x86-64

DYNAMIC SYMBOL TABLE:
0000000000002010 l d .init 0000000000000000 .init
0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 free
0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 __errno_location
0000000000000000 w D *UND* 0000000000000000 _ITM_deregisterTMCloneTable

Or use readelf:

$ readelf -Ws libz.so
Symbol table '.dynsym' contains 112 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
1: 0000000000002010 0 SECTION LOCAL DEFAULT 10
2: 0000000000000000 0 FUNC GLOBAL DEFAULT UND free@GLIBC_2.2.5 (14)
3: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __errno_location@GLIBC_2.2.5 (14)
4: 0000000000000000 0 NOTYPE WEAK DEFAULT UND _ITM_deregisterTMCloneTable

What is an undefined reference/unresolved external symbol error and how do I fix it?

Compiling a C++ program takes place in several steps, as specified by 2.2 (credits to Keith Thompson for the reference):

The precedence among the syntax rules of translation is specified by the following phases [see footnote].

  1. Physical source file characters are mapped, in an implementation-defined manner, to the basic source character set
    (introducing new-line characters for end-of-line indicators) if
    necessary. [SNIP]
  2. Each instance of a backslash character (\) immediately followed by a new-line character is deleted, splicing physical source lines to
    form logical source lines. [SNIP]
  3. The source file is decomposed into preprocessing tokens (2.5) and sequences of white-space characters (including comments). [SNIP]
  4. Preprocessing directives are executed, macro invocations are expanded, and _Pragma unary operator expressions are executed. [SNIP]
  5. Each source character set member in a character literal or a string literal, as well as each escape sequence and universal-character-name
    in a character literal or a non-raw string literal, is converted to
    the corresponding member of the execution character set; [SNIP]
  6. Adjacent string literal tokens are concatenated.
  7. White-space characters separating tokens are no longer significant. Each preprocessing token is converted into a token. (2.7). The
    resulting tokens are syntactically and semantically analyzed and
    translated as a translation unit. [SNIP]
  8. Translated translation units and instantiation units are combined as follows: [SNIP]
  9. All external entity references are resolved. Library components are linked to satisfy external references to entities not defined in the
    current translation. All such translator output is collected into a
    program image which contains information needed for execution in its
    execution environment.
    (emphasis mine)

[footnote] Implementations must behave as if these separate phases occur, although in practice different phases might be folded together.

The specified errors occur during this last stage of compilation, most commonly referred to as linking. It basically means that you compiled a bunch of implementation files into object files or libraries and now you want to get them to work together.

Say you defined symbol a in a.cpp. Now, b.cpp declared that symbol and used it. Before linking, it simply assumes that that symbol was defined somewhere, but it doesn't yet care where. The linking phase is responsible for finding the symbol and correctly linking it to b.cpp (well, actually to the object or library that uses it).

If you're using Microsoft Visual Studio, you'll see that projects generate .lib files. These contain a table of exported symbols, and a table of imported symbols. The imported symbols are resolved against the libraries you link against, and the exported symbols are provided for the libraries that use that .lib (if any).

Similar mechanisms exist for other compilers/ platforms.

Common error messages are error LNK2001, error LNK1120, error LNK2019 for Microsoft Visual Studio and undefined reference to symbolName for GCC.

The code:

struct X
{
virtual void foo();
};
struct Y : X
{
void foo() {}
};
struct A
{
virtual ~A() = 0;
};
struct B: A
{
virtual ~B(){}
};
extern int x;
void foo();
int main()
{
x = 0;
foo();
Y y;
B b;
}

will generate the following errors with GCC:

/home/AbiSfw/ccvvuHoX.o: In function `main':
prog.cpp:(.text+0x10): undefined reference to `x'
prog.cpp:(.text+0x19): undefined reference to `foo()'
prog.cpp:(.text+0x2d): undefined reference to `A::~A()'
/home/AbiSfw/ccvvuHoX.o: In function `B::~B()':
prog.cpp:(.text._ZN1BD1Ev[B::~B()]+0xb): undefined reference to `A::~A()'
/home/AbiSfw/ccvvuHoX.o: In function `B::~B()':
prog.cpp:(.text._ZN1BD0Ev[B::~B()]+0x12): undefined reference to `A::~A()'
/home/AbiSfw/ccvvuHoX.o:(.rodata._ZTI1Y[typeinfo for Y]+0x8): undefined reference to `typeinfo for X'
/home/AbiSfw/ccvvuHoX.o:(.rodata._ZTI1B[typeinfo for B]+0x8): undefined reference to `typeinfo for A'
collect2: ld returned 1 exit status

and similar errors with Microsoft Visual Studio:

1>test2.obj : error LNK2001: unresolved external symbol "void __cdecl foo(void)" (?foo@@YAXXZ)
1>test2.obj : error LNK2001: unresolved external symbol "int x" (?x@@3HA)
1>test2.obj : error LNK2001: unresolved external symbol "public: virtual __thiscall A::~A(void)" (??1A@@UAE@XZ)
1>test2.obj : error LNK2001: unresolved external symbol "public: virtual void __thiscall X::foo(void)" (?foo@X@@UAEXXZ)
1>...\test2.exe : fatal error LNK1120: 4 unresolved externals

Common causes include:

  • Failure to link against appropriate libraries/object files or compile implementation files
  • Declared and undefined variable or function.
  • Common issues with class-type members
  • Template implementations not visible.
  • Symbols were defined in a C program and used in C++ code.
  • Incorrectly importing/exporting methods/classes across modules/dll. (MSVS specific)
  • Circular library dependency
  • undefined reference to `WinMain@16'
  • Interdependent library order
  • Multiple source files of the same name
  • Mistyping or not including the .lib extension when using the #pragma (Microsoft Visual Studio)
  • Problems with template friends
  • Inconsistent UNICODE definitions
  • Missing "extern" in const variable declarations/definitions (C++ only)
  • Visual Studio Code not configured for a multiple file project


Related Topics



Leave a reply



Submit