0

Why do internally linked names appear in my object file's symbol table?

The question is not important. I am merely curious.

Here is sample code:

namespace {
    static int foo() {return 10;}
}
static int bar() {return 20;}

Using GNU's readelf -s foobar.o | c++filt -t, one finds these two entries in the object file's symbol table:

   Value  Size Type    Bind   Vis      Ndx Name
00000000    11 FUNC    LOCAL  DEFAULT    1 (anonymous namespace)::foo()
0000000b    11 FUNC    LOCAL  DEFAULT    1 bar()

The compiler has not been asked to emit debugging symbols.

I notice incidentally that if I change static to static inline, the symbols disappear.

For reference see also this answer, which answers a different question, explaining how to read the output of readelf. My question however does not regard the Readelf tool as such, but rather why the compiler would export symbols no other file as far as I know needs.

thb
  • 13,796
  • 3
  • 40
  • 68
  • Possible duplicate of [What are the meanings of the columns of the symbol table displayed by readelf?](https://stackoverflow.com/questions/3065535/what-are-the-meanings-of-the-columns-of-the-symbol-table-displayed-by-readelf) – 273K Mar 14 '19 at 02:50
  • @S.M. Thank you for the link. I do not believe that my question duplicates that one, though. – thb Mar 14 '19 at 02:52
  • Why you believe that internally linked names should disappear in object files? They marked there as local symbols according to the column Bind. Object files are generated before a linkage step, thus nothing is corrupted if local names occur in object files. It is clear from the words *internally linked names* that a linker is a some kind of barriers for such names, it's ok before linkage, they should disappear after linkage. Try readelf to parse your executable file. That's why I've marked your question as duplicate, in the original question the column **Bind** is described clearly. – 273K Mar 14 '19 at 03:11

2 Answers2

1

Turn on the optimizer, and they'll get optimized away.

Compiler Explorer with -O0: https://godbolt.org/z/xyOBgN
Compiler Explorer with -O2: https://godbolt.org/z/OlPQu3

Note, how there's no generated assembly when the optimized is turned on.

Should those links not work for you, or you'd prefer not to open them, both of them have the code from the question compiled to assembly with g++. The first link has the optimizer disabled, generating the following assembly:

(anonymous namespace)::foo():
        push    rbp
        mov     rbp, rsp
        mov     eax, 10
        pop     rbp
        ret
bar():
        push    rbp
        mov     rbp, rsp
        mov     eax, 20
        pop     rbp
        ret

And the one with the optimizer enabled with the following assembly output:

<No assembly generated>
Elias Kosunen
  • 433
  • 7
  • 20
  • Well, you are right, but of course a realistic source file would use those functions for something. Nevertheless, on your suggestion, I find that even if my source file does indeed use those functions for something, `-O2` kills the symbols. Interesting, +1. – thb Mar 14 '19 at 02:50
1

My question however does not regard the Readelf tool as such, but rather why the compiler would export symbols no other file as far as I know needs.

The compiler is not exporting these symbols (they have LOCAL binding).

The compiler is merely creating symbol table entries for them, to ease debugging.

The compiler has not been asked to emit debugging symbols.

Even without debugging symbols, the (non-debugging) symbols in the symbol table are useful for debugging. Consider:

#include <stdlib.h>

namespace {
    static int foo() {abort();}
}
static int bar() {return 20 + foo();}

int main() { return bar(); }


g++ t.cc && gdb -q ./a.out


(gdb) run
Starting program: /tmp/a.out 

Program received signal SIGABRT, Aborted.
__GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:51
51  ../sysdeps/unix/sysv/linux/raise.c: No such file or directory.
(gdb) bt
#0  __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:51
#1  0x00007ffff7a6e3fa in __GI_abort () at abort.c:89
#2  0x0000555555554653 in (anonymous namespace)::foo() ()
#3  0x000055555555465c in bar() ()
#4  0x000055555555466a in main ()

Note how useful frames 2 and 3 are. If you don't want this to happen, you can always strip the symbols:

gcc t.cc -Wl,--strip-all && gdb -q ./a.out

(gdb) run
Starting program: /tmp/a.out 

Program received signal SIGABRT, Aborted.
__GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:51
51  ../sysdeps/unix/sysv/linux/raise.c: No such file or directory.
(gdb) bt
#0  __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:51
#1  0x00007ffff7a6e3fa in __GI_abort () at abort.c:89
#2  0x0000555555554653 in ?? ()
#3  0x000055555555465c in ?? ()
#4  0x000055555555466a in ?? ()
#5  0x00007ffff7a5a2b1 in __libc_start_main (main=0x555555554661, argc=1, argv=0x7fffffffde08, init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7fffffffddf8)
    at ../csu/libc-start.c:291
#6  0x000055555555456a in ?? ()
Employed Russian
  • 199,314
  • 34
  • 295
  • 362