/*++ Copyright (c) 2002 Microsoft Corporation Module Name: tls.cpp Abstract: WinDbg Extension Api Author: Deon Brewis (deonb) 2-Jun-2002 Environment: User Mode. Kernel Mode. Revision History: --*/ #include "precomp.h" #pragma hdrstop #include #define TLS_ALL -2 #define TLS_CURRENT -1 // #define TLS_DBG #ifdef TLS_DBG #define trace dprintf #else #define trace __noop #endif EXTERN_C BOOL GetTeb32FromWowTeb(ULONG64 Teb, PULONG64 pTeb32); // implemented in peb.c EXTERN_C BOOL GetPeb32FromWowTeb(ULONG64 Teb, PULONG64 pPeb32); // implemented in peb.c BOOLEAN TestBit ( IN PRTL_BITMAP BitMapHeader, IN ULONG BitNumber ) { PCHAR ByteAddress; ULONG ShiftCount; ASSERT(BitNumber < BitMapHeader->SizeOfBitMap); ByteAddress = (PCHAR)BitMapHeader->Buffer + (BitNumber >> 3); ShiftCount = BitNumber & 0x7; return (BOOLEAN)((*ByteAddress >> ShiftCount) & 1); } ULONG64 GetPebForTarget() { ULONG64 pebAddress; ULONG64 peb; pebAddress = GetExpression("@$peb"); ULONG64 tebAddress; tebAddress = GetExpression("@$teb"); if (tebAddress) { if (TargetMachine == IMAGE_FILE_MACHINE_IA64 && tebAddress) { ULONG64 Peb32=0; if (GetPeb32FromWowTeb(tebAddress, &Peb32) && Peb32) { trace("Wow64 PEB32 at %lx\n", Peb32); pebAddress = Peb32; } } } if (pebAddress) { trace( "PEB at %p\n", pebAddress ); peb = IsPtr64() ? pebAddress : (ULONG64)(LONG64)(LONG)pebAddress; } else { trace( "PEB NULL...\n" ); peb = 0; } return peb; } ULONG64 GetTebForTarget(ULONG64 ulThread) { trace("GetTebForTarget %p\n", ulThread); ULONG64 tebAddress; ULONG64 teb; if (TLS_ALL == ulThread) { return 0; } if (TLS_CURRENT == ulThread) { tebAddress = GetExpression("@$teb"); } else { tebAddress = ulThread; // GetTebForThread!! } if ( tebAddress ) { if (TargetMachine == IMAGE_FILE_MACHINE_IA64 && tebAddress) { ULONG64 Teb32=0; if (GetTeb32FromWowTeb(tebAddress, &Teb32) && Teb32) { trace("Wow64 TEB32 at %p\n", Teb32); tebAddress = Teb32; trace("\n\nWow64 "); } } trace( "TEB at %p\n", tebAddress); } else { trace( "TEB NULL...\n" ); teb = 0; } if (tebAddress) { teb = IsPtr64() ? tebAddress : (ULONG64)(LONG64)(LONG)tebAddress; } else { teb = 0; } return teb; } // Function: HrReadPRtlBitmap // // Arguments: Address [in] Location of RTL BITMAP // pRtlBitmap [out] RTL Bitmap. Free with LocalFree / not HRESULT HrReadPRtlBitmap(IN ULONG64 pAddress, OUT PRTL_BITMAP *ppRtlBitmap) { HRESULT hr = S_OK; if (!pAddress || !ppRtlBitmap) { return E_INVALIDARG; } ULONG64 Address; if (!ReadPointer(pAddress, &Address)) { *ppRtlBitmap = NULL; return E_FAIL; } DWORD dwPtrSize; if (IsPtr64()) { dwPtrSize = sizeof(DWORD64); } else { dwPtrSize = sizeof(DWORD); } ULONG SizeOfBitMap; if (ReadMemory(Address, &SizeOfBitMap, sizeof(SizeOfBitMap), NULL)) { *ppRtlBitmap = reinterpret_cast(LocalAlloc(LMEM_FIXED | LMEM_ZEROINIT, sizeof(RTL_BITMAP) + (SizeOfBitMap / 8) )); if (*ppRtlBitmap) { // Create an internal pointer into itself (*ppRtlBitmap)->Buffer = reinterpret_cast(reinterpret_cast(*ppRtlBitmap) + sizeof(RTL_BITMAP)); (*ppRtlBitmap)->SizeOfBitMap = SizeOfBitMap; ULONG64 pBuffer = NULL; if (ReadPointer(Address + dwPtrSize, &pBuffer)) { if (!ReadMemory(pBuffer, (*ppRtlBitmap)->Buffer, SizeOfBitMap / 8, NULL)) { hr = E_FAIL; } } else { hr = E_FAIL; } if (FAILED(hr)) { LocalFree(*ppRtlBitmap); *ppRtlBitmap = NULL; } } else { hr = E_OUTOFMEMORY; } } return hr; } #define return_msg(hr, msg) dprintf(msg); return hr; // // Function: DumpTls // // Arguments: // ulSlot [in] Slot id || TLS_ALL for all. // ulThread [in] Thread id || TLS_CURRENT for current || TLS_ALL for all // // Returns: S_OK is succeeded // HRESULT DumpTls ( IN ULONG ulSlot, IN ULONG64 ulThread, IN LPCWSTR szThreadDescription ) { HRESULT hr; trace("DUMPTLS: %p %p\n", ulSlot, ulThread); if ( (TLS_ALL != ulSlot) && (ulSlot > 1088) ) { return_msg (E_INVALIDARG, "Slot must be 0 to 1088 (or -1 to dump slot 0)\n"); } ULONG64 Peb = GetPebForTarget(); if (!Peb) { return_msg (E_INVALIDARG, "Could not get Peb for target - check your symbols for nt!\n"); } trace("Peb = %p\n", Peb); ULONG64 Teb = GetTebForTarget(ulThread); if (!Teb) { return_msg (E_INVALIDARG, "Could not get Teb for target - check your symbols for nt!\n"); } trace("Teb = %p\n", Teb); WCHAR szOwnDescription[MAX_PATH]; if ( (TLS_CURRENT == ulThread) && !szThreadDescription) { ULONG64 ethread = GetExpression("@$thread"); if (ethread) { if (ERROR_SUCCESS == InitTypeRead(ethread, nt!_ETHREAD)) { ULONG64 Cid_UniqueProcess = GetExpression("@$tpid"); ULONG64 Cid_UniqueThread = GetExpression("@$tid"); if (Cid_UniqueProcess && Cid_UniqueThread) { trace("%04x %1p.%1p\n", ulSlot, Cid_UniqueProcess, Cid_UniqueThread); swprintf(szOwnDescription, L"%I64x.%I64x", Cid_UniqueProcess, Cid_UniqueThread); szThreadDescription = szOwnDescription; } } } } if (TLS_ALL == ulSlot) { if (szThreadDescription) { dprintf("TLS slots on thread: %S\n", szThreadDescription); } else { dprintf("TLS slots on thread: %p\n", Teb); } } DWORD dwPtrSize; if (IsPtr64()) { dwPtrSize = sizeof(DWORD64); } else { dwPtrSize = sizeof(DWORD); } hr = E_FAIL; PRTL_BITMAP pTlsBitmap = NULL; PRTL_BITMAP pTlsExpansionBitmap = NULL; ULONG TlsBitmap_Offset; GetFieldOffset("PEB", "TlsBitmap", &TlsBitmap_Offset); if (TlsBitmap_Offset) { ULONG TlsExpansionBitmap_Offset; GetFieldOffset("PEB", "TlsExpansionBitmap", &TlsExpansionBitmap_Offset); if (TlsExpansionBitmap_Offset) { hr = HrReadPRtlBitmap(Peb + TlsBitmap_Offset, &pTlsBitmap); if (SUCCEEDED(hr)) { hr = HrReadPRtlBitmap(Peb + TlsExpansionBitmap_Offset, &pTlsExpansionBitmap); if (SUCCEEDED(hr)) { trace("pTlsBitmap: %p\n", pTlsBitmap); trace("pTlsExpansionBitmap: %p\n", pTlsExpansionBitmap); } } } } if (FAILED(hr)) { LocalFree(pTlsBitmap); LocalFree(pTlsExpansionBitmap); return_msg (E_FAIL, "Could not get read TlsBitmap or TlsExpansionBitmap in peb - check your symbols for nt!\n"); } hr = E_FAIL; ULONG TlsSlots_Offset; ULONG TlsExpansionSlots_Offset; GetFieldOffset("TEB", "TlsSlots", &TlsSlots_Offset); if (TlsSlots_Offset) { GetFieldOffset("TEB", "TlsExpansionSlots", &TlsExpansionSlots_Offset); if (TlsExpansionSlots_Offset) { hr = S_OK; } } if (FAILED(hr)) { LocalFree(pTlsBitmap); LocalFree(pTlsExpansionBitmap); return_msg (E_FAIL, "Could not get read TlsSlots or TlsExpansionSlots in teb - check your symbols for nt!\n"); } if (TLS_ALL == ulSlot) { trace("All slots\n"); LPBYTE arrTlsSlots = new BYTE[dwPtrSize * 1088]; if (arrTlsSlots) { hr = E_FAIL; if (ReadMemory(Teb + TlsSlots_Offset, arrTlsSlots, 64 * dwPtrSize, NULL)) { ULONG64 pTlsExpansionSlots; if (ReadPointer(Teb + TlsExpansionSlots_Offset, &pTlsExpansionSlots)) { hr = S_OK; if (pTlsExpansionSlots) { if (!ReadMemory(pTlsExpansionSlots, arrTlsSlots + (64 * dwPtrSize), 1024, NULL)) { hr = E_FAIL; } } } } if (FAILED(hr)) { delete[] arrTlsSlots; LocalFree(pTlsBitmap); LocalFree(pTlsExpansionBitmap); return_msg (E_FAIL, "Could not read content of Tls Slots from teb - check your symbols for nt!\n"); } BOOL bFound = FALSE; for (int x = 0; x < 1088; x++) { if (CheckControlC()) { delete[] arrTlsSlots; LocalFree(pTlsBitmap); LocalFree(pTlsExpansionBitmap); return FALSE; } BOOL bSet = FALSE; if (x < TLS_MINIMUM_AVAILABLE) { if (!TestBit(pTlsBitmap, x)) { continue; } else { bFound = TRUE;; } } else { if (!TestBit(pTlsExpansionBitmap, x - TLS_MINIMUM_AVAILABLE)) { continue; } else { bFound = TRUE;; } } if ( sizeof(DWORD64) == dwPtrSize ) { dprintf("0x%04x : %p\n", x, reinterpret_cast(arrTlsSlots)[x]); } else { dprintf("0x%04x : %p\n", x, reinterpret_cast(arrTlsSlots)[x]); } } if (!bFound) { dprintf(" No TLS slots have been allocated for this process.\n"); } delete[] arrTlsSlots; } } else { ULONG64 Tls_Location = 0; if (ulSlot < TLS_MINIMUM_AVAILABLE) { Tls_Location = Teb + TlsSlots_Offset + ulSlot * dwPtrSize; } else { if (ReadPointer(Teb + TlsExpansionSlots_Offset, &Tls_Location)) { Tls_Location += (ulSlot * dwPtrSize); } } if (!Tls_Location) { LocalFree(pTlsBitmap); LocalFree(pTlsExpansionBitmap); return_msg (E_FAIL, "Could not read content TlsLocation from teb - check your symbols for nt!\n"); } ULONG64 Tls_SlotX; if (ReadPointer(Tls_Location, &Tls_SlotX)) { if (szThreadDescription) { dprintf("%S: %p\n", szThreadDescription, Tls_SlotX); } else { dprintf("%I64x: %p\n", Teb, szThreadDescription, Tls_SlotX); } } else { dprintf("Could not read TLS value from %p - check your symbols for nt!\n", Tls_Location); } } LocalFree(pTlsBitmap); LocalFree(pTlsExpansionBitmap); return TRUE; } HRESULT DumpThreadsUserMode(ULONG ulSlot) { ULONG ulOldThread; HRESULT hr = g_ExtSystem->GetCurrentThreadId(&ulOldThread); if (SUCCEEDED(hr)) { ULONG ulNumThreads; hr = g_ExtSystem->GetNumberThreads(&ulNumThreads); if (SUCCEEDED(hr)) { trace("Threads (current %d): %d\n", ulOldThread, ulNumThreads); PULONG pIds = new ULONG[ulNumThreads]; if (pIds) { PULONG pSysIds = new ULONG[ulNumThreads]; if (pSysIds) { hr = g_ExtSystem->GetThreadIdsByIndex(0, ulNumThreads, pIds, pSysIds); if (SUCCEEDED(hr)) { if (TLS_ALL != ulSlot) { dprintf("Per-thread values for slot 0x%03x:\n", static_cast(ulSlot)); } for (ULONG x = 0; x < ulNumThreads; x++) { hr = g_ExtSystem->SetCurrentThreadId(pIds[x]); if (SUCCEEDED(hr)) { ULONG64 Cid_UniqueProcess = GetExpression("@$tpid"); ULONG64 teb; g_ExtSystem->GetCurrentThreadTeb(&teb); WCHAR szThreadDescription[MAX_PATH]; swprintf(szThreadDescription, L"%I64x.%1x", Cid_UniqueProcess, pSysIds[x]); trace("Thread: %d %d %x %x\n", x, pIds[x], pSysIds[x], teb); DumpTls (ulSlot, teb, szThreadDescription); } } } delete[] pSysIds; } delete[] pIds; } } g_ExtSystem->SetCurrentThreadId(ulOldThread); } return S_OK; } ULONG ThreadListCallback ( PFIELD_INFO NextThrd, PVOID Context ) { ULONG ulSlot = static_cast(reinterpret_cast(Context)); ULONG64 RealThreadBase = NextThrd->address; if (!IsPtr64()) { RealThreadBase = (ULONG64) (LONG64) (LONG) RealThreadBase; } trace("Reading %p\n", RealThreadBase); if (InitTypeRead(RealThreadBase, nt!_ETHREAD)) { dprintf("*** Error in in reading nt!_ETHREAD @ %p\n", RealThreadBase); return TRUE; } ULONG64 Cid_UniqueProcess = ReadField(Cid.UniqueProcess); ULONG64 Cid_UniqueThread = ReadField(Cid.UniqueThread); ULONG64 Teb = ReadField(Tcb.Teb); trace("%04x %1p.%1p %1p\n", ulSlot, Cid_UniqueProcess, Cid_UniqueThread, Teb); WCHAR szThreadDescription[MAX_PATH]; swprintf(szThreadDescription, L"%I64x.%I64x", Cid_UniqueProcess, Cid_UniqueThread); DumpTls (ulSlot, Teb, szThreadDescription); return FALSE; } HRESULT DumpThreadsKernelMode(ULONG ulSlot) { trace("DumpThreadsKernelMode %p %p\n", ulSlot); ULONG64 ThreadListHead_Flink = 0; ULONG64 process = GetExpression("@$proc"); trace("Process is %p\n", process); GetFieldValue(process, "nt!_EPROCESS", "Pcb.ThreadListHead.Flink", ThreadListHead_Flink); trace("GetFieldValue returned %p\n", ThreadListHead_Flink); ULONG64 Next; if (!ReadPointer(ThreadListHead_Flink, &Next) || (Next == ThreadListHead_Flink)) { trace("Empty\n"); return S_OK; } if (TLS_ALL != ulSlot) { dprintf("Per-thread values for slot 0x%03x:\n", static_cast(ulSlot)); } ULONG ulList = ListType("nt!_ETHREAD", ThreadListHead_Flink, 1, "Tcb.ThreadListEntry.Flink", reinterpret_cast(static_cast(ulSlot)), &ThreadListCallback); trace("ListType returned %x\n",ListType); return S_OK; } DECLARE_API( tls ) { ULONG64 ulProcess = NULL; ULONG64 ulThread = NULL; ULONG64 ul64Slot = NULL; ULONG ulSlot = NULL; INIT_API(); BOOL bKernelMode = FALSE; KDDEBUGGER_DATA64 kdd; if (GetDebuggerData('GBDK', &kdd, sizeof(kdd))) { bKernelMode = TRUE; } // Skip past leading spaces while (*args == ' ') { args++; } if (!GetExpressionEx(args, &ul64Slot, &args)) { dprintf("Usage:\n" "tls [teb]\n" " slot: -1 to dump all allocated slots\n" " {0-1088} to dump specific slot\n" " teb: for current thread\n" " 0 for all threads in this process\n" " (not threadid) to dump for specific thread.\n" ); return S_OK; } ulSlot = static_cast(ul64Slot); if (ulSlot == -1) { ulSlot = TLS_ALL; } if (!GetExpressionEx(args, &ulThread, &args)) { ulThread = TLS_CURRENT; } if (0 == ulThread) { if (bKernelMode) { DumpThreadsKernelMode(ulSlot); } else { DumpThreadsUserMode(ulSlot); } } else { DumpTls (ulSlot, ulThread, NULL); } EXIT_API(); return S_OK; }