2012-04-02 17 views
8

Estoy trabajando con algunas funciones de trampolín para utilizar con llamadas de alto nivel en C/Objective-C, un ligero giro en el camino Apple does it.Eliminando argumentos de la pila en i386, ensamblado ARM

Si está familiarizado con la forma en que funciona Objective-C IMP, básicamente es un puntero de función donde los dos primeros argumentos son el receptor de un mensaje y el nombre del selector de mensajes, como void(*)(id obj, SEL sel, ...). Las versiones más recientes del tiempo de ejecución permiten sintetizar implementaciones de métodos en tiempo de ejecución usando bloques C, como void(^)(id obj, ...). Estos bloques no tienen el selector; el tiempo de ejecución crea un trampolín que sobrescribe el selector con el receptor, el receptor con el puntero de bloque y luego ejecuta su ejecución.

Quiero hacer algo vagamente similar que no implica tener ya sea de los dos primeros argumentos, por lo que los argumentos a este bloque son exactamente los mismos que los argumentos del método send tradicional, más el indicador de bloque para la ejecución propósitos, es decir, void(*)(Block *, ...). Esto solo requiere copiar en el puntero del bloque, y supongo que eliminar un argumento.

__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 

Aquí está el ensamblaje tengo en 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] 
existe

código similar para el x86_64; el código anterior es, por lo tanto, directamente de Apple. Para conocimiento personal, me pregunto por dónde empezar con la eliminación de una discusión, de modo que el primer argumento (lo que solía ser el receptor) es el literal del bloque, el segundo es el primer argumento real, y así sucesivamente.

Soy increíblemente inocente en ASM, por lo que cualquier ayuda es muy apreciada. Todo lo que probé se ha reventado de maneras cada vez más interesantes. Gracias por adelantado.

+1

Cuidado, muchas distribuciones de Linux se encuentran actualmente en el proceso de pasar al ARM Hard-Float ABI. Eso lo romperá completamente por completo, de nuevo. – ams

+0

Eso es interesante, lo tendré en cuenta para el futuro. Sin embargo, esto se dirige principalmente a Darwin. ¡Gracias! Editar: Eso significa ARMV6 y ARMV7, por el momento, al menos. – zwaldowski

Respuesta

2

El iOS ABI incorpora efectivamente el AAPCS y solo define las diferencias, por lo que primero querrá comenzar con http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ihi0042d/index.html. Luego, lea la Guía de llamadas a funciones de Apple ABI (para la que creo que necesita una membresía de iOS Dev Centre para acceder).

Resumiendo las reglas, para llamar a un ObjC IMP:

  • auto va en R0
  • _cmd va en R1
  • primer argumento int o puntero va en R2
  • segundo argumento int o puntero va en R3
  • todos los argumentos adicionales van a la pila

Por lo tanto, si sólo está buscando en argumentos con hasta 2 params, ninguno de ellos Punto/int64_t/estructura flotante, para eliminar los argumentos auto y _cmd es sólo una cuestión de barajar R0-R4:

mov r0, r2 
mov r1, r3 

O escribir una función que toma dos parametros y abarrota el yo y _cmd en antes de reenviar a un IMP, es sólo esto:

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

en el caso de trampolín bloque de Apple, lo que están haciendo está transformando una llamada a [foo performBlockOnSelf: block] en, de hecho, [block foo].Como dices, el puntero de bloque termina en r0 (la posición del self habitual) y el parámetro objetivo foo termina en r1 (la posición usual de cmd). Si los bloques fueran realmente IMP, por supuesto, esto no tendría sentido, porque foo no es un SEL, pero no lo son, así que no es un problema.

De su declaración "Quiero hacer algo vagamente similar que implica no tener ninguno de los dos primeros argumentos, para que los argumentos de este bloque sean exactamente los mismos que los del método tradicional de enviar," Estoy no del todo claro cuál de las dos cosas que estamos tratando de hacer:

  1. definir un objeto "delegado" (en términos de C#), básicamente un bloque con su objetivo al horno en el tiempo de construcción. En este caso, querrá buscar tanto r0 (el puntero de bloque) como r1 (el objetivo) de alguna tabla de delegados, en lugar de solo el puntero de bloque. Pero no vas a tener ningún compilador que ayude a configurar esa tabla, lo que significa que puedes configurarla y acceder a ella en C puro y que será igual de conveniente y crear un trampolín de ensamblaje personalizado. (Incluso podría hacerlo a través de los diccionarios ObjC, con alguna pérdida de rendimiento que puede no importar en la práctica.)

  2. Convierta un mensaje normal en un bloque, que implica obtener todo almacenado para que cuando el código trampolín de Apple intente llamar el bloque termina con el método tradicional de enviar parámetros en lugar de los parámetros del bloque. Si este es su objetivo, es más simple y más seguro usar un contenedor de bloques alrededor del mensaje en lugar de tratar de convertir los mensajes en bloques, y dudo que los costos de eficiencia o flexibilidad sean importantes.

+0

Mis disculpas por tomar tanto tiempo para responder, he repped y marcado esto como la respuesta adecuada, ya que es exactamente lo que quería en abril. Después de consultar con algunas personas que habían hecho cosas similares, fuimos con, e implementamos, una solución usando libffi. Eliminar ese segundo argumento (_cmd) era absolutamente imperativo. Entre la carga frontal de los argumentos (al menos en ARM), hubiera sido difícil hacerlo sin limitar a los usuarios de nuestro framework a cuatro argumentos o métodos no exhaustivos. Nuestra solución definitiva fue más rápida y funcional que su predecesora, así que estoy feliz. ¡Gracias! – zwaldowski

Cuestiones relacionadas