iOS 啟動優(yōu)化(三)-編譯器插樁篇

啟動優(yōu)化(一)-理論篇
啟動優(yōu)化(二)-二進制重排篇
啟動優(yōu)化(三)-編譯期插樁篇
啟動優(yōu)化(四)-生成 Order File

學前小知識

相關文獻:
clang.llvm.org

Tracing PCs是用來跟蹤cpu將要執(zhí)行的指令代碼。

工程中配置使用它-fsanitized-coverage=trace-pc-guard,編譯器會在每個代碼邊緣插入以下代碼: __sanitizer_cov_trace_pc_guard(&guard_variable)。

在工程上配置了-fsanitized-coverage=trace-pc-guard后再編譯工程就會報錯,需要定義兩個函數(shù)__sanitizer_cov_trace_pc_guard_init__sanitizer_cov_trace_pc_guard。
這是編譯器插入代碼的需要回調(diào)執(zhí)行的部分。這兩個函數(shù)的實現(xiàn)有一個案例了

可以自行查看這個編譯器文檔clang.llvm.org

概念

編譯器插樁就是在代碼編譯期間修改已有的代碼或生成新代碼。
編譯期時,在每一個函數(shù)內(nèi)部二進制源數(shù)據(jù)添加 hook 代碼來實現(xiàn)全局 hook 效果。

具體實現(xiàn)

新建空工程Test

添加配置 Target -> Build Setting -> Custom Complier Flags -> Other C Flags 添加 fsanitize-coverage=func,trace-pc-guard

配置.png

ViewController.m文件中添加代碼

#include <stdint.h>
#include <stdio.h>
#include <sanitizer/coverage_interface.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.
}

void __sanitizer_cov_trace_pc_guard(uint32_t *guard) {
  if (!*guard) return;
  void *PC = __builtin_return_address(0);
  char PcDescr[1024];
  printf("guard: %p %x PC %s\n", guard, *guard, PcDescr);
}

工程跑起來看輸出結果:

  • __sanitizer_cov_trace_pc_guard_init

start 里面存的是一堆序號,而stop 里面的并不是序號

打個斷點重新運行發(fā)現(xiàn) start 和 stop 中間是 01 ~ 0e,十進制的 1~14。

說明我們空項目里有14個符號。

這會不會是函數(shù)的序號呢?給他安排個方法touchBegin試試看:
__sanitizer_cov_trace_pc_guard_init最后添加打印N
NSLog(@"%llu", N);

注意看14+1變成了15!?。?br> 再添加一個block和一個C函數(shù)

15+2變成了17。驗證這是函數(shù)的序號。
再安排一個swfit 函數(shù)呢?

import UIKit

@objc class SwiftTest: NSObject {
    @objc class public func swiftTest() {
        print("SwiftTestObject打?。簍est")
    }
}

配置Target -> Build Setting -> Custom Complier Flags -> Other Swift Flags 添加:

-sanitize-coverage=func
-sanitize=undefined
image.png

在ViewController.m導入swift #import "Test-Swift.h"
數(shù)量一下子增加到了0x21,因生成文件同時生成了不少自帶方法。

__sanitizer_cov_trace_pc_guard_init 函數(shù)里可以獲取到整個工程里所有函數(shù)/方法的數(shù)量。

那么肯定也有辦法獲取方法具體的相關信息。
重點就是接下來要分析的__sanitizer_cov_trace_pc_guard

  • __sanitizer_cov_trace_pc_guard

[ViewController touchesBegan:withEvent:] 斷點運行后,點擊屏幕查看:

image.png

可以看到在調(diào)用方法的時候插入了__sanitizer_cov_trace_pc_guard 函數(shù)。
所以在系統(tǒng)調(diào)用每一個函數(shù)/方法之前都會先走__sanitizer_cov_trace_pc_guard 函數(shù)。
而把所有函數(shù)/方法之前插入這個函數(shù)做這件事最合適的就是編譯器啦。

__sanitizer_cov_trace_pc_guard 函數(shù)里面拿到 原函數(shù)/原方法 的地址!

獲取方法符號
#import <dlfcn.h>

dlfcn.h 中有一個 dladdr() 方法,可以通過函數(shù)內(nèi)部地址找到函數(shù)符號。
該方法需要用到結構體Dl_info

image.png

獲取Dl_info

void __sanitizer_cov_trace_pc_guard(uint32_t *guard) {
    if (!*guard) return;
    void *PC = __builtin_return_address(0);
    
    Dl_info info;
    dladdr(PC, &info);
    printf("fname=%s \n fbase=%p \n sname=%s \n saddr=%p \n",
           info.dli_fname, // 文件路徑
           info.dli_fbase, // 文件地址
           info.dli_sname, // 符號
           info.dli_saddr); // 符號地址
}

打印結果:

通過編譯器插樁獲取了方法符號,而且順序正是調(diào)用函數(shù)的順序!

那下一步我們可以通過獲取的方法符號,寫入link.order動態(tài)生成link.order

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

友情鏈接更多精彩內(nèi)容