tags:

views:

366

answers:

10

I would like to be able to determine if a pointer is on the stack or not at runtime for a number of reasons. Like if I pass it into a function call, I can determine whether I need to clone it or not. or whether I need to delete it.

In Microsft C (VC 6,7,8) is there a way to bounds check a pointer to see if it in on the stack or not? I am only concerned with determining this on the thread that owns the stack the object was placed on.

something like

static const int __stack_size and __stack_top

???? Thanks!

+3  A: 

Doing this depends on the calling convention of the function. Some calling conventions place arguments in registers, others place them in memory after the head of the stack. Each one is a different agreement between the caller/callee. So at any function boundary in the stack a different convention could have been used. This forces you to track the calling convention used at every level.

For example, in fastcall, one or more arguments can be passed via registers. See MSDN for more. This would mess up any scheme to figure out if an address exists within a certain range. In MS's thiscall, the this pointer is passed via registers. The &this would not resolve to somewhere between a range of values between the begin and end of the stack.

Bottom line, research calling conventions, it specifies how stack memory will be laid out. Here is a good tutorial

Note this is very platform specific!

Doug T.
I think he wants to know if the object pointed to by a pointer is on the stack, not the actual pointer itself. In that case I don't think calling convention would matter since objects are rarely (if ever?) passed in registers -- only the pointer would be.
DougN
I'm not convinced the layout of the memory in the stack will affect the method of determining whether a variable is on the stack or the heap. It's a C++ question, so you'd expect cdecl calls throughout.
Kieveli
cdecl won't be used for methods where a this pointer is involved.
Doug T.
also see my edit for situations where arguments will be loaded into registers and you cannot easily tell through a state pointer comparison to see if a given address is on the stack.
Doug T.
Mark Ransom
You are totaly right that this is compiler specific. It is to cover for a serious deficiency in the C++ system, the inability to know whether something will:- go out of scope (on stack)- can be deleted or not ("static", "on stack" OR "on heap") As such it is intended to be in only one place in the root code for an object management system.
Peter Kennard
+8  A: 

Interesting question.

Here's an idea on how to determine it, but not a function call. Create a dummy variable at the very start of your application on the stack. Create a variable on the stack in a function isOnStack( void *ptr ) Check to see that the 'ptr' is between the dummy variable and the local variable.

Remember that the stack is contiguous for a given thread. I'm not sure what would happen when you started checking from one thread to another for this information.

If it's not in the stack, then it must be on the heap.

Kieveli
+1 this is quite a good solution, but of course it's unportable.
and won't work for calling conventions that load arguments in registers.
Doug T.
@Iraimbilanja: Can you elaborate on why this is not portable? The functionality of a stack depends on the addresses being sequential, so I would expect it to work on all systems.
e.James
It won't work in the case of multiple stacks, for example, which you're likely to get with multiple threads. Also, stacks can grow up or down, so this would be implementation-specific.
David Thornley
"Also, stacks can grow up or down, so this would be implementation-specific". You mean if you don't implement it right it won't work?void* pLow = min( p1, p2 );void* pHigh = max( p1, p2 );if (pX > pLow
Paul Mitchell
Research "thiscall" and "fastcall" calling conventions for why this may not always work.
Doug T.
Calling sequence shouldn't matter, since the low and high pointers aren't parameters, they're local variables. I presume the one from startup would be copied to a global so you could access it from the function.
Mark Ransom
+5  A: 

I do not know any method to determine where an object was allocated.

I see this kind of behaviour should be avoided. Such things should imho be solved by contract between user and library developer. State these things in the documentation! If unsure copy the object (which requires a copy constructor and saves you from trying to copy uncopyable objects).

You can also use smart pointers from Boost. If unsure when an object is now longer needed, pass it as a shared pointer.

ebo
Good answer to the question not asked!
Kieveli
+1 for seeing the forest, not just the trees. :-D
Chris Jester-Young
It's the typical difference between what people want and what they really need.
ebo
+9  A: 

Knowing whether an object is on the stack or heap isn't going to tell you whether it should be cloned or deleted by the called function. After all, you can clone either type, and while you shouldn't try to delete a stack-allocated function you shouldn't try to delete all heap pointers either.

Having a function that will make some arcane check to see whether it should delete a passed pointer or not is going to cause confusion down the line. You don't want a situation where you may or may not be able to refer to fields in an object you passed, depending on context. Nor do you want to risk a mistake that will result in trying to free a stack object.

There isn't any standard way to tell what a pointer points to, and any nonstandard way is likely to break. You can't count on stack contiguity, particularly in multithreaded applications (and somebody could easily add a thread to an application without realizing the consequences).

The only safe ways are to have a calling convention that the called function will or will not delete a passed object, or to pass some sort of smart pointer. Anything else is asking for trouble.

David Thornley
+1 for "you shouldn't try to delete all heap pointers either." Ownership should be more tightly controlled than that.
Mark Ransom
A: 

This is very platform specific, and IMO suitable only for debug build diagnostics. What you'd need to do (on WIntel) is this:

When a thread is created, create a stack variable, and store its address in a global (threadid, stack base address) map.

IsOnStack needs to create its own local variable, and check if the pointer passed is between the stack base and the address in the current stack frame.

This will not tell you anything abotu variables within other threads. Stack addresses decrease, so the base address is higher than the current address.


As a portable solution, I'd pass a boost::shared_ptr, which can be associated with a deleter. (In boost, this is not a template parameter, so it doesn't "infect" the function consuming the pointer).

