2

只是好奇如何开始了解 iOS 下的 ARM。任何帮助都会非常好。

4

1 回答 1

20

在我看来,最好的开始方式是

  1. 编写一小段 C 代码(后来的 Objective-C)
  2. 看对应的汇编代码
  3. 找出足以理解汇编代码的内容
  4. 重复!

为此,您可以使用 Xcode:

  1. 创建一个新的 iOS 项目(单视图应用程序很好)
  2. 添加一个C文件scratchpad.c
  3. 在项目构建设置中,将“生成调试符号”设置为“否”
  4. 确保目标是 iOS 设备,而不是模拟器
  5. 打开scratchpad.c并打开助手编辑器
  6. 将助手编辑器设置为Assembly并选择“Release”

示例 1

将以下函数添加到暂存器.c:

void do_nothing(void)
{
    return;
}

如果您现在在助手编辑器中刷新程序集,您应该会看到很多以点(指令)开头的行,然后是

_do_nothing:
@ BB#0:
    bx  lr

让我们暂时忽略这些指令,看看这三行。通过在互联网上进行一些搜索,您会发现这些行是:

  1. 标签(以下划线为前缀的函数名称)。
  2. 只是编译器发出的注释。
  3. 返回声明。b方法分支,暂时忽略(x它与指令集之间的切换有关),并且lr是链接寄存器,调用者存储返回地址。

示例 2

让我们稍微加强一下并将代码更改为:

extern void do_nothing(void);

void do_nothing_twice(void)
{
    do_nothing();
    do_nothing();
}

保存并刷新程序集后,您将获得以下代码:

_do_nothing_twice:
@ BB#0:
    push    {r7, lr}
    mov r7, sp
    blx _do_nothing
    pop.w   {r7, lr}
    b.w _do_nothing

同样,通过在互联网上进行一些搜索,您会发现每一行的含义。需要做更多的工作,因为打了两个电话:第一个电话需要返回给我们,所以我们需要改变lr. 这是由blx指令完成的,它不仅跳转到_do_nothing,而且将下一条指令的地址(返回地址)存储在 中lr

因为我们改变了返回地址,所以我们必须将它存储在某个地方,所以它被压入堆栈。第二个跳转有一个.w后缀,但现在让我们忽略它。为什么函数看起来不像这样?

_do_nothing_twice:
@ BB#0:
    push    {lr}
    blx _do_nothing
    pop.w   {lr}
    b.w _do_nothing

这也可以,但在 iOS 中,约定是将帧指针存储在r7. 帧指针指向堆栈中我们存储前一个帧指针和前一个返回地址的位置。

所以代码的作用是:首先,它压入堆栈,r7然后lrr7指向新的堆栈帧(位于堆栈顶部,并sp指向堆栈顶部),然后它分支第一次,然后它恢复r7lr最后它第二次分支。bx lr不需要最后的A ,因为被调用的函数将返回到lr,它指向我们的调用者。

示例 3

让我们看最后一个例子:

void swap(int *x, int *y)
{
    int temp = *x;
    *x = *y;
    *y = temp;
}

汇编代码是:

_swap:
@ BB#0:
    ldr r2, [r0]
    ldr r3, [r1]
    str r3, [r0]
    str r2, [r1]
    bx  lr

通过一些搜索,您将了解到参数和返回值存储在寄存器r0-r3中,并且我们可以自由地使用它们进行计算。代码的作用很简单:它将r0andr1指向的值加载到r2and中r3,然后以交换的顺序将它们存储回来,然后分支回来。

等等

就是这样:编写小片段,获取足够的信息以大致了解每一行中发生的事情,然后重复。希望有帮助!

于 2012-10-31T22:10:43.643 回答