17

I'm reading a GNU as introductory book for programs running on top of an OS. I'm at the stack memory part and I was curious how the stack looks like in a freestanding environment.

I'm guessing that within an OS, the program only sees virtual memory and the stack is set up by the OS.

However in a freestanding environment, where does SP/ESP/RSP points to at the beginning? Does the CPU reserves the stack at initialization?

1
  • @PeterMortensen: "freestanding" is a single word, not hyphenated, in this context where it's a technical term used in computing. See for example en.wikipedia.org/wiki/Standalone_program which spells it as a single word. Commented Oct 15 at 22:05

3 Answers 3

20

There is no specific location for an initial stack pointer in freestanding, if you go back to the very beginning of program execution. CPU does not do that.

Usually there would be a bootloader/pre-execution environment which executes before any user program could be run, and it could set up some initial stack. However usually it is up to the user program to reserve some amount of space somewhere and to set stack pointer register. E.g. if you look at any example of a MCU firmware you'd find there's a stack area reserved (usually in the in linker script), and SP is set to point to that very early in the firmware execution. Take a look at any MBR bootloader and you're likely to see it resets SP to some hardcoded location in the very first instructions, etc (even though BIOS has set up some stack, it is safer to reset it to some place under user control).

UEFI can be thought to be an exception to this as it does set up a stack, but then one can argue UEFI applications/bootloaders are not really executing in freestanding environment. UEFI is a sort of OS for those applications.

Sign up to request clarification or add additional context in comments.

Comments

4

Normally you'd have some startup code written in assembler that loads the initial stack pointer, copies the initial values of the data section and zero-initializes the bss section, then runs global initializers and finally jumps to the main function.

Even in hosted environments, there is some startup code that provides an entrypoint that follows a specific calling convention, usually different from the language's default.

Linux does pre-initialize the stack pointer for executable programs, but mainly because the command line and environment are passed there. There is no rule against a program ignoring this and setting up its own stack space.

5 Comments

So I could malloc some memory, then with inline assembly, point rsp to its end, and I'm good to go?
Yes. That's also how additional threads get their stack space. On some architectures, you need to do more, e.g. POWER requires the stack pointer to point to 112 bytes before the end, and on pretty much all architectures you should create a sentinel stack frame so debuggers stop reading past the end while traversing the stack.
Well... if you had a stack to call malloc() with you could. ;) Generally in the very early days of execution, there's no memory management to speak of and you just grab a predefined area as stack (put some constant in rsp) to start working with before you do other things like set up your heap so malloc() itself will work. It all depends just how "freestanding" you really are.
As already pointed out, malloc is a function call, and to make a function call, we need to have the function call environment setup, and that includes a stack & stack pointer and many other things the normal runtime needs. So, can create another stack using malloc, if already have one setup. Otherwise, malloc is not (yet) available and have to use some other lower-level approach to identify and reserve stack memory and initialize the stack pointer.
malloc() is a compiled function (number two or three of the most complicated in the library), you need a stack to run a compiled function. You cannot call a C function to setup and prepare to call C functions. For bare metal (freestanding) in general, you own the whole system as in you do not ask anyone for anything. You live by the rules of the hardware. Allocation of memory, stack, in a bare metal system looks something like this. I declare the stack pointer shall be set at 0x1234000. Can declare that from a balcony in your underwear with a crown on if you like...then hardcode it
3

Just a data point... On most AVR microcontrollers, the stack pointer is initialized to RAMEND (the end of the RAM) by the hardware. Regardless, the avr-libc initializes it again (by default, to RAMEND, but see comments) in __init(), i.e. before the user code starts to run. I guess this is for compatibility with older devices that did not initialize it in hardware, or to cope with errata.

3 Comments

Nitpick: AVR-LibC initializes SP to the value of symbol __stack, which is weak and has a default value of RAMEND. The user can set __stack to anything they like, and hence initialize SP to anything they ike by means of, say, -Wl,--defsym,__stack=0x1234.
Thanks! I hadn't realized this. Updated the answer.
+1 Can confirm the same about bare-metal ARM. Perhaps also do touch on the heap initialization for completeness.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.