you can create an "unmanaged" pointer like this:

inline void boost_null_deleter(void *) {}

template <typename T> inline
boost::shared_ptr<T> unmanaged_ptr(T * x)
{
   return boost::shared_ptr<T>(x, ::boost_null_deleter);
}

and call your function like this

Foo local = { ... };
FooPtr heapy(new Foo);

FunnyFunc(unmanaged_ptr(&local));
FunnyFunc(heapy);
peterchen
A: 

You may find some insights here.

Nick D
A: 

I've wanted such a feature in C++ for a while now, but nothing good really exists. The best you can hope for is to document that you expect to be passed an object that lives on the heap, and then to establish an idiom in the code so that everyone working on the code base will know to pass heap allocated objects to your code. Using something like auto_ptr or boost::shared_ptr is a good idiom for this kind of requirement.

A: 

Well, I agree there is probably a better way of doing what you're trying to do. But it's an interesting question anyway. So for discussion's sake...

First, there is no way of doing this is portable C or C++. You have to drop to assembly, using at least a asm{ } block.

Secondly, I wouldn't use this in production code. But for VC++/x86 you can find out if a variable is on your stack by check that it's address is between the values of ESP and EBP registers.

Your ESP ( Extended Stack Pointer, the low value ) holds the top of your stack and the EBP ( Extended Base Pointer ) usually the bottom. Here's the Structure of the Call Stack on x86.

Calling convention will affect function parameters mainly, and how the return address is handled, etc. So it doesn't relate to your stack much. Not for your case anyway.

What throws things off are compiler optimizations. Your compiler may leave out the frame pointer ( EBP ). This is the -Oy flag in VC++. So instead of using the EBP as the base pointer you can use the address of function parameters, if you have any. Since those a bit higher up on the stack.

But what if that variable you're testing is on your caller's stack? Or a caller's stack several generations above you? Well you can walk the entire call stack, but you can see how this can get very ugly ( as if it isn't already :-) )

Since you're living dangerously, another compiler flag that may interest you is - Gh flag. With that flag and a suitable _penter hook function, you can setup these calculations for the functions or files or modules, etc. easily. But please don't do this unless you'd just like to see how things work.

Figuring out what's on the heap is even worse....

kervin
A: 

On some platforms, the stack can be split by the run-time system. That is, instead of getting a (no pun intended) stack overflow, the system automatically grabs some more stack space. Of course, the new stack space is usually not contiguous with the old stack space.

It's therefore really not safe to depend on whether something is on the stack.

The use of auto_ptr generally eliminates the need for this kind of thing, and is way cooler besides.

Phone Guy
A: 

The MSVC Windows compiler specific answer. This is of course specific to the thread the object is in. It's a pretty bad idea to pass any auto-stack item into any thread other than the one whos stack it is on so I'm not worried about that :)

bool __isOnStack(const void *ptr)
{

  • // FS:[0x04] 4 Win9x and NT Top of stack
    // FS:[0x08] 4 Win9x and NT Current bottom of stack
    const char *sTop;
    const char *sBot;
    __asm {
    mov EAX, FS:[04h]
    mov [sTop], EAX
    mov EAX, FS:[08h]
    mov [sBot], EAX

    }
    return( sTop > ((const char *)ptr) && ((const char *)ptr) > sBot);

}

Peter Kennard