27, iOS bottom Analysis - Optimization of promoter (binary rearrangement)

Start Optimization - Binary remake


iOS system would be virtual and physical memory paging, virtual memory to physical memory mapping are the smallest unit of a page .
 
 start up:

Click the icon to start from the first page to display when we are talking about APP start.
 

Cold start:

Program completely out (cases kill the process and then opened many other APP, this time the program corresponding to the physical memory is being used by another program needs to be reloaded from virtual memory to physical memory) or the first installation start (first once you install or uninstall heavy equipment, etc.).

Hot Start:

After killing program, followed by another start, this time to the corresponding physical memory is still keep our data does not require a full reload from virtual memory.
 Program starts, load from virtual memory to physical memory is paged loaded. Data is stored page by page (iOS a 16KB) in virtual memory. If you start the program when the physical memory is not what we need to load, it will reload the page fault from virtual memory. If less, then the perception does not come out, if more, then it will affect the startup speed.
 
 Instruments
 Xcode comes with debugging tools. Open the xcode command + I to open the
 debugging tools There are many here use the System Trace
 simply double-click to open, to see the data in each thread here, a thread here need to see is the virtual memory
 

 


 Re-install the ipa signature when the demo first to write a blank project, and then run to the phone, and then modify the installation of the ipa, the purpose is to add a description file to install the phone ipa. Profile exists already installed on the mobile phone, it is possible to directly command + R

Through debugging, first opened (cold start) when loaded with a lot of files, but kill the program, direct restarted (warm start) you will find a lot less loaded. After then we know that virtual memory is loaded into physical memory, physical memory to start again when there is no need to load, it will start soon. If the time to kill the program, then there are a lot of other open programs, we open the program again, and find that it is a cold start. Loaded before you can analyze the physical memory in use by another application out.
 Virtual memory to physical memory, virtual memory are contiguous, corresponding to a corresponding physical memory through the connector, the corresponding physical memory is not fixed is to find spare memory opened under the circumstances, or no idle time to overwrite corresponding number RAM.
 File Backed Page In the loaded data, a file is not accounted for one, one is 16KB, put a lot of energy.
 


Binary rearrangement

 Why binary rearrangement, because the program is executed in sequence.
 For example:
 Now virtual memory 6, 123 456, of which 246 is started to load, 135 is not required to start loading. But the execution order is to six fully loaded inside. Rearranged binary object is to page 246 needs to start loading the contents of the other and then moved up the order of execution of several pages.
 Binary rearrangement is difficult to know the start symbol sequence, then these symbols are rearranged.


xcode is configurable. xcode he ld connectors are used inside a -order_file, writing into the long, packed xcode time sequences of symbols will be packaged inside the file.
 
 To see how boot sequence
 1, sequence file
 Build phases -> Compile Source file inside the execution order is the order (adjustable)
 if no rearrangement of the file without modification method determines the order of the way, the function. A page in the page before B, then the method A will be executed before B method.
 
 2, the symbol table order.
 Build Settings -> Write Link Map File -> YES automatically generate a symbol table
 compiled after the completion of the translation will be written into this link map.
 Then open view
 xcode Products -> filedome.app -> Right Show In Finder -> up two directories (and similar Products) found Intermediates.noindex -> filedome.build -> Debug- iphoneos -> filedome.build -> filedome -LinkMap-normal-arm64.txt (find this at the end of the .text file)


 
 


 4, Symbols
 starting position (address codes) take up much space (in hexadecimal) belong to the symbol file name Method several .o, functions, etc.

