What is the main origin of heap and stack memory division? - heap-memory

I read a lot of explanation of heap and stack memory, and all of them obscure anyway in terms of origin. First of all I understand how this memories works with software, but I don't understand the main source of this division. I assume that they are the same unspecialized physical memory, but...
For example say we have PC without any OS, and we want create some bootable program with assembly language for x86. I assume we can do this (Personally I don't know assembly, but some people write OS anyway). So the main question is Can we already operate with heap and stack, or we must create some memory managment machinery for this? If yes, so how it can be possible in terms of bare metal programming?

Adding something to the other answer, fairly correct but perhaps not very complete.
Heap and stack are two (software) ways to "manage" memory. The physical memory, normally, is a flat array of cells where a program can read and write. It is up to the running program to use those cells as it wants. But there is more to say.
1^ thing. Heap is totally software, while stack is also (or mainly) a hardware thing. Most processors have hardware (or CPU instruction) to support the stack, while most (or all?) don't care about the heap. Even more: there are small embedded processors (or microcontrollers) which have a separated stack area - totally different from other ram areas where the program could create a "heap".
2^ thing. Whean speaking about "programs", one can/should think that the operating system (the OS) is a program, specialized in managing resources (memory included), and extendable with "applications" (which are programs). In such scenario, stack and heap are managed in cooperation from both OS and the applications.
So, to reply to your main question, the 90% correct answer is: in bare metal you have already a stack - perhaps you have to issue some short instruction to set it up, but it is straightforward. But you don't have a heap, you must implement it in your program. First you set aside some memory to be used as a stack; and then you can set aside some more memory to be used as a heap, not forgetting that you must preserve some memory for normal/static data. The part of the program that manages the heap should know what to do, using but not erratically overwriting the stack and the static data, to perform its functions.

Related

How does a JIT compiler like v8 structure its memory (i.e. the stack, heap, code, and data)?

I am trying to figure out how Linux stores the stack, or C even, since each process can have its own stack. Every diagram shows the assembly instructions and registers used in managing an extremely primitive stack (only 1 stack as well), so I'm in doubt that this is how higher-level systems do it (i.e. systems with multiple threads, processes, fibers, workers, coroutines, generators, etc.). Which is why this question is about v8....
How does v8 (at a high level, maybe linking to some related code if it's useful) structure the stack? How does it structure the heap? And code and data. You always see diagrams like this:
But (a) is that how a production JIT system like v8 does it? And (b) what is the actual implementation of the key structs? What data structures are used? Is the stack actually a linked list of some sort? Or is it an array? Is the heap broken down into a doubly-linked list of blocks of a certain size? Are they allocated in physically contiguous blocks of memory (in v8), or are they using OS-specific syscalls like mmap on Linux to allocate memory, which it then structures in its own way using some sort of block architecture (which is what I would like to know about)? Or is it just relying on malloc and such?
Basically, I am interested in simulating how a production JIT compiler works and am stuck at the beginning part of how the memory is structured (with structs and such). I am not sure if the stack is actually a linked-list in reality, or something else. Am very much interested to know.
The reason I think these diagrams are a lie is because we are usually dealing with virtual memory here, the physical memory is abstracted away. And even then, the virtual memory is divided into small pages, which might be scattered around or something, I don't know. So I feel these visuals are totally incorrect and misleading, and would like to know what it actually looks like.
How does v8 structure the stack?
Just like any other program: rbp points at the base of the current function's stack frame, and rsp points at the "top" (growing downwards, though); the push and pop instructions are used to write/read from where rsp points to while decrementing/incrementing rsp accordingly. There's no data structure, just raw memory.
Is the heap [...] using OS-specific syscalls like mmap on Linux to allocate memory
Yes. V8's managed heap is allocated one page at a time using mmap, or equivalents on other operating systems.
Note that what such diagrams call "heap" is heap-allocated memory in the C++ sense, not the managed heap in a VM sense. One could argue that the latter is just a special part/case of the former though, and whether you use mmap or malloc is an implementation detail that doesn't really matter (beyond possibly certain performance differences, depending on usage patterns).
I think these diagrams are a lie is because we are usually dealing with virtual memory here
I'd put it the opposite way: virtual memory is why these diagrams are (slightly simplified but) mostly accurate. Every process thinks that it is alone in the world, it doesn't see any other processes in its memory address space (just a bit of the kernel, for interacting with it). The memory that a process sees does look like raw memory; it's just that the addresses aren't the physical addresses, there is a translation layer in between (which is managed by kernel + CPU).

Is the paging file part of the heap? And am I correct in thinking that the heap doesn't have to be a continuous block of memory?

Just two simple questions I could not find a proper answer to. I decided I would learn assembly language since its one area of my programming capabilities that lacks.
Also where functions are called recursively, how does the OS determine how large the stack should be for a thread? or does it just place the stack where there is a large amount of memory it can expand into before colliding with the heap?
Is the paging file part of the heap?
Not really. You can't access it directly; the OS may use it transparently to page out parts of your process's virtual address space.
the heap doesn't have to be a continuous block of memory?
Yes, allocations with mmap(MAP_ANONYMOUS) are considered part of the heap, and can be anywhere in your virtual address space. As Weather Vane points out, "heap" is a pretty broad / archaic term. It's not a useful concept for understanding what really happens under the hood.
e.g. you can mmap a file to make its contents part of your virtual address space. Is that part of the "heap"? I don't know and don't care.
Also where functions are called recursively, how does the OS determine how large the stack should be for a thread?
It just sets a max stack size limit when your process starts. Using recursive functions has nothing to do with it. (Although it's the most common way to use up the fixed size limit, the other being large local arrays).

Does a compiler have consider the kernel memory space when laying out memory?

I'm trying to reconcile a few concepts.
I know of virtual memory is shared (mapped) between the kernel and all user processes, which I read here. I also know that when the compiler generates addresses for code + data, the kernel must load them at the correct virtual addresses for that process.
To constrain the scope of the question, I'll just mean gcc when I mention 'the compiler'.
So does the compiler need to be compliant each new release of an OS, to know not to place code or data at the high memory addresses reserved for the kernel? As in, someone writing that piece of the compiler must know those details of how the kernel plans to load the program (lest the compiler put executable code in high memory)?
Or am I confusing different concepts? I got a bit confused when going through this tutorial, especially at the very bottom where it has OS code in low memory addresses, because I thought Linux uses high memory for the kernel.
The compiler doesn't determine the address ranges in memory at which things are placed. That's handled by the OS.
When the program is first executed, the loader places the various portions of the program and its libraries in memory. For memory that's allocated dynamically, large chunks are allocated from the OS and then sometimes divided into smaller chunks.
The OS loader knows where to load things. And the OS's virtual memory allocation logic how to find safe, empty spaces in the address space the process uses.
I'm not sure what you mean by the "high memory addresses reserved for the kernel". If you're talking about a 2G/2G or 3G/1G split on a 32-bit operating system, that is a fundamental design element of those OSes that use it. It doesn't change with versions.
If you're talking about high physical memory, then no. Compilers don't care about physical memory.
Linux gives each application its own memory space, distinct from the kernel. The page table contains the translations between this memory space and physical RAM, and the kernel sets up the page table so there's no interference.
That said, the compiler usually doesn't even care where the program is loaded in memory. Why would it?

Beginner's confusion about x86 stack

First of all, I'd like to know if this model is an accurate representation of the stack "framing" process.
I've been told that conceptually, the stack is like a Coke bottle. The sugar is at the bottom and you fill it up to the top. With this in mind, how does the Call tell the EIP register to "target" the called function if the EIP is in another bottle (it's in the code segment, not the stack segment)? I watched a video on YouTube saying that the "Code Segment of RAM" (the place where functions are kept) is the place where the EIP register is.
Typically, a computer program uses four kinds of memory areas (also called sections or segments):
The text section: This contains the program code. It is reserved when the program is loaded by the operating system. This area is fixed and does not change while the program is running. This would better be called "code" section, but the name has historical reasons.
The data section: This contains variables of the program. It is reserved when the program is loaded and initialized to values defined by the programmer. These values can be altered by the program while it executes.
The stack: This is a dynamic area of memory. It is used to store data for function calls. It basically works by "pushing" values onto the stack and popping from the stack. This is also called "LIFO": last in first out. This is where local variables of a function reside. If a function complets, the data is removed from the stack and is lost (basically).
The heap: This is also a dynamic memory region. There are special function in the programming language which "allocate" (reserve) a piece of this area on request of the program. Another function is available to return this area to the heap if it is not required anymore. As the data is released explicitly, it can be used to store data which lives longer than just a function call (different from the stack).
The data for text and data section are stored in the program file (they can be found in Linux for example using objdump (add a . to the names). stack and heap are not stored anywhere in the file as they are allocated dynamically (on-demand) by the program itself.
Normally, after the program has been loaded, the memory area reamining is treated as a single large block where both, stack and heap are located. They start from opposite end of that area and grow towards each other. For most architectures the heap grows from low to high memory addresses (ascending) and the stack downwards (decending). If they ever intersect, the program has run out of memory. As this may happen undetected, the stack might corrupt (change foreign data) the heap or vice versa. This may result in any kind of errors, depending how/what data has changed. If the stack gets corrupted, this may result in the program going wild (this is actually one way a trojan might work). Modern operating systems, however should take measures to detect this situation before it becomes critical.
This is not only for x86, but also for most other CPU families and operating system, notably: ARM, x86, MIPS, MSP430 (microcontroller), AVR (microcontroller), Linux, Windows, OS-X, iOS, Android (which uses Linux OS), DOS. For microcontrollers, there is often no heap (all memory is allocated at run-time) and the stack may be organized a bit differently; this is also true for the ARM-based Cortex-M microcontrollers. But anyway, this is quite a special subject.
Disclaimer: This is very simplified, so please no comments like "how about bss, const, myspecialarea";-) . There also is not requirement from the C standard for these areas, specifically to use a heap or a stack. Indeed there are implementations which don't use either. Those are most times embedded systems with small (8 or 16 bit) MCUs or DSPs. Also modern architectures use CPU registers instead of the stack to pass parameters and keep local variables. Those are defined in the Application Binary Interface of the target platform.
For the stack, you might read the wikipedia article. Note the difference in implementation between the datatstructure "stack" and the "hardware stack" as implemented in a typical (micro)processor.

Microcontroller memory allocation

I've been thinking for day about the following question:
In a common pc when you allocate some memory, you ask for it to the OS that keeps track of which memory segments are occupied and which ones are not, and don't let you mess around with other programs memory etc.
But what about a microcontroller, I mean a microcontroller doesn't have an operating system running so when you ask for a bunch of memory what is going on? you cannot simply acess the memory chip and acess a random place cause it may be occupied... who keeps track of which parts of memory are already occupied, and gives you a free place to store something?
EDIT:
I've programmed microcontrollers in C... and I was thinking that the answer could be "language independent". But let me be more clear: supose i have this program running on a microcontroller:
int i=0;
int d=3;
what makes sure that my i and d variables are not stored at the same place in memory?
I think the comments have already covered this...
To ask for memory means you have some operating system managing memory that you are mallocing from (using a loose sense of the term operating system). First you shouldnt be mallocing memory in a microcontroller as a general rule (I may get flamed for that statement). Can be done in cases but you are in control of your memory, you own the system with your application, asking for memory means asking yourself for it.
Unless you have reasons why you cannot statically allocate your structures or arrays or use a union if there are mutually exclusive code paths that might both want much or all of the spare memory, you can try to allocate dynamically and free but it is a harder system engineering problem to solve.
There is a difference between runtime allocation of memory and compile time. your example has nothing to do with the rest of the question
int i=0;
int d=3;
the compiler at compile time allocates two locations in .data one for each of those items. the linker and/or script manages where .data lives and what its limitations are on size, if .data is bigger than what is available you should get a linker warning, if not then you need to fix your linker commands or script to match your system.
runtime allocation is managed at runtime and where and how it manages the memory is determined by that library, even if you have plenty of memory a bad or improperly written library could overlap .text, .data, .bss and/or the stack and cause a lot of problems.
excessive use of the stack is also a pretty serious system engineering problem which coming from non-embedded systems is these days often overlooked because there is so much memory. It is a very real problem when dealing with embedded code on a microcontroller. You need to know your worst case stack usage, leave room for at least that much memory if you are going to have a heap to dynamically allocate, or even if you statically allocate.

Resources