Удаление аргументов из стека в i386, сборка ARM - PullRequest
8 голосов
/ 02 апреля 2012

Я работаю с некоторыми функциями батута для использования с вызовами более высокого уровня в C / Objective-C, небольшой поворот на пути Apple делает это .

Если вы вообще знакомы с тем, как работает Objective-C IMP, это в основном указатель на функцию, где первые два аргумента являются получателем сообщения и именем селектора сообщения, например 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, поэтому любая помощь очень ценится. Все, что я пробовал, взорвалось все более интересными способами. Заранее спасибо.

1 Ответ

2 голосов
/ 08 мая 2012

iOS ABI эффективно включает в себя AAPCS и определяет только различия, поэтому сначала вы должны начать с http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ihi0042d/index.html. Затем прочтите Руководство по вызову функции Apple ABI для iOS (которое, я думаю, для доступа к платному членству в iOS Dev Center).

Подводя итог правилам, позвонить в ObjC IMP:

  • само идет в R0
  • _cmd идет в R1
  • первый аргумент int или указатель идет в R2
  • второй аргумент int или указатель идет в R3
  • все дальнейшие аргументы помещаются в стек

Итак, если вы смотрите только аргументы с количеством параметров до 2, ни один из них с плавающей запятой / int64_t / struct, чтобы удалить аргументы self и _cmd, это просто вопрос перетасовки R0-R4:

mov r0, r2
mov r1, r3

Или, чтобы написать функцию, которая принимает два параметра и записывает себя и _cmd перед отправкой в ​​IMP, это просто так:

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

В случае блочного батута Apple они фактически преобразуют вызов [foo executeBlockOnSelf: block] в [block foo]. Как вы говорите, указатель блока заканчивается в r0 (обычная позиция self), а целевой параметр foo заканчивается в r1 (обычная позиция _cmd). Конечно, если бы блоки были действительно IMP, это было бы бессмыслицей, потому что foo не является SEL, но это не так, поэтому это не проблема.

Из вашего утверждения «Я хочу сделать что-то неопределенно похожее, что подразумевает отсутствие одного из первых двух аргументов, так что аргументы этого блока точно такие же, как аргументы традиционного метода send»: «Я не Совершенно ясно, какую из двух вещей вы пытаетесь сделать:

  1. Определить объект «делегат» (в терминах C #), в основном, блок с целевой целью, встроенной во время создания. В этом случае вы захотите найти как r0 (указатель блока), так и r1 (цель) из некоторой таблицы делегатов, а не только указатель блока. Но вам не понадобится помощь компилятора в настройке этой таблицы - это означает, что вы можете настроить ее и получить к ней доступ в чистом C, и это будет столь же удобно, как и создание собственного батута сборки. (Вы можете даже сделать это через словари ObjC, с некоторой потерей производительности, которая может не иметь значения на практике.)

  2. Превратите обычное сообщение в блок, который включает в себя сохранение всего, чтобы когда батутный код Apple пытался вызвать блок, он заканчивался традиционным методом отправки параметров вместо параметров блока. Если это ваша цель, проще и гораздо безопаснее просто использовать блок-оболочку вокруг сообщения вместо того, чтобы пытаться конвертировать сообщения в блоки, и я сомневаюсь, что это будет иметь значение для эффективности или гибкости.

...