Symbols have always presented a conundrum for developers:
-
You want to remove all symbols from your program because this makes the code smaller, and potentially faster.
-
However, if you remove all symbols, your crash log backtraces will contain just hex values, which makes it significantly harder to interpret them.
Modern versions of Xcode make it easy to have your cake and eat it to. You can remove all symbols from your program before shipping it to end users, and still have easy access to symbolic debugger information when interpreting crash logs. This section explains how to do this.
Symbols Divided
When you build a program with Xcode, you must deal with two very different types of symbols:
-
Mach-O Symbols — These symbols may be interpreted by the linker (either static, ld, or dynamic, dyld).
-
Debugger Symbols — These symbols are interpreted by the debugger.
Mach-O symbols are subdivided into two groups:
-
local symbols — These symbols are ignored by the linker, and are solely present for the benefit of tools like CrashReporter. When you declare a function as
static
in C, it gets recorded as a local symbol. -
global symbols — These symbols are interpreted by both the static and dynamic linker. When you declare a function as
extern
in C, it gets recorded as a global symbol.
Xcode supports two forms of debugger symbols:
-
DWARF — This is the modern debugger symbols format, supported by Xcode 2.3 and later. DWARF symbols have numerous advantages, not the least being the nice integration with CrashReporter, as described below.
-
STABS — This older debugger symbols format is now deprecated.
Note: STABS debugger symbols are actually stored in the Mach-O symbol table. This made the process of managing symbols more confusing because it conflated two very different concepts (that is, Mach-O symbols and debugger symbols).
Stripping Debugger Symbols
Xcode's DWARF implementation makes it very easy to strip debugger symbols from your released program. All you need to do is set the "Debug Information Format" build setting for your release build to "DWARF with dSYM File" (DEBUG_INFORMATION_FORMAT = dwarf-with-dsym
). When you build your program Xcode will extract all of the debugging information and place it in a .dSYM
document, which it places right next to your program in the build folder. You can then:
-
package up your program and ship it to your users
-
archive the
.dSYM
file
If, at any time in the future, you need to debug your released program, you can just put it and the .dSYM
file in the same directory and GDB will automatically find and use the debugger symbols.
Stripping Mach-O Symbols
Stripping Mach-O symbols is a more complicated issue. The problem is that some Mach-O symbols are interpreted at runtime by the dynamic linker, and you must ensure that those symbols are not stripped. There's one overall approach, but you have modify it depending on the type of program you are building.
The first step is to enable the "Deployment Postprocessing" build setting (DEPLOYMENT_POSTPROCESSING = YES
) for your release build. This will cause Xcode to automatically remove some symbols from your program. The exact behavior depends on the "Strip Style" build setting. You have three choices for this setting:
-
All Symbols (
STRIP_STYLE = all
) — Xcode will strip all symbols that are not specifically marked as being needed at runtime. This is equivalent to running strip tool with the "-u" and "-r" flags. -
Non-Global Symbols (
STRIP_STYLE = non-global
) — Xcode will strip all local symbols. This is equivalent to running strip tool with the "-x" flags. -
Debugging Symbols (
STRIP_STYLE = debugging
) — Xcode will strip no symbols! Actually, it runs the strip tool with the "-S" flag, which causes it to remove all debugger symbols. This is relevant if you're using STABS debugger symbols. However, if you're sensible and are using DWARF, your debugger symbols will not be affected by this;strip
will remove some information from your program (notably the debug map used by dsymutil) but this will not affect the.dSYM
file that Xcode has already created.
Critically, the default value for the "Strip Style" build setting depends on your target type. Table 2 shows this relationship.
Table 2: Default strip style by target type
Target Type | Mach-O Type | Default Strip Style |
---|---|---|
Application, Command-Line Tool | MH_EXECUTE |
All Symbols |
Bundle | MH_BUNDLE |
Non-Global Symbols |
Framework, Dynamic Library | MH_DYLIB |
Debugging Symbols |
Static Library | MH_OBJECT |
Debugging Symbols |
IMPORTANT: If you do not set the "Strip Style" build setting at the project layer, Xcode will display a value of All Symbols. This is misleading. The actual value you get depends on the target type, as shown above. If you open the target build settings, you will set the actual value that will be used.
On the other hand, if you do set a value for the "Strip Style" build setting at the project layer, this will apply to all targets (unless you override it at the target layer). If your project has multiple targets of different types, this is unlikely to be useful. In this case you will probably want to set the "Strip Style" build setting at the target layer for all targets.
For more information about how Xcode's build settings are layered, see the Build Setting Evaluation section section of the Xcode User Guide.
There are two common approaches to stripping Mach-O symbols:
-
typical — In most cases it's reasonable to leave all Mach-O global and local symbols in your release program. They don't bloat the program too much, and their presence can be useful. For example:
-
your CrashReporter logs will include basic symbols in the backtrace
-
you can set DTrace probe points on these symbols
-
-
restricted — In some cases it's necessary to remove all non-essential symbols from a program. For example:
-
when you want to reduce the program's size as much as possible
-
when you don't want users seeing your symbol names
-
Implementing the typical approach is trivial. Set the "Strip Style" build setting to Debugging Symbols, and Xcode will leave all of your global and local Mach-O symbols in the program. This approach works for all target types.
Implementing the restricted approach is a bit trickier. You have to do different things depending on your target type. For an application or command-line tool, you can simply set the "Strip Style" build setting to "All Symbols".
https://developer.apple.com/library/content/technotes/tn2004/tn2123.html