ARM: registro de enlace y puntero de marco
(1)
Algunas convenciones de llamadas de registro dependen de la ABI (Interfaz Binaria de Aplicación). El FP
se requiere en el estándar APCS y no en el AAPCS más reciente (2003). Para AAPCS (GCC 5.0+) el FP
no tiene que ser usado, pero ciertamente puede serlo; la información de depuración se anota con la pila y el uso del puntero de marco para el seguimiento de pila y el código de desenrollado con el AAPCS . Si una función es static
, un compilador realmente no tiene que cumplir ninguna convención.
En general, todos los registros ARM son de propósito general . El lr
(registro de enlace, también R14) y pc
(contador de programa también R15) son especiales y se consagran en el conjunto de instrucciones. Tiene razón en que el lr
apuntaría a A. La pc
y la lr
están relacionadas. Una es "donde estás" y la otra es "donde estabas". Son el aspecto del código de una función.
Normalmente, tenemos sp
(puntero de pila, R13) y fp
( puntero de marco , R11). Estos dos también están relacionados. Este diseño de Microsoft hace un buen trabajo describiendo cosas. La pila se usa para almacenar datos temporales o locales en tu función. Cualquier variable en foo()
y bar()
, se almacena aquí, en la pila o en registros disponibles. El fp
realiza un seguimiento de las variables de la función a la función. Es una ventana de marco o imagen en la pila para esa función. El ABI define un diseño de este marco . Por lo general, el compilador guarda el registro lr
y otros registros detrás de escena, así como el valor anterior de fp
. Esto hace una lista vinculada de marcos de pila y si lo desea, puede rastrear todo el camino de regreso a main()
. La raíz es fp
, que apunta a un marco de pila (como una struct
) con una variable en la struct
es la fp
anterior. Puede ir a lo largo de la lista hasta el fp
final que normalmente es NULL
.
Entonces, sp
es donde está la pila y fp
es donde estaba la pila, muy parecida a la pc
y lr
. Cada lr
(registro de enlace) se almacena en el viejo fp
(puntero de cuadro). El sp
y el fp
son un aspecto de los datos de las funciones.
Su punto B es la pc
activa y sp
. El punto A es en realidad el fp
y lr
; a menos que llame otra función y luego el compilador se prepare para configurar el fp
para que apunte a los datos en B.
Lo siguiente es un ensamblador de ARM que podría demostrar cómo funciona todo esto. Esto será diferente dependiendo de cómo optimice el compilador, pero debería dar una idea,
; Prologue - setup
mov ip, sp ; get a copy of sp.
stmdb sp!, {fp, ip, lr, pc} ; Save the frame on the stack. See Addendum
sub fp, ip, #4 ; Set the new frame pointer.
...
; Maybe other functions called here.
Así es como se vería
; Older caller return lr
stored in stack frame.
bl baz
...
; Epilogue - return
ldm sp, {fp, sp, lr} ; restore stack, frame pointer and old link.
... ; maybe more stuff here.
bx lr ; return.
foo()
. Si no llama a bar()
, entonces el compilador realiza una optimización de hoja y no necesita guardar el marco ; solo se necesita bx lr
. Probablemente esta sea la razón por la que te confunden los ejemplos de la web. No es siempre lo mismo.
La comida para llevar debe ser,
-
pc
ylr
son registros de códigos relacionados. Uno es "Dónde estás", el otro es "Dónde estabas". -
sp
yfp
son registros de datos locales relacionados.
Uno es "Donde los datos locales son", el otro es "Dónde están los últimos datos locales". - El trabajo en conjunto junto con el paso de parámetros para crear maquinaria funcional .
- Es difícil describir un caso general porque queremos que los compiladores sean lo más rápidos posible, por lo que utilizan todos los trucos que pueden.
Estos conceptos son genéricos para todas las CPU y lenguajes compilados, aunque los detalles pueden variar. El uso del registro de enlace , el puntero de marco son parte del prólogo y epílogo de la función , y si usted entendió todo, usted sabe cómo funciona un desbordamiento de pila en un ARM.
Ver también: convención de llamadas ARM .
Artículo de MSDN ARM stack
Resumen APCS de la Universidad de Cambridge
ARM stack trace blog
Enlace de Apple ABI
El diseño de marco básico es,
- fp [-0] guardó la
pc
, donde almacenamos este marco. - fp [-1] guardó
lr
, la dirección de retorno para esta función. - fp [-2]
sp
anterior, antes de que esta función coma pila. - fp [-3]
fp
anterior, el último marco de la pila . - muchos registros opcionales ...
Un ABI puede usar otros valores, pero los anteriores son típicos para la mayoría de las configuraciones.
Addendum: Esto no es un error en el ensamblador; es normal. Una explicación está en la pregunta sobre prólogos generados por ARM .
Estoy tratando de entender cómo funcionan el registro de enlace y el puntero de marco en ARM. He estado en un par de sitios y quería confirmar mi comprensión.
Supongamos que tengo el siguiente código:
int foo(void)
{
// ..
bar();
// (A)
// ..
}
int bar(void)
{
// (B)
int b1;
// ..
// (C)
baz();
// (D)
}
int baz(void)
{
// (E)
int a;
int b;
// (F)
}
y llamo foo (). ¿El registro de enlace contendría la dirección del código en el punto (A) y el puntero de marco contendría la dirección en el código en el punto (B)? Y el puntero de la pila podría estar en cualquier lugar dentro de la barra (), después de que todos los locales hayan sido declarados?
[edit] Se agregó otra función llamada baz ()