Symbol hiding in static libraries built with Xcode Symbol hiding in static libraries built with Xcode xcode xcode

Symbol hiding in static libraries built with Xcode


Hiding internal names requires a few simple Xcode build settings, and it is not generally necessary to modify source or change the type of the built product.

  1. Eliminate any internal symbols required between modules by performing a single-object prelink. Set the Xcode build setting named "Perform Single-Object Prelink" to Yes (GENERATE_MASTER_OBJECT_FILE=YES). This causes ld to be run with the "-r" flag.
  2. Make sure that the setting "Strip Style" is set to "Non-global symbols" (STRIP_STYLE=non-global), this passes "-x" to ld.
  3. Stripping is only actually performed on static libraries if post-processing is enabled (and this is not the default). Set Xcode build setting "Deployment Postprocessing" to yes. (DEPLOYMENT_POSTPROCESSING=YES). Also make sure that "Use separate strip" is set to Yes (not always the default) (SEPARATE_STRIP=YES).
  4. If, in addition to local symbols, if you need to remove some of the global symbols you can supply additional options to the strip command, under the Xcode build setting "Additional strip flags". E.g. I commonly use the strip "-R somefile" option to provide a file with an additional list of symbols which I want removed from the global symbol table.


The main trick in hiding symbols within static libraries is to generate a Relocatable Object file (as opposed to a static library archive that simply consists of a collection of individual .o files). To build a relocatable object file, you need to choose your target in XCode as Bundle (as opposed to "Cocoa Touch Static Library"). The Bundle target appears under the OS X templates, and you can set its target to iOS in the Build settings if you are building for iOS.

Once you have set up your target correctly, the following steps will help get the symbol hiding correct:

  1. Set the "Symbols hidden by default" option to Yes in the build settings. This makes sure all the symbols compiled in the files are marked as private.

  2. As this is a library, you do need to keep some symbols public. You should put code for the functions you want to keep publicly visible in separate files, and compile those files with the -fvisibility=default flag (you can set this flag for individual files "Build Phases > Compile Sources > -- Compiler Flags" in Xcode). Alternately, you can prefix the name of the function/class that you wish to be visible with the __attribute__((visibility("default"))) directive.

  3. Under the linking settings in the X-code project, set the Mach-O type to "Relocatable Object File". What this means is that all the .o files will be relinked to generate a single object file. It is this step that helps mark all the symbols as private when the .o files are linked together into one file. If you build a static library (i.e. a .a file) this relinking step doesn't happen so symbols never get hidden. So choosing a Relocatable object file as your target is critical.

  4. Even after marking the symbols as private they still show up in the .o file. You need to enable stripping to get rid of the private symbols. This can be done by setting the "Stripped Linked Product" setting to Yes in the build settings. Setting this option runs the strip -x command on the object file that removes the private symbols from the object file.

  5. Double check all the internal symbols are gone by running the nm command on the final relocatable object file generated by the build process.

The above steps will help you get rid of symbol names from the nm command. You'll still see some function names and file names if you run the strings command on your object file (due to some strings and object names being compiled in via exceptions). One of my colleagues has a script that renames some of these symbols by looking into the binary sections and renaming those strings. I've posted it up here for you to use: https://gist.github.com/varungulshan/6198167. You can add this script as an extra build step in Xcode.


It is a bit unclear for me how to hide the symbols in static libraries from the linux command line environment based on the previous answers so I'll just post my solution here for posterity (given this is one of the top results on google for that question).

Let's say you have these two .c files:

// f1.cconst char *get_english_greeting(void){  return "hello";}__attribute__((visibility("default")))const char *get_greeting(void){  return get_english_greeting();}

and

// f2.c#include <stdio.h>const char *get_english_greeting(void);__attribute__((visibility("default")))void print_greeting(void){  puts(get_english_greeting());}

You want to convert these two files into a static library exporting both get_greeting and print_greeting but not get_english_greeting which you don't want to make static as you would like to use it throughout your library.

Here are the steps to achieve that:

gcc -fvisibility=hidden -c f1.c f2.cld -r f1.o f2.o -o libf.oobjcopy --localize-hidden libf.oar rcs libf.a libf.o

Now this works:

// gcc -L. main.c -lfvoid get_greeting(void);void print_greeting(void);int main(void){  get_greeting();  print_greeting();  return 0;}

And this doesn't:

// gcc -L. main.c -lfconst char *get_english_greeting(void);int main(void){  get_english_greeting();  return 0;}

For the latter you get this error:

/tmp/ccmfg54F.o: In function `main':main.c:(.text+0x8): undefined reference to `get_english_greeting'collect2: error: ld returned 1 exit status

Which is what we want.

Note that the hidden symbol names are still visible in the static library but the linker will refuse to link with them outside said static library. To completely remove the symbol names you'll need to strip and obfuscate.