# Address        Size        File          Name
 0x1000051C8    0x00000098    [  1] +[LJLWeakProxy proxyWithTarget:]
 0x100005260    0x0000006C    [  1] -[LJLWeakProxy methodSignatureForSelector:]
 0x1000052CC    0x0000009C    [  1] -[LJLWeakProxy forwardInvocation:]
 0x100005368    0x00000034    [  1] -[LJLWeakProxy target]
 0x10000539C    0x00000048    [  1] -[LJLWeakProxy setTarget:]
 0x1000053E4    0x0000003C    [  1] -[LJLWeakProxy .cxx_destruct]
 0x100005420    0x000000E0    [  2] -[LJLGCDTwoViewController viewDidLoad]
 0x100005500    0x00000014    [  2] -[LJLGCDTwoViewController createGroup]
 0x100005514    0x000000C4    [  2] -[LJLGCDTwoViewController groupRest1]
 0x1000055D8    0x0000003C    [  2] ___37-[LJLGCDTwoViewController groupRest1]_block_invoke
 0x100005614    0x0000003C    [  2] ___37-[LJLGCDTwoViewController groupRest1]_block_invoke_2
 0x100005650    0x0000002C    [  2] ___37-[LJLGCDTwoViewController groupRest1]_block_invoke_3
 0x10000567C    0x000001A0    [  2] -[LJLGCDTwoViewController groupRest2]
 0x10000581C    0x00000054    [  2] ___37-[LJLGCDTwoViewController groupRest2]_block_invoke


 Binary rearrangement start

 1. Create .order
 terminal to open the project root directory, enter: touch liujilou.order Enter. In the root directory to generate a liujilou.order (named according to their own situation project) files.
 
 2, xcode Configuration
 Build -> Settings -> Order File ./liujilou.order ( in the root directory with a relative path)

@implementation ViewController
//随便写一些代码
//函数
void test()
{
    block1();
}
void(^block1)(void) = ^(void){
    
};
......
//例如 :编辑 liujilou.order 
 _main
 -[LJLWeakProxy setTarget:]
 -[LJLWeakProxy .cxx_destruct]
 -[liujilou hello]

command + K clear about the cache, then the compiler command + B

# Symbols:
 # Address    Size        File  Name
 0x1000051C8    0x000000A4    [ 11] _main
 0x10000526C    0x00000048    [  1] -[LJLWeakProxy setTarget:]
 0x1000052B4    0x0000003C    [  1] -[LJLWeakProxy .cxx_destruct]
 0x1000052F0    0x00000098    [  1] +[LJLWeakProxy proxyWithTarget:]
 0x100005388    0x0000006C    [  1] -[LJLWeakProxy methodSignatureForSelector:]
 0x1000053F4    0x0000009C    [  1] -[LJLWeakProxy forwardInvocation:]
 0x100005490    0x00000034    [  1] -[LJLWeakProxy target]
 0x1000054C4    0x000000E0    [  2] -[LJLGCDTwoViewController viewDidLoad]
 0x1000055A4    0x00000014    [  2] -[LJLGCDTwoViewController createGroup]
 0x1000055B8    0x000000C4    [  2] -[LJLGCDTwoViewController groupRest1]
 0x10000567C    0x0000003C    [  2] ___37-[LJLGCDTwoViewController

 Contrast the above results, we can see now the order of the boot sequence of what we edited .order 

 _main
 -[LJLWeakProxy setTarget:]
 -[LJLWeakProxy .cxx_destruct]

One of our projects and do not have this method, even if we have written to the order, the error will not be simply ignored. So do not worry about the wrong situation and problems arise.

-[liujilou hello]

Extended:

See symbol other methods nm (loose, not entirely in accordance with the order of arrangement of the address)
 terminal:

//打开自己APP 目录,并不是让输入  ../
 cd  ... /filedome.app
 nm filedome
 nm -p filedome  书序排列,但是也不是完全按照启动顺序排列的
 nm -u filedome  只看系统的方法等
 nm -U filedome  只看自定义的方法等

It is not allowed, so it is still subject to the link map.
 

To achieve true binary rearrangement, we need to get started all methods, functions and other symbols, and save the order, then the file is written order to achieve binary rearrangement. Vibrato an article, but it does not hook into all the specific article to see it.
 Vibrato research and development practice: based solution start APP speed binary rearranged to enhance the over 15%

of its ideas:
All OC process will go objc_msgsend () function of this system, then you can go with a fishhook hook objc_msgSend () you can get All OC method call, and then hook this inside out objc_msgsend () the second parameter (SEL) got all the symbols.
But this one has to do is pit because objc_msgSend () is a variable parameter, can only write the assembler to maintain balance through the stack register. If you do not know iOS ram compiled this can not in-depth to explore, and to learn about fishhook.
 And such not initialize hook
 portion block hook than
 C ++ does not come out through indirect function call static scan register.
 You can rearrangement 80% to 90%. Require 100% if this is not recommended.
 
 I want to completely hook to all the symbols we use


 clang Instrumentation

 It translates the code coverage tool
 is capable of detecting code for all custom projects among.
 1. Configure
 Build Settings -> Other C Flags Add

-fsanitize-coverage=trace-pc-guard


 
 Will then compile error (undefined symbols, but we do not hand out the project with these Yeah, it only shows that at some point must system call).

Undefined symbol: ___sanitizer_cov_trace_pc_guard_init
Undefined symbol: ___sanitizer_cov_trace_pc_guard

This is because you have added -fsanitize-coverage = trace-pc-guard, added by default ___sanitizer_cov_trace_pc_guard in all methods. Look clang of the document. Find these two functions to the project.

extern "C" void __sanitizer_cov_trace_pc_guard_init(uint32_t *start,
                                                    uint32_t *stop) {
  static uint64_t N;  // Counter for the guards.
  if (start == stop || *start) return;  // Initialize only once.
  printf("INIT: %p %p\n", start, stop);
  for (uint32_t *x = start; x < stop; x++)
    *x = ++N;  // Guards should start from 1.
}

extern "C" void __sanitizer_cov_trace_pc_guard(uint32_t *guard) {
  if (!*guard) return;  // Duplicate the guard check.
  // If you set *guard to 0 this code will not be called again for this edge.
  // Now you can get the PC and do whatever you want:
  //   store it somewhere or symbolize it and print right away.
  // The values of `*guard` are as you set them in
  // __sanitizer_cov_trace_pc_guard_init and so you can make them consecutive
  // and use them to dereference an array or a bit vector.
  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);
}

