Get all symbols
As we know from IOS- Startup Optimization (above), we need to rearrange the binary symbols to get all the symbols before startup.
- So how can we get all the symbols before startup?
- 1, Fishhook: Can hook system functions, you can try to use
fishhook
hookobjc_msgSend
Delta function, but because ofobjc_msgSend
Hook requires assembly code (not feasible for authors), and this method can only hookOC
Can not cover all symbols - 2. Compiler staking: this method can cover all symbols
- 1, Fishhook: Can hook system functions, you can try to use
Clang plugging pile
LLVM
Code coverage monitoring tools are available,LLVM document- According to the official example, to develop our demo
According to the case
- In creating the Demo
Build Settings
In the searchOther C
To findOther C Flags
join-fsanitize-coverage=trace-pc-guard
- Direct compilation will report the following error
- Copy the code in the case column into the file at the end of the startup
ViewController
In theviewDidLoad
Method as the end method to start completion, so copy the code toViewController.m
In the file
- Compiling error again
- Comment out the offending code (
__sanitizer_cov_trace_pc_guard
Method), and the compilation succeeds. - Run Demo and view the displayed information through LLDB debugging
- Increasing or decreasing the value of the stop function, which is the total number of symbols, varies
- Clicking on the screen will print the following data that will be called every time we execute a method, function, or block
__sanitizer_cov_trace_pc_guard
function
- Can also be observed by compilation, as long as in
Other C Flags
Place to join-fsanitize-coverage=trace-pc-guard
Flag, opentrace PCs
Function. LLVM inserts a line of calls at the edge of each function (the start position)__sanitizer_cov_trace_pc_guard
The code. It was inserted at compile time. So you can get 100% sign coverage.
- This is the Clang peg, and once we’re done, we need to get all the function symbols, store them, and export them to the ORDER file.
Get function symbol
__builtin_return_address
- We are in
__builtin_return_address
Set a breakpoint to see what is stored on the PC
- found
__builtin_return_address(0)
Gets the address of the last function called - You can enter it by clicking on the screen
touchesBegan:withEvent:
And the address of the PC is called__sanitizer_cov_trace_pc_guard
The next line address of the function, LLDB, also passes__builtin_return_address
View the call stack, we can also pass 1 to get the call address of the previous function
To obtain symbol
- Import #import <dlfcn.h> and pass
Dl_info
Get the function information.Dl_info
It’s a structure
typedef struct dl_info {
const char *dli_fname; /* Pathname of shared object */
void *dli_fbase; /* Base address of shared object */
const char *dli_sname; /* Name of nearest symbol */
void *dli_saddr; /* Address of nearest symbol */
} Dl_info;
Copy the code
- Modify the
__sanitizer_cov_trace_pc_guard_init
The implementation of
void __sanitizer_cov_trace_pc_guard(uint32_t *guard) { if (! *guard) return; // Duplicate the guard check. void *PC = __builtin_return_address(0); // char PcDescr[1024]; // This function is a part of the sanitizer run-time. // To use it, link with AddressSanitizer or other sanitizer. // __sanitizer_symbolize_pc(PC, "%p %F %L", PcDescr, sizeof(PcDescr)); // printf("guard: %p %x PC %s\n", guard, *guard, PcDescr); Dl_info info; dladdr(PC, &info); // Read the PC address, Printf ("dli_fname:%s \n dli_fbase:%p \n dli_sname:%s \n dli_saddr:%p \n ", info.dli_fname, info.dli_fbase, info.dli_sname, info.dli_saddr); }Copy the code
- All we need to get is
info.dli_sname
Storage symbols
- Due to the
__sanitizer_cov_trace_pc_guard
The function is in a multithreaded environment, so you need to be write safe - Here we use atomic queue storage, import #include
header file, create atomic queue, define node structure:
#import <libkern/ osatomy.h > @interface ViewController () @end@implementation ViewController OSQueueHead symbolList = OS_ATOMIC_QUEUE_INIT; Typedef struct {void * PC; void * next; }SYNode;Copy the code
- We are in
__sanitizer_cov_trace_pc_guard
Put the symbol in the queue, intouchesBegan
It fetches data from the queue
void __sanitizer_cov_trace_pc_guard(uint32_t *guard) { void *PC = __builtin_return_address(0); //0 current function address, 1 above function address Dl_info info; // declare object dladdr(PC, &info); SYNode * node = malloc(sizeof(SYNode)); SYNode = malloc(sizeof(SYNode)); // create struct space *node = (SYNode){PC, NULL}; // Initial assignment of node (PC = current PC, NULL = next) // Add structure (offsetof: // Get and assign // Get the address symbolList, offset SYNode bytes, and assign node to the next pointer of the last node in symbolList. OSAtomicEnqueue(&symbolList, node, offsetof(SYNode, next)); } - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {// create an NSMutableArray<NSString *> * symbolNames = [NSMutableArray array]; // A hook (__sanitizer_cov_trace_pc_guard) is added to the while loop every time the jump is blocked. -fsanitize-coverage=func,trace-pc-guard specifies only func to Hook while (YES) {SYNode * node = OSAtomicDequeue(&symbolList, offsetof(SYNode, next)); if(node ==NULL) break; Dl_info info = {0}; // Fetch the PC of the node and assign the value to info dladdr(node-> PC, &info); // Free (node); // Save the name NSString *name = @(info.dli_sname); / / write the ternary operator BOOL isObjc = [name hasPrefix: @ "+ ["] | | [name hasPrefix: @" - ["]; nsstrings * symbolName = isObjc? Name: [NSString stringWithFormat:@"_%@",name]; NSLog(@"symbolName:%@",symbolName); [symbolNames addObject:symbolName]; } // NSEnumerator * enumerator = [symbolNames reverseObjectEnumerator]; / / create the array NSMutableArray * funcs = [NSMutableArray arrayWithCapacity: symbolNames. Count]; / / temp nsstrings * name; While (name = [enumerator nextObject]) {// Re-add if (! [funcs containsObject:name]) { [funcs addObject:name]; }} // Remove the current touchesBegan function (funcs removeObject:[NSString stringWithFormat:@"%s",__FUNCTION__]]; / / an array of strings nsstrings * funcStr = [funcs componentsJoinedByString: @ "\ n"); / / file path nsstrings * filePath = [NSTemporaryDirectory () stringByAppendingPathComponent: @ "xq. Order"]. / / the file content NSData * fielContents = [funcStr dataUsingEncoding: NSUTF8StringEncoding]; / / create a file [[NSFileManager defaultManager] createFileAtPath: filePath contents: fielContents attributes: nil]; NSLog(@"%@",funcs); NSLog(@"%@",filePath); NSLog(@"%@",fielContents); }Copy the code
- After successful operation, click the screen to find that it entered an infinite loop
- Looking at the assembly, we found that every b jump was triggered
__sanitizer_cov_trace_pc_guard
So every time the while loop fires__sanitizer_cov_trace_pc_guard
- So we need to be in
Build Settings
Found in theOther C Flags
Modified to-fsanitize-coverage=func,trace-pc-guard
This is the effect of ignoring some loops. - When this is done, the symbol files we need are stored in the TMP file in the sandbox.
- Open the.order file and you can see the symbol that we got
Swift plugging pile
- Because OC and Swift use a different compiler front end, SWIFT is handled separately
- Know Clang insert pile, SWIFT insert pile is simple, just modify the command
- Create one in your project
SwiftTest
Swift files automatically create bridge connectors. inSwiftTest
Write test code inViewController.m
To import the bridge joint file:#import "TracePCsDemp-Swift.h"
Execute the swift method in the load method
- Swift’s front-end compiler is Swift, so in
other Swift Flags
add-sanitize=undefined
和-sanitize-coverage=func
- After a successful run, you can see several additional methods
"+[ViewController load]",
"_$s12TracePCsDemp9SwiftTestC05swiftE0yyFZTo",
"_$s12TracePCsDemp9SwiftTestC05swiftE0yyFZ",
"_$ss5print_9separator10terminatoryypd_S2StFfA0_",
"_$ss5print_9separator10terminatoryypd_S2StFfA1_",
"_main",
"-[AppDelegate window]",
"-[AppDelegate setWindow:]",
"-[AppDelegate application:didFinishLaunchingWithOptions:]",
"-[ViewController viewDidLoad]"
Copy the code
- Once you have the.order file, you can test it directly in Xcode