Introduction to ARM64v8
Niveles de Excepción - EL (ARM64v8)
En la arquitectura ARMv8, los niveles de ejecución, conocidos como Niveles de Excepción (ELs), definen el nivel de privilegio y las capacidades del entorno de ejecución. Hay cuatro niveles de excepción, que van desde EL0 hasta EL3, cada uno con un propósito diferente:
EL0 - Modo Usuario:
Este es el nivel menos privilegiado y se utiliza para ejecutar código de aplicación regular.
Las aplicaciones que se ejecutan en EL0 están aisladas entre sí y del software del sistema, mejorando la seguridad y estabilidad.
EL1 - Modo Núcleo del Sistema Operativo:
La mayoría de los núcleos de sistemas operativos se ejecutan en este nivel.
EL1 tiene más privilegios que EL0 y puede acceder a recursos del sistema, pero con algunas restricciones para garantizar la integridad del sistema.
EL2 - Modo Hipervisor:
Este nivel se utiliza para la virtualización. Un hipervisor que se ejecuta en EL2 puede gestionar múltiples sistemas operativos (cada uno en su propio EL1) que se ejecutan en el mismo hardware físico.
EL2 proporciona características para el aislamiento y control de los entornos virtualizados.
EL3 - Modo Monitor Seguro:
Este es el nivel más privilegiado y se utiliza a menudo para el arranque seguro y entornos de ejecución confiables.
EL3 puede gestionar y controlar accesos entre estados seguros y no seguros (como arranque seguro, OS confiable, etc.).
El uso de estos niveles permite una forma estructurada y segura de gestionar diferentes aspectos del sistema, desde aplicaciones de usuario hasta el software del sistema más privilegiado. El enfoque de ARMv8 sobre los niveles de privilegio ayuda a aislar efectivamente diferentes componentes del sistema, mejorando así la seguridad y robustez del sistema.
Registros (ARM64v8)
ARM64 tiene 31 registros de propósito general, etiquetados de x0
a x30
. Cada uno puede almacenar un valor de 64 bits (8 bytes). Para operaciones que requieren solo valores de 32 bits, los mismos registros se pueden acceder en un modo de 32 bits usando los nombres w0 a w30.
x0
ax7
- Estos se utilizan típicamente como registros temporales y para pasar parámetros a subrutinas.
x0
también lleva los datos de retorno de una función.
x8
- En el núcleo de Linux,x8
se utiliza como el número de llamada al sistema para la instrucciónsvc
. ¡En macOS, el que se usa es x16!x9
ax15
- Más registros temporales, a menudo utilizados para variables locales.x16
yx17
- Registros de Llamada Intra-procedimental. Registros temporales para valores inmediatos. También se utilizan para llamadas a funciones indirectas y stubs de PLT (Tabla de Enlace de Procedimientos).
x16
se utiliza como el número de llamada al sistema para la instrucciónsvc
en macOS.
x18
- Registro de plataforma. Puede ser utilizado como un registro de propósito general, pero en algunas plataformas, este registro está reservado para usos específicos de la plataforma: Puntero al bloque de entorno de hilo local en Windows, o para apuntar a la estructura de tarea actualmente ejecutándose en el núcleo de linux.x19
ax28
- Estos son registros guardados por el llamado. Una función debe preservar los valores de estos registros para su llamador, por lo que se almacenan en la pila y se recuperan antes de volver al llamador.x29
- Puntero de marco para hacer un seguimiento del marco de pila. Cuando se crea un nuevo marco de pila porque se llama a una función, el registrox29
se almacena en la pila y la dirección del nuevo puntero de marco es (direcciónsp
) se almacena en este registro.
Este registro también puede ser utilizado como un registro de propósito general aunque generalmente se usa como referencia a variables locales.
x30
olr
- Registro de enlace. Contiene la dirección de retorno cuando se ejecuta una instrucciónBL
(Branch with Link) oBLR
(Branch with Link to Register) almacenando el valor depc
en este registro.
También podría ser utilizado como cualquier otro registro.
Si la función actual va a llamar a una nueva función y, por lo tanto, sobrescribir
lr
, lo almacenará en la pila al principio, este es el epílogo (stp x29, x30 , [sp, #-48]; mov x29, sp
-> Almacenarfp
ylr
, generar espacio y obtener nuevofp
) y lo recuperará al final, este es el prólogo (ldp x29, x30, [sp], #48; ret
-> Recuperarfp
ylr
y retornar).
sp
- Puntero de pila, utilizado para hacer un seguimiento de la parte superior de la pila.
El valor de
sp
siempre debe mantenerse con al menos una alineación de cuádruple palabra o puede ocurrir una excepción de alineación.
pc
- Contador de programa, que apunta a la siguiente instrucción. Este registro solo puede ser actualizado a través de generaciones de excepciones, retornos de excepciones y saltos. Las únicas instrucciones ordinarias que pueden leer este registro son las instrucciones de salto con enlace (BL, BLR) para almacenar la direcciónpc
enlr
(Registro de Enlace).xzr
- Registro cero. También llamadowzr
en su forma de registro de 32 bits. Puede ser utilizado para obtener fácilmente el valor cero (operación común) o para realizar comparaciones usandosubs
comosubs XZR, Xn, #10
almacenando los datos resultantes en ningún lado (enxzr
).
Los registros Wn
son la versión de 32 bits del registro Xn
.
Registros SIMD y de Punto Flotante
Además, hay otros 32 registros de 128 bits que pueden ser utilizados en operaciones optimizadas de datos múltiples de una sola instrucción (SIMD) y para realizar aritmética de punto flotante. Estos se llaman registros Vn aunque también pueden operar en 64 bits, 32 bits, 16 bits y 8 bits y luego se llaman Qn
, Dn
, Sn
, Hn
y Bn
.
Registros del Sistema
Hay cientos de registros del sistema, también llamados registros de propósito especial (SPRs), que se utilizan para monitorear y controlar el comportamiento de los procesadores.
Solo se pueden leer o establecer utilizando las instrucciones especiales dedicadas mrs
y msr
.
Los registros especiales TPIDR_EL0
y TPIDDR_EL0
se encuentran comúnmente al realizar ingeniería inversa. El sufijo EL0
indica la excepción mínima desde la cual se puede acceder al registro (en este caso, EL0 es el nivel de excepción (privilegio) regular con el que se ejecutan los programas regulares).
A menudo se utilizan para almacenar la dirección base de la región de almacenamiento local de hilos en memoria. Por lo general, el primero es legible y escribible para programas que se ejecutan en EL0, pero el segundo se puede leer desde EL0 y escribir desde EL1 (como el núcleo).
mrs x0, TPIDR_EL0 ; Leer TPIDR_EL0 en x0
msr TPIDR_EL0, X0 ; Escribir x0 en TPIDR_EL0
PSTATE
PSTATE contiene varios componentes del proceso serializados en el registro especial visible para el sistema operativo SPSR_ELx
, siendo X el nivel de permiso del excepción desencadenada (esto permite recuperar el estado del proceso cuando la excepción termina).
Estos son los campos accesibles:
Las banderas de condición
N
,Z
,C
yV
:N
significa que la operación produjo un resultado negativo.Z
significa que la operación produjo cero.C
significa que la operación tuvo acarreo.V
significa que la operación produjo un desbordamiento con signo:La suma de dos números positivos produce un resultado negativo.
La suma de dos números negativos produce un resultado positivo.
En la resta, cuando se resta un número negativo grande de un número positivo más pequeño (o viceversa), y el resultado no puede ser representado dentro del rango del tamaño de bits dado.
Obviamente, el procesador no sabe si la operación es con signo o no, por lo que verificará C y V en las operaciones e indicará si ocurrió un acarreo en caso de que fuera con signo o sin signo.
No todas las instrucciones actualizan estas banderas. Algunas como CMP
o TST
lo hacen, y otras que tienen un sufijo s como ADDS
también lo hacen.
La bandera de ancho de registro actual (
nRW
): Si la bandera tiene el valor 0, el programa se ejecutará en el estado de ejecución AArch64 una vez reanudado.El Nivel de Excepción (
EL
): Un programa regular que se ejecuta en EL0 tendrá el valor 0.La bandera de paso único (
SS
): Utilizada por depuradores para realizar un paso único configurando la bandera SS a 1 dentro deSPSR_ELx
a través de una excepción. El programa ejecutará un paso y emitirá una excepción de paso único.La bandera de estado de excepción ilegal (
IL
): Se utiliza para marcar cuando un software privilegiado realiza una transferencia de nivel de excepción inválida, esta bandera se establece en 1 y el procesador desencadena una excepción de estado ilegal.Las banderas
DAIF
: Estas banderas permiten a un programa privilegiado enmascarar selectivamente ciertas excepciones externas.Si
A
es 1 significa que se desencadenarán abortos asíncronos. LaI
configura la respuesta a las Solicitudes de Interrupción (IRQ) de hardware externo. y la F está relacionada con las Solicitudes de Interrupción Rápida (FIR).Las banderas de selección de puntero de pila (
SPS
): Los programas privilegiados que se ejecutan en EL1 y superiores pueden alternar entre usar su propio registro de puntero de pila y el de modelo de usuario (por ejemplo, entreSP_EL1
yEL0
). Este cambio se realiza escribiendo en el registro especialSPSel
. Esto no se puede hacer desde EL0.
Convención de Llamadas (ARM64v8)
La convención de llamadas ARM64 especifica que los primeros ocho parámetros a una función se pasan en los registros x0
a x7
. Los parámetros adicionales se pasan en la pila. El valor de retorno se pasa de vuelta en el registro x0
, o en x1
también si tiene 128 bits de largo. Los registros x19
a x30
y sp
deben ser preservados a través de las llamadas a funciones.
Al leer una función en ensamblador, busca el prólogo y epílogo de la función. El prólogo generalmente implica guardar el puntero de marco (x29
), configurar un nuevo puntero de marco, y asignar espacio en la pila. El epílogo generalmente implica restaurar el puntero de marco guardado y retornar de la función.
Convención de Llamadas en Swift
Swift tiene su propia convención de llamadas que se puede encontrar en https://github.com/apple/swift/blob/main/docs/ABI/CallConvSummary.rst#arm64
Instrucciones Comunes (ARM64v8)
Las instrucciones ARM64 generalmente tienen el formato opcode dst, src1, src2
, donde opcode
es la operación a realizar (como add
, sub
, mov
, etc.), dst
es el registro de destino donde se almacenará el resultado, y src1
y src2
son los registros de origen. También se pueden usar valores inmediatos en lugar de registros de origen.
mov
: Mover un valor de un registro a otro.Ejemplo:
mov x0, x1
— Esto mueve el valor dex1
ax0
.ldr
: Cargar un valor de memoria en un registro.Ejemplo:
ldr x0, [x1]
— Esto carga un valor de la ubicación de memoria apuntada porx1
enx0
.Modo de desplazamiento: Se indica un desplazamiento que afecta al puntero de origen, por ejemplo:
ldr x2, [x1, #8]
, esto cargará en x2 el valor de x1 + 8.ldr x2, [x0, x1, lsl #2]
, esto cargará en x2 un objeto del array x0, desde la posición x1 (índice) * 4.Modo pre-indexado: Esto aplicará cálculos al origen, obtendrá el resultado y también almacenará el nuevo origen en el origen.
ldr x2, [x1, #8]!
, esto cargaráx1 + 8
enx2
y almacenará en x1 el resultado dex1 + 8
.str lr, [sp, #-4]!
, Almacena el registro de enlace en sp y actualiza el registro sp.Modo post-indexado: Esto es como el anterior, pero se accede a la dirección de memoria y luego se calcula y almacena el desplazamiento.
ldr x0, [x1], #8
, cargax1
enx0
y actualiza x1 conx1 + 8
.Dirección relativa al PC: En este caso, la dirección a cargar se calcula en relación con el registro PC.
ldr x1, =_start
, Esto cargará la dirección donde comienza el símbolo_start
en x1 en relación con el PC actual.str
: Almacenar un valor de un registro en memoria.Ejemplo:
str x0, [x1]
— Esto almacena el valor enx0
en la ubicación de memoria apuntada porx1
.ldp
: Cargar par de registros. Esta instrucción carga dos registros desde ubicaciones de memoria consecutivas. La dirección de memoria se forma típicamente sumando un desplazamiento al valor en otro registro.Ejemplo:
ldp x0, x1, [x2]
— Esto cargax0
yx1
desde las ubicaciones de memoria enx2
yx2 + 8
, respectivamente.stp
: Almacenar par de registros. Esta instrucción almacena dos registros en ubicaciones de memoria consecutivas. La dirección de memoria se forma típicamente sumando un desplazamiento al valor en otro registro.Ejemplo:
stp x0, x1, [sp]
— Esto almacenax0
yx1
en las ubicaciones de memoria ensp
ysp + 8
, respectivamente.stp x0, x1, [sp, #16]!
— Esto almacenax0
yx1
en las ubicaciones de memoria ensp+16
ysp + 24
, respectivamente, y actualizasp
consp+16
.add
: Sumar los valores de dos registros y almacenar el resultado en un registro.Sintaxis: add(s) Xn1, Xn2, Xn3 | #imm, [shift #N | RRX]
Xn1 -> Destino
Xn2 -> Operando 1
Xn3 | #imm -> Operando 2 (registro o inmediato)
[shift #N | RRX] -> Realizar un desplazamiento o llamar a RRX.
Ejemplo:
add x0, x1, x2
— Esto suma los valores enx1
yx2
y almacena el resultado enx0
.add x5, x5, #1, lsl #12
— Esto equivale a 4096 (un 1 desplazado 12 veces) -> 1 0000 0000 0000 0000.adds
Esto realiza unadd
y actualiza las banderas.sub
: Restar los valores de dos registros y almacenar el resultado en un registro.Verifique la sintaxis de
add
.Ejemplo:
sub x0, x1, x2
— Esto resta el valor enx2
dex1
y almacena el resultado enx0
.subs
Esto es como sub pero actualizando la bandera.mul
: Multiplicar los valores de dos registros y almacenar el resultado en un registro.Ejemplo:
mul x0, x1, x2
— Esto multiplica los valores enx1
yx2
y almacena el resultado enx0
.div
: Dividir el valor de un registro por otro y almacenar el resultado en un registro.Ejemplo:
div x0, x1, x2
— Esto divide el valor enx1
porx2
y almacena el resultado enx0
.lsl
,lsr
,asr
,ror
,rrx
:Desplazamiento lógico a la izquierda: Agrega 0s desde el final moviendo los otros bits hacia adelante (multiplica por n veces 2).
Desplazamiento lógico a la derecha: Agrega 1s al principio moviendo los otros bits hacia atrás (divide por n veces 2 en sin signo).
Desplazamiento aritmético a la derecha: Como
lsr
, pero en lugar de agregar 0s si el bit más significativo es 1, se agregan 1s (divide por n veces 2 en con signo).Rotar a la derecha: Como
lsr
pero lo que se elimina de la derecha se agrega a la izquierda.Rotar a la derecha con extensión: Como
ror
, pero con la bandera de acarreo como el "bit más significativo". Así que la bandera de acarreo se mueve al bit 31 y el bit eliminado a la bandera de acarreo.bfm
: Movimiento de Campo de Bits, estas operaciones copian bits0...n
de un valor y los colocan en posicionesm..m+n
. El#s
especifica la posición del bit más a la izquierda y#r
la cantidad de rotación a la derecha.Movimiento de campo de bits:
BFM Xd, Xn, #r
.Movimiento de campo de bits con signo:
SBFM Xd, Xn, #r, #s
.Movimiento de campo de bits sin signo:
UBFM Xd, Xn, #r, #s
.Extracción e Inserción de Campo de Bits: Copia un campo de bits de un registro y lo copia a otro registro.
BFI X1, X2, #3, #4
Inserta 4 bits de X2 desde el tercer bit de X1.BFXIL X1, X2, #3, #4
Extrae desde el tercer bit de X2 cuatro bits y los copia a X1.SBFIZ X1, X2, #3, #4
Extiende el signo de 4 bits de X2 e inserta en X1 comenzando en la posición de bit 3, poniendo a cero los bits de la derecha.SBFX X1, X2, #3, #4
Extrae 4 bits comenzando en el bit 3 de X2, extiende el signo y coloca el resultado en X1.UBFIZ X1, X2, #3, #4
Extiende a cero 4 bits de X2 e inserta en X1 comenzando en la posición de bit 3, poniendo a cero los bits de la derecha.UBFX X1, X2, #3, #4
Extrae 4 bits comenzando en el bit 3 de X2 y coloca el resultado extendido a cero en X1.Extender Signo a X: Extiende el signo (o simplemente agrega 0s en la versión sin signo) de un valor para poder realizar operaciones con él:
SXTB X1, W2
Extiende el signo de un byte de W2 a X1 (W2
es la mitad deX2
) para llenar los 64 bits.SXTH X1, W2
Extiende el signo de un número de 16 bits de W2 a X1 para llenar los 64 bits.SXTW X1, W2
Extiende el signo de un byte de W2 a X1 para llenar los 64 bits.UXTB X1, W2
Agrega 0s (sin signo) a un byte de W2 a X1 para llenar los 64 bits.extr
: Extrae bits de un par de registros concatenados especificados.Ejemplo:
EXTR W3, W2, W1, #3
Esto concatena W1+W2 y obtiene desde el bit 3 de W2 hasta el bit 3 de W1 y lo almacena en W3.cmp
: Comparar dos registros y establecer banderas de condición. Es un alias desubs
estableciendo el registro de destino en el registro cero. Útil para saber sim == n
.Soporta la misma sintaxis que
subs
.Ejemplo:
cmp x0, x1
— Esto compara los valores enx0
yx1
y establece las banderas de condición en consecuencia.cmn
: Comparar el operando negativo. En este caso es un alias deadds
y soporta la misma sintaxis. Útil para saber sim == -n
.ccmp
: Comparación condicional, es una comparación que se realizará solo si una comparación anterior fue verdadera y establecerá específicamente los bits nzcv.cmp x1, x2; ccmp x3, x4, 0, NE; blt _func
-> si x1 != x2 y x3 < x4, salta a func.Esto se debe a que
ccmp
solo se ejecutará si la anteriorcmp
fue unNE
, si no lo fue, los bitsnzcv
se establecerán en 0 (lo que no satisfará la comparaciónblt
).Esto también se puede usar como
ccmn
(lo mismo pero negativo, comocmp
vscmn
).tst
: Verifica si alguno de los valores de la comparación son ambos 1 (funciona como un AND sin almacenar el resultado en ningún lado). Es útil para verificar un registro con un valor y comprobar si alguno de los bits del registro indicado en el valor es 1.Ejemplo:
tst X1, #7
Verifica si alguno de los últimos 3 bits de X1 es 1.teq
: Operación XOR descartando el resultado.b
: Salto incondicional.Ejemplo:
b myFunction
.Nota que esto no llenará el registro de enlace con la dirección de retorno (no es adecuado para llamadas a subrutinas que necesitan regresar).
bl
: Salto con enlace, utilizado para llamar a una subrutina. Almacena la dirección de retorno enx30
.Ejemplo:
bl myFunction
— Esto llama a la funciónmyFunction
y almacena la dirección de retorno enx30
.Nota que esto no llenará el registro de enlace con la dirección de retorno (no es adecuado para llamadas a subrutinas que necesitan regresar).
blr
: Salto con enlace a registro, utilizado para llamar a una subrutina donde el objetivo está especificado en un registro. Almacena la dirección de retorno enx30
. (Esto esEjemplo:
blr x1
— Esto llama a la función cuya dirección está contenida enx1
y almacena la dirección de retorno enx30
.ret
: Retornar de subrutina, típicamente usando la dirección enx30
.Ejemplo:
ret
— Esto retorna de la subrutina actual usando la dirección de retorno enx30
.b.<cond>
: Saltos condicionales.b.eq
: Salto si es igual, basado en la instruccióncmp
anterior.Ejemplo:
b.eq label
— Si la instruccióncmp
anterior encontró dos valores iguales, esto salta alabel
.b.ne
: Salto si no es igual. Esta instrucción verifica las banderas de condición (que fueron establecidas por una instrucción de comparación anterior), y si los valores comparados no eran iguales, salta a una etiqueta o dirección.Ejemplo: Después de una instrucción
cmp x0, x1
,b.ne label
— Si los valores enx0
yx1
no eran iguales, esto salta alabel
.cbz
: Comparar y saltar si es cero. Esta instrucción compara un registro con cero, y si son iguales, salta a una etiqueta o dirección.Ejemplo:
cbz x0, label
— Si el valor enx0
es cero, esto salta alabel
.cbnz
: Comparar y saltar si no es cero. Esta instrucción compara un registro con cero, y si no son iguales, salta a una etiqueta o dirección.Ejemplo:
cbnz x0, label
— Si el valor enx0
no es cero, esto salta alabel
.tbnz
: Prueba de bit y salto si no es cero.Ejemplo:
tbnz x0, #8, label
.tbz
: Prueba de bit y salto si es cero.Ejemplo:
tbz x0, #8, label
.Operaciones de selección condicional: Estas son operaciones cuyo comportamiento varía dependiendo de los bits condicionales.
csel Xd, Xn, Xm, cond
->csel X0, X1, X2, EQ
-> Si es verdadero, X0 = X1, si es falso, X0 = X2.csinc Xd, Xn, Xm, cond
-> Si es verdadero, Xd = Xn, si es falso, Xd = Xm + 1.cinc Xd, Xn, cond
-> Si es verdadero, Xd = Xn + 1, si es falso, Xd = Xn.csinv Xd, Xn, Xm, cond
-> Si es verdadero, Xd = Xn, si es falso, Xd = NOT(Xm).cinv Xd, Xn, cond
-> Si es verdadero, Xd = NOT(Xn), si es falso, Xd = Xn.csneg Xd, Xn, Xm, cond
-> Si es verdadero, Xd = Xn, si es falso, Xd = - Xm.cneg Xd, Xn, cond
-> Si es verdadero, Xd = - Xn, si es falso, Xd = Xn.cset Xd, Xn, Xm, cond
-> Si es verdadero, Xd = 1, si es falso, Xd = 0.csetm Xd, Xn, Xm, cond
-> Si es verdadero, Xd = <todos 1>, si es falso, Xd = 0.adrp
: Calcula la dirección de página de un símbolo y la almacena en un registro.Ejemplo:
adrp x0, symbol
— Esto calcula la dirección de página desymbol
y la almacena enx0
.ldrsw
: Cargar un valor 32 bits con signo de la memoria y extenderlo a 64 bits.Ejemplo:
ldrsw x0, [x1]
— Esto carga un valor de 32 bits con signo de la ubicación de memoria apuntada porx1
, lo extiende a 64 bits y lo almacena enx0
.stur
: Almacenar un valor de registro en una ubicación de memoria, usando un desplazamiento de otro registro.Ejemplo:
stur x0, [x1, #4]
— Esto almacena el valor enx0
en la dirección de memoria que es 4 bytes mayor que la dirección actualmente enx1
.svc
: Realiza una llamada al sistema. Significa "Supervisor Call". Cuando el procesador ejecuta esta instrucción, cambia de modo usuario a modo núcleo y salta a una ubicación específica en memoria donde se encuentra el código de manejo de llamadas al sistema del núcleo.Ejemplo:
Prólogo de Función
Guardar el registro de enlace y el puntero de marco en la pila:
Configurar el nuevo puntero de marco:
mov x29, sp
(configura el nuevo puntero de marco para la función actual)Asignar espacio en la pila para variables locales (si es necesario):
sub sp, sp, <size>
(donde<size>
es el número de bytes necesarios)
Epilogo de la Función
Desasignar variables locales (si se asignaron):
add sp, sp, <size>
Restaurar el registro de enlace y el puntero de marco:
Return:
ret
(devuelve el control al llamador utilizando la dirección en el registro de enlace)
Estado de Ejecución AARCH32
Armv8-A soporta la ejecución de programas de 32 bits. AArch32 puede ejecutarse en uno de dos conjuntos de instrucciones: A32
y T32
y puede alternar entre ellos a través de interworking
.
Los programas privilegiados de 64 bits pueden programar la ejecución de programas de 32 bits al ejecutar una transferencia de nivel de excepción al de 32 bits de menor privilegio.
Tenga en cuenta que la transición de 64 bits a 32 bits ocurre con una disminución del nivel de excepción (por ejemplo, un programa de 64 bits en EL1 que activa un programa en EL0). Esto se hace configurando el bit 4 de SPSR_ELx
registro especial a 1 cuando el hilo de proceso AArch32
está listo para ser ejecutado y el resto de SPSR_ELx
almacena el CPSR de los programas AArch32
. Luego, el proceso privilegiado llama a la instrucción ERET
para que el procesador transicione a AArch32
ingresando en A32 o T32 dependiendo de CPSR**.**
El interworking
ocurre utilizando los bits J y T de CPSR. J=0
y T=0
significa A32
y J=0
y T=1
significa T32. Esto se traduce básicamente en establecer el bit más bajo en 1 para indicar que el conjunto de instrucciones es T32.
Esto se establece durante las instrucciones de salto de interworking, pero también se puede establecer directamente con otras instrucciones cuando el PC se establece como el registro de destino. Ejemplo:
Otro ejemplo:
Registros
Hay 16 registros de 32 bits (r0-r15). Desde r0 hasta r14 se pueden usar para cualquier operación, sin embargo, algunos de ellos suelen estar reservados:
r15
: Contador de programa (siempre). Contiene la dirección de la siguiente instrucción. En A32 actual + 8, en T32, actual + 4.r11
: Puntero de marcor12
: Registro de llamada intra-procedimentalr13
: Puntero de pilar14
: Registro de enlace
Además, los registros se respaldan en registros bancados
. Que son lugares que almacenan los valores de los registros permitiendo realizar cambios de contexto rápidos en el manejo de excepciones y operaciones privilegiadas para evitar la necesidad de guardar y restaurar manualmente los registros cada vez.
Esto se hace guardando el estado del procesador desde el CPSR
al SPSR
del modo del procesador al que se toma la excepción. Al regresar de la excepción, el CPSR
se restaura desde el SPSR
.
CPSR - Registro de Estado del Programa Actual
En AArch32, el CPSR funciona de manera similar a PSTATE
en AArch64 y también se almacena en SPSR_ELx
cuando se toma una excepción para restaurar más tarde la ejecución:
Los campos se dividen en algunos grupos:
Registro de Estado del Programa de Aplicación (APSR): Banderas aritméticas y accesibles desde EL0
Registros de Estado de Ejecución: Comportamiento del proceso (gestionado por el SO).
Registro de Estado del Programa de Aplicación (APSR)
Las banderas
N
,Z
,C
,V
(igual que en AArch64)La bandera
Q
: Se establece en 1 siempre que ocurra saturación entera durante la ejecución de una instrucción aritmética de saturación especializada. Una vez que se establece en1
, mantendrá el valor hasta que se establezca manualmente en 0. Además, no hay ninguna instrucción que verifique su valor implícitamente, debe hacerse leyéndolo manualmente.Banderas
GE
(Mayor o igual): Se utilizan en operaciones SIMD (Instrucción Única, Múltiples Datos), como "suma paralela" y "resta paralela". Estas operaciones permiten procesar múltiples puntos de datos en una sola instrucción.
Por ejemplo, la instrucción UADD8
suma cuatro pares de bytes (de dos operandos de 32 bits) en paralelo y almacena los resultados en un registro de 32 bits. Luego establece las banderas GE
en el APSR
basándose en estos resultados. Cada bandera GE corresponde a una de las sumas de bytes, indicando si la suma para ese par de bytes desbordó.
La instrucción SEL
utiliza estas banderas GE para realizar acciones condicionales.
Registros de Estado de Ejecución
Los bits
J
yT
:J
debe ser 0 y siT
es 0 se utiliza el conjunto de instrucciones A32, y si es 1, se utiliza el T32.Registro de Estado del Bloque IT (
ITSTATE
): Estos son los bits del 10 al 15 y del 25 al 26. Almacenan condiciones para instrucciones dentro de un grupo con prefijoIT
.Bit
E
: Indica el endianness.Bits de Modo y Máscara de Excepción (0-4): Determinan el estado de ejecución actual. El 5to indica si el programa se ejecuta como 32 bits (un 1) o 64 bits (un 0). Los otros 4 representan el modo de excepción actualmente en uso (cuando ocurre una excepción y se está manejando). El número establecido indica la prioridad actual en caso de que se active otra excepción mientras se está manejando esta.
AIF
: Ciertas excepciones pueden ser deshabilitadas usando los bitsA
,I
,F
. SiA
es 1 significa que se activarán abortos asíncronos. ElI
configura la respuesta a las Solicitudes de Interrupción de Hardware (IRQ). y el F está relacionado con las Solicitudes de Interrupción Rápida (FIR).
macOS
Llamadas al sistema BSD
Consulta syscalls.master. Las llamadas al sistema BSD tendrán x16 > 0.
Trampas Mach
Consulta en syscall_sw.c la mach_trap_table
y en mach_traps.h los prototipos. El número máximo de trampas Mach es MACH_TRAP_TABLE_COUNT
= 128. Las trampas Mach tendrán x16 < 0, así que necesitas llamar a los números de la lista anterior con un menos: _kernelrpc_mach_vm_allocate_trap
es -10
.
También puedes consultar libsystem_kernel.dylib
en un desensamblador para encontrar cómo llamar a estas (y BSD) llamadas al sistema:
Nota que Ida y Ghidra también pueden descompilar dylibs específicas de la caché simplemente pasando la caché.
A veces es más fácil revisar el código descompilado de libsystem_kernel.dylib
que revisar el código fuente porque el código de varias syscalls (BSD y Mach) se genera a través de scripts (revisa los comentarios en el código fuente) mientras que en la dylib puedes encontrar lo que se está llamando.
llamadas machdep
XNU soporta otro tipo de llamadas llamadas dependientes de la máquina. Los números de estas llamadas dependen de la arquitectura y ni las llamadas ni los números están garantizados para permanecer constantes.
página de comunicación
Esta es una página de memoria de propietario del kernel que está mapeada en el espacio de direcciones de cada proceso de usuario. Está destinada a hacer la transición del modo usuario al espacio del kernel más rápida que usar syscalls para servicios del kernel que se utilizan tanto que esta transición sería muy ineficiente.
Por ejemplo, la llamada gettimeofdate
lee el valor de timeval
directamente de la página de comunicación.
objc_msgSend
Es muy común encontrar esta función utilizada en programas de Objective-C o Swift. Esta función permite llamar a un método de un objeto de Objective-C.
Parámetros (más info en la docs):
x0: self -> Puntero a la instancia
x1: op -> Selector del método
x2... -> Resto de los argumentos del método invocado
Así que, si pones un breakpoint antes de la rama a esta función, puedes encontrar fácilmente lo que se invoca en lldb con (en este ejemplo el objeto llama a un objeto de NSConcreteTask
que ejecutará un comando):
Estableciendo la variable de entorno NSObjCMessageLoggingEnabled=1
es posible registrar cuándo se llama a esta función en un archivo como /tmp/msgSends-pid
.
Además, estableciendo OBJC_HELP=1
y llamando a cualquier binario puedes ver otras variables de entorno que podrías usar para log cuando ocurren ciertas acciones de Objc-C.
Cuando se llama a esta función, es necesario encontrar el método llamado de la instancia indicada, para esto se realizan diferentes búsquedas:
Realizar búsqueda optimista en caché:
Si tiene éxito, hecho
Adquirir runtimeLock (lectura)
Si (realizar && !cls->realized) realizar clase
Si (inicializar && !cls->initialized) inicializar clase
Intentar caché propia de la clase:
Si tiene éxito, hecho
Intentar lista de métodos de la clase:
Si se encuentra, llenar caché y hecho
Intentar caché de la superclase:
Si tiene éxito, hecho
Intentar lista de métodos de la superclase:
Si se encuentra, llenar caché y hecho
Si (resolver) intentar resolver método, y repetir desde la búsqueda de clase
Si aún está aquí (= todo lo demás ha fallado) intentar reenviador
Shellcodes
Para compilar:
Para extraer los bytes:
Para macOS más reciente: