iOS compilation process principle (2)

I. Introduction

"Principle and iOS application build process" article introduces the basics of iOS and compile a simple application, but also very many questions have no clear explanation:

  • What Clang and LLVM what is
  • Details of the source file to machine code
  • Linker what had been done
  • Order to determine how to compile
  • What header files? XCode is how to find the header files?
  • Clang Module
  • What signature? Why signature

To understand these issues, we have to dig down details XCode compiler iOS applications.

Second, the compiler

To a programming language (original language) into another programming language (the target language) program called a compiler .

Most compilers consists of two parts: a front end and a rear end .

  • The front end lexical analysis, parsing, generating an intermediate code;
  • In the rear end of the intermediate code as input, architecture-independent code optimization, then generates machine code different for different architectures.

Front and rear ends dependent uniform format intermediate code (the IR) , such that the front and rear ends can vary independently. Add a language only need to modify the front end, and a new CPU architecture only need to modify the back-end.

The compiler front end of Objective-C / C / C ++ using Clang , SWIFT is SWIFT , the rear end is the LLVM .

![19](https://upload-images.jianshu.io/upload_images/5294842-dd65ea5de43d8fc1.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

Three, LLVM

LLVM (Low Level Virtual Machine) is a powerful compiler suite of development tools, sounds like a virtual machine, the virtual machine is not actually the relationship between LLVM and the traditional sense, but the original name LLVM project is nothing.

LLVM core library offers modern source-target-independent optimizers and supports many popular CPU architecture code generator, which is the core code around the LLVM IR (intermediate code) created.

Based on LLVM has spawned a number of powerful sub-projects, which iOS developer is familiar: Clang and LLDB.

Four, clang

clang compiler front-end C language family, born at the beginning as a replacement for GCC, providing faster compilation speed. Learn clang compiled a map of the general process:

![20](https://upload-images.jianshu.io/upload_images/5294842-867e93a43ad184f9.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

Next, look at the specific level from the code conversion process, a new main.c:

#include <stdio.h>
// 注释
#define DEBUG 1
int main() {
#ifdef DEBUG
  printf("hello debug\n");
#else
  printf("hello world\n");
#endif
  return 0;
}

V. pretreatment (Preprocessor)

Will be introduced into the pre-header file, macro substitution, annotation processing, conditional compilation (#ifdef) and other operations.

#include "stdio.h" is to tell the preprocessor to replace the contents of this line in the header file stdio.h, this process is recursive: because stdio.h also may contain other header files.

View Results pretreated with a clang:

$ xcrun clang -E main.c

There are a lot of files preprocessed line, at the end of the file, you can find the main function.

$ xcrun clang -E main.c

...

...

extern int __vsnprintf_chk (char * restrict, size_t, int, size_t,
       const char * restrict, va_list);
# 412 "/usr/include/stdio.h" 2 3 4
# 10 "main.c" 2




int main() {

    printf("hello debug\n");



    return 0;
}

You can see that in the pretreatment comments are deleted, conditional compilation is processed.

Sixth, lexical analysis (lexical anaysis)

Lexical analyzer read source file character stream , to organize them into meaningful morphemes (lexeme) sequence , for each morpheme, lexical analyzer generates lexical unit (token) as output.

$ xcrun clang -fmodules -fsyntax-only -Xclang -dump-tokens main.c

Output:

$ xcrun clang -fmodules -fsyntax-only -Xclang -dump-tokens main.c
annot\_module\_include '#include <stdio.h>

// 一点注释

#define DEBUG 1
int main() {
#ifdef DEBUG
    printf("hello debug\\n");
#else
    printf'     Loc=<main.c:9:1>
int 'int'    \[StartOfLine\]    Loc=<main.c:14:1>
identifier 'main'    \[LeadingSpace\]   Loc=<main.c:14:5>
l_paren '('     Loc=<main.c:14:9>
r_paren ')'     Loc=<main.c:14:10>
l_brace '{'  \[LeadingSpace\]   Loc=<main.c:14:12>
identifier 'printf'  \[StartOfLine\] \[LeadingSpace\]   Loc=<main.c:16:5>
l_paren '('     Loc=<main.c:16:11>
string_literal '"hello debug\\n"'       Loc=<main.c:16:12>
r_paren ')'     Loc=<main.c:16:27>
semi ';'        Loc=<main.c:16:28>
return 'return'  \[StartOfLine\] \[LeadingSpace\]   Loc=<main.c:20:5>
numeric_constant '0'     \[LeadingSpace\]   Loc=<main.c:20:12>
semi ';'        Loc=<main.c:20:13>
r_brace '}'  \[StartOfLine\]    Loc=<main.c:21:1>
eof ''      Loc=<main.c:21:2>

Loc = <main.c: 9: 1> 9 token marking the line in the source file main.c, starting from the first character. Save token position in the source file is to facilitate the subsequent analysis clang of time to find the original location of the error.

Seven, syntax analysis (semantic analysis)

Token lexical analysis of stream will be parsed into an abstract syntax tree (abstract syntax Tree - AST) .

$ xcrun clang -fsyntax-only -Xclang -ast-dump main.c | open -f

AST main function of the structure:

[0;1;32mTranslationUnitDecl 0x7fd9a18166e8 <<invalid sloc>> <invalid sloc>
|-TypedefDecl 0x7fd9a1816c60 <<invalid sloc>> <invalid sloc> implicit __int128_t '__int128'
| `-BuiltinType 0x7fd9a1816980 '__int128'
|-TypedefDecl 0x7fd9a1816cd0 <<invalid sloc>> <invalid sloc> implicit __uint128_t 'unsigned __int128'
| `-BuiltinType 0x7fd9a18169a0 'unsigned __int128'
|-TypedefDecl 0x7fd9a1816fa8 <<invalid sloc>> <invalid sloc> implicit __NSConstantString 'struct __NSConstantString_tag'
| `-RecordType 0x7fd9a1816db0 'struct __NSConstantString_tag'
|   `-Record 0x7fd9a1816d28 '__NSConstantString_tag'
|-TypedefDecl 0x7fd9a1817040 <<invalid sloc>> <invalid sloc> implicit __builtin_ms_va_list 'char *'
| `-PointerType 0x7fd9a1817000 'char *'
|   `-BuiltinType 0x7fd9a1816780 'char'
|-TypedefDecl 0x7fd9a1817308 <<invalid sloc>> <invalid sloc> implicit referenced __builtin_va_list 'struct __va_list_tag [1]'
| `-ConstantArrayType 0x7fd9a18172b0 'struct 

...

With the abstract syntax tree, clang can be analyzed for this tree to find errors in the code. Such as type mismatches, Objective-C, or will send a message to the unimplemented target.

AST is to write the main developer of interactive clang plug data structure , clang also offers many API to read AST. More details: Introduction to AST at The Clang .

Eight, CodeGen

CodeGen syntax tree traversal, generate LLVM IR codes . LLVM IR output, the input end of the rear end.

xcrun clang -S -emit-llvm main.c -o main.ll

main.ll contents of the file:

; ModuleID = 'main.c'
source_filename = "main.c"
target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-apple-macosx10.13.0"

@.str = private unnamed_addr constant \[13 x i8\] c"hello debug\\0A\\00", align 1

; Function Attrs: noinline nounwind optnone ssp uwtable
define i32 @main() #0 {
  %1 = alloca i32, align 4
  store i32 0, i32* %1, align 4
  %2 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds (\[13 x i8\], \[13 x i8\]* @.str, i32 0, i32 0))
  ret i32 0
}

...

Objective-C code in this step will be the bridge of the runtime: property synthesis, ARC processing.

LLVM IR generated will be optimized, the optimization will call the appropriate Pass processed. Pass by a plurality of nodes, all Pass subclass of the class, each node is responsible to do specific optimizations, more details: Writing AN LLVM Pass .

Nine, to generate assembly code

After LLVM IR for optimized object code will generate different for different architectures, the final output format in assembly code.

Generating arm 64 Assembly:

$ xcrun clang -S main.c -o main.s

Main.s view the generated files. Compilation of students interested can take a look at this article: iOS Quick Start compilation .

    .section    __TEXT,__text,regular,pure_instructions
    .macosx_version_min 10, 13
    .globl  _main                   ## -- Begin function main
    .p2align    4, 0x90
_main:                                  ## @main
    .cfi_startproc
## %bb.0:
    pushq   %rbp
    .cfi_def_cfa_offset 16
    .cfi_offset %rbp, -16
    movq    %rsp, %rbp
    .cfi_def_cfa_register %rbp
    subq    $16, %rsp
    leaq    L_.str(%rip), %rdi
    movl    $0, -4(%rbp)
    movb    $0, %al
    callq   _printf
    xorl    %ecx, %ecx
    movl    %eax, -8(%rbp)          ## 4-byte Spill
    movl    %ecx, %eax
    addq    $16, %rsp
    popq    %rbp
    retq
    .cfi_endproc
                                        ## -- End function
    .section    __TEXT,__cstring,cstring_literals
L_.str:                                 ## @.str
    .asciz  "hello debugn"


.subsections_via_symbols

Ten, assembler

Assembler as input to assembly code, assembly code into machine code, the final output object file (object file).

$ xcrun clang -fmodules -c main.c -o main.o

Remember the code calls a function printf it? By nm command to view the next symbol in main.o

$ xcrun nm -nm main.o
                 (undefined) external _printf
0000000000000000 (\_\_TEXT,\_\_text) external _main

_printf of an undefined external. undefined symbol _printf represents yet to find in the current file, while external symbol indicates that this is an external accessible, private correspondence indicates that the file is a symbol of non-external.

What is the sign (Symbols)?

Symbolic name is a pointer to a piece of code or data. There is also a known WeakSymols, that is not necessarily the sign of existence, need to decide at runtime. For example iOS12 unique API, on iOS11 no.

XI Links

Connector to compile and generate a .o file (dylib, a, tbd) file, generates a file mach-o.

$ xcrun clang main.o -o main

We get a mach executable file format o

$ file main
main: Mach-O 64-bit executable x86_64
$ ./main
hello debug

Then the nm command to view the symbol table of the executable file:

$ nm -nm main
                 (undefined) external _printf (from libSystem)
                 (undefined) external dyld\_stub\_binder (from libSystem)
0000000100000000 (\_\_TEXT,\_\_text) \[referenced dynamically\] external \_\_mh\_execute_header
0000000100000f60 (\_\_TEXT,\_\_text) external _main

_printf is still undefined, but later some more information: from libSystem, meaning that the symbol from libSystem, will be dynamic binding at runtime.

Twelve, XCode compiler

From the foregoing we probably understand the Clang compile a file of C language process, but XCode project includes not only the development of the code file, also includes pictures, plist and so on. XCode compiler to go through the process of what it once?

Create a single page of Demo project: CocoaPods dependent AFNetworking and SDWebImage, while relying on an internal Framework. Press Command + B, in the Report Navigator XCode module can find detailed log compilation:

![21](https://upload-images.jianshu.io/upload_images/5294842-850e886824831a69.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

Detailed steps:

  • Create a folder Product.app
  • Information (such as application-identifier) ​​is written to the Entitlements.plist DerivedData, the packaging process when needed.
  • Create some auxiliary files, such as various .hmap, which is headermap file, the following will explain the specific role.
  • Compiled before the script execution CocoaPods: Check Manifest.lock file.
  • Compile .m files, .o files are generated.
  • Dynamic link library. .o file, generate a mach o executable file format.
  • Compile assets, compile storyboard, storyboard link
  • Copy dynamic library Logger.framework, and its signature
  • After executing CocoaPods compiled script: copy CocoaPods Target generated Framework
  • For Demo.App signature and verify (validate)
  • Generated Product.app
  • Generate dYSM file

Entitlements.plist saved App requires the use of special privileges, such as iCloud, remote notification, Siri and so on.

XIII compilation order

Compile time there are a lot of Task (task) is to execute, how XCode decide execution order Task it?

The answer is: dependency .

Or in the Demo project just as an example, the entire dependency as follows:

![22](https://upload-images.jianshu.io/upload_images/5294842-9b0c7342fdc5b545.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

You can see the order of Target compiled from the Report Navigator XCode:

![23](https://upload-images.jianshu.io/upload_images/5294842-1c02d00b99428ab4.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

XCode compiler will use when possible performance multi-core, multi-Target concurrent compilation.

So, XCode and from where to get these dependencies it?

  • Target Dependencies - explicitly declared dependencies
  • Linked Frameworks and Libraries - implicit declaration of dependence
  • Build Phase - defines each step of compiling a Target

Fourth, incremental compilation

Daily development, a complete compilation may take a few minutes or even tens of minutes, while the incremental compilation takes less than one minute, why would the incremental compiler so fast?

Because each Task XCode will generate a hash value, only time will change the hash value recompiled.

For example, modify the ViewControler.m, only the figure of three gray Task will re-execute (not considered here build phase scripts).

![24](https://upload-images.jianshu.io/upload_images/5294842-0eaaea9ee243f79c.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

Fifth, the header file

C family of languages, the header file (.h) files used to introduce a function / class / macro definition statement, etc., allowing developers to more flexible organizational code without having to put all the code written to a file.

Header files for the compiler is a promise. Header file declaration, the compiler will think there is a corresponding implementation, the link time to resolve the location of a particular implementation.

![25](https://upload-images.jianshu.io/upload_images/5294842-b5be48b5d1a1c97d.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

When only a statement, did not materialize, the linker reports an error.

Undefined symbols for architecture arm64:
“_umimplementMethod”, referenced from:
-\[ClassA method\] in ClassA.o
ld: symbol(s) not found for architecture arm64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

Objective-C method of error will be when to run, because Objective-C is a dynamic language, the compiler can not determine the corresponding method name (SEL) at runtime in the end there is no realization (IMP).

Daily development, two common ways to introduce the header file:

#include "CustomClass.h" // 自定义
#include <Foundation/Foundation.h> // 系统或者内部 framework

When introduced does not identify a specific path to the file, the compiler is how to find these header files do?

Back to the Report Navigator XCode to find on a compilation record, you can see the specific log compiled ViewController.m of:

![27](https://upload-images.jianshu.io/upload_images/5294842-d620556105843b4f.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) ![26](https://upload-images.jianshu.io/upload_images/5294842-d1cd2dd2e5bb8a0e.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

This whole logs copied to the command line, and then finally add -v, that we want to get more log information, execute this code, the log can finally see how clang find header file:

#include "..." search starts here:
 /Users/.../Build/Intermediates.noindex/Demo.build/Debug-iphoneos/Demo.build/Demo-generated-files.hmap (headermap)
 /Users/.../Build/Intermediates.noindex/Demo.build/Debug-iphoneos/Demo.build/Demo-project-headers.hmap (headermap)
 /Users/.../Build/Products/Debug-iphoneos/AFNetworking/AFNetworking.framework/Headers
 /Users/.../Build/Products/Debug-iphoneos/SDWebImage/SDWebImage.framework/Headers
 
#include <...> search starts here:
 /Users/.../Build/Intermediates.noindex/Demo.build/Debug-iphoneos/Demo.build/Demo-own-target-headers.hmap (headermap)
 /Users/.../Build/Intermediates.noindex/Demo.build/Debug-iphoneos/Demo.build/Demo-all-non-framework-target-headers.hmap (headermap)
 /Users/.../Build/Intermediates.noindex/Demo.build/Debug-iphoneos/Demo.build/DerivedSources
 /Users/.../Build/Products/Debug-iphoneos (framework directory)
 /Users/.../Build/Products/Debug-iphoneos/AFNetworking (framework directory)
 /Users/.../Build/Products/Debug-iphoneos/SDWebImage (framework directory)
 /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/10.0.0/include
 /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include
 $SDKROOT/usr/include
 $SDKROOT/System/Library/Frameworks (framework directory)
 
End of search list.

Here's a file type called heademap, headermap is to help compiler finds auxiliary header file: file to store his head mapping between the physical path.

Through an auxiliary gadget hmap view the contents of hmap:

$ ./hmap print Demo-project-headers.hmap 
AppDelegate.h -> /Users/huangwenchen/Desktop/Demo/Demo/AppDelegate.h
Demo-Bridging-Header.h -> /Users/huangwenchen/Desktop/Demo/Demo/Demo-Bridging-Header.h
Dummy.h -> /Users/huangwenchen/Desktop/Demo/Framework/Dummy.h
Framework.h -> Framework/Framework.h
TestView.h -> /Users/huangwenchen/Desktop/Demo/Demo/View/TestView.h
ViewController.h -> /Users/huangwenchen/Desktop/Demo/Demo/ViewController.h

That is why backup / restore Mac, need to clean build folder, because the physical location of the corresponding two mac file may be different.

clang found #import "TestView.h" when the first look at headermap (Demo-generated-files.hmap, Demo-project-headers.hmap), if headermap file is not found, then look in the framework own target's :

/Users/.../Build/Products/Debug-iphoneos/AFNetworking/AFNetworking.framework/Headers/TestView.h
/Users/.../Build/Products/Debug-iphoneos/SDWebImage/SDWebImage.framework/Headers/TestView.h

When looking at the header file system is also a priority headermap, headermap not find looks for own target framework, and finally find the SDK directory.

To #import <Foundation / Foundation.h>, for example, when looking at the SDK directory:

  1. First, look for the existence of framework

    $SDKROOT/System/Library/Frameworks/Foundation.framework
  2. If a framework exists, and then to find out whether there is a header file in the directory headers

    $SDKROOT/System/Library/Frameworks/Foundation.framework/headers/Foundation.h

Sixteen, Clang Module

Traditional # include / # import are the semantics of the text: preprocessor replaced when processing this line will correspond to the text of the file header, which is simple and crude Alternatively lot of problems:

  1. Consumption of large amount of the pretreatment. If there are N header file, and the file header for each of M #include header file, then the entire pre-consumption is N * M.
  2. After the file is imported, macro definitions prone to problems. Because it is text import, and in turn replaced according to include, when a header file defines #define std hello_world, and another header file is just standard C ++ libraries, include different order may result in all of std will be replaced.
  3. Obvious boundary. .A group and get a .h file, it is difficult to determine which .a .h belonging, the need to import what order to compile correctly.

clang module is no longer used the text model, instead of using more efficient semantic model. clang module provides a new way of introduction: @ import, module will be compiled as a separate module, and generates independent cache to dramatically improve the efficiency of the pretreatment, so that the time consumed from M * N becomes M + N.

XCode is created Target Framework of time, the default settings will define module to YES, to support module, of course, like framwork Foundation and other systems also support module.

#import <Foundation / NSString.h>, the compiler will check whether NSString.h in a module, if that is the case, this line will be replaced with @import Foundation.

![28](https://upload-images.jianshu.io/upload_images/5294842-f9261463ed11b9e9.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

So, how to define a module it? The answer is: modulemap file that describes how to convert a set of header files into a module, for example:

framework module Foundation  [extern_c] [system] {
    umbrella header "Foundation.h" // 所有要暴露的头文件
    export *
    module * {
        export *
    }
    explicit module NSDebug { //submodule
        header "NSDebug.h"
        export *
    }
 }

swift can directly import a clang module, such as you have some C libraries need to use Swift, you can use modulemap way.

Seventeen, Swift compiled

The modern language almost abandoned the header file, swift no exception. The question is, swift header file is not a statement of how to find it?

The compiler did the dirty work. Swift compile a header file, you need to resolve all of Swift module file, locate the corresponding statement.

![29](https://upload-images.jianshu.io/upload_images/5294842-bd4a0844c9c85adb.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

When the development will inevitably have Objective-C and scenes Swift call each other, the two languages ​​of different ways to find the symbol at compile time, how to work with it?

  1. Swift 引用 Objective-C

    Swift internal compiler uses clang, so swift can be used directly clang module, thereby supporting framework written in direct import Objective-C.


    30

    swift compiler will look for symbols from Objective-C header files, source header file divided into two categories:

    • Bridging-Header.h exposed to a header file swfit
    • framework in the public header files, according to written languages, could find from modulemap or umbrella header.

    XCode provides macro definitions NS_SWIFT_NAME to allow developers to define Objective-C => Swift symbol mapping, by Related Items -> Generate Interface to view the results of the conversion:


    31

  2. Objective-C 引用 swift

    xcode module units will automatically generate a header file for the swift, references for Objective-C, usually this file is named ProductName-Swift.h.

    Providing the keyword @objc swift to the type of exposure to the Objective-C and Objective-C Runtime.

    @objc public class MyClass

XVIII depth understanding Linker

Linker compiler will generate a plurality of files, linked into an executable file. Link does not generate new code, mobile and just do a patch on the basis of existing code.

Enter the linker may be the following documents:

  • Edited result object file (.o), a single source file, containing the code and data represented by the symbol.
  • Dynamic library (.dylib), mach o types of executable files, links only when binding symbols dynamic library will be copied to the app, the run-time load
  • Static library (II.A), packaged by a set of command ar .o file, when the link will copy the code specific to the last mach-o.
  • tbd, only the library file that contains symbols

Reference is made to a concept: Symbol (Symbols), then sign what is it?

Symbol is the name of a piece of code or data, it is also possible inside a reference symbol another symbol.

With a piece of code as an example, look at what happened when the link?

Source:

- (void)log
{
    printf("hello world\n");
}

.o file:

#代码
adrp    x0, l_.str@PAGE
add     x0, x0, l_.str@PAGEOFF
bl      _printf

#字符串符号
l_.str:                                 ; @.str
        .asciz  "hello world\\n"

In .o file, the character string "hello world \ n" is cited as one symbol (l_.str), assembly code is read when reading plus the offset manner according to the page where l_.str, then call printf symbol. At this point, CPU do not know how to perform, because there are two issues have not been resolved:

  1. l_.str in which the location of the executable file?
  2. printf 函数来自哪里?

再来看看链接之后的 mach o 文件:

![32](https://upload-images.jianshu.io/upload_images/5294842-ac9f852d87c4ebb1.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

链接器如何解决这两个问题呢?

  1. 链接后,不再是以页+偏移量的方式读取字符串,而是直接读虚拟内存中的地址,解决了 l_.str 的位置问题。
  2. 链接后,不再是调用符号 _printf,而是在 DATA 段上创建了一个函数指针 _printf$ptr,初始值为 0x0(null),代码直接调用这个函数指针。启动的时候,dyld 会把 DATA 段上的指针进行动态绑定,绑定到具体虚拟内存中的 _printf 地址。更多细节,可以参考这篇文章:深入理解iOS App的启动过程

Mach-O 有一个区域叫做 LINKEDIT,这个区域用来存储启动时 dyld 需要动态修复的一些数据:比如刚刚提到的 printf 在内存中的地址。

十九、理解签名

  1. 非对称加密

    在密码学中,非对称加密需要两个密钥:公钥和私钥。私钥加密的只能用公钥解密,公钥加密的只能用私钥解密。

  2. 数字签名

    数字签名表示我对数据做了个标记,表示这是我的数据,没有经过篡改。

    数据发送方 Leo 产生一对公私钥,私钥自己保存,公钥发给接收方 Lina。Leo 用摘要算法,对发送的数据生成一段摘要,摘要算法保证了只要数据修改,那么摘要一定改变。然后用私钥对这个摘要进行加密,和数据一起发送给 Lina。


    33

    Lina 收到数据后,用公钥解密签名,得到 Leo 发过来的摘要;然后自己按照同样的摘要算法计算摘要,如果计算的结果和 Leo 的一样,说明数据没有被篡改过。


    34

    但是,现在还有个问题:Lina 有一个公钥,假如攻击者把 Lina 的公钥替换成自己的公钥,那么攻击者就可以伪装成 Leo 进行通信,所以 Lina 需要确保这个公钥来自于 Leo,可以通过数字证书来解决这个问题。

    数字证书由 CA(Certificate Authority)颁发,以 Leo 的证书为例,里面包含了以下数据:签发者、Leo 的公钥、Leo 使用的 Hash 算法、证书的数字签名、到期时间等。

    有了数字证书后,Leo 再发送数据的时候,把自己从 CA 申请的证书一起发送给 Lina。Lina 收到数据后,先用 CA 的公钥验证证书的数字签名是否正确,如果正确说明证书没有被篡改过,然后以信任链的方式判断是否信任这个证书,如果信任证书,取出证书中的数据,可以判断出证书是属于 Leo 的,最后从证书中取出公钥来做数据签名验证。

二十、iOS App 签名

为什么要对 App 进行签名呢?签名能够让 iOS 识别出是谁签名了 App,并且签名后 App 没有被篡改过。

除此之外,Apple 要严格控制 App 的分发:

  1. App 来自 Apple 信任的开发者
  2. 安装的设备是 Apple 允许的设备

20.1 证书

通过上文的讲解,我们知道数字证书里包含着申请证书设备的公钥,所以在 Apple 开发者后台创建证书的时候,需要上传 CSR 文件(Certificate Signing Request),用 keychain 生成这个文件的时候,就生成了一对公/私钥:公钥在 CSR 里,私钥在本地的 Mac 上。Apple 本身也有一对公钥和私钥:私钥保存在 Apple 后台,公钥在每一台 iOS 设备上。

![35](https://upload-images.jianshu.io/upload_images/5294842-ec7d73889dc3f8e4.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

20.2 Provisioning Profile

iOS App 安装到设备的途径(非越狱)有以下几种:

  • 开发包(插线,或者 archive 导出 develop 包)
  • Ad Hoc
  • App Store
  • 企业证书

开发包和 Ad Hoc 都会严格限制安装设备,为了把设备 uuid 等信息一起打包进 App,开发者需要配置 Provisioning Profile。

![36](https://upload-images.jianshu.io/upload_images/5294842-696d4d9bbf81ec36.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

可以通过以下命令来查看 Provisioning Profile 中的内容:

security cms -D -i embedded.mobileprovision > result.plist
open result.plist

本质上就是一个编码过后的 plist。

![37](https://upload-images.jianshu.io/upload_images/5294842-46a1386a0e86d8aa.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

20.3 iOS 签名

生成安装包的最后一步,XCode 会调用 codesign 对 Product.app 进行签名。

创建一个额外的目录 _CodeSignature 以 plist 的方式存放安装包内每一个文件签名

<key>Base.lproj/LaunchScreen.storyboardc/01J-lp-oVM-view-Ze5-6b-2t3.nib</key>
<data>
T2g5jlq7EVFHNzL/ip3fSoXKoOI=
</data>
<key>Info.plist</key>
<data>
5aVg/3m4y30m+GSB8LkZNNU3mug=
</data>
<key>PkgInfo</key>
<data>
n57qDP4tZfLD1rCS43W0B4LQjzE=
</data>
<key>embedded.mobileprovision</key>
<data>
tm/I1g+0u2Cx9qrPJeC0zgyuVUE=
</data>
...

代码签名会直接写入到 mach-o 的可执行文件里,值得注意的是签名是以页(Page)为单位的,而不是整个文件签名:

![38](https://upload-images.jianshu.io/upload_images/5294842-8c96d1f4a8fcb7b1.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

20.4 验证

安装 App 的时候

  • Remove the certificate from embedded.mobileprovision, verify that the certificate is from a trusted developer Apple
  • After verification by the certificate, the public key extracted from the certificate, Leo
  • Read the signature result in _CodeSignature, Leo verify that each file with the public key of the signature is correct
  • After the file embedded.mobileprovision verified by reading a list of equipment inside the id, to determine whether the current device can be installed (App Store and businesses do not do this step verification certificate)
  • After the verification is passed, installed App

App startup time

  • Verify bundle id, entitlements and embedded.mobileprovision in AppId, entitlements are the same
  • Judging device id included in embedded.mobileprovision years. App Store and enterprise certificate is not verified
  • If the certificate is the enterprise, verify that the user Trust Certificates
  • App After starting, when the page faults (page fault) occurs, the system will correspond to the mach-o pages into physical memory, and then verify the signature of this page are correct.
  • Above all through validation, App to normal startup

XXI article

Huangwen Chen & layman's language compiler iOS

Guess you like

Origin www.cnblogs.com/dins/p/12366643.html