在i386,ARM程序集中从堆栈中删除参数

时间:2012-04-02 04:56:10

标签: objective-c assembly x86 arm block

我正在使用一些蹦床函数与C / Objective-C中的高级调用一起使用,在Apple does it的方式上略有不同。

如果您对Objective-C IMP的工作方式一无所知,它基本上是一个函数指针,其中前两个参数是消息的接收者和消息选择器的名称,例如{ {1}}。更新版本的运行时允许使用C块在运行时合成方法实现,如void(*)(id obj, SEL sel, ...)。这些块没有选择器;运行时创建一个trampoline,用接收器覆盖选择器,接收器用块指针覆盖,然后继续执行它。

我想做一些模糊相似的事情,其中​​包括前两个参数中没有 ,所以这个块的参数与传统方法发送的参数完全相同,加上用于执行目的的块指针,即void(^)(id obj, ...)。这只需要复制块指针,我想摆脱一个参数。

void(*)(Block *, ...)

这是我在ARM上的程序集:

__a1a2_tramphead_argonly:
    popl %eax
    andl $0xFFFFFFF8, %eax
    subl $0x1000, %eax
    movl 4(%esp), %ecx // self -> ecx
    movl %ecx, 8(%esp) // ecx -> _cmd
    movl (%eax), %ecx // blockPtr -> ecx
    movl %ecx, 4(%esp) // ecx -> self
    jmp  *12(%ecx) // tail to block->invoke

x86_64存在类似的代码;上面的代码直接来自Apple。对于个人知识,我想知道从哪里开始切除一个参数,以便第一个参数(曾经是接收者)是块文字,第二个是第一个真正的参数,依此类推。

我在ASM非常无聊,所以非常感谢任何帮助。我尝试过的所有东西都以越来越有趣的方式被炸毁。提前谢谢。

1 个答案:

答案 0 :(得分:2)

iOS ABI有效地整合了AAPCS并且只定义了差异,因此您首先需要从http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ihi0042d/index.html开始。然后阅读Apple的iOS ABI功能调用指南(我认为您需要付费的iOS开发中心会员才能访问)。

总结规则,调用ObjC IMP:

  • 自我进入R0
  • _cmd进入R1
  • 第一个int或指针参数在R2
  • 第二个int或指针参数在R3
  • 所有进一步的参数都在堆栈上

所以,如果你只查看最多2个参数的参数,没有一个浮点/ int64_t / struct,删除self和_cmd参数只是改组R0-R4的问题:

mov r0, r2
mov r1, r3

或者,要编写一个函数,在转发到IMP之前需要两个参数并且将self和_cmd加入,这就是:

mov r3, r1
mov r2, r0
ldr r1, [address of _cmd]
ldr r0, [address of self]

在Apple的块蹦床的情况下,他们正在做的是将[foo performBlockOnSelf:block]的调用有效地转换为[block foo]。如你所说,块指针以r0(通常的自身位置)结束,目标参数foo以r1(通常的_cmd位置)结束。如果块真的是IMP,当然,这将是无稽之谈,因为foo不是SEL,但它们不是,所以这不是问题。

从你的陈述“我想要做一些模糊相似的事情,其中​​涉及没有前两个参数中的任何一个,因此这个块的参数与传统方法发送的参数完全相同,”我不是完全清楚你要做的两件事中的哪一件:

  1. 定义一个“委托”对象(以C#术语表示),基本上是一个在构建时烘焙目标的块。在这种情况下,您将要从某个委托表中查找r0(块指针)和r1(目标),而不仅仅是块指针。但是你不会有任何编译器帮助设置该表 - 这意味着你可以设置它并在纯C中访问它,它将同样方便并构建自定义程序集蹦床。 (你甚至可以通过ObjC词典来实现,在实践中可能无关紧要的性能损失。)

  2. 将常规消息转换为块,这涉及到存储所有内容,以便当Apple的trampoline代码尝试调用块时,它最终会使用传统方法发送参数而不是块参数。如果这是你的目标,那么在消息周围使用块包装器而不是尝试将消息转换为块更简单,更安全,我怀疑效率或灵活性成本是否重要。