Small knowledge, big challenge! This article is participating in the creation activity of “Essential Tips for Programmers”.
What’s the sign? In the linking process, functions and variables are collectively called symbols. The variable Name or function Name is the Symbol Name.
In iOS, we are most familiar with debug symbols, because in Release mode, Xcode will unsign debug symbols to help us reduce the size of the App package. Can other symbols be removed except debug symbols? What other types of symbols are there besides debug symbols? What is the format of the debug symbol? How do we use debug symbols to get the function call stack? With these questions in mind, we begin the discussion of this article
Global symbols and local symbols
View the symbol table
In main.m, we define global and local variables, and variables decorated with the static keyword are local and visible only to defined files.
int global_int_value = 10;
static int static_int_value = 20;
int main(int argc, const char * argv[]) {
global_int_value = 30;
static_int_value = 40;
return 0;
}
Copy the code
After compiling with Xcode, you get your Mach O file and look at its symbol table
objdump --macho --syms SymbolTask
Copy the code
The results for
SymbolTask: SYMBOL TABLE: 0000000100008004 l O __DATA,__data _static_int_value 0000000000000000 d *UND* main.m 0000000100003f80 d *UND* 0000000100003f80 d *UND* _main 000000000000002a d *UND* 000000000000002a d *UND* 0000000000000000 d *UND* _global_int_value 0000000100008004 d *UND* _static_int_value 0000000000000000 d *UND* 0000000100000000 g F __TEXT,__text __mh_execute_header 0000000100008000 g O __DATA,__data _global_int_value 0000000100003f80 g F __TEXT,__text _main 0000000000000000 *UND* dyld_stub_binderCopy the code
l
Representative:Local symbol
.g
Representative:Global symbol
.
As you can see, local variables become local and global variables become global.
The nature of global and local symbols is the visibility of symbols. Global symbols are visible to the entire project; local symbols are visible only to the current file.
visibility
We can use visibility(“hidden”) and visibility(“default”) to control the visibility of symbols.
The code is as follows:
int global_int_value = 10;
static int static_int_value = 20;
int hidden_var __attribute__((visibility("hidden"))) = 99;
double default_var __attribute__((visibility("default"))) = 100;
int main(int argc, const char * argv[]) {
global_int_value = 30;
static_int_value = 40;
return 0;
}
Copy the code
View the symbol table
bel@beldeMacBook-Pro Debug % objdump --macho --syms SymbolTask SymbolTask: SYMBOL TABLE: 0000000100008004 l O __DATA,__data _hidden_var 0000000100008010 l O __DATA,__data _static_int_value 0000000000000000 d *UND* main.m 0000000100003f80 d *UND* 0000000100003f80 d *UND* _main 000000000000002a d *UND* 000000000000002a d *UND* 0000000000000000 d *UND* _global_int_value 0000000000000000 d *UND* _hidden_var 0000000000000000 d *UND* _default_var 0000000100008010 d *UND* _static_int_value 0000000000000000 d *UND* 0000000100000000 g F __TEXT,__text __mh_execute_header 0000000100008008 g O __DATA,__data _default_var 0000000100008000 g O __DATA,__data _global_int_value 0000000100003f80 g F __TEXT,__text _main 0000000000000000 *UND* dyld_stub_binderCopy the code
As you can see, you can change a global symbol to a local symbol using __attribute__((visibility(“hidden”)).
Import/export symbol
Export symbols
The NSLog that we use a lot is where we import an import symbol from the Foundation framework. Foundation exported the NSLog notation.
Global symbols are exported by default, and global symbols become exported symbols
We use the –exports-trie option to view its export table.
//main.m
int global_int_value = 10;
static int static_int_value = 20;
int main(int argc, const char * argv[]) {
global_int_value = 30;
static_int_value = 40;
NSLog(@"%d", global_int_value + static_int_value);
return 0;
}
Copy the code
MacBook-Pro Debug % objdump --macho -exports-trie SymbolTask
SymbolTask:
Exports trie:
0x100000000 __mh_execute_header
0x100003F40 _main
0x100008010 _global_int_value
Copy the code
The global symbol _global_int_value is exported.
The indirect symbol table in Mach O holds exported symbols from other dynamic libraries.
bel@beldeMacBook-Pro Debug % objdump --macho --indirect-symbols SymbolTask
SymbolTask:
Indirect symbols for (__TEXT,__stubs) 1 entries
address index name
0x0000000100003f8e 15 _NSLog
Indirect symbols for (__DATA_CONST,__got) 1 entries
address index name
0x0000000100004000 17 dyld_stub_binder
Indirect symbols for (__DATA,__la_symbol_ptr) 1 entries
address index name
0x0000000100008000 15 _NSLog
Copy the code
OC method
OC methods export symbols by default, even if the method is not declared in an. H file.
// LYObject.m
#import "LYObject.h"
@interface LYObject : NSObject
@end
@implementation LYObject
-(void)run{
NSLog(@">>>>>>>>> run");
}
@end
Copy the code
View its exported symbol table
bel@beldeMacBook-Pro Debug % objdump --macho -exports-trie SymbolTask
SymbolTask:
Exports trie:
0x100000000 __mh_execute_header
0x100003F20 _main
0x1000080B8 _OBJC_METACLASS_$_LYObject
0x1000080E0 _OBJC_CLASS_$_LYObject
Copy the code
Set -xlinker -unexported_symbol -Xlinker _OBJC_CLASS_$_LYObject in Build Setting to change _OBJC_CLASS_$_LYObject to a local symbol.
SymbolTask:
Exports trie:
0x100000000 __mh_execute_header
0x100003F20 _main
0x1000080B8 _OBJC_METACLASS_$_LYObject
Copy the code
You can also batch convert exported symbols to local symbols in the form of files.
Why change an exported symbol to a local symbol?
Suppose we want to reduce the size of the OC dynamic library, since we cannot export symbols, we change the symbols that are not exposed to local symbols.
Swift symbol
In the Swift,
Classes and methods decorated with the Private keyword are native symbols.
Classes and methods decorated with the Public keyword are exported symbols.
Weak symbols
Weakly defined symbol
A weakly defined symbol can be defined using __attribute__((weak)).
The code is as follows:
// WeakSymbol.h
void weak_function(void) __attribute__((weak));
// WeakSymbol.m
#import "WeakSymbol.h"
void weak_function(void) {
NSLog(@"weak_function");
}
// man,m
int main(int argc, const char * argv[]) {
weak_function();
return 0;
}
Copy the code
Symbol table content, where the debug symbol content is omitted.
bel@beldeMacBook-Pro Debug % objdump --macho --syms SymbolTask ...... 0000000100008010 l O __DATA,__data __dyld_private ...... 0000000100000000 g F __TEXT,__text __mh_execute_header 0000000100003f40 g F __TEXT,__text _main 0000000100003f70 w F __TEXT, __TEXT _weak_function // global symbol......Copy the code
The weakly defined symbol is now a global symbol.
We can change a weakly defined symbol to a local symbol using __attribute__((weak, visibility(“hidden”))).
SYMBOL TABLE: 0000000100003F60 W F __TEXT, __TEXT _Weak_Function // Local symbol 0000000100008008 L O __DATA, __DATA __dyLD_private.... 0000000100000000 g F __TEXT,__text __mh_execute_header 0000000100003f30 g F __TEXT,__text _main ...Copy the code
For the linker, if there is a strong symbol with the same name as more than one weak symbol, the strong symbol is selected and no compilation error is reported.
Let’s add a new function of the same name to main.m
int main(int argc, const char * argv[]) { weak_function(); return 0; } void weak_function(){ NSLog(@">>>>>> new Function"); SymbolTask[9762:510689] >>>>>> new FunctionCopy the code
Weak reference symbol
Using __attribute__((weak_import)) can be defined as a weak reference symbol.
// WeakImportSymbol.h
void weak_import_function(void) __attribute__((weak_import));
// WeakImportSymbol.m
#import "WeakImportSymbol.h"
#import <Foundation/Foundation.h>
void weak_import_function(void) {
NSLog(@"weak_import_function");
}
// main.m
#import <Foundation/Foundation.h>
#import "WeakImportSymbol.h"
int main(int argc, const char * argv[]) {
if (weak_import_function) {
weak_import_function();
}
return 0;
}
Copy the code
View its symbol table:
bel@beldeMacBook-Pro Debug % objdump --macho --syms SymbolTask
......
0000000100008008 l O __DATA,__data __dyld_private
......
0000000100000000 g F __TEXT,__text __mh_execute_header
0000000100003f30 g F __TEXT,__text _main
0000000100003f60 g F __TEXT,__text _weak_import_function
......
Copy the code
Weak-referenced symbols are also global symbols. Weak-referenced symbols without a method implementation will report undefined symbol as an error.
-xlinker -u -xlinker _weak_import_function in Other Linker Flags option in Build Settings Tells the linker to dynamically look for the _weak_import_function function at run time.
Because it’s a weak reference function,It can be called normally if there is an implementation
If there is no implementation, the function is set toNULL
.
Debug symbols
DWARF
In the first example
0000000000000000 d *UND* main.m
0000000100003f80 d *UND*
0000000100003f80 d *UND* _main
000000000000002a d *UND*
000000000000002a d *UND*
0000000000000000 d *UND* _global_int_value 0000000100008004 d *UND* _static_int_value 0000000000000000 d *UND*
Copy the code
d
: indicates the debugging information symbol.
In iOS, Debug symbols are DWARF(Debug With Arbitrary Record Format), which Record function names, file names, and line numbers. DSYM files, a collection of DWARF data, are automatically generated in Release mode in Xcode.
inDebug
In mode, we’re going toDebug Information Format
Set toDWARF with dSYM File
. 将 Deployment Postprocessing
Set toYes
:
This allows dSYM files to be generated in Debug mode.
For information on how to take advantage of the DWARF debug information format in dSYM files and how to recover the function call stack, see my article on the iOS debug files dSYM and DWARF
conclusion
What symbols can we take off when we take off the sign? Debug symbols and local symbols can be removed. The symbols in the indirect symbol table in our App’s Mach O file cannot be removed. The symbols stored in the indirect symbol table are exported symbols from the system dynamic library, which need to be dynamically linked in the dynamic library.
We can change global symbols to local symbols through the linker parameter. For our own dynamic library, if we want to reduce the size of the dynamic library, try to change unnecessary global symbols to local symbols.
Weak symbols can be used for version-adaptation purposes, so if there is an API in iOS that is not available in iOS 10 but is available in iOS 11, you can use weak symbols.
Using debug notation we can restore the function call stack, remove debug information when publishing, and reduce package size.
If you feel that there is a harvest please follow the way to a love three even: 👍 : a praise to encourage. 🌟 : Collect articles, easy to look back! . 💬 : Comment exchange, mutual progress! .