objective c - maestria - ¿Cómo implementar una función IMP que devuelve un tipo de estructura grande determinada en tiempo de ejecución?
gestion del conocimiento tesis doctoral (1)
Fondo: CamelBones registra clases de Perl con el tiempo de ejecución de Objective-C. Para hacer esto, todos los métodos Perl se registran con la misma función IMP; esa función examina sus argumentos self
& _cmd
para encontrar qué método de Perl llamar.
Esto ha funcionado bastante bien durante varios años, para los mensajes que se enviaron con objc_msgSend
. Pero ahora quiero agregar soporte para regresar los tipos de estructura flotante y grande de los métodos de Perl. El punto flotante no es difícil; Simplemente escribiré otro IMP que devuelve el doble, para manejar los mensajes enviados con objc_msgSend_fpret
.
La pregunta es qué hacer con objc_msgSend_stret
. Escribir un IMP
por separado para cada posible tipo de retorno de estructura no es práctico, por dos razones: Primero, porque incluso si lo hice solo para los tipos de estructura que se conocen en tiempo de compilación, es un número absurdo de funciones. Y segundo, porque estamos hablando de un marco que puede vincularse con cualquier código arbitrario de Objective-C y Perl, no conocemos todos los tipos de estructuras potenciales cuando se está compilando el marco.
Lo que espero hacer es escribir un único IMP
que pueda manejar cualquier tipo de devolución que se objc_msgSend_stret
través de objc_msgSend_stret
. ¿Podría escribirlo como return void
y tomar un argumento de puntero a un buffer de retorno, como el antiguo objc_msgSend_stret
fue declarado? Incluso si eso funcionara por ahora, ¿podría confiar en que continuará funcionando en el futuro?
Gracias por cualquier consejo: he estado trabajando en mi cerebro en este caso. :-)
Actualizar:
Aquí está el consejo que recibí de uno de los ingenieros de tiempo de ejecución de Apple, en su lista de correo de objc:
Debe escribir el código de ensamblado para manejar este caso.
Su sugerencia falla en algunas arquitecturas, donde ABI para "función que devuelve vacío con un puntero a una estructura como primer argumento" difiere de "función que devuelve una estructura". (En i386, la persona que llama saca la dirección de la estructura de la pila en un caso y la llama en el otro caso). Es por
objc_msgSend_stret
se modificó el prototipo deobjc_msgSend_stret
.El código de ensamblado capturaría la dirección de retorno de estructura, la pasaría de contrabando a la llamada de función C sin estructura inversa sin alterar el resto de los parámetros, y luego haría la limpieza específica de ABI correcta al salir (
ret $4
en i386). Alternativamente, el código de ensamblaje puede capturar todos los parámetros. La maquinaria de reenvío hace algo como esto. Ese código podría estar en la base de código abierto CoreFoundation si desea ver cómo se ven las técnicas.
Dejaré esta pregunta abierta, en caso de que alguien haga una lluvia de ideas con una mejor idea, pero como esto proviene directamente del propio "controlador de tiempos de ejecución" de Apple, creo que es probablemente una respuesta tan autorizada como la que pueda obtener. Es hora de desempolvar los manuales de referencia x86 y eliminar el óxido de mi ensamblador-fu, supongo ...
Parece que el ingeniero de Apple tiene razón: el único camino a seguir es el código de ensamblaje. Aquí hay algunos consejos útiles para comenzar:
- Desde el código de tiempo de ejecución de Objective-C: los bloques de mensajes de mensajería hechos a mano i386 y x86_64 para los diversos métodos de mensajería.
- Una respuesta SO que proporciona una descripción general del despacho.
- Una revisión en profundidad del mecanismo de envío con un análisis línea por línea del código de ensamblaje
Espero eso ayude.