iOS--编译链接的过_1

news2024/12/25 22:13:14

iOS 开发中使用的是编译语言,所谓编译语言是在执行的时候,必须先通过编译器生成机器码,机器码可以直接在CPU上执行,所以执行效率较高,是使用 Clang / LLVM 来编译的。LLVM是一个模块化和可重用的编译器和工具链技术的集合,Clang 是 LLVM 的子项目,是 C,C++ 和 Objective-C 编译器,目的是提供惊人的快速编译。下面我们来看看编译过程,总的来说编译过程分为几个阶段:预处理 -> 词法分析 -> 语法分析 -> 静态分析 -> 生成中间代码和优化 -> 汇编 -> 链接
在这里插入图片描述
在这里插入图片描述

预处理

这一步编译器所做的处理是:
clang -E main.m -F

  • 宏替换(在源码中使用的宏定义会被替换为对应#define的内容)1. #define 删除,并展开对应的宏定义
    建议大家不要在需要预处理的代码中加入内联代码逻辑。
  • 头文件引入(#include,#import)
    使用对应文件.h的内容替换这一行的内容,所以尽量减少头文件中的#import,使用@class替代,把#import放到.m文件中。
  • 处理所有的条件预编译指令。如#if#ifdef#else#endif
  • 删除所有的注释 ///**/等。
  • 添加行号文件名标识。如 # 1 “main.m"(编译调试会用到)。

编译

clang -S main.i -o main.s
这个过程就是把上面的main.i文件进行:词法分析语法分析静态分析,优化生成相应的汇编代码,最终生成main.s文件。
这里我们需要了解一下这几个名词:

  • 词法分析:把源代码的字符序列分割成一个个token(关键字、表示符、字面量、特殊符号),比如把标识符放到符号表里面。
  • 语法分析: 生成抽象语法树AST,此时运算符号的优先级确定了;有些符号具有多重含义也确定了,比如:*是乘号还是对指针取内容;表达式不合法、括号不匹配等等,都会报错.
  • 静态分析:分析类型声明和匹配问题。比如整型和字符串相加,肯定会报错。
  • 中间语法生成: CodeGen根据AST(抽象语法树)自上向下逐步翻译成LLVM IR,并且对在编译期就可以确定的表达式进行优化,比如代码里面的a=1+3,可以优化成a=4。(假如开启了bitcode)
  • 目标代码生成与优化: 根据中间语法生成依赖具体机器的汇编语言;并优化汇编语言。`这个过程中,假如有变量且定义在同一个编译单元里,那么就给这个变量分配空间,确定变量的地址。假如变量或者函数不定义在这个编译单元里面,那就等到链接的时候才能确定地址。``

词法解析

这一步把源文件中的代码转化为特殊的标记流,源码被分割成一个一个的字符和单词,在行尾Loc中都标记出了源码所在的对应源文件和具体行数,方便在报错时定位问题。Clang定义的所有Token类型。 可以分为下面这4类:
clang -Xclang -dump-tokens main.m
关键字:语法中的关键字,比如 if、else、while、for 等;
标识符:变量名;
字面量:值、数字、字符串;
特殊符号:加减乘除等符号。
在这里插入图片描述

语法解析

clang -Xclang -ast-dump -fsyntax-only main.m
在这里插入图片描述
这一步是把词法分析生成的标记流,解析成一个抽象语法树(abstract syntax tree -- AST),同样地,在这里面每一节点也都标记了其在源码中的位置。[[AST抽象语法树]]

静态分析

把源码转化为抽象语法树之后,编译器就可以对这个树进行分析处理。静态分析会对代码进行错误检查,如出现方法被调用但是未定义、定义但是未使用的变量等,以此提高代码质量。当然,还可以通过使用 Xcode 自带的静态分析工具(Product -> Analyze)
类型检查
在此阶段clang会做检查,最常见的是检查程序是否发送正确的消息给正确的对象,是否在正确的值上调用了正常函数。如果你给一个单纯的 NSObject* 对象发送了一个 hello 消息,那么 clang 就会报错,同样,给属性设置一个与其自身类型不相符的对象,编译器会给出一个可能使用不正确的警告。

一般会把类型分为两类:动态的和静态的。动态的在运行时做检查,静态的在编译时做检查。以往,编写代码时可以向任意对象发送任何消息,在运行时,才会检查对象是否能够响应这些消息。由于只是在运行时做此类检查,所以叫做动态类型。
至于静态类型,是在编译时做检查。当在代码中使用 ARC 时,编译器在编译期间,会做许多的类型检查:因为编译器需要知道哪个对象该如何使用。

其他分析
ObjCUnusedIVarsChecker.cpp是用来检查是否有定义了,但是从未使用过的变量。(
This file defines a CheckObjCUnusedIvars, a checker that analyzes an Objective-C class’s interface/implementation to determine if it has any ivars that are never accessed.)
ObjCSelfInitChecker.cpp是检查在 你的初始化方法中中调用 self 之前,是否已经调用 [self initWith…] 或 [super init] 了(
This checks initialization methods to verify that they assign ‘self’ to the result of an initialization call (e.g. [super init], or [self initWith…]) before using ‘self’ or any instance variable.)。
在这里插入图片描述

中间代码生成和优化

LLVM IR有3种表示形式,但本质上是等价的。

  • text:便于阅读的文本格式,类似于汇编语言,拓展名 .ll
  • memory:内存格式
  • bitcode:二进制格式,拓展名 .bc
    我们对下面代码使用clang -O3 -S -emit-llvm main.m -o main.ll,生成main.ll
#import <Foundation/Foundation.h>

#define a1 1

  

**int** sum(**int** a, **int** b) {

    **int** c = a + b;

    **return** c;

}

  

**int** main(**int** argc, **const** **char** * argv[]) {

    **@autoreleasepool** {

        // insert code here...

        NSLog(@"Hello, World!");

        **int** a = 5;

        NSLog(@"%d", sum(a1, a));

    }

    **return** 0;

}
; ModuleID = 'main.m'
source_filename = "main.m"
target datalayout = "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-apple-macosx13.0.0"

%struct.__NSConstantString_tag = type { i32*, i32, i8*, i64 }

@__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
@.str.1 = private unnamed_addr constant [3 x i8] c"%d\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 ([3 x i8], [3 x i8]* @.str.1, i32 0, i32 0), i64 2 }, section "__DATA,__cfstring", align 8 #0

; Function Attrs: mustprogress nofree norecurse nosync nounwind readnone ssp uwtable willreturn
define i32 @sum(i32 %0, i32 %1) local_unnamed_addr #1 {
  %3 = add nsw i32 %1, %0
  ret i32 %3
}

; Function Attrs: ssp uwtable
define i32 @main(i32 %0, i8** nocapture readnone %1) local_unnamed_addr #2 {
  %3 = tail call i8* @llvm.objc.autoreleasePoolPush() #3
  notail call void (i8*, ...) @NSLog(i8* bitcast (%struct.__NSConstantString_tag* @_unnamed_cfstring_ to i8*))
  notail call void (i8*, ...) @NSLog(i8* bitcast (%struct.__NSConstantString_tag* @_unnamed_cfstring_.2 to i8*), i32 6)
  tail call void @llvm.objc.autoreleasePoolPop(i8* %3)
  ret i32 0
}

; Function Attrs: nounwind
declare i8* @llvm.objc.autoreleasePoolPush() #3

declare void @NSLog(i8*, ...) local_unnamed_addr #4

; Function Attrs: nounwind
declare void @llvm.objc.autoreleasePoolPop(i8*) #3

attributes #0 = { "objc_arc_inert" }
attributes #1 = { mustprogress nofree norecurse nosync nounwind readnone ssp uwtable willreturn "darwin-stkchk-strong-link" "frame-pointer"="all" "min-legal-vector-width"="0" "no-trapping-math"="true" "probe-stack"="___chkstk_darwin" "stack-protector-buffer-size"="8" "target-cpu"="penryn" "target-features"="+cx16,+cx8,+fxsr,+mmx,+sahf,+sse,+sse2,+sse3,+sse4.1,+ssse3,+x87" "tune-cpu"="generic" }
attributes #2 = { ssp uwtable "darwin-stkchk-strong-link" "frame-pointer"="all" "min-legal-vector-width"="0" "no-trapping-math"="true" "probe-stack"="___chkstk_darwin" "stack-protector-buffer-size"="8" "target-cpu"="penryn" "target-features"="+cx16,+cx8,+fxsr,+mmx,+sahf,+sse,+sse2,+sse3,+sse4.1,+ssse3,+x87" "tune-cpu"="generic" }
attributes #3 = { nounwind }
attributes #4 = { "darwin-stkchk-strong-link" "frame-pointer"="all" "no-trapping-math"="true" "probe-stack"="___chkstk_darwin" "stack-protector-buffer-size"="8" "target-cpu"="penryn" "target-features"="+cx16,+cx8,+fxsr,+mmx,+sahf,+sse,+sse2,+sse3,+sse4.1,+ssse3,+x87" "tune-cpu"="generic" }

!llvm.module.flags = !{!0, !1, !2, !3, !4, !5, !6, !7, !8, !9, !10}
!llvm.ident = !{!11}

!0 = !{i32 2, !"SDK Version", [2 x i32] [i32 13, i32 1]}
!1 = !{i32 1, !"Objective-C Version", i32 2}
!2 = !{i32 1, !"Objective-C Image Info Version", i32 0}
!3 = !{i32 1, !"Objective-C Image Info Section", !"__DATA,__objc_imageinfo,regular,no_dead_strip"}
!4 = !{i32 1, !"Objective-C Garbage Collection", i8 0}
!5 = !{i32 1, !"Objective-C Class Properties", i32 64}
!6 = !{i32 1, !"Objective-C Enforce ClassRO Pointer Signing", i8 0}
!7 = !{i32 1, !"wchar_size", i32 4}
!8 = !{i32 7, !"PIC Level", i32 2}
!9 = !{i32 7, !"uwtable", i32 1}
!10 = !{i32 7, !"frame-pointer", i32 2}
!11 = !{!"Apple clang version 14.0.0 (clang-1400.0.29.202)"}

接下来 LLVM 会对代码进行编译优化,例如针对全局变量优化、循环优化、尾递归优化等,最后输出汇编代码。使用xcrun clang -S -o - main.m | open -f生成汇编代码:

	.section	__TEXT,__text,regular,pure_instructions
	.build_version macos, 13, 0	sdk_version 13, 1
	.globl	_sum                            ## -- Begin function sum
	.p2align	4, 0x90
_sum:                                   ## @sum
	.cfi_startproc
## %bb.0:
	pushq	%rbp
	.cfi_def_cfa_offset 16
	.cfi_offset %rbp, -16
	movq	%rsp, %rbp
	.cfi_def_cfa_register %rbp
	movl	%edi, -4(%rbp)
	movl	%esi, -8(%rbp)
	movl	-4(%rbp), %eax
	addl	-8(%rbp), %eax
	movl	%eax, -12(%rbp)
	movl	-12(%rbp), %eax
	popq	%rbp
	retq
	.cfi_endproc
                                        ## -- End function
	.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	$32, %rsp
	movl	$0, -4(%rbp)
	movl	%edi, -8(%rbp)
	movq	%rsi, -16(%rbp)
	callq	_objc_autoreleasePoolPush
	movq	%rax, -32(%rbp)                 ## 8-byte Spill
	leaq	L__unnamed_cfstring_(%rip), %rdi
	movb	$0, %al
	callq	_NSLog
	movl	$5, -20(%rbp)
	movl	-20(%rbp), %esi
	movl	$1, %edi
	callq	_sum
	movl	%eax, %esi
	leaq	L__unnamed_cfstring_.2(%rip), %rdi
	movb	$0, %al
	callq	_NSLog
	movq	-32(%rbp), %rdi                 ## 8-byte Reload
	callq	_objc_autoreleasePoolPop
	xorl	%eax, %eax
	addq	$32, %rsp
	popq	%rbp
	retq
	.cfi_endproc
                                        ## -- End function
	.section	__TEXT,__cstring,cstring_literals
L_.str:                                 ## @.str
	.asciz	"Hello, World!"

	.section	__DATA,__cfstring
	.p2align	3                               ## @_unnamed_cfstring_
L__unnamed_cfstring_:
	.quad	___CFConstantStringClassReference
	.long	1992                            ## 0x7c8
	.space	4
	.quad	L_.str
	.quad	13                              ## 0xd

	.section	__TEXT,__cstring,cstring_literals
L_.str.1:                               ## @.str.1
	.asciz	"%d"

	.section	__DATA,__cfstring
	.p2align	3                               ## @_unnamed_cfstring_.2
L__unnamed_cfstring_.2:
	.quad	___CFConstantStringClassReference
	.long	1992                            ## 0x7c8
	.space	4
	.quad	L_.str.1
	.quad	2                               ## 0x2

	.section	__DATA,__objc_imageinfo,regular,no_dead_strip
L_OBJC_IMAGE_INFO:
	.long	0
	.long	64

.subsections_via_symbols
	.section	__TEXT,__text,regular,pure_instructions
	.build_version macos, 13, 0	sdk_version 13, 1
	.globl	_sum                            ## -- Begin function sum
	.p2align	4, 0x90

看这几行,他们是汇编指令不是汇编代码
.section指令指定了接下来会执行哪一个段
.globl指令说明_main是一个外部符号。这就是我们的main()函数。这个函数对外部是可见的,因为系统要调用它来运行可执行文件。
.p2align指令指出了后面代码的对齐方式。在我们的代码中,后面的代码会按照 16(2^4) 字节对齐,如果需要的话,用 0x90 补齐。

汇编

在这一阶段,汇编器将上一步生成的可读的汇编代码转化为机器代码。最终产物就是 以 .o 结尾的目标文件。使用Xcode构建的程序会在DerivedData目录中找到这个文件。

链接

这一阶段是将上个阶段生成的目标文件和引用的静态库链接起来,最终生成可执行文件,链接器解决了目标文件和库之间的链接。
可执行文件类型为 Mach-O 类型,在 MAC OS 和 iOS 平台的可执行文件都是这种类型
至此,编译过程结束。
在这里插入图片描述

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

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

相关文章

Java基本数据类型默认初始化值测试

1.基本数据类型为成员变量 public class Test {boolean b;char c;byte bt;short s;int i;long l;float f;double d;public static void main(String[] args) {Test t new Test();System.out.println("boolean类型默认值&#xff1a;" t.b);System.out.println(t.c…

Android 中 cgroup抽象层详解

源码基于&#xff1a;Android R 0. 前言 在之前的博文《Android中app freezer原理》一文中&#xff0c;我们看到冻结器的enable、freeze、unfreeze 都是通过 cgroup 的机制进行处理。 本文将介绍下 Android 中 cgroup 的抽象层基本信息和使用方式。 1. cgroups 简介 cgroups…

Linux常用命令——dump命令

在线Linux命令查询工具 dump 用于备份ext2或者ext3文件系统 补充说明 dump命令用于备份ext2或者ext3文件系统。可将目录或整个文件系统备份至指定的设备&#xff0c;或备份成一个大文件。 语法 dump(选项)(参数)选项 -0123456789&#xff1a;备份的层级&#xff1b; -b&…

layui框架学习(32:表单)

Layui中的分页模块laypage支持在指定的容器中显示分页信息&#xff0c;主要是指总页数、当前页面、上一页/下一页等信息&#xff0c;可以配合其它组件实现分页显示数据或者分页显示内容&#xff0c;事实上&#xff0c;Layui中的动态数据表格等模块都附带有显示分页功能。laypag…

hive之文件格式与压缩

hive文件格式&#xff1a; 概述&#xff1a; 为Hive表中的数据选择一个合适的文件格式&#xff0c;对提高查询性能的提高是十分有益的。Hive表数据的存储格式&#xff0c;可以选择text file、orc、parquet、sequence file等。 文本文件&#xff1a; 文本文件就是txt文件&…

【算法 -- LeetCode】(025) K 个一组翻转链表

1、题目 给你链表的头节点 head &#xff0c;每 k 个节点一组进行翻转&#xff0c;请你返回修改后的链表。 k 是一个正整数&#xff0c;它的值小于或等于链表的长度。如果节点总数不是 k 的整数倍&#xff0c;那么请将最后剩余的节点保持原有顺序。 你不能只是单纯的改变节点…

企业服务器数据库中了360后缀勒索病毒怎么解决加密的方式有哪些

随着网络安全问题日益突出&#xff0c;企业服务器数据库成为黑客攻击的重要目标之一。近日&#xff0c;我们收到很多企业的求助&#xff0c;企业的服务器数据库遭到了360后缀的勒索病毒攻击&#xff0c;导致企业内部的许多重要数据被加密无法读取使用&#xff0c;严重影响了企业…

05网络模型练习题

新版MATLAB中&#xff0c;图论工具箱的函数进行了更换 MATLAB中文参考文档&#xff1a;https://ww2.mathworks.cn/help/matlab/index.html 遍历、最短路径和循环 bfsearch 广度优先图搜索 dfsearch 深度优先图搜索 shortestpath 两个单一节点之间的最短路径 shortestpathtree …

Flask 笔记

Flask 笔记 一、Flask介绍 1、学习Flask框架的原因 2020 Python 开发者调查结果显示Flask和Django是Python Web开发使用的最主要的两个框架。 2、Flask介绍 ​ Flask诞生于2010年&#xff0c;是Armin ronacher用Python 语言基于Werkzeug工具箱编写的轻量级Web开发框架。 ​…

企业数字化转型要转什么?怎么转?_光点科技

随着科技的飞速发展和互联网的普及&#xff0c;数字化转型已成为现代企业持续发展和提高竞争力的必经之路。数字化转型是指企业在信息技术的驱动下&#xff0c;将传统的业务模式、流程和文化进行全面优化和改造&#xff0c;以适应数字化时代的新要求。 一、企业数字化转型的重要…

二极管常见参数

写在前面&#xff1a; 本文章旨在总结备份、方便以后查询&#xff0c;由于是个人总结&#xff0c;如有不对&#xff0c;欢迎指正&#xff1b;另外&#xff0c;内容大部分来自网络、书籍、和各类手册&#xff0c;如若侵权请告知&#xff0c;马上删帖致歉。 目录 肖特基二极管PN…

CCLINK IE转MODBUS-TCP网关cclink与以太网的区别

你是否曾经遇到过需要同时处理CCLINK IE FIELD BASIC和MODBUS两种数据协议的情况&#xff1f;远创智控的YC-CCLKIE-TCP网关可以帮助你解决这个问题。 远创智控YC-CCLKIE-TCP网关可以分别从CCLINK IE FIELD BASIC一侧和MODBUS一侧读写数据&#xff0c;然后将数据存入各自的缓冲区…

SpringBoot整合可视化监控工具——SpringBoot Admin

目录 父项目 子模块——server 子模块——client 开放监控指标 性能 环境 日志配置 映射 问题一: 明明项目启动却还是Instance 是 office 问题二: 记springboot中yml文件最后一位是星号*的写法 在说软件监控之前&#xff0c;我们先来了解一下软件的发展史&#xff0c…

【字符流】案例:文件到集合

案例&#xff1a;文件到集合 1.需求&#xff1a; 把文本文件中的数据读取到集合&#xff0c;并遍历集合。要求&#xff1a;文件中的每一行数据是一个集合元素 2.思路 创建字符缓冲输入流对象创建ArrayList集合对象调用字符缓冲输入流对象的方法读数据把读取到的字符串数据存…

【Lingo软件求解案例一:MCM1988:B题两辆平板车的装货问题】

题目描述 有 七种规格的包装箱 要装到 两辆平板车 上 包装箱的宽和高是一样的 但是厚度&#xff08;t 厘米&#xff09;和重量&#xff08;w 公斤&#xff09;是不一样的 表格中给出每种包装箱的厚度、重量和数量 每辆平板车有10.2米长的地方可以用来装包装箱 像面包片一样 载…

【PostgreSQL内核学习(七)—— 查询规划(生成路径)】

查询规划——预处理 生成路径 声明&#xff1a;本文的部分内容参考了他人的文章。在编写过程中&#xff0c;我们尊重他人的知识产权和学术成果&#xff0c;力求遵循合理使用原则&#xff0c;并在适用的情况下注明引用来源。 本文主要参考了《PostgresSQL数据库内核分析》一书 生…

进阶C语言——动态内存管理

好久不见&#xff0c;今天我们学习一下C语言的动态内存管理&#xff0c;这是一个和指针一样重要的章节&#xff0c;所以大家一定要好好学这章。 1. 为什么存在动态内存分配 我们已经掌握的内存开辟方式有&#xff1a; int val 20;//在栈空间上开辟四个字节 char arr[10] {0};…

Obsidian同步到Notion

插件介绍 将Obsidian的内容同步到Notion需要使用一个第三方插件"Obsidian shared to Notion"EasyChris/obsidian-to-notion: Share obsidian markdown file to notion and generate notion share link 同步obsdian文件到notion&#xff0c;并生成notion分享链接&am…

数据仓库表设计理论

数据仓库表设计理论 数仓顾名思义是数据仓库&#xff0c;其数据来源大多来自于业务数据(例如:关系型数据库)&#xff0c;当设计数仓中表类型时(拉链表、增量表、全量表、流水表、切片表)时&#xff0c;应先观察业务数据的特点再设计数仓表结构 首先业务数据是会不断增长的-即…