公司網(wǎng)站設(shè)計(jì)方案網(wǎng)站查詢是否安全
二進(jìn)制重排作用
??二進(jìn)制重排的主要目的是將連續(xù)調(diào)用的函數(shù)連接到相鄰的虛擬內(nèi)存地址,這樣在啟動時可以減少缺頁中斷的發(fā)生,提升啟動速度。目前網(wǎng)絡(luò)上關(guān)于ios應(yīng)用啟動優(yōu)化,通過XCode實(shí)現(xiàn)的版本比較多。MacOS上的應(yīng)用也是通過clang進(jìn)行編譯的,理論上也可以進(jìn)行二進(jìn)制重排,主要分為兩步。
??首先是獲取啟動過程調(diào)用的函數(shù)符號,需要通過clang插樁方式實(shí)現(xiàn),對于其它編譯器目前沒有找到類似的功能。
編譯選項(xiàng)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize-coverage=func,trace-pc-guard")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize-coverage=func,trace-pc-guard")
入口函數(shù)
??然后是入口函數(shù)實(shí)現(xiàn),收集調(diào)用函數(shù)符號序列,通過下面的代碼可以實(shí)現(xiàn)生成。
#ifndef APPCALLCOLLECTOR_H_
#define APPCALLCOLLECTOR_H_#import <Foundation/Foundation.h>//! Project version number for AppCallCollecter.
FOUNDATION_EXPORT double AppCallCollecterVersionNumber;//! Project version string for AppCallCollecter.
FOUNDATION_EXPORT const unsigned char AppCallCollecterVersionString[];/// 與CLRAppOrderFile只能二者用其一
extern NSArray <NSString *> *getAppCalls(void);/// 與getAppCalls只能二者用其一
extern void appOrderFile(NSString* orderFilePath);// In this header, you should import all the public headers of your framework using statements like #import <AppCallCollecter/PublicHeader.h>
#endif
#import "appcallcollector.h"
#import <dlfcn.h>
#import <libkern/OSAtomicQueue.h>
#import <pthread.h>static OSQueueHead qHead = OS_ATOMIC_QUEUE_INIT;
static BOOL stopCollecting = NO;typedef struct {void *pointer;void *next;
} PointerNode;// dyld鏈接dylib時調(diào)用,start和stop地址之間的保存該dylib的所有符號的個數(shù)
// 可以不實(shí)現(xiàn)具體內(nèi)容,不影響后續(xù)調(diào)用
extern "C" void __sanitizer_cov_trace_pc_guard_init(uint32_t *start,uint32_t *stop) {static uint32_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.printf("totasl count %i\n", N);
}// This callback is inserted by the compiler on every edge in the
// control flow (some optimizations apply).
// Typically, the compiler will emit the code like this:
// if(*guard)
// __sanitizer_cov_trace_pc_guard(guard);
// But for large functions it will emit a simple call:
// __sanitizer_cov_trace_pc_guard(guard);
/* 通過匯編可發(fā)現(xiàn),每個函數(shù)調(diào)用前都被插入了bl 0x102b188c0 ; symbol stub for: __sanitizer_cov_trace_pc_guard所以在每個函數(shù)調(diào)用時都會先跳轉(zhuǎn)執(zhí)行該函數(shù)
*/
extern "C" void __sanitizer_cov_trace_pc_guard(uint32_t *guard) {// If initialization has not occurred yet (meaning that guard is uninitialized), that means that initial functions like +load are being run. These functions will only be run once anyways, so we should always allow them to be recorded and ignore guard// +load方法先于guard_init調(diào)用,此時guard為0if(!*guard) { return; }if (stopCollecting) {return;}// __builtin_return_address 獲取當(dāng)前調(diào)用棧信息,取第一幀地址(即下條要執(zhí)行的指令地址,被插樁的函數(shù)地址)void *PC = __builtin_return_address(0);PointerNode *node = (PointerNode *)malloc(sizeof(PointerNode));*node = (PointerNode){PC, NULL};// 使用原子隊(duì)列要存儲幀地址OSAtomicEnqueue(&qHead, node, offsetof(PointerNode, next));
}extern NSArray <NSString *> *getAllFunctions(NSString *currentFuncName) {NSMutableSet<NSString *> *unqSet = [NSMutableSet setWithObject:currentFuncName];NSMutableArray <NSString *> *functions = [NSMutableArray array];while (YES) {PointerNode *front = (PointerNode *)OSAtomicDequeue(&qHead, offsetof(PointerNode, next));if(front == NULL) {break;}Dl_info info = {0};// dladdr獲取地址符號信息dladdr(front->pointer, &info);NSString *name = @(info.dli_sname);// 去除重復(fù)調(diào)用if([unqSet containsObject:name]) {continue;}BOOL isObjc = [name hasPrefix:@"+["] || [name hasPrefix:@"-["];// order文件格式要求C函數(shù)和block前需要添加_NSString *symbolName = isObjc ? name : [@"_" stringByAppendingString:name];[unqSet addObject:name];[functions addObject:symbolName];}// 取反得到正確調(diào)用排序return [[functions reverseObjectEnumerator] allObjects];;
}#pragma mark - publicextern NSArray <NSString *> *getAppCalls(void) {stopCollecting = YES;// 內(nèi)存屏障,防止cpu的亂序執(zhí)行調(diào)度內(nèi)存(原子鎖)__sync_synchronize();NSString* curFuncationName = [NSString stringWithUTF8String:__FUNCTION__];return getAllFunctions(curFuncationName);
}extern void appOrderFile(NSString* orderFilePath) {stopCollecting = YES;__sync_synchronize();NSString* curFuncationName = [NSString stringWithUTF8String:__FUNCTION__];NSArray *functions = getAllFunctions(curFuncationName);NSString *orderFileContent = [functions.reverseObjectEnumerator.allObjects componentsJoinedByString:@"\n"];NSLog(@"[orderFile]: %@",orderFileContent);NSString *filePath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"orderFile.order"];NSData * fileContents = [orderFileContent dataUsingEncoding:NSUTF8StringEncoding];// NSArray *functions = getAllFunctions(curFuncationName);// NSString * funcString = [symbolAry componentsJoinedByString:@"\n"];// NSString * filePath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"lb.order"];// NSData * fileContents = [funcString dataUsingEncoding:NSUTF8StringEncoding];BOOL result = [[NSFileManager defaultManager] createFileAtPath:filePath contents:fileContents attributes:nil];if (result) {NSLog(@"%@",filePath);}else{NSLog(@"文件寫入出錯");}
}
鏈接器配置
??拿到函數(shù)符號列表后,需要通過鏈接選項(xiàng)將列表文件傳遞給鏈接器,也可以通過鏈接選項(xiàng)輸出link map,查看重排前后的符號順序。
-order_file_statistics
??Logs information about the processing of a -order_file.
-map map_file_path
??Writes a map file to the specified path which details all symbols and their addresses in the output image.
-order_file file
??Alters the order in which functions and data are laid out. For each section in the outputfile, any symbol in that section that are specified in the order file file is moved to the start of its section and laid out in the same order as in the order file file. Order files are text files with one symbol name per line. Lines starting with a # are comments. A symbol name may be optionally preceded with its object file leaf name and a colon (e.g. foo.o:_foo). This is useful for static functions/data that occur in multiple files. A symbol name may also be optionally preceded with the architecture (e.g. ppc:_foo or ppc:foo.o:_foo). This enables you to have one order file that works for multiple architec-tures. Literal c-strings may be ordered by by quoting the string (e.g. “Hello, world\n”) in the order file.
可執(zhí)行程序模塊重排
set(CMAKE_CXX_LINK_FLAGS "-Xlinker -map -Xlinker /Users/Desktop/out/out001.txt -Xlinker -order_file_statistics -Xlinker -order_file -Xlinker /Users/Desktop/out/orderFile_cpp.order ${CMAKE_CXX_LINK_FLAGS}")
動態(tài)庫重排
set(CMAKE_SHARED_LINKER_FLAGS "-Xlinker -map -Xlinker /Users/Desktop/out/out002.txt -Xlinker -order_file_statistics -Xlinker -order_file -Xlinker /Users/Desktop/out/orderFile_add.order ${CMAKE_SHARED_LINKER_FLAGS}")