1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
4 
5 //
6 // Implementation of the portions of the Redhawk Platform Abstraction Layer (PAL) library that are common among
7 // multiple PAL variants.
8 //
9 // Note that in general we don't want to assume that Windows and Redhawk global definitions can co-exist.
10 // Since this code must include Windows headers to do its job we can't therefore safely include general
11 // Redhawk header files.
12 //
13 
14 #include <windows.h>
15 #include <stdio.h>
16 #include <errno.h>
17 #include <evntprov.h>
18 #include "CommonTypes.h"
19 #include "daccess.h"
20 #include "PalRedhawkCommon.h"
21 #include "PalRedhawk.h"
22 #include <winternl.h>
23 #include "CommonMacros.h"
24 #include "rhassert.h"
25 
26 
27 #define REDHAWK_PALEXPORT extern "C"
28 #define REDHAWK_PALAPI __stdcall
29 
30 
31 // Given the OS handle of a loaded module, compute the upper and lower virtual address bounds (inclusive).
PalGetModuleBounds(HANDLE hOsHandle,_Out_ UInt8 ** ppLowerBound,_Out_ UInt8 ** ppUpperBound)32 REDHAWK_PALEXPORT void REDHAWK_PALAPI PalGetModuleBounds(HANDLE hOsHandle, _Out_ UInt8 ** ppLowerBound, _Out_ UInt8 ** ppUpperBound)
33 {
34     BYTE *pbModule = (BYTE*)hOsHandle;
35     DWORD cbModule;
36 
37     IMAGE_NT_HEADERS *pNtHeaders = (IMAGE_NT_HEADERS*)(pbModule + ((IMAGE_DOS_HEADER*)hOsHandle)->e_lfanew);
38     if (pNtHeaders->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC)
39         cbModule = ((IMAGE_OPTIONAL_HEADER32*)&pNtHeaders->OptionalHeader)->SizeOfImage;
40     else
41         cbModule = ((IMAGE_OPTIONAL_HEADER64*)&pNtHeaders->OptionalHeader)->SizeOfImage;
42 
43     *ppLowerBound = pbModule;
44     *ppUpperBound = pbModule + cbModule - 1;
45 }
46 
47 // Reads through the PE header of the specified module, and returns
48 // the module's matching PDB's signature GUID, age, and build path by
49 // fishing them out of the last IMAGE_DEBUG_DIRECTORY of type
50 // IMAGE_DEBUG_TYPE_CODEVIEW.  Used when sending the ModuleLoad event
51 // to help profilers find matching PDBs for loaded modules.
52 //
53 // Arguments:
54 //
55 // [in] hOsHandle - OS Handle for module from which to get PDB info
56 // [out] pGuidSignature - PDB's signature GUID to be placed here
57 // [out] pdwAge - PDB's age to be placed here
58 // [out] wszPath - PDB's build path to be placed here
59 // [in] cchPath - Number of wide characters allocated in wszPath, including NULL terminator
60 //
61 // This is a simplification of similar code in desktop CLR's GetCodeViewInfo
62 // in eventtrace.cpp.
PalGetPDBInfo(HANDLE hOsHandle,_Out_ GUID * pGuidSignature,_Out_ UInt32 * pdwAge,_Out_writes_z_ (cchPath)WCHAR * wszPath,Int32 cchPath)63 REDHAWK_PALEXPORT void REDHAWK_PALAPI PalGetPDBInfo(HANDLE hOsHandle, _Out_ GUID * pGuidSignature, _Out_ UInt32 * pdwAge, _Out_writes_z_(cchPath) WCHAR * wszPath, Int32 cchPath)
64 {
65     // Zero-init [out]-params
66     ZeroMemory(pGuidSignature, sizeof(*pGuidSignature));
67     *pdwAge = 0;
68     if (cchPath <= 0)
69         return;
70     wszPath[0] = L'\0';
71 
72     BYTE *pbModule = (BYTE*)hOsHandle;
73 
74     IMAGE_NT_HEADERS const * pNtHeaders = (IMAGE_NT_HEADERS*)(pbModule + ((IMAGE_DOS_HEADER*)hOsHandle)->e_lfanew);
75     IMAGE_DATA_DIRECTORY const * rgDataDirectory = NULL;
76     if (pNtHeaders->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC)
77         rgDataDirectory = ((IMAGE_OPTIONAL_HEADER32 const *)&pNtHeaders->OptionalHeader)->DataDirectory;
78     else
79         rgDataDirectory = ((IMAGE_OPTIONAL_HEADER64 const *)&pNtHeaders->OptionalHeader)->DataDirectory;
80 
81     IMAGE_DATA_DIRECTORY const * pDebugDataDirectory = &rgDataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG];
82 
83     // In Redhawk, modules are loaded as MAPPED, so we don't have to worry about dealing
84     // with FLAT files (with padding missing), so header addresses can be used as is
85     IMAGE_DEBUG_DIRECTORY const *rgDebugEntries = (IMAGE_DEBUG_DIRECTORY const *) (pbModule + pDebugDataDirectory->VirtualAddress);
86     DWORD cbDebugEntries = pDebugDataDirectory->Size;
87     if (cbDebugEntries < sizeof(IMAGE_DEBUG_DIRECTORY))
88         return;
89 
90     // Since rgDebugEntries is an array of IMAGE_DEBUG_DIRECTORYs, cbDebugEntries
91     // should be a multiple of sizeof(IMAGE_DEBUG_DIRECTORY).
92     if (cbDebugEntries % sizeof(IMAGE_DEBUG_DIRECTORY) != 0)
93         return;
94 
95     // CodeView RSDS debug information -> PDB 7.00
96     struct CV_INFO_PDB70
97     {
98         DWORD          magic;
99         GUID           signature;       // unique identifier
100         DWORD          age;             // an always-incrementing value
101         _Field_z_ char  path[MAX_PATH];  // zero terminated string with the name of the PDB file
102     };
103 
104     // Temporary storage for a CV_INFO_PDB70 and its size (which could be less than
105     // sizeof(CV_INFO_PDB70); see below).
106     struct PdbInfo
107     {
108         CV_INFO_PDB70 *     m_pPdb70;
109         ULONG               m_cbPdb70;
110     };
111 
112     // Grab module bounds so we can do some rough sanity checking before we follow any
113     // RVAs
114     UInt8 * pbModuleLowerBound = NULL;
115     UInt8 * pbModuleUpperBound = NULL;
116     PalGetModuleBounds(hOsHandle, &pbModuleLowerBound, &pbModuleUpperBound);
117 
118     // Iterate through all debug directory entries. The convention is that debuggers &
119     // profilers typically just use the very last IMAGE_DEBUG_TYPE_CODEVIEW entry.  Treat raw
120     // bytes we read as untrusted.
121     PdbInfo pdbInfoLast = {0};
122     int cEntries = cbDebugEntries / sizeof(IMAGE_DEBUG_DIRECTORY);
123     for (int i = 0; i < cEntries; i++)
124     {
125         if ((UInt8 *)(&rgDebugEntries[i]) + sizeof(rgDebugEntries[i]) >= pbModuleUpperBound)
126         {
127             // Bogus pointer
128             return;
129         }
130 
131         if (rgDebugEntries[i].Type != IMAGE_DEBUG_TYPE_CODEVIEW)
132             continue;
133 
134         // Get raw data pointed to by this IMAGE_DEBUG_DIRECTORY
135 
136         // AddressOfRawData is generally set properly for Redhawk modules, so we don't
137         // have to worry about using PointerToRawData and converting it to an RVA
138         if (rgDebugEntries[i].AddressOfRawData == NULL)
139             continue;
140 
141         DWORD rvaOfRawData = rgDebugEntries[i].AddressOfRawData;
142         ULONG cbDebugData = rgDebugEntries[i].SizeOfData;
143         if (cbDebugData < size_t(&((CV_INFO_PDB70*)0)->magic) + sizeof(((CV_INFO_PDB70*)0)->magic))
144         {
145             // raw data too small to contain magic number at expected spot, so its format
146             // is not recognizeable. Skip
147             continue;
148         }
149 
150         // Verify the magic number is as expected
151         const DWORD CV_SIGNATURE_RSDS = 0x53445352;
152         CV_INFO_PDB70 * pPdb70 = (CV_INFO_PDB70 *) (pbModule + rvaOfRawData);
153         if ((UInt8 *)(pPdb70) + cbDebugData >= pbModuleUpperBound)
154         {
155             // Bogus pointer
156             return;
157         }
158 
159         if (pPdb70->magic != CV_SIGNATURE_RSDS)
160         {
161             // Unrecognized magic number.  Skip
162             continue;
163         }
164 
165         // From this point forward, the format should adhere to the expected layout of
166         // CV_INFO_PDB70. If we find otherwise, then assume the IMAGE_DEBUG_DIRECTORY is
167         // outright corrupt.
168 
169         // Verify sane size of raw data
170         if (cbDebugData > sizeof(CV_INFO_PDB70))
171             return;
172 
173         // cbDebugData actually can be < sizeof(CV_INFO_PDB70), since the "path" field
174         // can be truncated to its actual data length (i.e., fewer than MAX_PATH chars
175         // may be present in the PE file). In some cases, though, cbDebugData will
176         // include all MAX_PATH chars even though path gets null-terminated well before
177         // the MAX_PATH limit.
178 
179         // Gotta have at least one byte of the path
180         if (cbDebugData < offsetof(CV_INFO_PDB70, path) + sizeof(char))
181             return;
182 
183         // How much space is available for the path?
184         size_t cchPathMaxIncludingNullTerminator = (cbDebugData - offsetof(CV_INFO_PDB70, path)) / sizeof(char);
185         ASSERT(cchPathMaxIncludingNullTerminator >= 1);   // Guaranteed above
186 
187         // Verify path string fits inside the declared size
188         size_t cchPathActualExcludingNullTerminator = strnlen_s(pPdb70->path, cchPathMaxIncludingNullTerminator);
189         if (cchPathActualExcludingNullTerminator == cchPathMaxIncludingNullTerminator)
190         {
191             // This is how strnlen indicates failure--it couldn't find the null
192             // terminator within the buffer size specified
193             return;
194         }
195 
196         // Looks valid.  Remember it.
197         pdbInfoLast.m_pPdb70 = pPdb70;
198         pdbInfoLast.m_cbPdb70 = cbDebugData;
199     }
200 
201     // Take the last IMAGE_DEBUG_TYPE_CODEVIEW entry we saw, and return it to the caller
202     if (pdbInfoLast.m_pPdb70 != NULL)
203     {
204         memcpy(pGuidSignature, &pdbInfoLast.m_pPdb70->signature, sizeof(GUID));
205         *pdwAge = pdbInfoLast.m_pPdb70->age;
206 
207         // Convert build path from ANSI to UNICODE
208         errno_t ret;
209         size_t cchConverted;
210         ret = mbstowcs_s(
211             &cchConverted,
212             wszPath,
213             cchPath,
214             pdbInfoLast.m_pPdb70->path,
215             _countof(pdbInfoLast.m_pPdb70->path) - 1);
216         if ((ret != 0) && (ret != STRUNCATE))
217         {
218             // PDB path isn't essential.  An empty string will do if we hit an error.
219             ASSERT(cchPath > 0);        // Guaranteed at top of function
220             wszPath[0] = L'\0';
221         }
222     }
223 }
224 
PalGetProcessCpuCount()225 REDHAWK_PALEXPORT Int32 REDHAWK_PALAPI PalGetProcessCpuCount()
226 {
227     static int CpuCount = 0;
228 
229     if (CpuCount != 0)
230         return CpuCount;
231     else
232     {
233         // The concept of process CPU affinity is going away and so CoreSystem obsoletes the APIs used to
234         // fetch this information. Instead we'll just return total cpu count.
235         SYSTEM_INFO sysInfo;
236 #ifndef APP_LOCAL_RUNTIME
237         ::GetSystemInfo(&sysInfo);
238 #else
239         ::GetNativeSystemInfo(&sysInfo);
240 #endif
241         CpuCount = sysInfo.dwNumberOfProcessors;
242         return sysInfo.dwNumberOfProcessors;
243     }
244 }
245 
246 //Reads the entire contents of the file into the specified buffer, buff
247 //returns the number of bytes read if the file is successfully read
248 //returns 0 if the file is not found, size is greater than maxBytesToRead or the file couldn't be opened or read
PalReadFileContents(_In_z_ const TCHAR * fileName,_Out_writes_all_ (maxBytesToRead)char * buff,_In_ UInt32 maxBytesToRead)249 REDHAWK_PALEXPORT UInt32 REDHAWK_PALAPI PalReadFileContents(_In_z_ const TCHAR* fileName, _Out_writes_all_(maxBytesToRead) char* buff, _In_ UInt32 maxBytesToRead)
250 {
251     WIN32_FILE_ATTRIBUTE_DATA attrData;
252 
253     BOOL getAttrSuccess = GetFileAttributesExW(fileName, GetFileExInfoStandard, &attrData);
254 
255     //if we weren't able to get the file attributes, or the file is larger than maxBytesToRead, or the file size is zero
256     if ((!getAttrSuccess) || (attrData.nFileSizeHigh != 0) || (attrData.nFileSizeLow > (DWORD)maxBytesToRead) || (attrData.nFileSizeLow == 0))
257     {
258         return 0;
259     }
260 
261     HANDLE hFile = PalCreateFileW(fileName, GENERIC_READ, FILE_SHARE_DELETE | FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
262     if (hFile == INVALID_HANDLE_VALUE)
263     {
264         return 0;
265     }
266 
267     UInt32 bytesRead;
268 
269     BOOL readSuccess = ReadFile(hFile, buff, (DWORD)maxBytesToRead, (DWORD*)&bytesRead, NULL);
270 
271     CloseHandle(hFile);
272 
273     if (!readSuccess)
274     {
275         return 0;
276     }
277 
278     return bytesRead;
279 }
280 
281 
282 // Retrieves the entire range of memory dedicated to the calling thread's stack.  This does
283 // not get the current dynamic bounds of the stack, which can be significantly smaller than
284 // the maximum bounds.
PalGetMaximumStackBounds(_Out_ void ** ppStackLowOut,_Out_ void ** ppStackHighOut)285 REDHAWK_PALEXPORT bool REDHAWK_PALAPI PalGetMaximumStackBounds(_Out_ void** ppStackLowOut, _Out_ void** ppStackHighOut)
286 {
287     // VirtualQuery on the address of a local variable to get the allocation
288     // base of the stack.  Then use the StackBase field in the TEB to give
289     // the highest address of the stack region.
290     MEMORY_BASIC_INFORMATION mbi = { 0 };
291     SIZE_T cb = VirtualQuery(&mbi, &mbi, sizeof(mbi));
292     if (cb != sizeof(mbi))
293         return false;
294 
295     NT_TIB* pTib = (NT_TIB*)NtCurrentTeb();
296     *ppStackHighOut = pTib->StackBase;      // stack base is the highest address
297     *ppStackLowOut = mbi.AllocationBase;    // allocation base is the lowest address
298     return true;
299 }
300 
301 #if !defined(_INC_WINDOWS) || defined(APP_LOCAL_RUNTIME)
302 
303 typedef struct _UNICODE_STRING {
304     USHORT Length;
305     USHORT MaximumLength;
306     PWSTR  Buffer;
307 } UNICODE_STRING;
308 typedef UNICODE_STRING *PUNICODE_STRING;
309 typedef const UNICODE_STRING *PCUNICODE_STRING;
310 
311 typedef struct _PEB_LDR_DATA {
312     BYTE Reserved1[8];
313     PVOID Reserved2[3];
314     LIST_ENTRY InMemoryOrderModuleList;
315 } PEB_LDR_DATA, *PPEB_LDR_DATA;
316 
317 typedef struct _LDR_DATA_TABLE_ENTRY {
318     PVOID Reserved1[2];
319     LIST_ENTRY InMemoryOrderLinks;
320     PVOID Reserved2[2];
321     PVOID DllBase;
322     PVOID Reserved3[2];
323     UNICODE_STRING FullDllName;
324     BYTE Reserved4[8];
325     PVOID Reserved5[3];
326     union {
327         ULONG CheckSum;
328         PVOID Reserved6;
329     } DUMMYUNIONNAME;
330     ULONG TimeDateStamp;
331 } LDR_DATA_TABLE_ENTRY, *PLDR_DATA_TABLE_ENTRY;
332 
333 typedef struct _PEB {
334     BYTE Reserved1[2];
335     BYTE BeingDebugged;
336     BYTE Reserved2[1];
337     PVOID Reserved3[2];
338     PPEB_LDR_DATA Ldr;
339     PVOID /*PRTL_USER_PROCESS_PARAMETERS*/ ProcessParameters;
340     PVOID Reserved4[3];
341     PVOID AtlThunkSListPtr;
342     PVOID Reserved5;
343     ULONG Reserved6;
344     PVOID Reserved7;
345     ULONG Reserved8;
346     ULONG AtlThunkSListPtr32;
347     PVOID Reserved9[45];
348     BYTE Reserved10[96];
349     PVOID /*PPS_POST_PROCESS_INIT_ROUTINE*/ PostProcessInitRoutine;
350     BYTE Reserved11[128];
351     PVOID Reserved12[1];
352     ULONG SessionId;
353 } PEB, *PPEB;
354 
355 typedef struct _TEB {
356     PVOID Reserved1[12];
357     PPEB ProcessEnvironmentBlock;
358     PVOID Reserved2[399];
359     BYTE Reserved3[1952];
360     PVOID TlsSlots[64];
361     BYTE Reserved4[8];
362     PVOID Reserved5[26];
363     PVOID ReservedForOle;  // Windows 2000 only
364     PVOID Reserved6[4];
365     PVOID TlsExpansionSlots;
366 } TEB, *PTEB;
367 
368 #endif // !defined(_INC_WINDOWS) || defined(APP_LOCAL_RUNTIME)
369 
370 // retrieves the full path to the specified module, if moduleBase is NULL retreieves the full path to the
371 // executable module of the current process.
372 //
373 // Return value:  number of characters in name string
374 //
375 //NOTE:  This implementation exists because calling GetModuleFileName is not wack compliant.  if we later decide
376 //       that the framework package containing mrt100_app no longer needs to be wack compliant, this should be
377 //       removed and the windows implementation of GetModuleFileName should be substitued on windows.
PalGetModuleFileName(_Out_ const TCHAR ** pModuleNameOut,HANDLE moduleBase)378 REDHAWK_PALEXPORT Int32 PalGetModuleFileName(_Out_ const TCHAR** pModuleNameOut, HANDLE moduleBase)
379 {
380     TEB* pTEB = NtCurrentTeb();
381     LIST_ENTRY* pStartLink = &(pTEB->ProcessEnvironmentBlock->Ldr->InMemoryOrderModuleList);
382     LIST_ENTRY* pCurLink = pStartLink->Flink;
383 
384     do
385     {
386         LDR_DATA_TABLE_ENTRY* pEntry = CONTAINING_RECORD(pCurLink, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks);
387 
388         //null moduleBase will result in the first module being returned
389         //since the module list is ordered this is the executable module of the current process
390         if ((pEntry->DllBase == moduleBase) || (moduleBase == NULL))
391         {
392             *pModuleNameOut = pEntry->FullDllName.Buffer;
393             return pEntry->FullDllName.Length / 2;
394         }
395         pCurLink = pCurLink->Flink;
396     }
397     while (pCurLink != pStartLink);
398 
399     *pModuleNameOut = NULL;
400     return 0;
401 }
402 
PalGetTickCount64()403 REDHAWK_PALEXPORT UInt64 __cdecl PalGetTickCount64()
404 {
405     return GetTickCount64();
406 }
407