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.