Allocating a new call stack

(I think there is a high probability that this question will either be duplicated or otherwise mentioned here already, but finding the answer is difficult due to interference from the "stack allocation" and related terms.)

I have a toy compiler that I worked on for a scripting language. In order to be able to pause the execution of the script and return to the host program, it has its own stack: a simple memory block with the variable "stack pointer", which is incremented using regular C code operations for these kinds of things, etc. etc. Not interesting yet.

I am currently compiling C. But I am also interested in exploring compilation and machine code - while maintaining a secondary stack and the ability to return to the host program at predefined breakpoints.

So ... I believe that it is unlikely to be a problem to use regular stack registers in my own code, I assume what happens to the registers, there is my own business, while everything is restored when this is done (do it right if I am wrong in this matter ) But ... if I want the script code to call some other library code, is it safe to leave the program using this "virtual stack" or is it important that the original folder is returned for this

This and this answers indicate that the stack is not an ordinary block of memory, but that it relies on special, system-specific behavior associated with page errors and something else.

So:

  • Is it safe to move stack pointers to another memory location? Is the stack memory not "special"? I believe that thread libraries should do something like this, as they create more stacks ...
  • Assuming that any memory area is safe for management using stack registers and instructions, I cannot think of any reason why it would be a problem to call any functions with a known call depth (i.e., without recursion, without function pointers) if this sum available on the virtual stack. Correctly?
  • stack overflow is obviously a problem in normal code anyway, but will there be any additional catastrophic consequences for overflow in such a system?

This, obviously, is not really required, since simply returning the pointers to the real stack would be perfectly functional or, for that matter, not abusing it in the first place and simply placing fewer registers, and I probably shouldn't try to do this is generally (not least due to the fact that I am clearly not in my depths). But I'm still curious. Want to know how it works.

EDIT: Sorry, of course, it should be said. I am working on x86 (32-bit for my own machine), Windows and Ubuntu. Nothing exotic.

+7
source share
3 answers

All of these answers are based on "common processor architectures", and since it involves generating assembler code, it should be "target" - if you decide to do this on processor X, which has some weird stack processing, obviously it’s not worth lower the screen that says [replace paper]. For x86 as a whole, the following are, unless otherwise indicated.

is it safe to move the stack pointers into some other area of memory? 

Is the stack memory not "special"? I draw stream libraries should do something like this as they create more stacks ...

Memory as such is not special. However, this suggests that this is not for the x86 architecture, where the stack segment is used to limit the use of the stack. Although this is possible, it is quite rare in the implementation. I know that a few years ago Nokia had a special operating system that uses segments in 32-bit mode. As far as I can imagine now, the only thing I contacted with this is using the stack segment, as described in x86 segmentation mode.

Assuming that any memory area is safe for management using a stack of registers and instructions, I cannot think of why it would be a problem of calling any functions with a known call depth (i.e. there is no recursion, no pointers to functions), if this sum available on the virtual stack. Correctly?

Correctly. Until you expect that you can return to some other function without returning to the original stack. A limited level of recursion would also be acceptable if the stack were deep enough [there are certain types of problems that are certainly difficult to solve without recursion, such as searching for a binary tree).

stack overflow is obviously a problem in normal code, but will there be any additional catastrophic consequences for such a system overflow?

In fact, it would be a terrible mistake if you were a little unlucky.

I would suggest that you use a call to VirtualProtect() (Windows) or mprotect() (Linux, etc.) to mark the "end of the stack" as unreadable and impregnable, so that if your code accidentally leaves the stack, it will fire properly, and not some other more subtle undefined behavior [because it does not guarantee that the memory just below (lower address) is inaccessible, so you can overwrite some other useful things if it leaves the stack, and this may cause some it’s very difficult to debug errors].

Adding a bit of code that periodically checks the stack depth (you know where your stack starts and ends, so it shouldn't be difficult to check if the particular stack value is “out of range” [if you give yourself a little “extra buffer space” between the top part of the stack and the “we are dead” zone protected by you - the “collapse zone”, as they might call it if it was a car in disrepair]. You can also fill the entire stack with a recognizable template and check how many of them are “untouched” .

+4
source

Typically on x86 you can use an existing stack without any problems if:

  • you do not overflow it
  • you do not increase the stack pointer case (with pop or add esp, positive_value / sub esp, negative_value ) beyond the one your code starts with (if you do interrupts or asynchronous callbacks (signals) or any other action using the stack will destroy its contents)
  • you are not raising any CPU exception (if you do this, the exception handling code will not be able to disable the stack to the nearest point where the exception can be processed)

The same applies to using another block of memory for the temporary stack and specifying esp to the end.

The problem with handling exceptions and disabling the stack is due to the fact that your compiled C and C ++ code contains some data structures related to exception handling, such as eip ranges with links to their respective exception handlers (this indicates where the nearest handler is exceptions for each piece of code), and there is also information related to the identification of the calling function (i.e. where the return address is on the stack, etc.), so you can create exception bubbles. If you simply plug in the source machine code into this framework, you will not properly extend these exception handling structures to cover it, and if everything goes wrong, they are most likely to be wrong (the whole process may fail or get corrupted despite the fact that you have exception handlers around the generated code).

So yes, if you are careful, you can play with stacks.

+2
source

You can use any region that you like for the processor stack (modulo memory protection).

Essentially, you simply load the ESP register ("MOV ESP, ...") with a pointer to a new area, but you managed to isolate it.

You should have enough for your program and what it can call (for example, the Windows OS API) and any ridiculous behaviors that exist on the OS. You may be able to figure out how much space your code requires; a good compiler can do this easily. Determining how much Windows needs is more complicated; you can always highlight "too much", which is usually what Windows programs do.

If you decide to manage this space tightly, you may have to switch stacks to invoke Windows functions. This will not be enough; you are likely to get a burn from various Windows surprises. I describe one of them here on Windows: avoid pushing the full x86 context onto the stack . I have mediocre solutions, but not good solutions for this.

+2
source

All Articles