软件信息网 前沿探讨 iOS汇编入门必看

iOS汇编入门必看

一、汇编中指令和伪指令的区别?

指令:机器码助记符,每条指令会生成机器码,由CPU读取执行。
伪指令(伪操作):没有与之对应的机器码,非可执行指令,需要汇编器来解释。

二、OS X 伪指令:

所有汇编程序伪指令(GUN汇编器通用伪指令)的名称都是以‘.’开头。名称大多数不区分大小写,通常使用小写字母表示。

1、定义数据伪指令(下面数据类型空间大小为arm64下的size)

  1. .byte // 定义1个字节大小的变量
  2. .short // 定义2个字节大小的变量
  3. .int // 定义4个字节大小的变量
  4. .long // 定义8个字节大小的变量
  5. .quad(大数) // 定义8个字节大小的变量
  6. .ascii // 定义字符串以非零结束,行尾需要添加’’
  7. .asciz // 定义字符串以零结束

2、汇编控制
(1)条件判断伪指令:

  1. .if /* 条件判断开始 */
  2. .else
  3. .endif /* 条件判断结束 */

(2)宏定义伪指令:

  1. .macro 宏名 /* 宏定义开始 */
  2. .endmacro /* 宏定义结束 */

3、定义一个section
Mach-O文件中的数据或指令都是存在segment中(可通过MachOView
查看),一个segment由零个或者多个section组成,segment 名称用双下划线开头 +全字母大写(例如:DATA)表示,section名称用双下划线开头+全字母小写(例如:data)表示

  1. .section segname , sectname [[[ , type ] , attribute ] , sizeof_stub ]
  2. 示例:
  3. .section __DATA __data // 等价于 .data
  4. .section __TEXT __text // 等价于 .text
  5. .section __TEXT __const // 等价于 .const
  6. .section __TEXT,__cstring, cstring_literals // 等价于 .cstring

更多内容详见文章末尾参考资料:OS X Assembler Directives

4、对齐align
用fill_expression(指定,则必须是绝对的,未指定,则为零)使当前位置与align_expression边界对齐 。align_expression是介于0到15之间2的幂(例如:.align 3意味着2 ^ 3(8)字节对齐)。

  1. .align align_expression [,1byte_fill_expression [,max_bytes_to_fill]]
  2. 示例:
  3. .align 3 // 相当于2 ^ 3(8)字节对齐

5、填充fill
重复拷贝指定字节,重复repeat_expression次,repeat_expression必须是大于0的绝对表达式,fill_size以字节为单位且必须有值,值可以为1,2,4或者8。fill_expression可以是任何绝对表达式(它会被截断为填充大小)。

  1. .fill repeat_expressionfill_sizefill_expression
  2. 示例:
  3. .fill 16, 1, 0

6、私有外部符号private_extern
.private_extern可以把symbol_name变成私有外部符号。当链接编辑器将此模块与其他模块组合在一起(且keep_private_externs 命令行选项未指定)时,该符号会将它从全局更改为静态。如果在同一符号上同时使用了.private_extern和.globl汇编程序指令,则和只使用 .private_extern 效果一样。

  1. .private_extern symbol_name
  2. 示例
  3. .private_extern _objc_restartableRanges

7、标签
标签是用来标记程序和数据对象的位置的标识符,每个标签由一个标识符和一个终止冒号组成,标识符中的字母区分大小写,格式如下:

  1. identifier: [ identifier: ] ...
  2. 示例:
  3. L_method_invoke_small:

8、其他伪指令

  1. .abort // 使汇编器忽略进一步的输入并退出处理
  2. .text // 表示代码段
  3. .data // 表示数据段
  4. .globl // 声明为全局符号
  5. .include // 引入头文件
  6. .set // 把符号定义成一个表达式,相当于 symbol_name=absolute_expression

三、注释风格:

1、特定平台单行注释:x86-64使用#开头,arm使用;开头

  1. add x10, x10, #0x1 # This a comment line 注释代码:x86-64
  2. add x10, x10, #0x1 ; This a comment line 注释代码:arm

2、GNU通用语法
(1)多行注释

  1. /*
  2. 注释代码
  3. */

(2)单行注释

  1. MOV R1,#15 // Load R1 with 15

四、寄存器

典型的CPU主要组成部分:运算器、控制器、寄存器。
寄存器是CPU用来暂存指令、数据、地址的电脑存储器。它的存储容量有限,是CPU中最快的可读写存储器。

1、ARM64中常见寄存器
(1)31个通用寄存器R0-R30,每个寄存器可以通过以下方式访问:

  • 通过X0-X30访问的时候,它是一个64位的通用寄存器
  • 通过W0-W30访问的时候,它是一个32位(访问低32位)的通用寄存器
    image

    X30通用寄存器被用作过程调用链接寄存器。

(2)SP:64位专用堆栈指针寄存器,使用SP/WSP对堆栈指针寄存器进行访问,WSP表示访问堆栈指针的低32位。

  1. 没有X31W31的寄存器,由指令来决定寄存器31是堆栈指针或者是零寄存器。当用作堆栈指针时,将其称为SP。当用作零寄存器时,在32位环境中将其称为WZR,在64位环境中将其称为XZR
  2. ZR:零寄存器,当它用作源寄存器时,零寄存器读取为零,当它用作目标寄存器时,则丢弃结果。

