Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Where, and why, is the x64 frame pointer supposed to point? (Windows x64 ABI)

I've been reading a long catalog of very good articles on the Windows x64 ABI. A very minor aspect of these articles is the description of the frame pointer. The general gist is that, because the Windows x64 call stack rules are so rigid, a dedicated frame pointer is typically not needed, although it is optional.

The one exception I have seen consistently noted is when alloca() is used to dynamically allocate memory on the stack. Functions doing so apparently require a frame pointer. For example, to quote from Microsoft's documentation on "Stack Allocation" (italics and bold added by me):

If space is dynamically allocated (alloca) in a function, then a nonvolatile register must be used as a frame pointer to mark the base of the fixed part of the stack and that register must be saved and initialized in the prolog. Note that when alloca is used, calls to the same callee from the same caller may have different home addresses for their register parameters.

To this, Microsoft's x64 ABI alloca() documentation cryptically adds:

_alloca is required to be 16-byte aligned and additionally required to use a frame pointer.

First of all, why must it be used? I assume for call stack unwinding on exception but I haven't yet found a satisfactory explanation.

Next question: where must it point? In the first of the two above quotations, it says it "must" be used to mark the base of the "fixed part of the stack". What's the "fixed part of the stack"? I get the impression this term denotes, in a given frame, the range of addresses that comprises (higher addresses to lower ones):

  • the caller return address (if you consider it part of the current function's frame);
  • the addresses to which non-volatile registers were saved by the function prologue; and
  • the addresses where local variables are being stored.

Again, I haven't found a satisfactory definition for this "fixed part". The "Stack Allocation" page I linked to above contains the diagram below along with the words "if used, the stack pointer will generally point here":

enter image description here

This very nifty blog post is equally vague, including a diagram stating the frame pointer "points somewhere in here", where "here" is the addresses for the saved non-volatile registers and the locals.

One last bit of crypticness, from Microsoft's MSDN article entitled "Dynamic Parameter Stack Area Construction", which contains only this:

If a frame pointer is used, the option exists to dynamically create the parameter stack area. This is not currently done in the x64 compiler.

What does "generally" mean? Where is "somewhere in here"? What's the option that exists? Is there a rule? Who cares?

Or, tl;dr: What the title asks. Any answer containing annotated assembly gratefully accepted.

like image 295
0xbe5077ed Avatar asked Jul 03 '14 09:07

0xbe5077ed


2 Answers

The diagram makes it quite clear that the frame pointer points to the bottom of the fixed portion of the local stack frame. The "fixed portion" is the part whose size does not change and whose location is fixed relative to the initial stack pointer. In the diagram it is labelled "Local variables and saved nonvolatile registers."[1]

The precise location of the frame pointer doesn't matter to the operating system because from an information-theoretical point of view, local variables are indistinguishable from memory allocated by alloca immediately upon entry to a function.

void function1()
{
  int a;
  int *b = (int*)alloca(sizeof(int));
  ...
}

void function2()
{
  int& a = *(int*)alloca(sizeof(int));
  int *b = (int*)alloca(sizeof(int));
  ...
}

The operating system has no way of distinguishing between these two functions. They both store a on the stack directly below the nonvolatile registers.

This equivalence is why the diagram says "generally". In practice, compilers point it where indicated, but in theory they could point it anywhere inside the local frame, as long as the distance from the frame pointer to the return address is a constant.

The function needs to inform the operating system where the frame pointer is so that the stack can be unwound during exception handling. Without this information, it would not be possible to walk the stack because the frame is variable-sized.

[1] You can infer this from the fact that the text says that the frame pointer points to "the base of the fixed part of the stack" and the diagram says "The frame pointer will generally point here", and it's pointing at the base of the local variables and saved nonvolatile registers. Assuming the text and diagram are in agreement, this implies that the fixed part of the stack is the same as the local variables and saved nonvolatile registers. This is the same sort of inference you make every day without even realizing it. For example, if a story says

Sally called out to her brother. "Billy, where are you?"

You can infer that Billy is Sally's brother.

like image 109
Raymond Chen Avatar answered Nov 11 '22 15:11

Raymond Chen


alloca is intended to be used with a size available only at runtime. As such, it will change the stack pointer by an amount that's not known at compilation time. You can normally address your local variables and arguments on the stack relative to the stack pointer due to the fixed layout, but alloca messes that up hence the need for another register that is stable. This frame pointer may point anywhere you want as long as you know the relation to the fixed area.

The frame pointer is also handy when the time comes to free the alloca memory because you can simply restore the stack pointer to a known location without having to worry about how much the stack pointer changed.

I don't think the ABI requires the frame pointer as such, or that it must be rbp or that it must point at any particular place (disclaimer: I don't use windows).

like image 20
Jester Avatar answered Nov 11 '22 16:11

Jester