2

I found a lot of questions on the hidden symbol topic in C static library. I tried a lot of things:
-> Add strip and optimisation -O3 -s CFLAGS
-> Add -Wl,--exclude-libs,ALL" "-Wl,-Bsymbolic LDFLAGS
-> Add -fvisibility=hidden and __attribute__ ((visibility ("hidden")))

I cannot make it work properly. Instead of a long explanation, nothing is better than a project example using a very simple source tree to demonstrate my issue: https://github.com/Evanok/static_library_hidden_test

In this repository, I am using CMake to generate a static library. The only public API is in tutu1.h. I also add a test directory to validate the linking and execution of the library with a main.

I was able to hide the source file name using a post bash-script, but I cannot hide symbols in the final static library file.

This is the current result:

rm -rf build/ && cmake -DDEBUG=OFF -B build -S . && cmake --build build && nm build/libtutu.a
(...)
[100%] Built target static_tutu_library

private_sklock0.o:
0000000000000000 r .LC0
                 U normal_tutu3_function
                 U __printf_chk
0000000000000000 T this_is_the_only_public_function_tutu1

private_sklock1.o:
0000000000000000 r .LC0
0000000000000000 r .LC1
                 U __printf_chk
0000000000000000 T unused_tutu2_function

private_sklock2.o:
0000000000000000 T normal_tutu3_function

I expect only the function this_is_the_only_public_function_tutu1 to be visible in my symbol/library definition list.

EDIT1:

Answer from the comment section -> This is not possible. Indeed after checking documentation from https://gcc.gnu.org/wiki/Visibility about hidden feature. This feature is only valid for shared library. Not static library.

EDIT2:

I run the test on a shared library. I was able to hide everything by using the CFLAGS -fvisibility=hidden + the attribute ((visibility ("default"))) on my public API.

If you want to test the result with shared library, I created a branch here that is using cmake shared library example instead of static one: https://github.com/Evanok/static_library_hidden_test/tree/shared_library

EDIT3:

This is also a script I am using to hide all the object filename, $1 is the argument of the script. It should be the original static library file:

[arthur] cat hide_object_name.sh
#!/bin/sh

i=0
echo "lets hide symbol from $1"
rm -f *.o *.obj *.c.obj
ar xv "$1"

for f in $(ls *.obj); do
  if [ -f "$f" ]; then
    echo "process $f.."

    extension="${f##*.}"
    new_name="private_$i.$extension"
    echo "rename $f as $new_name"
    mv "$f" "$new_name"
    i=$((i + 1))
  else
    echo "File $f not found."
  fi
done

rm -f "$1"
echo "generating new library $1..."
ar crs "$1" private_*.*
rm -f *.o *.obj *.c.obj
sync
6
  • 4
    You cannot hide symbols in a static library, only in a shared library. Commented Sep 9, 2024 at 17:50
  • Why shouldn't this be possible? Do you have a source? Why does GCC not provide simple flags to replace all function prototypes and references which are not in the API header with random hash strings, for example? If hiding symbols and functions is not possible in static, why are there so many questions about it with answers? Commented Sep 9, 2024 at 18:00
  • 2
    Why shouldn't this be possible? Because a static library is nothing more than a collection of *.o object files. The only things that can be hidden in any way are static functions and variables inside individual sources files - things that have file scope. If hiding symbols and functions is not possible in static, why are there so many questions about it with answers? And do those answers work? If they did, why did you ask your question? Commented Sep 9, 2024 at 18:16
  • 4
    If you have multiple object modules in a library, then any symbol one module uses and another module defines needs to be visible, because the modules are not linked together until they are built into a program. The linker needs to see the definition of a symbol to resolve references to it. If you want to hide these symbols, you could link the modules together into a single object module in a partial link (e.g., using the -r switch in the GNU ld linker), and then those symbols can be stripped. Commented Sep 9, 2024 at 18:42
  • 1
    Note well that partial linking and then stripping produces a result that is roughly equivalent to what you can more easily get by instead combining all your source files into one (or at most one per public symbol, anyway), and making all the identifiers that you want to hide static. That has the additional advantage that it will also work on systems and with linkers that don't support partial linking. Commented Sep 9, 2024 at 22:23

1 Answer 1

1

Looking back at the "good old times" when all the separate steps were called individually:

  • The compiler (cc) translates .h and .c files into a .o file.

  • The archiver (ar) puts several .o files together into a .a file (static library).

  • The linker (ld) puts several .o files (directly or from .a files) plus some extra bits together into a .so file (shared library) or a .exe file (executable).

You will note that there is no linker involved in creating a static library. Nor does the archiver (which is involved) read any header files to do any kind of checking. (It really just archives files together. ar and tar are closely related this way. A bit like .jar and .zip files are closely related.)

The static library is simply a place to store .o files you already have compiled, so you can generate binaries (.so or .exe) more quickly, without having to re-compile the library source.

If you want certain identifiers to not show up as symbols in your .o files, don't give them external linkage. Instead, declare them static to your .c files (so the compiler does not generate the symbol in the first place). This will work regardless of the toolchain you'd be using.

And in the end, be aware that symbol visibility is not a safety feature. If you are worried about people tampering with your binaries, you will need much better protection than just making your symbols disappear...

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.