(3)PC:64位程序计数器,用来保存当前指令地址。软件不能直接写入程序计数器。
(4)SIMD&FP:32个向量&浮点寄存器,V0-V31。每个寄存器可以通过以下方式访问:

  • 通过Q0-Q31访问的时候,它是一个128位寄存器
  • 通过D0-D31访问的时候,它是一个64位(访问低64位)寄存器
  • 通过S0-S31访问的时候,它是一个32位(访问低32位)寄存器
  • 通过H0-H31访问的时候,它是一个16位(访问低16位)寄存器
  • 通过B0-B31访问的时候,它是一个8位(访问低8位)寄存器
  • 元素的128位向量
    image

2、通用寄存器中的参数
出于函数调用目的,通用寄存器被分为4组:

  • X0-X7:参数寄存器,用于将参数传递给函数并返回结果,返回值通过X0返回。
  • X9-X15:调用者保存的临时寄存器。
  • X19-X29:被调用者保存的寄存器。
  • X8,X16-X18,X29,X30:特殊用途的寄存器
  1. X8:间接结果寄存器,用于传递间接结果的地址位置,如函数返回了一个大型结构。
  2. X16X17:表示IP0IP1,过程内调用临时寄存器。
  3. X18是平台寄存器,保留供平台ABI使用
  4. X29:帧指针寄存器(FP
  5. X30:链接寄存器(LR

image

在lldb中可以通过register read查看各寄存器状态

五、常见计算指令

1、移动指令:MOV,寄存器和寄存器之间传值

  1. mov x2, x16 ;把x16的值传递给寄存器x2

2、算术运算指令: 加(ADD)、减(SUB)

  1. add x1, x2, x3 ;把x2+x3的值传递给寄存器x1
  2. sub x1, x2, x3 ;把x2-x3的值传递给x1

3、逻辑运算指令:与(AND)、或(ORR)、异或(EOR)

  1. and x1x1,#0xf ;把x1中的值与0xf按位与后传递给x1
  2. orr x1x1,#6 ;把x1中的值与6按位与后传递给x1
  3. eor x1x1,#0xf ;把x1中的值与0xf按位异或后传递给x1

4、桶形移位器操作指令:LSL、LSR、ASR、ROR

  1. LSL:逻辑左移
  2. LSR:逻辑右移
  3. ASR:算术右移
  4. ROR:循环右移

5、存/取数据指令:STR(寄存器加载到内存中)、取数据LDR (把内存中的数据传递给寄存器)

  1. str x1, [sp, #0x4] ;把x1寄存器的数据传递给sp+0x4地址值指向的内存空间
  2. ldr x1, [sp, #0x4] ;把sp+0x4地址值内的数据传递给寄存器x1

6、栈操作指令:STP(入栈)、LDP(出栈)

  1. stp x0, x1, [sp, #0x4]
  2. ldp x0, x1, [sp, #0x4]

7、比较指令:CMP,CBZ,CBNZ,TBZ,TBNZ

  1. cmp x0,x1 ;把x0的内容和x1的内容进行比较,根据结果更新条件标志,并丢弃结果,相当于subs xzr x0, x1
  2. cbz x0, LGetImpMiss1 ;如果x0等于0就跳转到LGetImpMiss1,不影响条件标志
  3. cbnz x0, LGetImpMiss1 ;如果x0不等于0就跳转到LGetImpMiss1,不影响条件标志
  4. tbz x0, #20, LGetImpMiss1 ;寄存器中指定位某个值是否为零,如果x0中的第20位(x0[20])等于0就跳转到LGetImpMiss1,不影响条件标志
  5. tbnz x0, #20, LGetImpMiss1 ;寄存器中指定位比较,如果x0中的第20位(x0[20])不等于0就跳转到LGetImpMiss1,不影响条件标志

8、跳转指令:
B:无返回跳转,配合CMP使用
BL:带返回的跳转,会将返回地址存储到寄存器x30,说明这是一个子程序调用

  1. 示例1
  2. b LLookupExample ;直接跳转到LLookupExample
  3. 示例2:配合cmp使用
  4. cmp x0,#6
  5. b.eq LReturnZeroExample ;如果x0等于6,则跳转LReturnZeroExample
  6. 条件代码:
  7. b.eq ;等于
  8. b.ne ;不等于
  9. b.le ;有符号的小于或等于
  10. b.ge ;有符号的大于或等于
  11. b.lt ;有符号小于
  12. b.gt ;有符号大于
  13. 示例3
  14. bl lookUpFindExample

9、子程序返回指令:RET(返回地址存储在x30)

  1. LTestExample:
  2. mov x2, #0
  3. ret

10、寻址指令:ADRP(将PC相对地址形成4KB页面,即:取指定label的基地址,存储到指定寄存器中)

  1. adrp x1, __example_handle@PAGE ;获取__example_handle所在页的基地址存储到x1寄存器中

11、无符号位域选取指令:UBFX(提取指定位)

  1. ubfx x11, x0, #60, #4 ;从源寄存器x0中提取4位,位置从60开始。即将x0中的60-63位复制到目标寄存器x11的最低有效位,并将该x11上的其他高位设置为零
六、参考资料

访问我的Github仓库查看更多精彩分享

作者: 软件定制开发

李铁牛,一直致力于企业客户软件定制开发,计算机专业毕业后,一直从事于互联网产品开发到现在。系统开发,系统源码:15889726201
上一篇
下一篇
联系我们

联系我们

15889726201

在线咨询: QQ交谈

邮箱: 187395037@qq.com

工作时间:周一至周五,9:00-17:30,节假日休息

关注微信
微信扫一扫关注我们

微信扫一扫关注我们

关注微博
返回顶部