By Ben Zotto


2010-07-18 17:15:38 8 Comments

I'm trying to figure out whether I can build a static library that hides all of its internal objects and functions, etc, except for the interfaces I want to export. I'm experimenting with Xcode (gcc 4.2).

I've used the __attribute__((visibility("hidden"))) attribute on some C++ classes per this documentation. I've also defined little helper C functions as being file-local (static), etc.

However, when I run strings on the resulting .a library file, even when compiled in Release configuration, I still see the names of my ostensibly-hidden classes, with their method names, and even the names of file-local functions strewn around in there as well.

I've added the -fvisibility=hidden and even -fno-rtti to the gcc flags. While this reduces some of the strings, the class names, method names, and static functions names are all still in there in plain or mangled-but-readable form.

Is there a reliable way to get the compiler to build this stuff without having the string names of all the internal stuff emitted into the binary? It shouldn't be necessary to have for any external clients linking in.

(To clarify: I'm asking about obfuscation of internal naming, versus literal export binding needs. I'm disconcerted that all the internal workings are visible via the strings command, regardless of whether these symbols are formally exported or not.)

Thanks.

3 comments

@bleater 2013-09-22 22:10:53

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.

@David H 2014-01-27 16:24:06

Excellent! I just did as you suggested with Xcode 5.1 Beta, and it worked perfectly. Just what I needed.

@DanielHsH 2014-05-12 12:17:28

I would suggest to add 5. to the above list: Set "SymbolsHiddenByDefult" to YES. (It is in the Code Generation section). It is equivalent to -fvisibility=hidden. The step 1..4 helped me to remove strings but step 5. hides also the non public methods, static variables and buffers.

@bleater 2015-07-29 22:52:43

@DanielHsH The OP's question stated that he had already controlled symbol visibility, but yes, your suggestion is a good idea for other cases where explicit control has not yet been put in place. It does pay to test that the final product can be correctly linked (into a dynamic section or executable) however, since there is the possibility that callers might be inadvertently linking to private symbols.

@Kaganar 2018-04-25 02:10:05

"Use separate strip" is no longer an option -- deprecated by Apple with the claim that stripping is always done separately. Despite that, I still got some mileage out of this post by doing the rest -- appeared to work.

@ypsu 2015-05-04 11:07:11

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.c
const 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.c
ld -r f1.o f2.o -o libf.o
objcopy --localize-hidden libf.o
ar rcs libf.a libf.o

Now this works:

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

And this doesn't:

// gcc -L. main.c -lf
const 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.

@Varun Gulshan 2013-05-11 00:11:53

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.

@Yoav 2013-05-31 09:55:27

Thanks for the answer, we did it but now we cannot run it on a simulator, it doesn't link. it only works on the device. The error we are getting is: ld: Assertion failed: (atom->fixupCount() == 1), function targetClassName, file /SourceCache/ld64/ld64-136/src/ld/parsers/macho_relocatable_‌​file.cpp, line 4973. clang: error: linker command failed with exit code 1 (use -v to see invocation). Did you encounter this issue? Thanks

@Varun Gulshan 2013-08-01 19:13:55

Hi Yoav, Sorry for the late reply. No, I never tried this on the simulator, so it is possible it breaks linking there. One of our engineers has come up with an even better method (he wrote to script to remove all locally visible symbols by scanning the binary sections). I'll post it soon.

@Yoav 2013-08-12 13:47:59

OK, can you post it or send it to my mail? We still have issues with it....

@wukong 2019-05-08 19:41:53

@VarunGulshan would take time to my question? Thank you stackoverflow.com/questions/56045295/…

Related Questions

Sponsored Content

18 Answered Questions

[SOLVED] Reference — What does this symbol mean in PHP?

1 Answered Questions

[SOLVED] How to download Xcode DMG or XIP file?

  • 2012-04-26 14:41:18
  • Proud Member
  • 1486073 View
  • 981 Score
  • 1 Answer
  • Tags:   xcode dmg

24 Answered Questions

[SOLVED] Xcode error "Could not find Developer Disk Image"

  • 2015-06-09 15:48:57
  • Nathan Kramer
  • 535606 View
  • 870 Score
  • 24 Answer
  • Tags:   ios iphone xcode

20 Answered Questions

[SOLVED] Git ignore file for Xcode projects

11 Answered Questions

[SOLVED] How do I list the symbols in a .so file

12 Answered Questions

[SOLVED] Hide strange unwanted Xcode logs

9 Answered Questions

[SOLVED] How to "add existing frameworks" in Xcode 4?

2 Answered Questions

[SOLVED] Symbol visibility and namespace

2 Answered Questions

[SOLVED] Unit testing for private functions of a shared library

Sponsored Content