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 ?? ()