iOS ------ 编译链接

news2024/9/22 7:08:37

编译流程分析

编译可以分为四步:

  • 预处理(Prepressing)
  • 编译(Compilation)
  • 汇编 (Assembly)
  • 链接(Linking)

在这里插入图片描述

预编译(Prepressing)

过程是源文件main.c和相关头文件被(stdio.h)被预编译器cpp预编译成一个.i文件

使用命令:clang -E main.m 或在Xcode的Product->Perform Action->Preprocess得到预编译结果。

#import <Foundation/Foundation.h>
#import "Person.h"
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // insert code here...
        NSLog(@"Hello, World!");
        Person* person = [[Person alloc] init];
    }
    return 0;
}

预编译后

# 187 "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks/Foundation.framework/Headers/Foundation.h" 2 3
# 9 "main.m" 2
# 1 "./Person.h" 1
# 10 "./Person.h"
#pragma clang assume_nonnull begin

@interface Person : NSObject
@property NSString* name;
@end
# 14 "./Person.h"
#pragma clang assume_nonnull end
# 10 "main.m" 2
int main(int argc, const char * argv[]) {
    @autoreleasepool {

        NSLog(@"Hello, World!");
        Person* person = [[Person alloc] init];
        person.name = @"111";
    }
    return 0;
}

预编译主要是处理源代码中以“#”开头的预编译指令:

  • "define"删除并展开对应宏定义
  • 处理所有的条件预编译指令,如#if/#ifdef/#else/#endif
  • "#include/#import"包含的文件递归插入此处
  • 删除所有的注释"//或/**/"
  • 添加行号或文件名标识。如“#1"main.m"”,编译调试会用到

编译(compliation)

编译过程就是把预处理完的文件进行一系列的:词法分析语法分析语义分析及优化后生产相应的汇编代码文件,此过程是整个程序构建的核心部分,也是最复杂的部分之一。
其编译过程相当于如下命令:

clang -S main.i -o main.s
  • 词法分析:这一步把源文件中的代码转化为特殊的标记流,源码被分割成一个一个 token(关键字、标识符、字面量、特殊符号),在行尾Loc中都标记出了源码所在的对应源文件和具体行数,方便在报错时定位问题。

使用命令clang -Xclang -dump-tokens main.m

star '*'		Loc=<main.m:14:15>
identifier 'person'	 [LeadingSpace]	Loc=<main.m:14:17>
equal '='	 [LeadingSpace]	Loc=<main.m:14:25>
l_square '['	 [LeadingSpace]	Loc=<main.m:14:27>
l_square '['		Loc=<main.m:14:28>
identifier 'Person'		Loc=<main.m:14:29>
identifier 'alloc'	 [LeadingSpace]	Loc=<main.m:14:36>
r_square ']'		Loc=<main.m:14:41>
identifier 'init'	 [LeadingSpace]	Loc=<main.m:14:43>
r_square ']'		Loc=<main.m:14:47>
semi ';'		Loc=<main.m:14:48>
  • 语法分析:把语法分析生成的标记流,解析成一个抽象语法树(AST),每个节点也标记了其在源码的位置

执行 clang 命令 clang -Xclang -ast-dump -fsyntax-only main.m

抽象语法树(abstract syntax code,AST)是源代码的抽象语法结构的树状表示,树上的每个节点都表示源代码中的一种结构,这所以说是抽象的,是因为抽象语法树并不会表示出真实语法出现的每一个细节,比如说,嵌套括号被隐含在树的结构中,并没有以节点的形式呈现。

 |   |     `-ObjCMessageExpr 0x130078978 <col:28, col:41> 'Person *' selector=alloc class='Person'
    |   `-PseudoObjectExpr 0x130078b50 <line:15:9, col:24> 'NSString *'
    |     |-BinaryOperator 0x130078af8 <col:9, col:24> 'NSString *' '='
    |     | |-ObjCPropertyRefExpr 0x130078ab0 <col:9, col:16> '<pseudo-object type>' lvalue objcproperty Kind=PropertyRef Property="name" Messaging=Setter
    |     | | `-OpaqueValueExpr 0x130078a98 <col:9> 'Person *'
    |     | |   `-ImplicitCastExpr 0x130078a10 <col:9> 'Person *' <LValueToRValue>
    |     | |     `-DeclRefExpr 0x1300789f0 <col:9> 'Person *' lvalue Var 0x130078900 'person' 'Person *'
    |     | `-OpaqueValueExpr 0x130078ae0 <col:23, col:24> 'NSString *'
    |     |   `-ObjCStringLiteral 0x130078a78 <col:23, col:24> 'NSString *'
    |     |     `-StringLiteral 0x130078a58 <col:24> 'char[4]' lvalue "111"
  • 静态分析:分析类型声明和匹配问题。比如整型和字符串相加,肯定会报错。
    中间语言生成:CodeGen根据AST自顶向下遍历逐步翻译成 LLVM IR,并且在编译期就可以确定的表达式进行优化,比如代码里t1=2+6,可以优化t1=8。(假如开启了bitcode,)

