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

前言

计算机语言分为机器语言:汇编语言,高级语言。
可以将高级语言分为两种:1,编译语言和解释型语言(直译式语言)。

img

编译型语言(一次性翻译)

编译型语言的程序只要经过编译器编译之后,每次运行程序都可以直接运行如oc,swift等

解释型语言(逐步进行解释执行)

解释语言编写的程序在每次运行时都需要通过解释器对程序进行动态解释和执行,如php,javascript等

编译链接过程:

1,预处理:macro 宏, import 头文件替换及处理其他的预编译指令,产生.i文件。(都是以#号开头)

2,编译:把预处理完的一系列文件进行一系列词法、语法、静态分析,并且优化后生成相应的汇编代码,产生.s文件;

3,汇编:汇编器将汇编代码生成机器指令,输出目标文件,产生.o文件(根据汇编指令和机器指令的对照表一一翻译就可以了);

4,链接:在一个文件中可能会到其他文件,因此,还需要将编译生成的目标文件和系统提供的文件组合到一起,这个过程就是链接。经过链接,最后生成可执行文件。

img

预处理(预编译)-> 产生.i文件

使用终端到main.m所在文件夹,使用命令:

clang -E main.m -o main.i

处理源代码文件中的以"#"开头的预编译指令:

  • "#define"删除并展开对应宏定义。

  • 处理所有的条件预编译指令。如#if/#ifdef/#else/#endif。

  • "#include/#import"包含的文件递归插入到此处。

  • 删除所有的注释"//或/**/"。

  • 添加行号和文件名标识。如“# 1 “main.m””,编译调试会用到。

编译 -> 产生.s文件

编译器:用来把源文件转换为更低级的语言,xcode使用的clang前端编译器的作用就是把源代码转化为更为低级的LLVM IR,这个LLVM IR是操作系统无关的,然后后端编译器LLVM通过这个中间语言来进行下一步二进制文件的产出。这要得益于LLVM的三层架构,假如你需要增加一种语言,只需要增加一种前端,如过你需要增加一种处理器架构,也只需要增加一种后端。

img

编译器前端:Clang编译器前端的任务是进行:语法分析,语义分析,生成中间代码(intermediate representation )。在这个过程中,会进行类型检查,如果发现错误或者警告会标注出来在哪一行。Clang 是一个C、C++、Objective-C和Objective-C++编程语言的编译器前端。

公用优化器:将生成的中间文件进行优化,去除冗余代码,进行结构优化。将优化后的中间代码再次转换,变成汇编语言,并再次进行优化,最后将各个文件代码转换为机器代码并链接。

编译器后端:

clang -S main.i -o main.s

编译过程也分为 词法分析 -> 语法分析 -> 静态分析 最后优化生成相应的汇编代码,得到.s文件

词法分析:源代码的字符序列分割成一个个token(关键字、标识符、字面量、特殊符号),比如把标识符放到符号表(静态链接那篇,重点讲符号表)。

语法分析:把词法分析生成的token生成抽象语法树 AST,此时运算符号的优先级确定了;有些符号具有多重含义也确定了,比如“*”是乘号还是对指针取内容;表达式不合法、括号不匹配等,都会报错。

静态分析:分析类型声明和匹配问题。比如整型和字符串相加,肯定会报错。

一般会把类型分为两类:动态的和静态的。动态的在运行时做检查,静态的在编译时做检查。以往,编写代码时可以向任意对象发送任何消息,在运行时,才会检查对象是否能够响应这些消息。由于只是在运行时做此类检查,所以叫做动态类型。

静态类型,是在编译时做检查。当在代码中使用 ARC 时,编译器在编译期间,会做许多的类型检查:因为编译器需要知道哪个对象该如何使用。

中间语言生成和优化:CodeGen根据AST自顶向下遍历逐步翻译成 LLVM IR,并且在编译期就可以确定的表达式进行优化,比如代码里t1=2+6,可以优化t1=8。(假如开启了bitcode,)

目标代码生成与优化:根据中间语言生成依赖具体机器的汇编语言。并优化汇编语言。这个过程中,假如有变量且定义在同一个编译单元里,那给这个变量分配空间,确定变量的地址。假如变量或者函数不定义在这个编译单元,得链接时候,才能确定地址。

汇编 -> 产生.o文件

clang -c main.s -o main.o

汇编器将上一步生成的可读的汇编代码转化为机器代码。最终产物就是 以 .o 结尾的目标文件。

链接

clang main.o -o main

这一阶段是将上个阶段生成的目标文件和引用的静态库链接起来,最终生成可执行文件(Mach-O),链接器解决了目标文件和库之间的链接。

动态库和静态库

动态库

动态库在程序编译时并不会被连接到目标代码中,而是在程序运行是才被载入。不同的应用程序如果调用相同的库,那么在内存里只需要有一份该共享库的实例,规避了空间浪费问题。动态库在程序运行时才被载入,也解决了静态库对程序的更新、部署和发布页会带来麻烦。用户只需要更新动态库即可,增量更新

  • 动态库把对一些库函数的链接载入推迟到程序运行的时期。
  • 可以实现进程之间的资源共享。(因此动态库也称为共享库)将一些程序升级变得简单。
  • 甚至可以真正做到链接载入完全由程序员在程序代码中控制(显示调用)。

静态库

在程序编译期的链接阶段,会将汇编生成的目标文件.o与引用到的库一起链接打包到可执行文件中。因此对应的链接方式称为静态链接。

  • 程序在运行时独立运行,不需要依赖外部库。

  • 静态库的调用通常比动态库的调用更快,因为不需要动态链接

  • 生成的可执行程序较大。如果多个使用静态链接生成的程序同时运行会占用大量的内存空间静态库一旦被编译链接到可执行文件中,想要更新或替换静态库的代码需要重新编译整个程序。

动态库和静态库的区别

静态库

  • 在编译时加载

  • 优点:代码装载和执行速度比动态库快。

  • 缺点:浪费内存和磁盘空间,模块更新困难。

    动态库

  • 在运行时加载

  • 优点:体积比静态库小很多,更加节省内存。

  • 缺点:代码装载和执行速度比静态库慢。

动态链接器

动态链接器的定义

动态链接器通常指的是dylddyld是它负责处理程序和动态库(dylib,动态链接库)之间的链接操作。dyld的主要任务是在程序启动时解析和加载所需的动态库,并解决这些库中的符号引用。

  1. 加载动态库dyld在程序启动时会加载指定的动态库。这包括系统库和应用程序可能依赖的任何第三方库。dyld能够延迟加载某些库,直到它们被实际使用,这有助于提高启动速度和内存效率。
  2. 解析符号: 当程序引用了一个动态库中的函数或变量时,dyld会查找并绑定这些符号到正确的地址。这个过程称为符号解析,它确保了程序能正确地访问库中的功能。
  3. 重定位dyld还负责重定位,这意味着它会调整库中的地址,以确保它们在进程的虚拟地址空间中正确放置。这通常涉及修改库内部的指针,使它们指向正确的内存位置。
  4. 缓存dyld使用缓存机制来存储已加载的库和已解析的符号,这样在后续的程序执行中就不需要重复加载或解析相同的内容,从而加快了程序的运行速度。
  5. 懒惰链接(Lazy Binding): 为了进一步提高性能,dyld可以采用懒惰链接策略,只在程序真正尝试访问一个库中的符号时才解析该符号。这避免了在程序启动时不必要的工作。
  6. 可执行文件和动态库的集成: 在iOS中,dyld不仅处理动态库,还负责加载和执行可执行文件本身。可执行文件中包含了dyld的启动信息,告诉它需要加载哪些库和如何初始化程序。
  7. 安全特性dyld还实现了一些安全特性,如ASLR(地址空间布局随机化),通过随机化库的加载地址来增加攻击者预测内存布局的难度,从而提高系统的安全性。

dyld2和dyld3的区别

dyld2dyld3dyld的不同版本,它们在功能和性能上有一些关键区别

下图为dyld2执行流程:

img

下图为dyld3执行流程:

img

相比于dyld 3,dyld 2的加载过程没有那么优化。它没有将工作分成两个阶段,而是直接在进程中完成所有任务,包括解析、查找依赖、映射文件、查找符号以及绑定和重定位等。这意味着这些操作可能会阻塞主线程,导致应用程序启动速度变慢。此外,由于没有生成和使用闭包,dyld 2也没有提供额外的安全性保障来防止恶意软件尝试修改或注入代码到正在运行的应用程序中。

dyld 3 包含这三个部分:

  • 进程外 Mach-O 分析器和编译器 (out-of-process mach-o parser
    由于 dyld 2 存在的问题,dyld 3 中将采用提前写入把结果数据缓存成文件的方式构成一个 lauch closure(可以理解为缓存文件)
  • 进程内引擎 执行 launch closure 处理 (in-process engine)
    验证”lauch closures“是否正确,映射dylib,执行main函数。此时,它不再需要分析mach-o header和执行符号查找,节省了不少时间。
  • launch closure 缓存服务 (launch closure cache )
    系统程序的 lauch closure 直接内置在 shared cache 中,而对于第三方APP,将在APP安装或更新时生成,这样就能保证 launch closure 总是在 APP 打开之前准备好。

dyld 3的符号缺失问题

dyld 2 默认采取的是 lazy symbol 的符号加载方式,但在 dyld 3中,在 App 启动之前,符号解析的结果已经在 lauch closure 内了,所以 lazy symbol 就不再需要。这时,如果有符号缺失的情况,APP 的行为会有不同:在 dyld 2 中,首次调用缺失符号时 APP 会 crash;而 dyld 3 中,缺失符号会导致 APP 一启动就会 crash。

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

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

相关文章

服务的弹性守护者:在Eureka中实现服务降级策略

标题:服务的弹性守护者:在Eureka中实现服务降级策略 在微服务架构中,服务的稳定性和可靠性至关重要。Eureka作为Netflix开源的服务发现框架,不仅提供了服务注册与发现功能,还支持实现服务的降级策略。服务降级是一种在…

uniapp自定义tabBar

uniapp自定义tabBar 1、在登录页中获取该用户所有的权限 getAppFrontMenu().then(res>{if(res.length > 0){// 把所有权限存入缓存中let firstPath res.reverse()[0].path;uni.setStorageSync(qx_data, res);uni.switchTab({url: firstPath,})// 方法二 通过uni.setTabB…

Netty SSL/TLS

Netty中的SSL/TLS详细原理主要涉及到SSL/TLS协议在Netty框架中的实现方式和加密通信过程。SSL/TLS(安全套接层/传输层安全协议)是一种安全协议,用于在两个通信应用程序之间提供私密性和数据完整性。以下是Netty中SSL/TLS的详细原理&#xff1…

Android 大屏外接显示器锁屏无触摸

一、大海捞针 1、首先查看log,发现异常log log表示主显示器即id 0的显示器有可交互窗口但是没有焦点,副显示器即id 4有焦点但是没有可交互窗口。猜想副显示器把主显示器的焦点抢走了,只需把焦点从副显示器挪回主显示器即可。 通过查看源代码知道上面这段log来自于InputDi…

【时时三省】单元测试 简介

目录 1,单元测试简介 2,单元测试的目的 3,单元测试检查范围 4,单元测试用例设计方法 5,单元测试判断通过标准 6,测试范围 7,测试频率 8,输出成果 经验建议: 山不在高,有仙则名。水不在深,有龙则灵。 ----CSDN 时时三省 1,单元测试简介 单元测试在以V模型…

filebeat,kafka,clickhouse,ClickVisual搭建轻量级日志平台

springboot集成链路追踪 springboot版本 <parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.6.3</version><relativePath/> <!-- lookup parent from…

【CSS in Depth 2 精译_019】3.2 CSS 的盒模型

当前内容所在位置&#xff08;可进入专栏查看其他译好的章节内容&#xff09; 第一章 层叠、优先级与继承&#xff08;已完结&#xff09; 1.1 层叠1.2 继承1.3 特殊值1.4 简写属性1.5 CSS 渐进式增强技术1.6 本章小结 第二章 相对单位&#xff08;已完结&#xff09; 2.1 相对…

【Qt安装与简易串口控制Arduino开发板小灯教程】

【Qt安装与简易串口控制Arduino开发板小灯教程】 1. 前言2. QT环境搭建3. 验证QT_Creator是否安装成功3.1 设计流程3. 2 上位机界面设计 4. 上位机逻辑代码编写4.1 添加串口库、包含串口相关头文件4.2 添加QSerialPort成员4.3 创建串口对象、搜索所有可用串口4.4 在编写“打开串…

如何基于 Apache SeaTunnel 同步数据到 Iceberg

概述 Apache SeaTunnel Apache SeaTunnel 是一个分布式、高性能、易扩展、用于海量数据&#xff08;离线&实时&#xff09;同步和转化的开源数据集成平台&#xff0c; 支持spark、flink 及自研 Zeta 引擎&#xff0c;有庞大的用户社群. Apache Iceberg Apache Iceberg …

MySQL 架构中的三层服务是什么?

MySQL的架构可以分为客户端层、服务器层、存储引擎层三次服务&#xff0c;这些服务协同工作以提供高效的数据库管理服务。 1. 客户端层&#xff08;Client Layer&#xff09; 功能&#xff1a;客户端层是与用户交互的部分&#xff0c;用户通过客户端向MySQL服务器发送请求、接…

模板方法设计模式

模板方法设计模式: 模板方法设计模式&#xff1a;解决方法中存在重复代码的问题。 模板方法设计模式的写法&#xff1a; 1、定义一个抽象类 2、在里面定义2个方法 ​ 一个是模板方法&#xff1a;把相同代码放里面去 ​ 一个是抽象方法&#xff1a;具体实现交给子类完成 建议使用…

微软成为PostgreSQL主要贡献者

微软对PostgreSQL贡献的很多新功能都来自于客户在使用微软Azure上的PostgreSQL管理实例数据库&#xff0c;所以这些新功能都来自于真实的客户需求 微软贡献的这些新功能都是比较实用的功能 在这里&#xff0c;【真实的客户需求】要突出一下&#xff0c;因为现在很多社区贡献者…

4. docker镜像、Dockerfile

docker镜像、Dockerfile 一、docker镜像1、镜像介绍2、镜像核心技术 二、Dockerfile定制镜像1、Dockerfile使用流程1.1 编写Dockerfile1.2、构建镜像1.3 创建容器测试镜像定制操作 2、Dockerfile常用指令 一、docker镜像 1、镜像介绍 分层的文件系统 优势&#xff1a;节省空间…

基于JAVA+SpringBoot+uniapp的心理小程序(小程序版本)

✌全网粉丝20W,csdn特邀作者、博客专家、CSDN新星计划导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ 技术范围&#xff1a;SpringBoot、Vue、SSM、HLMT、Jsp、SpringCloud、Layui、Echarts图表、Nodejs、爬…

使用 JavaScript 实现图片预览功能

在本文中&#xff0c;我们将学习如何使用 JavaScript 实现一个简单的图片预览功能。我们将使用 HTML、CSS 和 JavaScript 来创建一个用户界面&#xff0c;用户可以输入图片 URL 并实时预览图片。 创建 HTML 页面结构 首先&#xff0c;我们需要创建一个包含用于输入图片 URL 和…

docker默认存储地址 var/lib/docker 满了,换个存储地址操作流程

1. 查看docker 存储地址 docker info如下 var/lib/docker2、查看内存大小 按需执行 df -h 找超过100M的大文件 find / -type f -size 100M -exec ls -lh {} \; df -Th /var/lib/docker 查找这个文件的容量 df -h 查找所有挂载点 du -hs /home/syy_temp/*1、df -h 2、sud…

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

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

冥想太极八段锦八部金刚功易筋经,更清晰地认识自己。

接下来&#xff0c;我将开始进行我的太极生涯&#xff0c;我的爱好实际上就是对于我自己的修行&#xff0c;以后对于抖音视频的文案&#xff0c;自己就不再使自己进行一个长篇大论了&#xff0c;自己在csdn记录下自己不断地进行学习和提升的反思。对自己真实就是最好的成长&…

科普文:多线程如何使用CPU缓存?

一、前言 计算机的基础知识聊的比较少&#xff0c;但想要更好的理解多线程以及为后续多线程的介绍做铺垫&#xff0c;所以有必要单独开一篇来聊一下 CPU cache。 二、CPU 前面有一篇文章关于 CPU是如何进行计算 感兴趣的同学&#xff0c;可以先移步了解一下&#xff0c;不了…

2、PostgreSQL之基本的SQL语言

PostgreSQL之基本的SQL语言 在上一篇文章中&#xff0c;我们已经安装好了PostgreSQL&#xff0c;并且能够通过psql访问数据库&#xff0c;以及远程访问数据库。下面就来介绍一些PostgreSQL的基本操作。 1、创建一个新表 在psql中输入以下命令&#xff1a; CREATE TABLE wea…