This post is about 32-bit Linux on x86. I don't know about other architectures/OSes.
Are these inventions of the C++ compiler? Does the [OS] specify memory
sections in RAM designated "stack" and "heap"?
Heap or free store
A program has different sections, one of them the .data
section. Dynamic memory allocation is usually implemented using the brk
system call (sbrk
builds on top of it). man brk
says the following:
brk()
and sbrk()
change the location of the program break,
which defines the end of the process's data segment (i.e., the
program break is the first location after the end of the
uninitialized data segment). Increasing the program break has
the effect of allocating memory to the process; decreasing the
break deallocates memory.
That means the "heap" or "free store" is in reality the .data
section.
As @kfx said in the comments to this answer, no standard states that malloc
has to be implemented using brk
. An implementation using mmap
is possible as well.
From man mmap
:
The mmap()
function shall establish a mapping between an address space
of a process and a memory object.
This basically means that a file (Unix ideology: "everything is a file") is mapped into memory.
Stack
The stack is also in the .data
section and grows downward. Technically, x86 enables you to define a downward-growing stack segment but Linux does not use this feature. Dunno why.
I'm pretty sure they are not built into the hardware but I could be
wrong.
No, they aren't. Segments are set up at runtime of the OS and not stored in the hardware.
The following is from Linux 4.2.
When the MBR has jumped to the bootloader and the bootloader has executed, this is executed (the path is /arch/x86/boot/header.S
):
# Normalize the start address
ljmp $BOOTSEG, $start2
start2:
movw %cs, %ax
movw %ax, %ds
movw %ax, %es
movw %ax, %ss
xorw %sp, %sp
All those segment registers are initialized to $BOOTSEG
here, which is 0x7c0
. sp
is set to 0x00
. No esp
since we're still in Real Mode!
After the initialization stuff is done, the jump into the real kernel is performed. The segment registers are set up as follows:
movw $__BOOT_DS, %cx
movw $__BOOT_TSS, %di
movl %cr0, %edx
orb $X86_CR0_PE, %dl # Protected mode
movl %edx, %cr0
# Transition to 32-bit mode
.byte 0x66, 0xea # ljmpl opcode
2: .long in_pm32 # offset
.word __BOOT_CS # segment
ENDPROC(protected_mode_jump)
.code32
.section ".text32","ax"
GLOBAL(in_pm32)
# Set up data segments for flat 32-bit mode
movl %ecx, %ds
movl %ecx, %es
movl %ecx, %fs
movl %ecx, %gs
movl %ecx, %ss
The segment registers are set to the content of cs
again.
Also, is the compiler responsible for generating assembly code that
specify which local or function data will be stored on the stack vs
CPU registers?
Yes. For function invocations, there are different calling conventions: some push their arguments onto the stack, some move them into registers.
Local variables can be implemented with registers or the stack as well.
Unoptimized C compilers push the arguments onto the stack, call the function, and pop them off afterwards ("stdcall" calling convention).
They use the stack for local variables, in combination with the ebp
register.