zwa*_*ski 8 x86 assembly arm block objective-c
我正在使用一些蹦床函数与C/Objective-C中的高级调用一起使用,这与Apple的方式略有不同.
如果您对Objective-C的IMP工作方式一无所知,它基本上是一个函数指针,其中前两个参数是消息的接收者和消息选择器的名称,例如void(*)(id obj, SEL sel, ...).更新版本的运行时允许使用C块在运行时合成方法实现,例如void(^)(id obj, ...).这些块没有选择器; 运行时创建一个trampoline,用接收器覆盖选择器,接收器用块指针覆盖,然后继续执行它.
我想做一些模糊相似的事情,其中涉及没有前两个参数中的任何一个,因此该块的参数与传统方法发送的参数完全相同,加上用于执行目的的块指针,即void(*)(Block *, ...).这只需要复制块指针,我想摆脱一个参数.
__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
Run Code Online (Sandbox Code Playgroud)
这是我在ARM上的程序集:
__a1a2_tramphead_argonly:
// calculate the trampoline's index (512 entries, 8 bytes each)
#ifdef _ARM_ARCH_7
// PC bias is only 4, no need to correct with 8-byte trampolines
ubfx r1, r1, #3, #9
#else
sub r1, r1, #8 // correct PC bias
lsl r1, r1, #20
lsr r1, r1, #23
#endif
// load block pointer from trampoline's data
adr r12, __a1a2_tramphead_argonly // text page
sub r12, r12, #4096 // data page precedes text page
ldr r12, [r12, r1, LSL #3] // load block pointer from data + index*8
// shuffle parameters
mov r1, r0 // _cmd = self
mov r0, r12 // self = block pointer
// tail call block->invoke
ldr pc, [r12, #12]
Run Code Online (Sandbox Code Playgroud)
x86_64存在类似的代码; 上面的代码直接来自Apple.对于个人知识,我想知道从哪里开始删除一个参数,以便第一个参数(曾经是接收者)是块文字,第二个是第一个真正的参数,依此类推.
我在ASM非常高兴,所以非常感谢任何帮助.我尝试过的所有东西都以越来越有趣的方式被炸毁.提前致谢.
iOS ABI 有效地合并了 AAPCS,并且仅定义了差异,因此您需要从http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ihi0042d/index 开始。首先是HTML。然后阅读 Apple 的 iOS ABI 函数调用指南(我认为您需要付费的 iOS 开发中心会员才能访问)。
总结一下调用 ObjC IMP 的规则:
因此,如果您只查看最多 2 个参数的参数,其中没有一个是浮点/int64_t/struct,则删除 self 和 _cmd 参数只需对 R0-R4 进行改组即可:
mov r0, r2
mov r1, r3
Run Code Online (Sandbox Code Playgroud)
或者,要编写一个接受两个参数并在转发到 IMP 之前填充 self 和 _cmd 的函数,就是这样:
mov r3, r1
mov r2, r0
ldr r1, [address of _cmd]
ldr r0, [address of self]
Run Code Online (Sandbox Code Playgroud)
就 Apple 的块蹦床而言,他们正在做的是将 [foo PerformBlockOnSelf:block] 的调用有效地转换为 [block foo]。正如您所说,块指针以 r0 (通常的 self 位置)结束,目标参数 foo 以 r1 (通常的 _cmd 位置)结束。如果块真的是 IMP,当然,这将是无稽之谈,因为 foo 不是 SEL,但它们不是,所以这不是问题。
从你的陈述“我想做一些模糊相似的事情,其中涉及不包含前两个参数中的任何一个,以便该块的参数与传统方法发送的参数完全相同”,我不完全清楚哪个您正在尝试做的两件事:
定义一个“委托”对象(用 C# 术语来说),基本上是一个在构造时包含其目标的块。在这种情况下,您需要从某个委托表中查找 r0(块指针)和 r1(目标),而不仅仅是块指针。但是您不会有任何编译器帮助您设置该表,这意味着您可以在纯 C 中设置并访问它,并且它与构建自定义汇编蹦床一样方便。(您甚至可以通过 ObjC 字典来完成此操作,但在实践中可能会造成一些性能损失,但这并不重要。)
将常规消息转换为块,这涉及到存储所有内容,以便当 Apple 的 Trampoline 代码尝试调用该块时,它最终会使用传统方法发送参数而不是块参数。如果这是您的目标,那么仅在消息周围使用块包装器而不是尝试将消息转换为块会更简单、更安全,而且我怀疑效率或灵活性成本是否重要。
| 归档时间: |
|
| 查看次数: |
601 次 |
| 最近记录: |