>>109853707
i fucking LOVE how people get triggered by that. Thanks!
>>109853710
Yes, predictable is the keyword here. You know where the stack is at any time. It doesn't matter where it is physically located tho.
See you have this link script for example:
MEMORY
{
RAM (rwx) : ORIGIN = 0x80010000, LENGTH = 64K
LED (rwx) : ORIGIN = 0xf0000000, LENGTH = 4K
/* VGA (rwx) : ORIGIN = 0x90900000, LENGTH = 16K
UART (rw) : ORIGIN = 0x10010000, LENGTH = 4K */
}
ENTRY(_start);
SECTIONS {
. = ORIGIN(RAM);
.text : ALIGN(4) {
KEEP(*(.text.boot))
*(.init)
*(.text .text.*)
} > RAM
.rodata : ALIGN(4) {
*(.rodata .rodata.*)
} > RAM
.data : ALIGN(4) {
*(.data .data.*)
} > RAM
.bss : ALIGN(4) {
__bss = .;
PROVIDE(__bss_start = .);
*(.bss .bss.* .sbss .sbss.*)
__bss_end = .;
} > RAM
. = ALIGN(4);
__stack_top = ORIGIN(RAM) + LENGTH(RAM) - 4;
}
__stack_top is at the end of the memory. Then in assembly you just get that symbol/address and store it in your stack pointer (risc-v here):
.section .init
.globl _start
_start:
la sp, __stack_top
Then on a function call this happens:
call help_cmd // this is just a jump and link pseudo-instruction - store current PC into ra and jump to location
[...]
help_cmd:
addi sp, sp, -16 // get some space on the stack
sw ra, 0(sp) // store return address
sw s0, 4(sp) // save other callee-saved registers that you want to use
sw s1, 8(sp)
[...] more code here [...]
lw s1, 8(sp)
lw s0, 4(sp)
lw ra, 0(sp)
addi sp, sp, 16 // restore everything and return
ret
SIMPLE AS THAT