This is the 10th day of my participation in the August More Text Challenge. For details, see:August is more challenging

Hi 👋

  • Wechat: RyukieW
  • 📦 Technical article archive
  • 🐙 making
My personal project Mine Elic endless sky ladder Dream of books
type The game financial
AppStore Elic Umemi

preface

Binary rearrangement has been mentioned a lot in startup optimizations for the past two years. Although it sounds great, it’s actually an old concept.

After the last “iOS official slim ODR (2) : Skin system transformation | practice of on-demand Resources”, I once again took my own personal project mice “dream account” to practice.

Why binary rearrangement?

1.1 Virtual memory and paging

As we know, modern operating systems generally use virtual memory management mechanism, using segments and pages to manage virtual memory.

Segmentation is to distinguish data segments, code segments, heap memory, stack memory, etc. Different segments of data have different read and write permissions. In iOS, for example, the code snippet (_TEXT) is readable and executable but not writable.

Paging is for easy and efficient memory management. Because of the virtual memory management mechanism, it is necessary to establish a virtual memory to physical memory mapping table, called page table. If each byte of virtual memory is designed to correspond to physical memory one by one, the granularity is fine enough to not waste memory (memory fragmentation), but to maintain a large page table; But if a page of data is too large, say 5M, then storing one byte will allocate a 5M page, which is a huge waste. There are drawbacks to having a memory page that is either too large or too small; most systems today have a page size of 4096 bytes, addressed by a page number and an in-page offset. You can use the pagesize command to view the pagesize of the current system.

1.2 a Page Fault

One of the purposes of using virtual memory is to address the problem of tight physical memory resources. When dyld loads the binary, it uses mmap to map the Mach-o file into the virtual memory address space without using too much physical memory. When reading a virtual memory address that does not exist in physical memory, a Page Fault is triggered and the contents of the file are read into physical memory.

When a page missing interrupt occurs, do the following:

Allocate memory

Free memory is found and allocated by the memory management unit.

IO operations

Read a file from disk and write it to memory.

Decryption attestation

If the APP is downloaded from the AppStore, iOS also decrypts and verifies the signature of each page (only for _TEXT data, _DATA data is not required).

The above operations will occur in each Page Fault. If a large number of Page faults exist during APP startup, the startup speed will be affected.

What is binary rearrangement

Frequent Page faults can affect startup speed, so is it possible to interfere with the mapping order of mach-O’s _TEXT segment functions to concentrate the methods needed for APP startup on one Page or several pages? The answer is yes, the principle of binary rearrangement is literally understood, reducing startup time by reducing the number of Page faults.

In theory, Page Fault does affect startup speed, but the size of the effect should be treated separately. In general, the binary rearrangement is considered after the normal optimization methods have been done. And for small apps, if there aren’t many methods to execute at startup, then binary reordering doesn’t make much sense.

For iOS 13, with dyLD3 enabled, there is no need to perform a decrypt check when a Page Fault occurs (lauch Closure files are generated in advance) and the performance impact is even less.

3. System Trace viewing time

You are advised to reinstall the application

  • Select the specified device, select the installed App and click[*]Button, apply the first page (not the start page) to display after stopping.
  • Find your own project
  • The selectedMain Thread
  • The selectedVirtual Memory

The number of File Backed Page In is the number of Page Fault. “Dream ledger” takes 341ms.

Click the little arrow here to see the call stack

Of course, we can’t do this manually. So what’s the way to get all the calls?

Get all methods called at startup

Comparison of existing schemes

  • hook objc_msgSend
    • Can only capture based onobjcMethod call
  • Statically scan MachO files for symbols and function data + parse MachO Trace files
    • Easy to get+load,C++ constructors
    • Can’t initialize the hooks
    • Some block hooks fail
    • C++ through the register indirect function call static scan does not show
  • Compiler pilingClang
    • You can getOC,Swift,C,blockAll calls

Five, Clang pile insertion

5.1 Scheme based on Clang SantizerCoverage

SanitizerCoverage is a code coverage tool built into Clang. It implements global AOP by inserting a series of function calls prefixed with __sanitizer_cov_trace_pc_ into user-defined functions. / Swift/Objective-C/C/C /C++, Method/Function/Block support.

The way to start SanitizerCoverage is:

  • inbuild settingsIn theOther C Flagsadd-fsanitize-coverage=func,trace-pc-guard
  • If they containSwiftcode
    • Need to be inOther Swift Flagsadd-sanitize-coverage=func-sanitize=undefined
  • All links toAppBoth binaries need to be turned onSanitizerCoverageIn order to fully cover all calls
    • The Pod library, for example, is set in Target

You can see the LLVM official detailed introduction to Sanitizers Coverage, including sample code.

5.2 Obtaining the Order File

Here we use AppOrderFiles directly to obtain. Do not post the code, the source code is not much, interested can be viewed by themselves.

In the AppDelegate call:

/ / I am on ` func application (_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool 'last called
AppOrderFiles { path in
    if let path = path { print("AppOrderFiles: \(path)")}}Copy the code

After the installation, obtain the app.order file from the.xcappdata file of the device where the application is obtained by Xcode according to the path.

5.3 Setting the Order File Path

There’s no need to join the Bundle

5.4 Verification Sequence LinkMap

openLinkMapThe output file

Compiled forLinkMap

Start by finding the.app directory in the project’s Product folder

Click the path shown in the figure to find the linkMap file

contrastlinkmaporderfile

Search for the Address Size File Name

It turns out the order is the same

5.5 System Trace Check Effect

It’s only 141ms. It’s more than half optimized. The specific effect will vary according to different projects.

reference

  • App binary reordering has been broken
  • SanitizerCoverage
  • AppOrderFiles
  • Improving App Performance with Order Files
  • Douyin DEVELOPMENT practice: Based on binary file rearrangement solution APP startup speed increased by more than 15%
  • Binary reordering for iOS startup optimization