2

I am trying to strip non-public symbols from my shared library using the strip command, but when I afterwards apply "nm -C -D" I still see all symbols.

I've alread checked this and other posts: Stripping linux shared libraries as well as the gcc Wiki: https://gcc.gnu.org/wiki/Visibility But with my sample code the proposed solution seems not to work.

I declare two simple classes, one meant to remain visible, the other to be removed. For the public one I declare __attribute__ ((visibility ("default"))) in the source.

When I compile and link with -fvisibility=hidden and subsequently run "strip --strip-all --discard-all" on the shared library, both classes are still visible in the T(ext) section.

This is my header file:

#pragma once

#if __GNUC__ >= 4
    #define DLL_PUBLIC __attribute__ ((visibility ("default")))
#else
    #define DLL_PUBLIC
#endif

class DLL_PUBLIC PublicClass
{
public:
  PublicClass() = default;
  ~PublicClass() = default;

  void doSomethingPublic();
};

class PrivateClass
{
public:
  PrivateClass() = default;
  ~PrivateClass() = default;

  void doSomethingPrivate();
};

and this the source:

#include <iostream>
#include "test.hpp"

void PublicClass::doSomethingPublic() { std::cout << "public call" << std::endl; } 
void PrivateClass::doSomethingPrivate() { std::cout << "private call" << std::endl; } 

I compile an object file and link the shared library with gcc 7.4.0 on Ubuntu 18.04:

$ /usr/local/bin/c++ -fvisibility=hidden -O3 -DNDEBUG -fPIC -std=gnu++14 -c test.cpp -o test.cpp.o
$ /usr/local/bin/c++ -fvisibility=hidden -fPIC -DNDEBUG -O3 -shared -Wl,-soname,libtest.so -o libtest.so test.cpp.o

Applying the strip command and displaying the symbol table:

$ strip --strip-all --discard-all --verbose libtest.so
copy from `libtest.so' [elf64-x86-64] to `stSNqAeg' [elf64-x86-64]
$ nm -C -D libtest.so
0000000000201058 B __bss_start
                 U __cxa_atexit
                 w __cxa_finalize
0000000000201058 D _edata
0000000000201060 B _end
0000000000000b84 T _fini
                 w __gmon_start__
00000000000008a8 T _init
                 w _ITM_deregisterTMCloneTable
                 w _ITM_registerTMCloneTable
0000000000000a60 T PublicClass::doSomethingPublic()
0000000000000af0 T PrivateClass::doSomethingPrivate()
                 U std::ctype<char>::_M_widen_init() const
0000000000000b80 W std::ctype<char>::do_widen(char) const
                 U std::ostream::put(char)
                 U std::ostream::flush()
                 U std::ios_base::Init::Init()
                 U std::ios_base::Init::~Init()
                 U std::basic_ostream<char, std::char_traits<char> >& std::__ostream_insert<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, long)
                 U std::__throw_bad_cast()
                 U std::cout

The strip command actually rewrites the shared library (date and size is changed) but I still see the private symbols. I would expect the PrivateClass::doSomethingPrivate method to be stripped from the symbol table.

I'd be grateful for any kind of hint.

joggerwolf
  • 59
  • 1
  • 6

1 Answers1

2

You need to compile with -fvisibility=hidden:

$ g++ -fvisibility=hidden -shared -fPIC tmp.cc
$ readelf -D -sW a.out | grep doSomething
    9  16: 000000000000092c    58 FUNC    GLOBAL DEFAULT  10 _ZN11PublicClass17doSomethingPublicEv
yugr
  • 19,769
  • 3
  • 51
  • 96
  • Good catch, sorry, I pasted the wrong command lines. I did specify -fvisibility=hidden originally and just did it without to check whether it makes a difference at all. I'll edit the post. – joggerwolf Oct 24 '19 at 06:38
  • @joggerwolf I'm afraid I can't repro this behavior with stock g++ 7.4 on Ubuntu 18.04 (there's no `doSomethingPrivate` symbol in `nm`'s output). Where is `/usr/local/bin/c++` coming from? – yugr Oct 24 '19 at 16:06
  • thanks for checking. I assume it's coming directly from the distribution and right now trying to reproduce it with the example it works as expected :-( – joggerwolf Oct 25 '19 at 09:26
  • Unfortunately it still doesn't work in my real project which is set up with cmake and I can't share for IP reasons. I checked the verbose cmake output and the -fvisibility=hidden is there on the command line as expected, but looking at the object file with objdump -t , there is a .hidden indication at some symbols, but a lot fewer than I would expect since I have only marked a single class with visibility default. – joggerwolf Oct 25 '19 at 09:32
  • @joggerwolf "I assume it's coming directly from the distributio" - normally distro does not install anything to `/usr/local` (e.g. my default Ubuntu 18 installation does not have `c++` there). Can you check where this executable is coming from (via `dpkg -S`)? – yugr Oct 25 '19 at 09:44
  • "Unfortunately it still doesn't work in my real project which is set up with cmake and I can't share for IP reasons" - this shouldn't be needed. We just need a small repro-case which reproduces on standard Ubuntu 18.04. – yugr Oct 25 '19 at 09:48
  • I will check with IT (I got my Ubuntu laptop pre-installed). But the same effect is visible on an embedded system, which is my actual target. There I installed gcc/g++ with sudo apt-get install gcc=4:7.3.0-3ubuntu2 and this displays: ... Get:1 http://ports.ubuntu.com/ubuntu-ports bionic/main arm64 gcc arm64 4:7.3.0-3ubuntu2 [5204 B] Fetched 5204 B in 0s (59.0 kB/s) ... Same for g++ – joggerwolf Oct 25 '19 at 10:42
  • I will also try to prepare a small example where I can reliably reproduce the effect, but that may take a while longer. – joggerwolf Oct 25 '19 at 10:43