谁能从 "stack" 的角度解释这段汇编代码?
Can anyone explain this assembly code for the "stack" point of view?
任何人都可以从堆栈的角度解释基于 arm 的汇编代码吗?特别是在调用“main”、“save_context”和“resume”部分之前“reset_handler”的堆栈视图? (请注意,我知道代码在做什么,但我无法理解或想象代码在 运行 时堆栈的外观或行为如何)。
*/ 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();
}
伪代码:
reset_handler:
stack_pointer = &process+process_size;
call main
这意味着堆栈指针指向&process.running_stack[SIZE]
。当一个栈指针像这样指向栈缓冲区的末尾时,就意味着栈是完全空的(ARM使用递减栈)。
作为背景——处理器的寄存器几乎定义了它在做什么。它们通常被称为 context
。最重要的是程序计数器pc
,它包含下一条指令的内存地址;但是它们都很重要。那么让我们看看如何保存上下文:
save_context:
stmfd sp!, {r0-r12, lr}
-- that instruction saved to processor context to the stack
-- it could be broken down as follows:
-- sp = sp - 14*4 4, because each register is 4 bytes, and there are 14 specfied
-- for (i=0; i < 13; i++) sp[i] = r(i);
-- sp[i] = lr `lr` is special, it holds the return address of the instruction that called us.
ldr r0, =running
-- put the address of the variable `running` into r0
ldr r1, [r0, #0]
-- load r1 with the memory address from r0. So r1 = running.
str sp, [r1, #4]
-- store the stack pointer (sp) in the `saved_sp` field of running.
-- so these three instructions perform: running->saved_stack = sp;
-- now we "fall through" to load, or `resume` a context.
resume:
ldr r0, =running
ldr r1, [r0, #0]
ldr sp, [r1, #4]
-- the inverse of the above, these three instructions effectively perform:
-- sp = running->saved_stack
ldmfd sp!, {r0-r12, lr}
-- this is the complimentary operation to the complicated save one above; but this time it is:
-- for (i=0; i < 13; i++) r(i) = sp[i];
-- lr = sp[i];
-- sp += 14*4;
mov pc, lr
-- this is a return instruction, where the program counter is loaded with the contents of the link register `lr`.
-- so, with this, it will return to main just after the call to context_switch
上面有一些模糊的地方:sp[i]
必须按寄存器的大小缩放 i (4);但较早的 sp 减少了 14*4。由于伪C不是真的,所以看起来还可以。
任何人都可以从堆栈的角度解释基于 arm 的汇编代码吗?特别是在调用“main”、“save_context”和“resume”部分之前“reset_handler”的堆栈视图? (请注意,我知道代码在做什么,但我无法理解或想象代码在 运行 时堆栈的外观或行为如何)。
*/ 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();
}
伪代码:
reset_handler:
stack_pointer = &process+process_size;
call main
这意味着堆栈指针指向&process.running_stack[SIZE]
。当一个栈指针像这样指向栈缓冲区的末尾时,就意味着栈是完全空的(ARM使用递减栈)。
作为背景——处理器的寄存器几乎定义了它在做什么。它们通常被称为 context
。最重要的是程序计数器pc
,它包含下一条指令的内存地址;但是它们都很重要。那么让我们看看如何保存上下文:
save_context:
stmfd sp!, {r0-r12, lr}
-- that instruction saved to processor context to the stack
-- it could be broken down as follows:
-- sp = sp - 14*4 4, because each register is 4 bytes, and there are 14 specfied
-- for (i=0; i < 13; i++) sp[i] = r(i);
-- sp[i] = lr `lr` is special, it holds the return address of the instruction that called us.
ldr r0, =running
-- put the address of the variable `running` into r0
ldr r1, [r0, #0]
-- load r1 with the memory address from r0. So r1 = running.
str sp, [r1, #4]
-- store the stack pointer (sp) in the `saved_sp` field of running.
-- so these three instructions perform: running->saved_stack = sp;
-- now we "fall through" to load, or `resume` a context.
resume:
ldr r0, =running
ldr r1, [r0, #0]
ldr sp, [r1, #4]
-- the inverse of the above, these three instructions effectively perform:
-- sp = running->saved_stack
ldmfd sp!, {r0-r12, lr}
-- this is the complimentary operation to the complicated save one above; but this time it is:
-- for (i=0; i < 13; i++) r(i) = sp[i];
-- lr = sp[i];
-- sp += 14*4;
mov pc, lr
-- this is a return instruction, where the program counter is loaded with the contents of the link register `lr`.
-- so, with this, it will return to main just after the call to context_switch
上面有一些模糊的地方:sp[i]
必须按寄存器的大小缩放 i (4);但较早的 sp 减少了 14*4。由于伪C不是真的,所以看起来还可以。