使用命令clang -O3 -S -emit-llvm main.m -o main.ll

@__CFConstantStringClassReference = external global [0 x i32]
@.str = private unnamed_addr constant [14 x i8] c"Hello, World!\00", section "__TEXT,__cstring,cstring_literals", align 1
@_unnamed_cfstring_ = private global %struct.__NSConstantString_tag { i32* getelementptr inbounds ([0 x i32], [0 x i32]* @__CFConstantStringClassReference, i32 0, i32 0), i32 1992, i8* getelementptr inbounds ([14 x i8], [14 x i8]* @.str, i32 0, i32 0), i64 13 }, section "__DATA,__cfstring", align 8 #0
@"OBJC_CLASS_$_Person" = external global %struct._class_t
@"OBJC_CLASSLIST_REFERENCES_$_" = internal global %struct._class_t* @"OBJC_CLASS_$_Person", section "__DATA,__objc_classrefs,regular,no_dead_strip", align 8
@.str.1 = private unnamed_addr constant [4 x i8] c"111\00", section "__TEXT,__cstring,cstring_literals", align 1
@_unnamed_cfstring_.2 = private global %struct.__NSConstantString_tag { i32* getelementptr inbounds ([0 x i32], [0 x i32]* @__CFConstantStringClassReference, i32 0, i32 0), i32 1992, i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str.1, i32 0, i32 0), i64 3 }, section "__DATA,__cfstring", align 8 #0
@llvm.compiler.used = appending global [1 x i8*] [i8* bitcast (%struct._class_t** @"OBJC_CLASSLIST_REFERENCES_$_" to i8*)], section "llvm.metadata"
  • 目标代码生成与优化:根据中间语言生成依赖具体机器的汇编语言。并优化汇编语言。这个过程中,假如有变量且定义在同一个编译单元里,那给这个变量分配空间,确定变量的地址。假如变量或者函数不定义在这个编译单元,得链接时候,才能确定地址。

使用命令xcrun clang -S -o - main.m | open -f

.section	__TEXT,__text,regular,pure_instructions
	.build_version macos, 13, 0	sdk_version 13, 1
	.globl	_main                           ; -- Begin function main
	.p2align	2
_main:                                  ; @main
	.cfi_startproc
