Okay, I needed to find a method of determining the stack bottom.  By using the forum's Search feature, I eventually got myself into a post by Maverick regarding the "TIB" (haven't figured out what TIB is yet, but I'll try).  Apparently, the "TIB" is referenced via the FS segment register and contains information on the what I hope is the thread:
FS:[0] - Exception list
FS:[1 *4] - Stack base (bottom of stack, highest-numbered address)
FS:[2 *4] - Stack limit (topmost limit of stack, lowest-numbered address)
.
. other stuff that was commented as "?"
.
Gosh, I forgot to get the address of Maverick's post.  Oh well.  Anyway, is my understanding correct?  I need to determine the stack base since my garbage collector needs to take a look at pointers in the stack.  Can I assume that unless I specifically re-target those two pointers, the limits I get will be the stack area that Windows has deigned to give me?  Thanks.

BTW threads within a process have the same address space right?  So I presume a new process will be given a different virtual address for the stack?

Posted on 2005-05-12 05:59:00 by AmkG


BTW threads within a process have the same address space right?? So I presume a new process will be given a different virtual address for the stack?

Yes. Yes. Each process has its own stack. Standard architecture is for the stack to start at the top of your address space and fill downwards, while your data area starts at the end of your executable code and fills upwards, till the two of them meet  :D
Posted on 2005-05-12 06:05:21 by sluggy
TIB = Thread Information Block. Those three fields should be available on all windows versions, but otherwise it's pretty system-specific (especially between 9x and NT).

As far as I know it's not really the stack upper/lower bounds you can read from that structure, though - it's the *allowed* values. Upon a context switch, a thread with esp outside those values will be automatically terminated, without warning.

All threads within a process have the same address space, yes (a process is nothing more than a "container", which among other things includes the address space). Different threads, obviously, have different stacks though.
Posted on 2005-05-12 06:09:32 by f0dder
Okay, from how I understand f0dder, the "allowed" values are read from the structure, not necessarily the actual limits of the stack of the process (or thread?).  From Maverick's post I gathered that the stack limits can be set to any value, and if I ever wanted to change my stack, I can allocate space using, say, VirtualAlloc, and set FS:[1 *4] and FS:[2 *4] to the limits of that newly allocated space.

So if I have a brand-new thread, the esp for that thread, without any calls etc. is equal to FS:[1 *4] (or a couple dwords down it)?  Can my garbage collector mark any structures found on the stack from esp to FS:1[ *4]?  This assumes that the program the GC is GC'ing for does not do any tricks with esp (since I AM writing the program).

BTW the last question in the original post should have been "I presume a new thread (within a process) will be given a different virtual address for the stack?"

A little OT, I read somewhere on the forum that GlobalAlloc and company are no longer very stable for Win32?  Dang.  No wonder I kept getting problems with it, especially with GlobalReAlloc.
Posted on 2005-05-13 00:59:19 by AmkG

A little OT, I read somewhere on the forum that GlobalAlloc and company are no longer very stable for Win32?  Dang.  No wonder I kept getting problems with it, especially with GlobalReAlloc.

They're "deprecated", but still work (on NT internally they end up calling the Heap* functions). I would suggest using Heap* functions except where PlatformSDK says you have to use Global* (clipboard routines, for instance).

If you've had problems with the Global* functions, it's probably because you've accidentally overwritten some heap control structures. This can happen if you blindly treat all return values from Global/Local* functions as pointers - iirc, if you haven't specified the GMEM_FIXED flag, there's a few dwords of information prepended before the actual memory you've requested.
Posted on 2005-05-17 08:15:13 by f0dder
Hrm, I don't remember exactly what problems I kept getting on Global*, I just vaguely remember it being with GlobalReAlloc.  As far as I can remember, I needed to extend a memory area and when I do, it succeeds, but further GlobalAlloc's would crash me.  Happened on an XP system too IIRC.  Still I haven't been programming in Win32 for quite some time now and I'm most definitely not 100% sure on what happened.  Since then I've just allocated one big memory area and allocated little sections from it myself.

I think I also tried using Heap* functions directly but I couldn't remember if I solved it that way or if the problem persisted.
Posted on 2005-05-17 21:05:34 by AmkG

I think I also tried using Heap* functions directly but I couldn't remember if I solved it that way or if the problem persisted.

If it didn't, there's basically two things that could be wrong... either HeapRealloc fails, which means you're out of heap memory (probably because of fragmentation). Or it succeeds, and subsequent allocations crash, which means you're overwriting memory somewhere :)
Posted on 2005-05-18 01:02:50 by f0dder
Hmm I'll try to see if I haven't overwritten the code that failed on Global* allocation, and see what happened to my Heap* code... anyway now I either put up a little heap of my own in the .DATA? section or I call VirtualAlloc once (or at most, twice).  I then manage the "heap" myself.
Posted on 2005-05-18 02:30:28 by AmkG
to get the "physicall" stack limits you can do a VirtualQuery with a local variable, for example:

GetStackInfo proc

local mbi:MEMORY_BASIC_INFORMATION

      invoke VirtualQuery, addr mbi, addr mbi, sizeof mbi

After the call in mbi.AllocationBase should be the stack bottom, in mbi.RegionSize the stack size.





Posted on 2005-05-18 12:16:02 by japheth
Presumably I can also call VirtualQuery with my stack pointer instead of a pointer to a local variable?  Thanks japheth, I think I'd be more comfortable with that technique.
Posted on 2005-05-18 21:12:04 by AmkG

Yes, esp as 1. parameter will work fine

My previous reply wasn't quite accurate. It is:

mbi.AllocationBase: stack bottom
mbi.RegionSize: stack committed so far
mbi.BaseAddress: start of committed stack region

so to get stack top add BaseAddress and RegionSize

Posted on 2005-05-19 02:33:42 by japheth

As far as I know it's not really the stack upper/lower bounds you can read from that structure, though - it's the *allowed* values. Upon a context switch, a thread with esp outside those values will be automatically terminated, without warning.


In a graphics algorithm, I have used esp as a spare register and it ran fine! Thread was not terminated. How do you explain that?
Posted on 2005-05-19 07:17:39 by comrade
comrade, probably because your routine was never switched? Or perhaps the check isn't done on context switch but on some API calls? I haven't checked it myself, it was maverick who brought it up quite a while ago.
Posted on 2005-05-19 07:46:38 by f0dder