¿Alguien puede explicar este código de ensamblador desde el punto de vista de la “pila”?
¿Alguien puede explicar el código de ensamblador basado en ARM desde el punto de vista del stack? Específicamente, ¿cómo se ve el stack para el “reset_handler” justo antes de llamar a “main”, “save_context” y “resume”? (Nota que entiendo lo que hace el código, pero no puedo comprender o imaginar cómo se ve o se comporta exactamente el stack mientras se ejecuta el código).
*/ asm.s */
.global main, process, process_size
.global reset_handler, context_switch, running
reset_handler:
ldr r0, =process
ldr r1, =process_size
ldr r2, [r1, #0]
add r0, r0, r2
mov sp, r0
bl main
context_switch:
save_context:
stmfd sp!, {r0-r12, lr}
ldr r0, =running
ldr r1, [r0, #0]
str sp, [r1, #4]
resume:
ldr r0, =running
ldr r1, [r0, #0]
ldr sp, [r1, #4]
ldmfd sp!, {r0-r12, lr}
mov pc, lr
*/ cfile.c */
#define SIZE 2048
typedef struct process
{
struct process *next;
int *saved_stack;
int running_stack[SIZE];
}PROC;
int process_size = sizeof(PROC);
PROC process, *running;
main()
{
running = &process;
context_switch();
}
davy.ai
La pila en la programación en ensamblador ARM se utiliza para almacenar información importante como valores de registros, direcciones de retorno y parámetros de función. En el código proporcionado, la pila se utiliza para guardar el contexto de un proceso antes de pasar a otro proceso.
Al comienzo de la función
reset_handler
, la instrucciónldr
carga la dirección de la estructura de datosprocess
en el registror0
. La siguiente instrucciónldr
carga la dirección de la variableprocess_size
en el registror1
. La instrucciónldr
posterior carga el primer elemento ([r1, #0]
) de la estructura de datos apuntada porprocess_size
y lo almacena en el registror2
. Este elemento es el tamaño del arreglorunning_stack
en bytes.La instrucción
add
suma el tamaño derunning_stack
a la dirección almacenada enr0
, lo que mueve efectivamente el puntero de pilasp
al final del espacio de pila asignado. La instrucciónmov
luego almacena el valor desp
enr0
, que se transfiere al puntero de pilasp
.En este punto, la pila contiene los registros
r0-r12
ylr
guardados, los cuales se empujaron a la pila en la funciónsave_context
. La instrucciónstr
guarda el valor actual del puntero de pilasp
en el camposaved_stack
de la estructura del procesorunning
. Esto se hace para que cuando el proceso se reanude más tarde, se pueda restaurar su pila guardada.En la función
resume
, la instrucciónldr
carga la dirección de la variablerunning
en el registror0
. La siguiente instrucciónldr
carga el valor del camposaved_stack
de la estructurarunning
y lo almacena ensp
. La instrucciónldmfd
luego carga los valores de los registros guardados ylr
de vuelta en sus respectivos registros.Finalmente, la instrucción
mov
copia el valor delr
en el contador de programapc
, lo que hace que el procesador reanude la ejecución en la ubicación apuntada porlr
. Esta ubicación será la instrucción inmediatamente después de la llamada acontext_switch
en la funciónmain
, lo que efectivamente reanuda la ejecución del proceso interrumpido previamente.