Я работаю с некоторыми функциями батута для использования с высокоуровневыми вызовами в C/Objective-C, небольшой поворот в пути Apple делает это.
Если вы вообще знакомы с тем, как работает Objective-C IMP
, это в основном указатель на функцию, где первые два аргумента — получатель сообщения и имя селектора сообщения, например as void(*)(id obj, SEL sel, ...)
. Более поздние версии среды выполнения позволяют синтезировать реализации методов во время выполнения с использованием блоков C, например void(^)(id obj, ...)
. Эти блоки не имеют селектора; среда выполнения создает батут, который перезаписывает селектор с помощью получателя, получателя с указателем блока, а затем переходит к его выполнению.
Я хочу сделать что-то отдаленно похожее, которое предполагает отсутствие ни из первых двух аргументов, чтобы аргументы этого блока были точно такими же, как аргументы традиционного метода send, плюс блок указатель для выполнения, т. е. 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
Вот сборка, которая у меня есть на 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]
Аналогичный код существует для x86_64; приведенный выше код пока что напрямую от Apple.Для личного сведения мне интересно, с чего начать с вырезания аргумента, чтобы первый аргумент (то, что раньше было получателем) был литералом блока, второй - первым реальным аргументом и так далее.
Я невероятный нуб в ASM, поэтому любая помощь приветствуется. Все, что я пробовал, взрывалось все более интересными способами. Заранее спасибо.