tags:

views:

84

answers:

2

Looking at the disassembly (along with an instruction trace) of ld.so installed in Ubuntu 9.04, I swear I'm seeing data being stored below the stack pointer (i.e., beyond the top of the stack) at times. This seems insane to me, but maybe this is more common than I think. Does this happen often?

Here's what I see:

ebp: 0xBF8269E8, esp: 0xBF8269DC

c98:       8b 45 f0                mov    -0x10(%ebp),%eax
c9b:       8d 14 06                lea    (%esi,%eax,1),%edx
c9e:       8b 83 28 03 00 00       mov    0x328(%ebx),%eax
ca4:       3b 50 04                cmp    0x4(%eax),%edx
+1  A: 

What exactly leads you to believe that stuff is being stored below the stack pointer. All I can see is a negative offset from ebp which is the frame pointer.

This is typically used as a pointer to the next stack frame up from the current for a number of reasons.

  • Either side of ebp are the parameters passed in to this function (above ebp) and locals for this function (below ebp). Using -0x10(%ebp) just means that you're doing something with a local variable.
  • It easy to revert to the previous stack frame just by loading %esp with %ebp and returning.

Of course, you may be hitting something below %esp but that would depend on the data being loaded, which isn't actually shown in your sample.

A "diagram" may help:

     +------------------------+
     | Parameters passed to x |
     +------------------------+
     | Return address         |
%ebp +------------------------+
     | Locals for x           |
%esp +------------------------+

My memory of this is rusty (the specific instruction may not be exact but they should be good enough to indicate the concept behind it), but the typical function calling sequence is:

  • caller pushes parameters on the stack (push, push, ...).
  • caller pushes current %ebp on to the stack (push %ebp).
  • caller loads %ebp with %esp% (mov %ebp, %esp).
  • caller calls callee (call XYZ).
  • callee allocates space for locals (sub %esp,N).
  • callee can then use (%ebp+N) for parameters, (%ebp-N) for locals.

And, for returning:

  • callee loads %esp with %ebp (mov %esp, %ebp).
  • callee returns to caller (ret).
  • caller pops previous %ebp off the stack (pop %ebp).
  • caller cleans up parameter section (add %esp,N).
paxdiablo
Thanks. Most of that I think I knew (although that is a great summary). It makes sense that you'd go as low as (%ebp-N) (or maybe N-4 since there's a call in there), which still keeps you within the bounds of the current stack pointer. But in the example I'm citing, -0x10(%ebp) would look into memory 4 bytes below the current stack pointer (0xBF8269D8, whereas esp is 0xBF8269DC). That's something I haven't seen, and I wanted a sanity check to see if this trace represents something reasonable.
Soybean
A: 

Signal handlers need to be able to create a stack frame anytime. Therefore you always need to follow your ABI's stack protocol.

On PowerPC, a certain number of bytes (half a kilobyte?) are reserved below the stack pointer. (Of course, this may vary between platforms.) Signal handlers then have to waste that much space to avoid interfering with anything. The advantage being elimination of the store+subtract and add instructions that would create a frame for very small leaf functions.

Potatoswatter