; %bb.0:
	sub	sp, sp, #64
	stp	x29, x30, [sp, #48]             ; 16-byte Folded Spill
	add	x29, sp, #48
	.cfi_def_cfa w29, 16
	.cfi_offset w30, -8
	.cfi_offset w29, -16
                                        ; implicit-def: $x8
	mov	w8, #0
	str	w8, [sp, #20]                   ; 4-byte Folded Spill
	stur	wzr, [x29, #-4]
	stur	w0, [x29, #-8]
	stur	x1, [x29, #-16]

汇编(Assembly)

汇编就是把上面得到的.s文件里的汇编指令一一翻译为机器语言。汇编器的汇编过程相较于编译器来讲比较简单,只是根据汇编指令和机器指令的对照表一一翻译就可以了。

汇编指令:

clang -c main.s -o main.o

链接(Linking)

链接指令:

clang main.o -o main

现在程序为了便于维护都是分模块组成,比如一个App,对应有多个源代码文件。每个源代码文件汇编成目标文件,根据上面流程A目标文件访问B目标文件的函数或者变量,是不知道地址的,链接就是要解决这个问题。链接过程主要包括地址和空间分配、符号决议和重定位。

链接就是把目标文件(一个或多个)和需要的库(静态库/动态库)链接成可执行文件。后面会分别讲静态链接和动态链接。

静态库和动态库

什么是库?

  • 库就是程序代码的集合,将N个文件组织起来,是共享程序代码的一种方式
  • 本质上是一种可执行代码的二进制文件,可以被载入程序中运行

静态库和动态库

  • 什么是静态库和动态库
    • 静态和动态是相对于编译期和运行期而言的,静态库会在程序编译时会被链接到目标代码中,程序运行时将不再需要该静态库。动态库在程序编译时不会被链接到目标代码中,只在程序运行时才被载入
      静态库在程序编译链接的时候,如下图所示
      在这里插入图片描述

动态库在程序编译链接的时候,如下图所示:

在这里插入图片描述

  • 静态库和动态库都是程序编译好的二进制文件,苹果官方的解释:

    • 动态库:可以在运行或启动的时候加载到内存中,加载到一块独立于App的内存地址
    • 静态库:当程序启动的时候时,会将App的代码(包括静态库的代码)一同加载到App所处的内存地址上。相比于静态库的方案,使用动态库会花费更多的启动时间和内存消耗,还会增加可执行文件的大小
  • 存在形式:

    • 静态库:以 “.a” 或者 “.framework” 为文件后缀名;.a 是一个纯二进制文件,.framework 中除了有二进制文件之外还有资源文件。.a 要有 .h 文件以及资源文件配合,.framework 文件可以直接使用。总的来说,.a + .h + sourceFile = .framework,因此创建静态库最好还是用 .framework 的形式。
    • 动态库:以 “.dylib” 或者 “.framework” 为文件后缀名(Xcode7 之后 .tbd 替代 .dylib)。
  • 使用区别

    • 静态库链接时会被完整的复制到可执行文件中,被多次使用就会被多份拷贝
    • 在这里插入图片描述

因为整个数据库的代码都被整合到目标代码中,则编译成的文件比较大。编译后的执行程序不再需要外部的数据库支持。如果静态数据库改变了,那么程序必须重新编译。

  • 动态库链接时不复制,程序运行时有系统动态加载到内存中,供程序调用,而且系统只加载一次,多个程序调用节省内存
    在这里插入图片描述

  • 动态库在编译时,并没有编译进目标代码,所以产生的可执行文件比较小。当程序运行时执行到相关函数才才动态申请并调用函数库的相应函数,所以程序的运行环境必须提供相应的库。且动态函数库的升级并不影响程序。

  • 各种优点:

静态库:

  • 模块化,分工合作,提高了代码的复用及核心技术的保密程度;
  • 避免少量改动经常导致大量的重复编译连接;
  • 也可以重用,注意不是共享使用。
    动态库:
  • 可以将最终可执行文件体积缩小,将整个应用程序分模块,团队合作,进行分工,影响比较小;
  • 多个应用程序共享内存中得同一份库文件,节省资源;
  • 可以不重新编译连接可执行程序的前提下,更新动态库文件达到更新应用程序的目的;

dyld动态链接器

动态库在程序中是怎么加载到内存呢?系统是怎么链接的?这就要用到dyld(the dymanic link editor)动态链接器

dyld(the dymanic link editor)动态链接器是苹果操作系统的一个重要组成部分,在系统内核XNU完成Mach- O文件的加载,做好程序准备工作后,加载或链接动态共享库或可执行文件确保程序能正确执行。

加载流程:

1,环境变量控制:根据环境的状态配置和环境变量下的相应的条件判断状态值及获取当前架构
2,共享缓存解析处理:检查是否开启了共享缓存及共享缓存是否映射到共享区域,例如UIKi t,CoreFoundation等
3,主程序初始化:调用instantiateFromLoadedImage函数实例化出一个ImageLoader对象
4,插入动态库:遍历环境变量DYLD_INSERT_LIBRARIES待插入动态库表容器组,调用loadInsertedDylib加载引入
5,链接主程序和动态库:进行符号和地址的绑定,加载所有类,最后执行load方法和clang attribute的constructor修饰函数

dyld2

dyld2是如何加载程序的?

  • Parse mach-0 header/Find dependencies:分析mach-o headers,通过分析得知需要加载哪些库,然后通过递归查找上述的库又需要那些其他库的支持,知道获得所有dylib完整的二进制文件。普通的iOS程序需要3-600个dylib,数据很庞大,需要大量的处理。
  • 第二Map mach-o files:映射所有的mach-o文件,将他们放入到地址空间内,即内存
  • 第三Perform symbol lookups:执行符号查找,假设程序内使用了printf函数,将会查找printf是否在系统库中,然后找到函数的地址,将它复制到你的程序汇中的函数指针
  • 第四Bind and rebase:进行符号的重绑定,复制这些指针,由于使用随机地址,所有指针必须使用基地址
  • 第五Run initializers:运行所有的初始化器
  • 第六:准备运行main函数
    在这里插入图片描述

dyld3

经过对dyld 2的优化,dyld 3的加载过程的不同之处

  • 将perform symbol lookups移到第二步,向磁盘写入闭包处理。
  • 将dyld分成了3部分,红色部分是一个进程外的mach-o分析器与编译器,也是一个进程内引擎,执行启动闭包处理,也是一个启用闭包的缓存服务,大多数的程序启动会使用缓存,但是始终不需要调用进程外mach-o分析器和编译器,启用闭包比mach-o更加的简单,它们是内存映射文件,不需要复杂的方法进行分析
  • 进程外编译器部分:首先解析所有的搜索路径,所有rpaths、所有环境变量、然后分析mach-o的二进制数据,执行所有的符号查找,利用这些结果来创建闭包处理
  • dyld3也是一个小型进程内引擎,这部分驻留在进程内,它所做的事情就是验证闭包是否正确,然后映射到dylib中,再跳转到main函数,与dyld2对比,dyld3不需要分析mach-o文件头或执行符号查找,不需要做这些事情就可以启动应用,因此极大的提升了程序的启动速度
  • 最后dyld3还会启动一个闭包缓存服务,这里指的是将系统程序闭包直接加入到共享缓存

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1934551.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

vue v-for展示元素分两栏 中间使用分割线

1.效果展示: 2.代码展示: <template><div class"container"><div class"column" v-for"(item, index) in items" :key"index"><div class"item">{{ item }}</div><div v-if"index %…

在 K8s 上使用 KubeBlocks 提供的 MySQL operator 部署高可用 WordPress 站点

引言 WordPress WordPress 是全球最流行的内容管理系统&#xff08;CMS&#xff09;&#xff0c;自 2003 年发布以来&#xff0c;已成为网站建设的首选工具。其广泛的插件和主题生态系统使用户能够轻松扩展功能和美化外观。活跃的社区提供丰富的资源和支持&#xff0c;进一步…

npm install时卡在sill idealTree buildDeps卡着不动

场景&#xff1a;做导出功能的时候要用上xlsx&#xff0c;正常npm install xlsx --save 问题描述&#xff1a;npm install时卡在sill idealTree buildDeps&#xff0c;&#xff0c;卡着不动 过程&#xff1a;在网上一顿百度试过好多种方法 1、切换taobao的镜像地址 npm conf…

Flink History Server配置

目录 问题复现 History Server配置 HADOOP_CLASSPATH配置 History Server配置 问题修复 启动flink集群 启动Histroty Server 问题复现 在bigdata111上执行如下命令开启socket&#xff1a; nc -lk 9999 如图&#xff1a; 在bigdata111上执行如下命令运行flink应用程序 …

神经网络之多层感知机

目录 一、全连接层&#xff1a;二、单层感知机概念&#xff1a;三、多层感知机概念&#xff1a; 一、全连接层&#xff1a; 在神经网络中&#xff0c;全连接层就是每个神经元都与上一层的所有神经元相连接&#xff0c;即每个神经元都接收上一层所有神经元的输入&#xff0c;并…

Java后端开发(十四)-- Win10安装多版本JDK并随时切换

目录 1. 多版本JDK并随时切换的解决办法 2. jdk17切回jdk8时一直失败的解决办法 3. 测试jdk版本 我目前使用的是window10的操作系统,在环境变量中关于jdk的配置如下: 1. 多版本JDK并随时切换的解决办法 最后一步就是切换 JAVA_HOME 的环境变量的值,就能随意切换jdk的版本…

Amisco供应汽车线圈与Husco是一家私营公司高性能液压和机电部件在汽车和非公路应用的组件设计和制造方面拥有超过 75 年的经验10于年的合作

Amisco和Husco在汽车线圈和高性能液压和机电部件的设计和制造方面合作已经超过10年。 Amisco是一家供应汽车线圈的公司&#xff0c;而Husco则专注于高性能液压和机电部件的设计和制造。 这两家公司在汽车和非公路应用领域拥有超过75年的经验。通过合作&#xff0c;Amisco和Husc…

Go语言并发编程-Goroutine调度

goroutine 概念 在Go中&#xff0c;每个并发执行的单元称为goroutine。通常称为Go协程。 go 关键字启动goroutine go中使用关键字 go 即可启动新的goroutine。 示例代码&#xff1a; 两个函数分别输出奇数和偶数。采用常规调用顺序执行&#xff0c;和采用go并发调用&…

elementUI在手机端使用遇到的问题总结

之前的博客有写过用vue2elementUI封装手机端选择器picker组件&#xff0c;支持单选、多选、远程搜索多选&#xff0c;最终真机调试的时候发现有很多细节样式需要调整。此篇博客记录下我调试过程中遇到的问题和解决方法。 一、手机真机怎么连电脑本地代码调试&#xff1f; 1.确…

2-38 基于matlab的蚁群算法优化无人机uav巡检

基于matlab的蚁群算法优化无人机uav巡检&#xff0c;巡检位置坐标可根据需求设置&#xff0c;从基地出发&#xff0c;返回基地&#xff0c;使得路径最小。可设置蚁群数量&#xff0c;信息素系数。输出最佳路线长度。程序已调通&#xff0c;可直接运行。 2-38 蚁群算法优化无人…

C学习(数据结构)-->单链表习题

目录 一、环形链表 题一&#xff1a;环形链表 思路&#xff1a; 思考一&#xff1a;为什么&#xff1f; 思考二&#xff1a;快指针一次走3步、4步、......n步&#xff0c;能否相遇 step1&#xff1a; step2&#xff1a; 代码&#xff1a; 题二&#xff1a; 环形链表 I…

PolarisMesh源码系列--Polaris-Go注册发现流程

导语 北极星是腾讯开源的一款服务治理平台&#xff0c;用来解决分布式和微服务架构中的服务管理、流量管理、配置管理、故障容错和可观测性问题。在分布式和微服务架构的治理领域&#xff0c;目前国内比较流行的还包括 Spring Cloud&#xff0c;Apache Dubbo 等。在 Kubernete…

22.《C语言》——【如何进行文件操作?】上集

&#x1f195; 开场白 亲爱的读者&#xff0c;大家好&#xff01;我是一名正在学习编程的高校生。在这个博客里&#xff0c;我将和大家一起探讨编程技巧、分享实用工具&#xff0c;并交流学习心得。希望通过我的博客&#xff0c;你能学到有用的知识&#xff0c;提高自己的技能&…

鸿蒙语言基础类库:【@system.prompt (弹窗)】

弹窗 说明&#xff1a; 从API Version 8 开始&#xff0c;该接口不再维护&#xff0c;推荐使用新接口[ohos.prompt]。本模块首批接口从API version 3开始支持。后续版本的新增接口&#xff0c;采用上角标单独标记接口的起始版本。 导入模块 import prompt from system.prompt;…

Python算法实现之排序算法的Python实现详解

概要 排序算法是计算机科学中最基础和最重要的算法之一。它们在数据处理中起着关键作用,广泛应用于搜索、数据分析和优化等领域。本文将详细介绍几种常见的排序算法及其Python实现,包括冒泡排序、选择排序、插入排序、归并排序和快速排序,并通过具体示例代码展示它们的工作…

插画感言:成都亚恒丰创教育科技有限公司

插画感言&#xff1a;笔触间的灵魂对话 在这个快节奏、高压力的时代&#xff0c;我们时常在寻找那些能够触动心灵、让灵魂得以片刻栖息的角落。而插画&#xff0c;这一融合了艺术与情感的独特形式&#xff0c;便如同一股清泉&#xff0c;缓缓流淌进每个人的心田&#xff0c;以…

【iOS】——编译链接和动态链接器

前言 计算机语言分为机器语言&#xff1a;汇编语言&#xff0c;高级语言。 可以将高级语言分为两种&#xff1a;1&#xff0c;编译语言和解释型语言&#xff08;直译式语言&#xff09;。 编译型语言&#xff08;一次性翻译&#xff09; 编译型语言的程序只要经过编译器编译之…

关于dom4j主节点的xmlns无法写入的问题

由于最近需要做一个xml的文件&#xff0c;使用dom4j的时候发现了一个bug&#xff0c;就是我的xmlns根本无法写入到xml的头部标签中。 Element element document.addElement("test"); element.addAttribute("xmlns", "urn:Declaration:datamodel:sta…

小程序js 把链接转换为二维码

GitHub - Rookie-M/weapp-qrcode: weapp.qrcode.js 在 微信小程序 中&#xff0c;快速生成二维码 1.要下载上面地址的插件包 2.引用 import drawQrcode from ../../utils/weapp.qrcode.minonLoad(options) {let that thisconsole.log(JSON.parse(options.info))that.setData…

[word] word表格跨页断开实现教程 #职场发展#媒体

word表格跨页断开实现教程 选中整个word表格 单击鼠标右键&#xff0c;选择“表格属性”选项 切换至“行”标签&#xff0c;找到“允许跨页断行”选项 勾选上“允许跨页断行”&#xff0c;单击“确定”按钮&#xff0c;完成&#xff01; word表格跨页断开实现教程的下载地址&a…