Hiding Symbols in ELF Binaries

Under Linux when you compile C++ code using GCC or Clang there ends up being tons of debugging information that simply should not be there when you are not releasing open source software. The strip command does not do enough to remove that information.

In order to do this you need to pass -fvisibility=hidden and -fvisibility-inlines-hidden to the compiler. This will hide symbol names and reduce the size of the binary. However, if you are linking to something like boost, you won’t be able to hide symbols for the entire binary. In that case, you need to create two header files to programmatically change the visibility of your .cpp and .h files.

visibility_hidden.h:

1
2
3
#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__)
#pragma GCC visibility push(hidden)
#endif

visibility_pop.h:

1
2
3
#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__)
#pragma GCC visibility pop
#endif

In every code file / header file you’ll need to do this:

1
2
3
4
5
6
7
... other includes
#include "visibility_hidden.h"
... your code
#include "visibility_pop.h"

After you compile your program, you should then run strip --strip-all --discard-all ... and also sstrip ... from ELF Kickers.

Here’s a quick Python script I wrote to add the headers to .cpp files on Windows. It will look for the stdafx include and insert the visibility include underneath it then add the pop header to the end of the file.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import os
stdafx = '#include "stdafx.h"'
for name in os.listdir():
if not name.endswith('.cpp'):
continue
with open(name, 'r', newline="\r\n") as f:
text = f.read()
if text.find('visibility') != -1:
continue
idx = text.find(stdafx)
if idx == -1:
continue
text = text[:idx + len(stdafx)].strip() + "\r\n" + '#include "visibility_hidden.h"' + text[idx + len(stdafx):]
text += "\r\n\r\n" + '#include "visibility_pop.h"'
with open(name, 'w', newline='') as f:
f.write(text)