Undefined reference when linking a .o file to a .elf file

0

I have an .o file compiled from a C project which references a function named init_static_pools. I use objdump -t to show its symbol dependency info:

00000000 UND 00000000 init_static_pools

According to this thread, the UND simply says "I need someone else to provide me that function".

So I linked this .o file to an .elf file, which contains the definition of init_static_pools. The objdump -t shows that the symbol is indeed in this file:

00004dcf g F .text 00000048 init_static_pools

According to this thread, the g and F flags mean it is a glbal function. I guess it means this function can be statically linked.

I tried to link the .o file and the .elf file with below command line:

/usr/bin/c++ -m32 -rdynamic unittest1.o -o unittest1 target.elf lib/libgtest.a lib/libgtest_main.a -lpthread

I got below error:

unittest1.cc:(.text+0x2d): undefined reference to `init_static_pools'

The function is just in the .so file, why it cannot be linked?

Could this be related to the different symbol resolving mechanisms between dynamic and static linking? Because I use objdump -f and see that the target.elf is a dynamic object. As shown below:

target.elf: file format elf32-i386

architecture: i386, flags 0x00000150:

HAS_SYMS, DYNAMIC, D_PAGED

start address 0x00001144

ADD 1 - 9:17 AM 11/6/2019

Based on the comment by @EmployedRussian, I tried readelf.

For the target.elf, it only contains 1 line saying:

486: 00004dcf 72 FUNC GLOBAL DEFAULT 13 init_static_pools

For the unittest1.o, it contains 2 lines saying:

0000002d 0000fb04 R_386_PLT32 00000000 init_static_pools

and

251: 00000000 0 NOTYPE GLOBAL DEFAULT UND init_static_pools

And for the completion, their headers are:

target.elf:

ELF Header:
  Magic:   7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 
  Class:                             ELF32
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              DYN (Shared object file)
  Machine:                           Intel 80386
  Version:                           0x1
  Entry point address:               0x1144
  Start of program headers:          52 (bytes into file)
  Start of section headers:          246936 (bytes into file)
  Flags:                             0x0
  Size of this header:               52 (bytes)
  Size of program headers:           32 (bytes)
  Number of program headers:         9
  Size of section headers:           40 (bytes)
  Number of section headers:         43
  Section header string table index: 42

unittest1.o:

ELF Header:
  Magic:   7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 
  Class:                             ELF32
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              REL (Relocatable file)
  Machine:                           Intel 80386
  Version:                           0x1
  Entry point address:               0x0
  Start of program headers:          0 (bytes into file)
  Start of section headers:          36988 (bytes into file)
  Flags:                             0x0
  Size of this header:               52 (bytes)
  Size of program headers:           0 (bytes)
  Number of program headers:         0
  Size of section headers:           40 (bytes)
  Number of section headers:         262
  Section header string table index: 261

So far, I cannot tell the root cause of the linkage failure yet. Luckily I just found the series on linker by the Ian Lance Taylor. Hope that can enlighten me. But it will take some time I guess.

ADD 2 - 10:33 AM 11/6/2019

Based on @EmployedRussian's reply, I tried below commands:

nm    target.elf | grep init_static_pools
nm -D target.elf | grep init_static_pools

Just as @EmployedRussian suspected, the second command line doesn't have output. So that means target.elf is not exporting init_static_pools in its dynamic symbol table, and that makes the symbol not eligible for linking to from outside of target.elf.

Below are some flags related to the linking of the target.elf:

-Wl,-T zephyr/linker.cmd  (this is quite long, but it seems to be mostly layout info)

-Wl,-Map=target_prebuilt.map

-Wl,--whole-archive

-Wl,--gc-sections

-Wl,--build-id=none

-Wl,--sort-common=descending

-Wl,--sort-section=alignment

-ldl

-lm

Or should I check the compile flags as well?

And I found the --export-all-symbols, --export-dynamic, --gc-keep-exported options, I am trying them.

It seems the --export-all-symbols is ignored. I guess it is meant for DLL.

I put --export-dynamic and --gc-keep-exported together, and the build can pass. But the nm -D still shows below message:

target.elf : no symbols

ADD 3 - 11:16 AM 11/6/2019

An article about dynamic symbol table (.dynsym) and symbol table(.symtab). https://blogs.oracle.com/solaris/inside-elf-symbol-tables-v2

Some quote:

In fact, in the days before sharable libraries and dynamic linking, none of it was needed at runtime. There was a single, non-allocable symbol table (reasonably named "symtab"). When dynamic linking was added to the system, the original designers faced a choice: Make the symtab allocable, or provide a second smaller allocable copy. The symbols needed at runtime are a small subset of the total, so a second symbol table saves virtual memory in the running process. This is an important consideration. Hence, a second symbol table was invented for dynamic linking, and consequently named "dynsym".

So, I think the .dynsym is meant for dynamic linking at runtime. But I think I am expecting my unittest1.o and target.elf to be staticly linked at build time.

So it leads me to this question: Can I statically link to an .ELF file? On Windows, I usually statically link to a .lib file rather than a .DLL file. The .ELF file here looks more like a .DLL. And btw, my build process also generate a libtarget.a file. Should I use it instead of target.elf?

Unfortunately, I tried nm -t and readelf -s on libtarget.a, neither of them show the existence of init_static_pools.

BTW, if you know something about my issue, feel free to drop some hint. Thanks!

linker
linker-errors
ld
static-linking
dynamic-linking
asked on Stack Overflow Nov 5, 2019 by smwikipedia • edited Nov 6, 2019 by smwikipedia

1 Answer

1

For the target.elf, it only contains 1 line saying:
486: 00004dcf 72 FUNC GLOBAL DEFAULT 13 init_static_pools

Unfortunately, this still isn't sufficient to tell for sure what's happening.

To tell for sure, run these two commands:

nm    target.elf | grep init_static_pools
nm -D target.elf | grep init_static_pools

I suspect that the first command would produce output, while the second one would not.

If that's the case, then target.elf is not exporting init_static_pools in its dynamic symbol table, and that makes the symbol not eligible for linking to from outside of target.elf.

As to how you ended up with this symbol not exported, I can only guess (since you didn't provide the link command used to link it). You are likely using a linker script which hides it.

answered on Stack Overflow Nov 6, 2019 by Employed Russian

User contributions licensed under CC BY-SA 3.0