May 1996

Matt Pietrek is the author of Windows 95 System Programming Secrets (IDG Books, 1995). He works at NuMega Technologies Inc., and can be reached at 71774.362@compuserve.com.
While Windows NT™ and Windows® 95 are quite different under the
hood, they both share a key system data structure that many programmers
aren't aware of. To be a bit more precise, certain fields of this data
structure are shared. Regardless of the differences, this structure is
used extensively, and is even accessed by compiler-generated code.
That's right, your C++ compiler generates code to access system level
information directly.
What structure am I talking about? It goes by at least two different
names. The Windows 95 code calls it a Thread Information Block (TIB).
In Windows NT, it's called the Thread Environment Block (TEB). However,
I've seen it referred to as a TIB in some Windows NT header files so,
for the purposes of this column, I'll refer to it as a TIB.
What's in a Thread Information Block that makes it so special? As
its name implies, the data found in a TIB relates to threads, and
there's a TIB for each thread in the system. In fields shared by
Windows NT and Windows 95, you'll find information like a pointer to
the thread's structured exception handler list, the location of the
thread's stack, and the location of the thread local storage slots.
Other fields in the TIB differ between Windows NT and Windows 95.
You might be surprised to learn that the TIB didn't first appear in
Windows NT or Windows 95. The TIB has its ancestry in OS/2, before
Microsoft created Windows NT, and it still exists in OS/2 today. In
fact, the OS/2 TIB shares much of the same format, and is accessed in
the same manner as under Win32®. There's even a line in a Microsoft
header file (NTDDK.H) that says
<< begin snippet >>
// This structure MUST MATCH OS/2 V2.0!
<< end snippet >>
How would you get at the TIB if you were inclined to go poking
around? It's as easy as looking at what the FS register points to. Hey!
Didn't segments and segment registers go away in Win32? For the most
part, that's true. However, in all Intel-based Win32 implementations
(even the forgotten stepchild, Win32s), the FS register points to the
TIB. Thus, all of the structure offsets that I'll detail later can be
used as offsets into the segment pointed to by the FS register. For
example, FS:[0] points to the structured exception handling chain,
while FS:[2C] points to the thread's thread local storage array.
I just mentioned that compilers access the TIB structure directly.
Let's look at a small example to see this in action. This is a very
small C program that uses structured exception handling:
int main()
{
__try
{ int i = 0;
}
__except( 1 )
{ int j = 1: }
return 0;
}
When compiled, the first part of the resulting code looks like this:
401000: PUSH EBP
401001: MOV EBP,ESP
401003: PUSH FF
401005: PUSH 00404000
40100A: PUSH 00401140
40100F: MOV EAX,FS:[00000000]
401015: PUSH EAX
401016: MOV DWORD PTR FS:[00000000],ESP
The thing to notice is the series of PUSH instructions. They create
a data structure on the stack (sort of like an invisible local
variable). The instruction at offset 0x_40100F retrieves the head of
the structured exception handling chain out of the thread information
block and stores it into EAX-this is where the FS:[00000000] part of
the instruction comes from. The code then pushes the current head of
the structured exception handling chain list onto the stack. This
finishes the process of creating a local data structure on the stack.
Finally, the
MOV DWORD PTR FS:[00000000],ESP
instruction changes the head of the structured exception handling chain to point at the newly created data structure.
The key point is that Win32 compilers implicitly know about the TIB
and generate code that accesses it. Because the compiler can't know
which Win32 system the code will run on, you can safely assume that any
compiler-generated code that references the FS segment uses fields
common between Win32 platforms.
Common Fields in the TIBYou just saw
one example of a TIB structure field that's common to all Win32
implementations. In this section, I'll list all of the common fields,
along with a short description. The fields that differ between Win32
implementations are described later.
There are several different header files floating around that define
the fields of a TIB. Unfortunately, they're not always consistent with
each other or complete. In the Windows NT DDK, you'll find a structure
called an NT_TIB defined in NTDDK.H. In the Windows NT 3.51 service
pack 3 SDK update, a new WINNT.H was added that also defines an NT_TIB
structure. In addition, someone from the Windows 95 team posted online
a snippet from an .H file that described a TIB. In my descriptions,
I've tried to use the most descriptive name. You'll see these names in
the TIB.H file (see Figure 1) included with the SHOWTIB program I'll present later on.
The 00h DWORD pvExcept field contains a pointer to the head of the
thread's structured exception handling chain. The chain is a linked
list of EXCEPTION_REGISTRATION_RECORD structures (which unfortunately
are not defined in any official .H file). For more information on the
structured exception handling chain, you might refer to chapter 3 of my
book "Windows 95 System Programming Secrets."
The 04h DWORD pvStackUserTop field contains the linear address of
the topmost address of the thread's stack. Put another way, at no point
will this thread have a stack pointer value that's greater than or
equal to the value of this field.
The 08h DWORD pvStackUserBase field contains the linear address of
the lowest committed page in the thread's user mode stack. As the
thread uses successively lower addresses in the stack, those pages will
be committed, and this field will be updated accordingly.
The 14h DWORD pvArbitrary field is theoretically available for
applications to use however they want. It's almost like an extra thread
local storage slot for you to use, although I've never seen an
application use it.
The 18h DWORD ptibSelf field holds the linear address of the TIB.
Put another way, the TIB block contains a pointer to itself. Why bother
doing this? If the TIB's fields are used extensively, it makes sense
for 32-bit code to read and write the TIB using regular pointers rather
than using a segment register override (for example, by using
FS:[xxxxxxxx]). The SHOWTIB program presented later uses this field.
The 2Ch DWORD pvTLSArray field contains a pointer to the thread
local storage (TLS) slots for the thread. For example, if you had a TLS
index value of 4, you could take this pointer, add 10h to it ( 4 *
sizeof(DWORD) ), and retrieve the TLS value directly. Knowing that this
field points to the thread's TLS slots, you could write your own
versions of TlsSetValue and TlsGetValue easily. If you use _
_declspec(thread) variables in your code, check out the ASM code
emitted by your compiler. You'll find that it uses this field.
The location of the TLS slots is quite different between Windows NT
and Windows 95. In Windows NT, this field contains a null pointer until
the first time a TLS slot is used in the thread, then the system
allocates memory for the TLS slots out of the default process heap. (In
Windows NT, a buffer overrun of a HeapAlloc'ed block could trash your
TLS data.) Under Windows 95, this field always points to the TLS slots
that are kept as part of the Ring 3 thread database.
Windows NT TIB fieldsThe meaning of
some TIB data differs depending on whether you're running under Windows
NT or Windows 95. This section of the OS/2 subsystem-Windows NT support
running OS/2 1.X applications. For regular Win32 apps, this field
appears to always be zero.
The 10h DWORD FiberData field's meaning depends on what version of
Windows NT the thread is running. In the Windows NT 3.51 service pack 3
SDK update, WINNT.H describes this field as pointing to fiber data.
Fibers are described in the accompanying HLP file as lightweight
threads that are scheduled manually. Prior to the service pack update,
this field was named "Version". Presumably this means what version of
the system the thread expects to be running on, but I was unable to
make sense of the values in this field.
The 20h DWORD processID field holds the process ID of the thread.
The GetCurrentProcessId function in Windows NT 3.51 simply returns
whatever is in this field.
The 24h DWORD threadID field holds the thread's ID. The
GetCurrentThreadId function in Windows NT 3.51 returns the value in
this field.
The segment pointed at by the Windows NT TIB actually extends far
beyond the fields that I've described here. I've only mentioned the
fields that fall within the first 34h bytes (the size of a Windows 95
TIB).
Windows 95 TIB fieldsWhile the Windows
NT TIB fields are relatively sedate, the Windows 95 TIB contains a fair
amount of intriguing information. This section covers fields specific
to the Windows 95 TIB.
The 0CH WORD pvTDB contains the task database selector for the task
associated with the thread. The task database is a segment allocated
from the 16-bit global heap, and the handle is known as an HTASK. In
Windows 95, every process (even a Win32 process) has a task database
created for it.
The 0EH WORD pvThunkSS field contains the selector that Windows 95
uses as the 16-bit stack selector when a thread thunks from 32-bit code
to 16-bit code.
The 1CH WORD TIBFlags field is intended to hold various bit flags.
The only known value is TIB_WIN32 (that is, 1). If the low bit of this
value is set, it's a 32-bit thread, otherwise it's a thread from a
16-bit process.
The 1Eh WORD Win16MutexCount field is related to the thread's
ownership of the Win16Mutex, which is a global critical section that
only allows one thread at a time to be in 16-bit code. Normally, this
field's value is -1, which indicates that the thread doesn't own the
Win16Mutex. As the thread enters and leaves thunking code, the value of
this field is incremented and decremented accordingly.
The 20h DWORD DebugContext field normally contains the value zero.
However, when you're debugging the thread's process, this field
contains a pointer to a structure that contains register values and is
similar to, but not the same as, the CONTEXT structure defined in
WINNT.H.
The 24h DWORD pCurrentPriority field points to a DWORD containing
the thread's scheduling priority. This will be some value between zero
(lowest) and 31 (highest). The DWORD pointed to by this field is above
3GB in linear memory, which places it in VxD land. This makes sense, as
threads are scheduled by the ring 0 Virtual Machine Manager (VMM). For
normal priority threads, the priority DWORD will contain 9.
The 28h DWORD pvQueue field contains the message queue selector
assigned to the thread. Message queues are the means by which window
messages get to the appropriate windows. In Windows 95, each thread can
have its own message queue, but it is initially created without one.
Therefore, this field may contain zero.
The 30h PVOID* pProcess field contains a linear address for the
process database representing the process that owns the thread.
However, this is not the same as a process handle or process ID.
Some Random Notes on TIBsAs I was
experimenting with TIBs for this column, I came across some tidbits of
information worth passing on. First, in Windows 95, at offset 52h in
each task database segment, you'll find the TIB selector for the
primary thread in the process. At offset 54h in the task database,
you'll find the linear address of the TIB. This is particularly
interesting in that task databases are used by the 16-bit components of
Windows 95. It appears that the 16-bit components may occasionally
access thread-specific data in the TIB.
I also noticed the different uses of the FS register between Windows
NT and Windows 95. Under Windows NT, the FS register is always the same
for each thread's TIB. This implies that the linear address for the FS
selector has to change whenever a thread switch occurs. In contrast,
Windows 95 dedicates a different selector for each TIB (and hence, for
each thread). The linear address of a Windows 95 TIB selector doesn't
change. I'll let you guess which method is kinder to system resources.
The SHOWTIB programTo bring the TIB to life, I wrote the SHOWTIB program (see Figure 1).
SHOWTIB is a simple command-line program with two goals. The first is
to create one or more threads. (You specify the actual number of
threads on the command line. For example, "SHOWTIB 5" tells SHOWTIB to
spin off five threads and display their TIBs.) The second goal is to
display the various fields of each thread's TIB structure once all the
threads are running. I show only the TIB structures for threads created
by the primary thread, not for the primary thread itself.
In displaying the TIB for each thread, SHOWTIB first displays the
fields common to all Win32 operating systems, then decides whether it's
running on Windows NT or Windows 95. Depending on which system is
running, SHOWTIB displays specific fields in the TIB relevant to the
operating system. To make each TIB display come out coherent and in one
piece, the DisplayTIB function guards the display code with a critical
section.
There are two interesting pieces of code in SHOWTIB.CPP. Near the
start of the DisplayTIB function, the code uses a bit of in-line
assembler to grab the field at offset 18h in the TIB and stash it away
into a pointer. Offset 18h is the linear address of the TIB. I did this
so I could access the rest of the TIB with a regular pointer. The
alternative would have meant using in-line assembler and FS segment
overrides to retrieve all of the values. Win32 compilers simply don't
have a way to let you easily read from any segment other than the data
segment (the DS register).
The second interesting piece of code is near the end of main. After
creating all the threads and storing all the corresponding thread
handles into an array, I call WaitForMultipleObjects, passing in the
array of thread handles. If I didn't do this, the primary thread could
return from the main routine and call the exit function before the
worker-bee threads had terminated. The result would be an incomplete
display of all the various TIBs.
While Windows NT and Windows 95 are quite different under the hood,
the TIB is one of the few areas you can rely on to be the same. This
isn't a coincidence; since threading is such an integral part of both
operating systems, it's only natural that some common method of
supplying thread-specific information would be needed. The TIB is not
described in any official documentation other than .H files.
Nonetheless, it's an integral part of the Win32 specification that all
Win32 implementations must conform to.
Have a question about programming in Windows? You can mail it
directly to Under The Hood, Microsoft Systems Journal, 825 Eighth
Avenue, 18th Floor, New York, New York 10019, or send it to MSJ (re:
Under The Hood) via:
Internet: | Matt Pietrik 71774.362@compuserve.com
Eric Maffei ericm@microsoft.com |
From the May 1996 issue of Microsoft Systems Journal.
|