Of course __sanitizer_symbolize_pc () will error, and then continue the important first comment.


Now start the project, pause after startup is complete. Then print it

 INIT: 0x1029135e8 0x102913e70
 ......
 (lldb) x 0x1029135e8
 0x1029135e8: 01 00 00 00 02 00 00 00 03 00 00 00 04 00 00 00  ................
 0x1029135f8: 05 00 00 00 06 00 00 00 07 00 00 00 08 00 00 00  ................
 (lldb) x 0x102913e70
 0x102913e70: 98 cb 90 02 01 00 00 00 00 00 00 00 00 00 00 00  ................
 0x102913e80: ca 98 90 02 01 00 00 00 00 00 00 00 00 00 00 00  ................
 (lldb) x 0x102913e70-0x4
 0x102913e6c: 22 02 00 00 98 cb 90 02 01 00 00 00 00 00 00 00  "...............
 0x102913e7c: 00 00 00 00 ca 98 90 02 01 00 00 00 00 00 00 00  ................

The last print is the end position, the display is four by four, so moving forward 4, print out should be the last one.
Little-endian mode 22,020,000 is hexadecimal 222. The boot loader 546 decimal notation.
In viewController plus load method (not necessarily have to be a load, when the load starts to load, mainly to see if this is not the start I guess print out the number of symbols)

 INIT: 0x10286b610 0x10286be9c
 ......
 (lldb) x 0x10286b610
 0x10286b610: 01 00 00 00 02 00 00 00 03 00 00 00 04 00 00 00  ................
 0x10286b620: 05 00 00 00 06 00 00 00 07 00 00 00 08 00 00 00  ................
 (lldb) x 0x10286be9c
 0x10286be9c: 00 00 00 00 98 4b 86 02 01 00 00 00 00 00 00 00  .....K..........
 0x10286beac: 00 00 00 00 c7 18 86 02 01 00 00 00 00 00 00 00  ................
 (lldb) x 0x10286be9c-0x4
 0x10286be98: 23 02 00 00 00 00 00 00 98 4b 86 02 01 00 00 00  #........K......
 0x10286bea8: 00 00 00 00 00 00 00 00 c7 18 86 02 01 00 00 00  ................

