23

My question is pretty simple and straightforward: if I have e.g. 1MB of RAM assigned to the program's stack, can I get the addresses of the start and the end, or the start and the length?

I'm using Visual Studio 2013.

rev
  • 1,861
  • 17
  • 27

3 Answers3

31

You should question your assumptions about stack layout.

Maybe the stack doesn't have just one top and bottom

Maybe it has no fixed bottom at all

Clearly there's no portable way to query concepts which are not portable.

From Visual C++, though, you can use the Win32 API, depending on Windows version.

On Windows 8 it is very easy, just call GetCurrentThreadStackLimits

Earlier versions need to use VirtualQueryEx and process the results somewhat. Getting one address in the stack is easy, just use & on a local variable. Then you need to find the limits of the reserved region that includes that address. Joe Duffy has written a blog post showing the details of finding the bottom address of the stack

Community
  • 1
  • 1
Ben Voigt
  • 277,958
  • 43
  • 419
  • 720
  • As [Thread Stack Size](https://msdn.microsoft.com/en-us/library/windows/desktop/ms686774%28v=vs.85%29.aspx) says, *default* initial and reserve sizes can be obtained from the PE header (it doesn't specify *which module's* header). – ivan_pozdeev Feb 25 '15 at 01:52
  • @ivan_pozdeev: That's the stack size for the main thread only, although it also serves as a default for threads created later, their size is set dynamically. And of course it is the PE header of the executable image, the one matching `GetModuleHandle(NULL)`. – Ben Voigt Feb 25 '15 at 02:42
  • Your assumption that Visual C++ == Win32 isn't going to be sound [much longer](http://blogs.msdn.com/b/vcblog/archive/2014/11/12/cross-platform-mobile-development-with-visual-c.aspx) – Damien_The_Unbeliever Feb 25 '15 at 07:05
  • @Damien_The_Unbeliever: That page is playing fast and loose with the name "Visual C++". Yes, the Visual Studio development environment may target other platforms, even C++ native development on other platforms, but it is being done via toolchain plugins for compilers other than Visual C++, not by adding other platforms to the Visual C++ toolchain. Would be a better argument using Windows Store API, which isn't Win32 and is done with the Visual C++ toolchain. – Ben Voigt Feb 25 '15 at 14:46
  • @BenVoigt: the *default* stack size specified in the PE header doesn't *always* get applied to secondary threads. The caller decides that when calling `CreateThread()`/`_beginthread/ex()`. – Remy Lebeau Dec 11 '17 at 22:33
  • @RemyLebeau: Yes, that's why I said it's not the stack size for secondary threads, only a default. – Ben Voigt Dec 11 '17 at 22:51
9

GetCurrentThreadStackLimits seems to do what you're looking for, getting the lower/upper boundaries of the stack into pointer addresses:

ULONG_PTR lowLimit;
ULONG_PTR highLimit;
GetCurrentThreadStackLimits(&lowLimit, &highLimit);

Looks like it is only available on Windows 8 and Server 2012 though.

Check the MSDN

Iharob Al Asimi
  • 52,653
  • 6
  • 59
  • 97
E. Moffat
  • 3,165
  • 1
  • 21
  • 34
0

On Windows before 8, implement GetCurrentThreadStackLimits() yourself:

#include <windows.h>
#if _WIN32_WINNT < 0x0602
VOID WINAPI GetCurrentThreadStackLimits(LPVOID *StackLimit, LPVOID *StackBase)
{
    NT_TIB *tib = (NT_TIB *) NtCurrentTeb();
    *StackLimit = tib->StackLimit;
    *StackBase = tib->StackBase;
}
#endif
  • From the Joe Duffy article I linked in my answer: *Unfortunately, the `StackLimit` is only updated as you actually touch pages on the stack, and thus **it’s not a reliable way to find out how much uncommitted stack is left**. The CLR uses `kernel32!VirtualAlloc` to commit the pages, not by actually moving the guard page, so **`StackLimit` is not updated as you might have expected.*** But if you read the comments under the question before answering, you would already have known that the TIB does not contain the needed information. – Ben Voigt Dec 11 '17 at 23:07
  • @BenVoigt TIB does contain the information. The "low" value that GetCurrentThreadStackLimits returns is the undocumented `DeallocationStack` field, located at `FS:[0xE0C]` (x86) / `GS:[0x1478]` (x64). At least since Windows 2000. So, one may use undocumented field on OS prior to Win8, and use GetCurrentThreadStackLimits on Win8+. – Alex Apr 06 '21 at 11:09
  • @Alex: Perhaps, but this answer references the `StackLimit` field which gives wrong results. – Ben Voigt Apr 06 '21 at 14:21
  • @BenVoigt Well, should someone edit the answer then? It is a valid answer (considering the "fix") - certainly better than the accepted answer suggests (call into kernel via `VirtualQuery`). I don't have enough expierence in MS C++ to properly write that. – Alex Apr 07 '21 at 11:57
  • @Alex: I still stand by my answer... it works correctly on all platforms, while pulling the undocumented field out of the TIB requires per-platform code. – Ben Voigt Apr 07 '21 at 16:57