Discovery has changed, and now is 23.02 million more than one.

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    test();
}

 + (void)initialize
 {
 
 }
 void(^block)(void) = ^(void) {
 
 };
 
 void test(){
     block();
 }

A point on the screen to print three times.

 guard: 0x10de29ae0 f5 PC (\3041
 guard: 0x10de29ae4 f6 PC \211\352:
 guard: 0x10de29ae8 f7 PC 0/\337\341\376

Use initialize, block, a function will increase, so this method may be able to hook all symbols.
 
 
 Then compile View
 
 

filedome`-[ViewController touchesBegan:withEvent:]:
.....
 0x10214cca4 <+40>:  bl     0x10214ce44               ; __sanitizer_cov_trace_pc_guard at ViewController.m:117
 0x10214cca8 <+44>:  ldr    x8, [sp, #0x28]

 bl compilation means jump instruction to jump to the address 0x10214ce44
 ; semicolon is meant an explanatory comment annotation content is the address of the first line in the 117 __sanitizer_cov_trace_pc_guard in the compilation ViewController.m

filedome`test:
 ->  0x100df0d2c <+0>:  stp    x29, x30, [sp, #-0x10]!
 0x100df0d30 <+4>:  mov    x29, sp
 0x100df0d34 <+8>:  adrp   x8, 23
 0x100df0d38 <+12>: add    x0, x8, #0x9d8            ; =0x9d8
 0x100df0d3c <+16>: bl     0x100df0e44               ; __sanitizer_cov_trace_pc_guard at ViewController.m:117
filedome`block1_block_invoke:
 0x102becd60 <+0>:  sub    sp, sp, #0x30             ; =0x30
 0x102becd64 <+4>:  stp    x29, x30, [sp, #0x20]
 0x102becd68 <+8>:  add    x29, sp, #0x20            ; =0x20
 0x102becd6c <+12>: adrp   x8, 23
 0x102becd70 <+16>: add    x8, x8, #0x9dc            ; =0x9dc
 0x102becd74 <+20>: str    x0, [sp, #0x8]
 0x102becd78 <+24>: mov    x0, x8
 0x102becd7c <+28>: bl     0x102bece44               ; __sanitizer_cov_trace_pc_guard at ViewController.m:117

In the test block are marked with a breakpoint, and then had lost one by one and found all jump up __sanitizer_cov_trace_pc_guard.
When we clang arranged such code is inserted to overlay. Instrumented be static, in the method, a function, an internal block included in the top line of code. At the edge of the insertion is to insert this line of code, new conduct hook.
 To determine the address of the Compendium, will save the address of the next function to be executed when the function is executed when a complete return to the x30 register.

#import "ViewController.h"
#import <dlfcn.h>
#import <libkern/OSAtomic.h>
#import "filedome-Swift.h"
void __sanitizer_cov_trace_pc_guard_init(uint32_t *start,
                                                    uint32_t *stop) {
    static uint64_t N;  // Counter for the guards.
    if (start == stop || *start) return;  // Initialize only once.
    printf("INIT: %p %p\n", start, stop);
    for (uint32_t *x = start; x < stop; x++)
        *x = ++N;  // Guards should start from 1.
}

After the need to save to get all of the symbols, but can not use the array, because there may be executed in the child thread, so there will be problems with the array 

//原子队列  1、先进后出  2、线程安全  3、只能保存结构体
static OSQueueHead symbolList = OS_ATOMIC_QUEUE_INIT;

//定义符号结构体
typedef struct {
    void *pc;
    void *next;//下一个数据的结构体指针地址
}SYNode;


void __sanitizer_cov_trace_pc_guard(uint32_t *guard) {
//    if (!*guard) return;  // Duplicate the guard check.
    
//    汇编一个函数执行完返回会将下一个要执行的函数地址给保存到 x30 寄存器中,将返回值给下一个函数。
//    所以这个函数的命名就是 返回 内存地址。 这里PC 就是拿到的内存地址
    void *PC = __builtin_return_address(0);
    
    SYNode * node = malloc(sizeof(SYNode));
    *node = (SYNode){PC,NULL};
//    进入 入栈
//    最后一个参数是下一个数据的位置,也就是往上边的*node 的第二位插入
//    offsetof(SYNode, next)  next 成员在 结构体 SYNode 中的偏移值
    OSAtomicEnqueue(&symbolList, node, offsetof(SYNode, next));
    
    
    
//    dlopen 通过动态库拿到句柄 通过句柄拿到函数的内存地址
//    dladdr 通过函数内存地址拿到函数
//    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;
//    Dl_info info;
//    dladdr(PC, &info);
//    printf("fnam:%s \n fbase:%p \n sname:%s \n saddr:%p \n",
//           info.dli_fname,
//           info.dli_fbase,
//           info.dli_sname,
//           info.dli_saddr);
//fnam:/Users/liujilou/Library/Developer/CoreSimulator/Devices/F8857F80-4697-496A-8FE8-255DA2056C17/data/Containers/Bundle/Application/B57CF494-5D7D-4044-87B4-949E971BD39D/filedome.app/filedome
//fbase:0x108a1d000
//sname:-[LGPerson setNum:]
//saddr:0x108a27ff0
//    拿到了全部的符号之后需要保存,但是不能用数组,因为有可能会有在子线程执行的,所以用数组会有问题
//    用原子队列 #import <libkern/OSAtomic.h>
}

After completion of the storage required to traverse removed, and then edit the order symbol table file.

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    NSMutableArray <NSString *>* symbolNames = [NSMutableArray array];
    while (YES) {
        SYNode * node = OSAtomicDequeue(&symbolList, offsetof(SYNode, next));
        if (node == NULL) {
            break;
        }
        Dl_info info;
        dladdr(node->pc, &info);
        
        NSString * name = @(info.dli_sname);
        BOOL isObjc = [name hasPrefix:@"+["] || [name hasPrefix:@"-["];
        NSString * symbolName = isObjc? name : [@"_" stringByAppendingString:name];//c函数前面带下划线
        [symbolNames addObject:symbolName];
        printf("%s \n",info.dli_sname);
    }
//    symbolNames = [[symbolNames reverseObjectEnumerator] allObjects];//取反
    NSEnumerator * emt = [symbolNames reverseObjectEnumerator];
    NSMutableArray<NSString*>* funcs = [NSMutableArray arrayWithCapacity:symbolNames.count];
    NSString * name;
    while (name = [emt nextObject]) {
        if (![funcs containsObject:name]) {
            [funcs addObject:name];
        }
    }
    //    删掉当前方法(因为这个点击方法不是必须的)
    [funcs removeObject:[NSString stringWithFormat:@"%s",__FUNCTION__]];
    
    NSString * funcStr = [funcs componentsJoinedByString:@"\n"];
    
    NSString * filePath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"liujilou.order"];
    NSData * fileContents = [funcStr dataUsingEncoding:NSUTF8StringEncoding];
//    在路径上创建文件
    [[NSFileManager defaultManager] createFileAtPath:filePath contents:fileContents attributes:nil];
    
    
    NSLog(@"%@",filePath);

//    1、压栈先进后出
//    2、获取的符号会有重复
//    3、load 的首位是0,所以要把if (!*guard) return; 注释掉。由此可以知道如果我们想过来某些函数,可以类似这样操作
}

This place has a hole

Direct while (YES) {} will be infinite loop, it has been in print

-[ViewController touchesBegan:withEvent:]
-[ViewController touchesBegan:withEvent:]
......

Break point by looking at the assembler view, you will find

- [ViewController touchesBegan: withEvent:] : call three times
__sanitizer_cov_trace_pc_guard
CBNZ x8,
this is our while loop, triggered __sanitizer_cov_trace_pc_guard.
If you have a break b will jump out, if the code does not continue to the top.
Cycle come
OSAtomicEnqueue (& symbolList, node, offsetof (SYNode, next));
adding a
SYNode * node = OSAtomicDequeue (& symbolList , offsetof (SYNode, next));
taken out will be at least one, and then enters plus 1, in at least one removed. It has been the cycle.
Solution Build -> Settings -> Other C Flags
-fsanitize-coverages = the trace-PC-Guard plus a func,

-fsanitize-coverage=func,trace-pc-guard

printf (); call is not going to jump into this, it is because () method is not our internal printf. Only inside our internal plus method

This allows the compiler by running the program, and then click on the screen will generate a liujilou.order files in a sandbox. Now export the file, the file before the project liujilou.order project replaced.

See real machine Xcode app sandbox content
to open the menu Window-> Devices And Simulators (shortcut shift + command + 2)
in the real machine connected to selected DEVICES


 


 Alternatively, after completion of re-compile command + B and then to take a look at the sequence

 # Symbols:
 # Address    Size        File  Name
 0x100005710    0x000000C8    [ 11] _main
 0x1000057D8    0x00000058    [ 23] -[AppDelegate window]
 0x100005830    0x00000070    [ 23] -[AppDelegate setWindow:]
 0x1000058A0    0x000001A0    [ 23] -[AppDelegate application:didFinishLaunchingWithOptions:]
 0x100005A40    0x00000090    [ 10] -[ViewController .cxx_destruct]
 0x100005AD0    0x0000066C    [ 10] -[ViewController viewDidLoad]
 0x10000613C    0x00000070    [ 10] -[ViewController setDataArr:]
 0x1000061AC    0x00000068    [ 12] -[LGPerson setNum:]
 0x100006214    0x00000068    [ 12] -[LGPerson setHeight:]
 0x10000627C    0x00000068    [ 12] -[LGPerson setAge:]
 0x1000062E4    0x00000074    [ 23] -[AppDelegate applicationDidBecomeActive:]

Rearrangement has been completed, we start sequence
 
 
 swift slightly different configuration
 for the test, following increasing Dome swift code.

//  SwiftTest.swift
//  filedome

//import Foundation
import UIKit
//简单点用UIKit
class SwiftTest: NSObject {
    @objc class public func swiftTestLoad(){
        print("swiftTest");
    }
}

ViewController import header file and call it

#import "filedome-Swift.h"

- (void)viewDidLoad {    
    [SwiftTest swiftTestLoad];
}

Build Setting -> Other Swift Flags (required project has swift codes, whether the person is less than the search)

-sanitize-coverage=func
-sanitize=undefined

Print look print run

-[ViewController touchesBegan:withEvent:]
 -[AppDelegate applicationDidBecomeActive:]
 -[AppDelegate window]
// 下面的这4个就是swift 的
 $ss5print_9separator10terminatoryypd_S2StFfA1_
 $ss5print_9separator10terminatoryypd_S2StFfA0_
 $s8filedome9SwiftTestC05swiftC4LoadyyFZ
 $s8filedome9SwiftTestC05swiftC4LoadyyFZTo
 -[LGPerson setAge:]
 -[LGPerson setHeight:]
 

After the last row of binary weight, generates the order file, the configuration of the link map. This time, even if our binary rearrangement rearrangement is completed, this time the need to delete the Other C Flags / Other Swift Flags configuration because this configuration is automatically inserted jump execution __sanitizer_cov_trace_pc_guard in our code. Heavy End row is not required, it is necessary to remove.
At the same time our ViewController in __sanitizer_cov_trace_pc_guard also want to get rid of.
 
 Optimized results:
 not very obvious, the main purpose is to optimize the project has been optimized when there is no more space can be optimized, while the project has very large when it is necessary to look at the binary rearrangement.

Published 83 original articles · won praise 12 · views 180 000 +

Guess you like

Origin blog.csdn.net/shengdaVolleyball/article/details/104984267