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 | // File: UTIL.CPP |
6 | // |
7 | |
8 | // =========================================================================== |
9 | |
10 | |
11 | #include "common.h" |
12 | #include "excep.h" |
13 | #include "corhost.h" |
14 | #include "eventtrace.h" |
15 | #include "posterror.h" |
16 | #include "eemessagebox.h" |
17 | |
18 | #include <shlobj.h> |
19 | |
20 | #include "dlwrap.h" |
21 | |
22 | #ifndef DACCESS_COMPILE |
23 | |
24 | // Helper function that encapsulates the parsing rules. |
25 | // |
26 | // Called first with *pdstout == NULL to figure out how many args there are |
27 | // and the size of the required destination buffer. |
28 | // |
29 | // Called again with a nonnull *pdstout to fill in the actual buffer. |
30 | // |
31 | // Returns the # of arguments. |
32 | static UINT ParseCommandLine(LPCWSTR psrc, __inout LPWSTR *pdstout) |
33 | { |
34 | CONTRACTL |
35 | { |
36 | NOTHROW; |
37 | GC_NOTRIGGER; |
38 | FORBID_FAULT; |
39 | } |
40 | CONTRACTL_END |
41 | |
42 | UINT argcount = 1; // discovery of arg0 is unconditional, below |
43 | LPWSTR pdst = *pdstout; |
44 | BOOL fDoWrite = (pdst != NULL); |
45 | |
46 | BOOL fInQuotes; |
47 | int iSlash; |
48 | |
49 | /* A quoted program name is handled here. The handling is much |
50 | simpler than for other arguments. Basically, whatever lies |
51 | between the leading double-quote and next one, or a terminal null |
52 | character is simply accepted. Fancier handling is not required |
53 | because the program name must be a legal NTFS/HPFS file name. |
54 | Note that the double-quote characters are not copied, nor do they |
55 | contribute to numchars. |
56 | |
57 | This "simplification" is necessary for compatibility reasons even |
58 | though it leads to mishandling of certain cases. For example, |
59 | "c:\tests\"test.exe will result in an arg0 of c:\tests\ and an |
60 | arg1 of test.exe. In any rational world this is incorrect, but |
61 | we need to preserve compatibility. |
62 | */ |
63 | |
64 | LPCWSTR pStart = psrc; |
65 | BOOL skipQuote = FALSE; |
66 | |
67 | if (*psrc == W('\"')) |
68 | { |
69 | // scan from just past the first double-quote through the next |
70 | // double-quote, or up to a null, whichever comes first |
71 | while ((*(++psrc) != W('\"')) && (*psrc != W('\0'))) |
72 | continue; |
73 | |
74 | skipQuote = TRUE; |
75 | } |
76 | else |
77 | { |
78 | /* Not a quoted program name */ |
79 | |
80 | while (!ISWWHITE(*psrc) && *psrc != W('\0')) |
81 | psrc++; |
82 | } |
83 | |
84 | // We have now identified arg0 as pStart (or pStart+1 if we have a leading |
85 | // quote) through psrc-1 inclusive |
86 | if (skipQuote) |
87 | pStart++; |
88 | while (pStart < psrc) |
89 | { |
90 | if (fDoWrite) |
91 | *pdst = *pStart; |
92 | |
93 | pStart++; |
94 | pdst++; |
95 | } |
96 | |
97 | // And terminate it. |
98 | if (fDoWrite) |
99 | *pdst = W('\0'); |
100 | |
101 | pdst++; |
102 | |
103 | // if we stopped on a double-quote when arg0 is quoted, skip over it |
104 | if (skipQuote && *psrc == W('\"')) |
105 | psrc++; |
106 | |
107 | while ( *psrc != W('\0')) |
108 | { |
109 | LEADINGWHITE: |
110 | |
111 | // The outofarg state. |
112 | while (ISWWHITE(*psrc)) |
113 | psrc++; |
114 | |
115 | if (*psrc == W('\0')) |
116 | break; |
117 | else |
118 | if (*psrc == W('#')) |
119 | { |
120 | while (*psrc != W('\0') && *psrc != W('\n')) |
121 | psrc++; // skip to end of line |
122 | |
123 | goto LEADINGWHITE; |
124 | } |
125 | |
126 | argcount++; |
127 | fInQuotes = FALSE; |
128 | |
129 | while ((!ISWWHITE(*psrc) || fInQuotes) && *psrc != W('\0')) |
130 | { |
131 | switch (*psrc) |
132 | { |
133 | case W('\\'): |
134 | iSlash = 0; |
135 | while (*psrc == W('\\')) |
136 | { |
137 | iSlash++; |
138 | psrc++; |
139 | } |
140 | |
141 | if (*psrc == W('\"')) |
142 | { |
143 | for ( ; iSlash >= 2; iSlash -= 2) |
144 | { |
145 | if (fDoWrite) |
146 | *pdst = W('\\'); |
147 | |
148 | pdst++; |
149 | } |
150 | |
151 | if (iSlash & 1) |
152 | { |
153 | if (fDoWrite) |
154 | *pdst = *psrc; |
155 | |
156 | psrc++; |
157 | pdst++; |
158 | } |
159 | else |
160 | { |
161 | fInQuotes = !fInQuotes; |
162 | psrc++; |
163 | } |
164 | } |
165 | else |
166 | for ( ; iSlash > 0; iSlash--) |
167 | { |
168 | if (fDoWrite) |
169 | *pdst = W('\\'); |
170 | |
171 | pdst++; |
172 | } |
173 | |
174 | break; |
175 | |
176 | case W('\"'): |
177 | fInQuotes = !fInQuotes; |
178 | psrc++; |
179 | break; |
180 | |
181 | default: |
182 | if (fDoWrite) |
183 | *pdst = *psrc; |
184 | |
185 | psrc++; |
186 | pdst++; |
187 | } |
188 | } |
189 | |
190 | if (fDoWrite) |
191 | *pdst = W('\0'); |
192 | |
193 | pdst++; |
194 | } |
195 | |
196 | |
197 | _ASSERTE(*psrc == W('\0')); |
198 | *pdstout = pdst; |
199 | return argcount; |
200 | } |
201 | |
202 | |
203 | // Function to parse apart a command line and return the |
204 | // arguments just like argv and argc |
205 | // This function is a little funky because of the pointer work |
206 | // but it is neat because it allows the recipient of the char** |
207 | // to only have to do a single delete [] |
208 | LPWSTR* CommandLineToArgvW(__in LPWSTR lpCmdLine, DWORD *pNumArgs) |
209 | { |
210 | |
211 | CONTRACTL |
212 | { |
213 | NOTHROW; |
214 | GC_NOTRIGGER; |
215 | INJECT_FAULT(return NULL;); |
216 | } |
217 | CONTRACTL_END |
218 | |
219 | DWORD argcount = 0; |
220 | LPWSTR retval = NULL; |
221 | LPWSTR *pslot; |
222 | // First we need to find out how many strings there are in the command line |
223 | _ASSERTE(lpCmdLine); |
224 | _ASSERTE(pNumArgs); |
225 | |
226 | LPWSTR pdst = NULL; |
227 | argcount = ParseCommandLine(lpCmdLine, &pdst); |
228 | |
229 | // This check is because on WinCE the Application Name is not passed in as an argument to the app! |
230 | if (argcount == 0) |
231 | { |
232 | *pNumArgs = 0; |
233 | return NULL; |
234 | } |
235 | |
236 | // Now we need alloc a buffer the size of the command line + the number of strings * DWORD |
237 | retval = new (nothrow) WCHAR[(argcount*sizeof(WCHAR*))/sizeof(WCHAR) + (pdst - (LPWSTR)NULL)]; |
238 | if(!retval) |
239 | return NULL; |
240 | |
241 | pdst = (LPWSTR)( argcount*sizeof(LPWSTR*) + (BYTE*)retval ); |
242 | ParseCommandLine(lpCmdLine, &pdst); |
243 | pdst = (LPWSTR)( argcount*sizeof(LPWSTR*) + (BYTE*)retval ); |
244 | pslot = (LPWSTR*)retval; |
245 | for (DWORD i = 0; i < argcount; i++) |
246 | { |
247 | *(pslot++) = pdst; |
248 | while (*pdst != W('\0')) |
249 | { |
250 | pdst++; |
251 | } |
252 | pdst++; |
253 | } |
254 | |
255 | |
256 | |
257 | *pNumArgs = argcount; |
258 | return (LPWSTR*)retval; |
259 | |
260 | } |
261 | |
262 | |
263 | |
264 | |
265 | //************************************************************************ |
266 | // CQuickHeap |
267 | // |
268 | // A fast non-multithread-safe heap for short term use. |
269 | // Destroying the heap frees all blocks allocated from the heap. |
270 | // Blocks cannot be freed individually. |
271 | // |
272 | // The heap uses COM+ exceptions to report errors. |
273 | // |
274 | // The heap does not use any internal synchronization so it is not |
275 | // multithreadsafe. |
276 | //************************************************************************ |
277 | CQuickHeap::CQuickHeap() |
278 | { |
279 | LIMITED_METHOD_CONTRACT; |
280 | |
281 | m_pFirstQuickBlock = NULL; |
282 | m_pFirstBigQuickBlock = NULL; |
283 | m_pNextFree = NULL; |
284 | } |
285 | |
286 | CQuickHeap::~CQuickHeap() |
287 | { |
288 | CONTRACTL |
289 | { |
290 | NOTHROW; |
291 | GC_NOTRIGGER; |
292 | FORBID_FAULT; |
293 | } |
294 | CONTRACTL_END |
295 | |
296 | QuickBlock *pQuickBlock = m_pFirstQuickBlock; |
297 | while (pQuickBlock) { |
298 | QuickBlock *ptmp = pQuickBlock; |
299 | pQuickBlock = pQuickBlock->m_next; |
300 | delete [] (BYTE*)ptmp; |
301 | } |
302 | |
303 | pQuickBlock = m_pFirstBigQuickBlock; |
304 | while (pQuickBlock) { |
305 | QuickBlock *ptmp = pQuickBlock; |
306 | pQuickBlock = pQuickBlock->m_next; |
307 | delete [] (BYTE*)ptmp; |
308 | } |
309 | } |
310 | |
311 | LPVOID CQuickHeap::Alloc(UINT sz) |
312 | { |
313 | CONTRACTL |
314 | { |
315 | THROWS; |
316 | GC_NOTRIGGER; |
317 | SO_TOLERANT; // So long as we cleanup the heap when we're done, all the memory goes with it |
318 | INJECT_FAULT(COMPlusThrowOM();); |
319 | } CONTRACTL_END; |
320 | |
321 | sz = (sz+7) & ~7; |
322 | |
323 | if ( sz > kBlockSize ) { |
324 | |
325 | QuickBlock *pQuickBigBlock = (QuickBlock*) new BYTE[sz + sizeof(QuickBlock) - 1]; |
326 | pQuickBigBlock->m_next = m_pFirstBigQuickBlock; |
327 | m_pFirstBigQuickBlock = pQuickBigBlock; |
328 | |
329 | return pQuickBigBlock->m_bytes; |
330 | |
331 | |
332 | } else { |
333 | if (m_pNextFree == NULL || sz > (UINT)( &(m_pFirstQuickBlock->m_bytes[kBlockSize]) - m_pNextFree )) { |
334 | QuickBlock *pQuickBlock = (QuickBlock*) new BYTE[kBlockSize + sizeof(QuickBlock) - 1]; |
335 | pQuickBlock->m_next = m_pFirstQuickBlock; |
336 | m_pFirstQuickBlock = pQuickBlock; |
337 | m_pNextFree = pQuickBlock->m_bytes; |
338 | } |
339 | LPVOID pv = m_pNextFree; |
340 | m_pNextFree += sz; |
341 | return pv; |
342 | } |
343 | } |
344 | |
345 | //---------------------------------------------------------------------------- |
346 | // Output functions that avoid the crt's. |
347 | //---------------------------------------------------------------------------- |
348 | |
349 | static |
350 | void NPrintToHandleA(HANDLE Handle, const char *pszString, size_t BytesToWrite) |
351 | { |
352 | CONTRACTL |
353 | { |
354 | NOTHROW; |
355 | GC_NOTRIGGER; |
356 | FORBID_FAULT; |
357 | } |
358 | CONTRACTL_END |
359 | |
360 | if (Handle == INVALID_HANDLE_VALUE || Handle == NULL) |
361 | return; |
362 | |
363 | BOOL success; |
364 | DWORD dwBytesWritten; |
365 | const size_t maxWriteFileSize = 32767; // This is somewhat arbitrary limit, but 2**16-1 doesn't work |
366 | |
367 | while (BytesToWrite > 0) { |
368 | DWORD dwChunkToWrite = (DWORD) min(BytesToWrite, maxWriteFileSize); |
369 | // No CharNextExA on CoreSystem, we just assume no multi-byte characters (this code path shouldn't be |
370 | // used in the production codepath for currently supported CoreSystem based products anyway). |
371 | #ifndef FEATURE_CORESYSTEM |
372 | if (dwChunkToWrite < BytesToWrite) { |
373 | break; |
374 | // must go by char to find biggest string that will fit, taking DBCS chars into account |
375 | //dwChunkToWrite = 0; |
376 | //const char *charNext = pszString; |
377 | //while (dwChunkToWrite < maxWriteFileSize-2 && charNext) { |
378 | // charNext = CharNextExA(0, pszString+dwChunkToWrite, 0); |
379 | // dwChunkToWrite = (DWORD)(charNext - pszString); |
380 | //} |
381 | //if (dwChunkToWrite == 0) |
382 | // break; |
383 | } |
384 | #endif // !FEATURE_CORESYSTEM |
385 | |
386 | // Try to write to handle. If this is not a CUI app, then this is probably |
387 | // not going to work unless the dev took special pains to set their own console |
388 | // handle during CreateProcess. So try it, but don't yell if it doesn't work in |
389 | // that case. Also, if we redirect stdout to a pipe then the pipe breaks (ie, we |
390 | // write to something like the UNIX head command), don't complain. |
391 | success = WriteFile(Handle, pszString, dwChunkToWrite, &dwBytesWritten, NULL); |
392 | if (!success) |
393 | { |
394 | #if defined(_DEBUG) |
395 | // This can happen if stdout is a closed pipe. This might not help |
396 | // much, but we'll have half a chance of seeing this. |
397 | OutputDebugStringA("CLR: Writing out an unhandled exception to stdout failed!\n" ); |
398 | OutputDebugStringA(pszString); |
399 | #endif //_DEBUG |
400 | |
401 | break; |
402 | } |
403 | else { |
404 | _ASSERTE(dwBytesWritten == dwChunkToWrite); |
405 | } |
406 | pszString = pszString + dwChunkToWrite; |
407 | BytesToWrite -= dwChunkToWrite; |
408 | } |
409 | |
410 | } |
411 | |
412 | static |
413 | void PrintToHandleA(HANDLE Handle, const char *pszString) |
414 | { |
415 | CONTRACTL |
416 | { |
417 | NOTHROW; |
418 | GC_NOTRIGGER; |
419 | FORBID_FAULT; |
420 | } |
421 | CONTRACTL_END |
422 | |
423 | size_t len = strlen(pszString); |
424 | NPrintToHandleA(Handle, pszString, len); |
425 | } |
426 | |
427 | void PrintToStdOutA(const char *pszString) { |
428 | CONTRACTL |
429 | { |
430 | NOTHROW; |
431 | GC_NOTRIGGER; |
432 | FORBID_FAULT; |
433 | } |
434 | CONTRACTL_END |
435 | |
436 | HANDLE Handle = GetStdHandle(STD_OUTPUT_HANDLE); |
437 | PrintToHandleA(Handle, pszString); |
438 | } |
439 | |
440 | |
441 | void PrintToStdOutW(const WCHAR *pwzString) |
442 | { |
443 | CONTRACTL |
444 | { |
445 | THROWS; |
446 | GC_NOTRIGGER; |
447 | INJECT_FAULT(COMPlusThrowOM();); |
448 | } |
449 | CONTRACTL_END |
450 | |
451 | MAKE_MULTIBYTE_FROMWIDE_BESTFIT(pStr, pwzString, GetConsoleOutputCP()); |
452 | |
453 | PrintToStdOutA(pStr); |
454 | } |
455 | |
456 | void PrintToStdErrA(const char *pszString) { |
457 | CONTRACTL |
458 | { |
459 | NOTHROW; |
460 | GC_NOTRIGGER; |
461 | FORBID_FAULT; |
462 | } |
463 | CONTRACTL_END |
464 | |
465 | HANDLE Handle = GetStdHandle(STD_ERROR_HANDLE); |
466 | PrintToHandleA(Handle, pszString); |
467 | } |
468 | |
469 | |
470 | void PrintToStdErrW(const WCHAR *pwzString) |
471 | { |
472 | CONTRACTL |
473 | { |
474 | THROWS; |
475 | GC_NOTRIGGER; |
476 | INJECT_FAULT(COMPlusThrowOM();); |
477 | } |
478 | CONTRACTL_END |
479 | |
480 | MAKE_MULTIBYTE_FROMWIDE_BESTFIT(pStr, pwzString, GetConsoleOutputCP()); |
481 | |
482 | PrintToStdErrA(pStr); |
483 | } |
484 | |
485 | |
486 | |
487 | void NPrintToStdOutA(const char *pszString, size_t nbytes) { |
488 | CONTRACTL |
489 | { |
490 | NOTHROW; |
491 | GC_NOTRIGGER; |
492 | FORBID_FAULT; |
493 | } |
494 | CONTRACTL_END |
495 | |
496 | HANDLE Handle = GetStdHandle(STD_OUTPUT_HANDLE); |
497 | NPrintToHandleA(Handle, pszString, nbytes); |
498 | } |
499 | |
500 | |
501 | void NPrintToStdOutW(const WCHAR *pwzString, size_t nchars) |
502 | { |
503 | CONTRACTL |
504 | { |
505 | THROWS; |
506 | GC_NOTRIGGER; |
507 | INJECT_FAULT(COMPlusThrowOM();); |
508 | } |
509 | CONTRACTL_END |
510 | |
511 | LPSTR pStr; |
512 | MAKE_MULTIBYTE_FROMWIDEN_BESTFIT(pStr, pwzString, (int)nchars, nbytes, GetConsoleOutputCP()); |
513 | |
514 | NPrintToStdOutA(pStr, nbytes); |
515 | } |
516 | |
517 | void NPrintToStdErrA(const char *pszString, size_t nbytes) { |
518 | CONTRACTL |
519 | { |
520 | NOTHROW; |
521 | GC_NOTRIGGER; |
522 | FORBID_FAULT; |
523 | } |
524 | CONTRACTL_END |
525 | |
526 | HANDLE Handle = GetStdHandle(STD_ERROR_HANDLE); |
527 | NPrintToHandleA(Handle, pszString, nbytes); |
528 | } |
529 | |
530 | |
531 | void NPrintToStdErrW(const WCHAR *pwzString, size_t nchars) |
532 | { |
533 | CONTRACTL |
534 | { |
535 | THROWS; |
536 | GC_NOTRIGGER; |
537 | INJECT_FAULT(COMPlusThrowOM();); |
538 | } |
539 | CONTRACTL_END |
540 | |
541 | LPSTR pStr; |
542 | |
543 | MAKE_MULTIBYTE_FROMWIDEN_BESTFIT(pStr, pwzString, (int)nchars, nbytes, GetConsoleOutputCP()); |
544 | |
545 | NPrintToStdErrA(pStr, nbytes); |
546 | } |
547 | //---------------------------------------------------------------------------- |
548 | |
549 | |
550 | |
551 | |
552 | |
553 | //+-------------------------------------------------------------------------- |
554 | // |
555 | // Function: VMDebugOutputA( . . . . ) |
556 | // VMDebugOutputW( . . . . ) |
557 | // |
558 | // Synopsis: Output a message formatted in printf fashion to the debugger. |
559 | // ANSI and wide character versions are both provided. Only |
560 | // present in debug builds (i.e. when _DEBUG is defined). |
561 | // |
562 | // Arguments: [format] --- ANSI or Wide character format string |
563 | // in printf/OutputDebugString-style format. |
564 | // |
565 | // [ ... ] --- Variable length argument list compatible |
566 | // with the format string. |
567 | // |
568 | // Returns: Nothing. |
569 | // |
570 | // Notes: Has internal static sized character buffer of |
571 | // width specified by the preprocessor constant DEBUGOUT_BUFSIZE. |
572 | // |
573 | //--------------------------------------------------------------------------- |
574 | #ifdef _DEBUG |
575 | |
576 | #define DEBUGOUT_BUFSIZE 1024 |
577 | |
578 | void __cdecl VMDebugOutputA(__in LPSTR format, ...) |
579 | { |
580 | STATIC_CONTRACT_NOTHROW; |
581 | STATIC_CONTRACT_GC_NOTRIGGER; |
582 | STATIC_CONTRACT_FORBID_FAULT; |
583 | |
584 | va_list argPtr; |
585 | va_start(argPtr, format); |
586 | |
587 | char szBuffer[DEBUGOUT_BUFSIZE]; |
588 | |
589 | if(vsprintf_s(szBuffer, DEBUGOUT_BUFSIZE-1, format, argPtr) > 0) |
590 | OutputDebugStringA(szBuffer); |
591 | va_end(argPtr); |
592 | } |
593 | |
594 | void __cdecl VMDebugOutputW(__in LPWSTR format, ...) |
595 | { |
596 | STATIC_CONTRACT_NOTHROW; |
597 | STATIC_CONTRACT_GC_NOTRIGGER; |
598 | STATIC_CONTRACT_FORBID_FAULT; |
599 | STATIC_CONTRACT_DEBUG_ONLY; |
600 | |
601 | va_list argPtr; |
602 | va_start(argPtr, format); |
603 | |
604 | WCHAR wszBuffer[DEBUGOUT_BUFSIZE]; |
605 | |
606 | if(vswprintf_s(wszBuffer, DEBUGOUT_BUFSIZE-2, format, argPtr) > 0) |
607 | WszOutputDebugString(wszBuffer); |
608 | va_end(argPtr); |
609 | } |
610 | |
611 | #endif // #ifdef DACCESS_COMPILE |
612 | |
613 | //***************************************************************************** |
614 | // Compare VarLoc's |
615 | //***************************************************************************** |
616 | |
617 | bool operator ==(const ICorDebugInfo::VarLoc &varLoc1, |
618 | const ICorDebugInfo::VarLoc &varLoc2) |
619 | { |
620 | STATIC_CONTRACT_NOTHROW; |
621 | STATIC_CONTRACT_GC_NOTRIGGER; |
622 | STATIC_CONTRACT_FORBID_FAULT; |
623 | |
624 | if (varLoc1.vlType != varLoc2.vlType) |
625 | return false; |
626 | |
627 | switch(varLoc1.vlType) |
628 | { |
629 | case ICorDebugInfo::VLT_REG: |
630 | case ICorDebugInfo::VLT_REG_BYREF: |
631 | return varLoc1.vlReg.vlrReg == varLoc2.vlReg.vlrReg; |
632 | |
633 | case ICorDebugInfo::VLT_STK: |
634 | case ICorDebugInfo::VLT_STK_BYREF: |
635 | return varLoc1.vlStk.vlsBaseReg == varLoc2.vlStk.vlsBaseReg && |
636 | varLoc1.vlStk.vlsOffset == varLoc2.vlStk.vlsOffset; |
637 | |
638 | case ICorDebugInfo::VLT_REG_REG: |
639 | return varLoc1.vlRegReg.vlrrReg1 == varLoc2.vlRegReg.vlrrReg1 && |
640 | varLoc1.vlRegReg.vlrrReg2 == varLoc2.vlRegReg.vlrrReg2; |
641 | |
642 | case ICorDebugInfo::VLT_REG_STK: |
643 | return varLoc1.vlRegStk.vlrsReg == varLoc2.vlRegStk.vlrsReg && |
644 | varLoc1.vlRegStk.vlrsStk.vlrssBaseReg == varLoc2.vlRegStk.vlrsStk.vlrssBaseReg && |
645 | varLoc1.vlRegStk.vlrsStk.vlrssOffset == varLoc2.vlRegStk.vlrsStk.vlrssOffset; |
646 | |
647 | case ICorDebugInfo::VLT_STK_REG: |
648 | return varLoc1.vlStkReg.vlsrStk.vlsrsBaseReg == varLoc2.vlStkReg.vlsrStk.vlsrsBaseReg && |
649 | varLoc1.vlStkReg.vlsrStk.vlsrsOffset == varLoc2.vlStkReg.vlsrStk.vlsrsBaseReg && |
650 | varLoc1.vlStkReg.vlsrReg == varLoc2.vlStkReg.vlsrReg; |
651 | |
652 | case ICorDebugInfo::VLT_STK2: |
653 | return varLoc1.vlStk2.vls2BaseReg == varLoc2.vlStk2.vls2BaseReg && |
654 | varLoc1.vlStk2.vls2Offset == varLoc2.vlStk2.vls2Offset; |
655 | |
656 | case ICorDebugInfo::VLT_FPSTK: |
657 | return varLoc1.vlFPstk.vlfReg == varLoc2.vlFPstk.vlfReg; |
658 | |
659 | default: |
660 | _ASSERTE(!"Bad vlType" ); return false; |
661 | } |
662 | } |
663 | |
664 | #endif // #ifndef DACCESS_COMPILE |
665 | |
666 | //***************************************************************************** |
667 | // The following are used to read and write data given NativeVarInfo |
668 | // for primitive types. For ValueClasses, FALSE will be returned. |
669 | //***************************************************************************** |
670 | |
671 | SIZE_T GetRegOffsInCONTEXT(ICorDebugInfo::RegNum regNum) |
672 | { |
673 | STATIC_CONTRACT_NOTHROW; |
674 | STATIC_CONTRACT_GC_NOTRIGGER; |
675 | STATIC_CONTRACT_FORBID_FAULT; |
676 | |
677 | #ifdef _TARGET_X86_ |
678 | switch(regNum) |
679 | { |
680 | case ICorDebugInfo::REGNUM_EAX: return offsetof(T_CONTEXT,Eax); |
681 | case ICorDebugInfo::REGNUM_ECX: return offsetof(T_CONTEXT,Ecx); |
682 | case ICorDebugInfo::REGNUM_EDX: return offsetof(T_CONTEXT,Edx); |
683 | case ICorDebugInfo::REGNUM_EBX: return offsetof(T_CONTEXT,Ebx); |
684 | // TODO: Fix AMBIENT_SP handling. |
685 | // AMBIENT_SP It isn't necessarily the same value as ESP. We probably shouldn't try |
686 | // and handle REGNUM_AMBIENT_SP here, and instead update our callers (eg. |
687 | // GetNativeVarVal) to handle this case explicitly. This logic should also be |
688 | // merged with the parallel (but correct in this case) logic in mscordbi. |
689 | case ICorDebugInfo::REGNUM_ESP: |
690 | case ICorDebugInfo::REGNUM_AMBIENT_SP: |
691 | return offsetof(T_CONTEXT,Esp); |
692 | case ICorDebugInfo::REGNUM_EBP: return offsetof(T_CONTEXT,Ebp); |
693 | case ICorDebugInfo::REGNUM_ESI: return offsetof(T_CONTEXT,Esi); |
694 | case ICorDebugInfo::REGNUM_EDI: return offsetof(T_CONTEXT,Edi); |
695 | default: _ASSERTE(!"Bad regNum" ); return (SIZE_T) -1; |
696 | } |
697 | #elif defined(_TARGET_AMD64_) |
698 | switch(regNum) |
699 | { |
700 | case ICorDebugInfo::REGNUM_RAX: return offsetof(CONTEXT, Rax); |
701 | case ICorDebugInfo::REGNUM_RCX: return offsetof(CONTEXT, Rcx); |
702 | case ICorDebugInfo::REGNUM_RDX: return offsetof(CONTEXT, Rdx); |
703 | case ICorDebugInfo::REGNUM_RBX: return offsetof(CONTEXT, Rbx); |
704 | case ICorDebugInfo::REGNUM_RSP: return offsetof(CONTEXT, Rsp); |
705 | case ICorDebugInfo::REGNUM_RBP: return offsetof(CONTEXT, Rbp); |
706 | case ICorDebugInfo::REGNUM_RSI: return offsetof(CONTEXT, Rsi); |
707 | case ICorDebugInfo::REGNUM_RDI: return offsetof(CONTEXT, Rdi); |
708 | case ICorDebugInfo::REGNUM_R8: return offsetof(CONTEXT, R8); |
709 | case ICorDebugInfo::REGNUM_R9: return offsetof(CONTEXT, R9); |
710 | case ICorDebugInfo::REGNUM_R10: return offsetof(CONTEXT, R10); |
711 | case ICorDebugInfo::REGNUM_R11: return offsetof(CONTEXT, R11); |
712 | case ICorDebugInfo::REGNUM_R12: return offsetof(CONTEXT, R12); |
713 | case ICorDebugInfo::REGNUM_R13: return offsetof(CONTEXT, R13); |
714 | case ICorDebugInfo::REGNUM_R14: return offsetof(CONTEXT, R14); |
715 | case ICorDebugInfo::REGNUM_R15: return offsetof(CONTEXT, R15); |
716 | default: _ASSERTE(!"Bad regNum" ); return (SIZE_T)(-1); |
717 | } |
718 | #elif defined(_TARGET_ARM_) |
719 | |
720 | switch(regNum) |
721 | { |
722 | case ICorDebugInfo::REGNUM_R0: return offsetof(T_CONTEXT, R0); |
723 | case ICorDebugInfo::REGNUM_R1: return offsetof(T_CONTEXT, R1); |
724 | case ICorDebugInfo::REGNUM_R2: return offsetof(T_CONTEXT, R2); |
725 | case ICorDebugInfo::REGNUM_R3: return offsetof(T_CONTEXT, R3); |
726 | case ICorDebugInfo::REGNUM_R4: return offsetof(T_CONTEXT, R4); |
727 | case ICorDebugInfo::REGNUM_R5: return offsetof(T_CONTEXT, R5); |
728 | case ICorDebugInfo::REGNUM_R6: return offsetof(T_CONTEXT, R6); |
729 | case ICorDebugInfo::REGNUM_R7: return offsetof(T_CONTEXT, R7); |
730 | case ICorDebugInfo::REGNUM_R8: return offsetof(T_CONTEXT, R8); |
731 | case ICorDebugInfo::REGNUM_R9: return offsetof(T_CONTEXT, R9); |
732 | case ICorDebugInfo::REGNUM_R10: return offsetof(T_CONTEXT, R10); |
733 | case ICorDebugInfo::REGNUM_R11: return offsetof(T_CONTEXT, R11); |
734 | case ICorDebugInfo::REGNUM_R12: return offsetof(T_CONTEXT, R12); |
735 | case ICorDebugInfo::REGNUM_SP: return offsetof(T_CONTEXT, Sp); |
736 | case ICorDebugInfo::REGNUM_PC: return offsetof(T_CONTEXT, Pc); |
737 | case ICorDebugInfo::REGNUM_LR: return offsetof(T_CONTEXT, Lr); |
738 | case ICorDebugInfo::REGNUM_AMBIENT_SP: return offsetof(T_CONTEXT, Sp); |
739 | default: _ASSERTE(!"Bad regNum" ); return (SIZE_T)(-1); |
740 | } |
741 | #elif defined(_TARGET_ARM64_) |
742 | |
743 | switch(regNum) |
744 | { |
745 | case ICorDebugInfo::REGNUM_X0: return offsetof(T_CONTEXT, X0); |
746 | case ICorDebugInfo::REGNUM_X1: return offsetof(T_CONTEXT, X1); |
747 | case ICorDebugInfo::REGNUM_X2: return offsetof(T_CONTEXT, X2); |
748 | case ICorDebugInfo::REGNUM_X3: return offsetof(T_CONTEXT, X3); |
749 | case ICorDebugInfo::REGNUM_X4: return offsetof(T_CONTEXT, X4); |
750 | case ICorDebugInfo::REGNUM_X5: return offsetof(T_CONTEXT, X5); |
751 | case ICorDebugInfo::REGNUM_X6: return offsetof(T_CONTEXT, X6); |
752 | case ICorDebugInfo::REGNUM_X7: return offsetof(T_CONTEXT, X7); |
753 | case ICorDebugInfo::REGNUM_X8: return offsetof(T_CONTEXT, X8); |
754 | case ICorDebugInfo::REGNUM_X9: return offsetof(T_CONTEXT, X9); |
755 | case ICorDebugInfo::REGNUM_X10: return offsetof(T_CONTEXT, X10); |
756 | case ICorDebugInfo::REGNUM_X11: return offsetof(T_CONTEXT, X11); |
757 | case ICorDebugInfo::REGNUM_X12: return offsetof(T_CONTEXT, X12); |
758 | case ICorDebugInfo::REGNUM_X13: return offsetof(T_CONTEXT, X13); |
759 | case ICorDebugInfo::REGNUM_X14: return offsetof(T_CONTEXT, X14); |
760 | case ICorDebugInfo::REGNUM_X15: return offsetof(T_CONTEXT, X15); |
761 | case ICorDebugInfo::REGNUM_X16: return offsetof(T_CONTEXT, X16); |
762 | case ICorDebugInfo::REGNUM_X17: return offsetof(T_CONTEXT, X17); |
763 | case ICorDebugInfo::REGNUM_X18: return offsetof(T_CONTEXT, X18); |
764 | case ICorDebugInfo::REGNUM_X19: return offsetof(T_CONTEXT, X19); |
765 | case ICorDebugInfo::REGNUM_X20: return offsetof(T_CONTEXT, X20); |
766 | case ICorDebugInfo::REGNUM_X21: return offsetof(T_CONTEXT, X21); |
767 | case ICorDebugInfo::REGNUM_X22: return offsetof(T_CONTEXT, X22); |
768 | case ICorDebugInfo::REGNUM_X23: return offsetof(T_CONTEXT, X23); |
769 | case ICorDebugInfo::REGNUM_X24: return offsetof(T_CONTEXT, X24); |
770 | case ICorDebugInfo::REGNUM_X25: return offsetof(T_CONTEXT, X25); |
771 | case ICorDebugInfo::REGNUM_X26: return offsetof(T_CONTEXT, X26); |
772 | case ICorDebugInfo::REGNUM_X27: return offsetof(T_CONTEXT, X27); |
773 | case ICorDebugInfo::REGNUM_X28: return offsetof(T_CONTEXT, X28); |
774 | case ICorDebugInfo::REGNUM_FP: return offsetof(T_CONTEXT, Fp); |
775 | case ICorDebugInfo::REGNUM_LR: return offsetof(T_CONTEXT, Lr); |
776 | case ICorDebugInfo::REGNUM_SP: return offsetof(T_CONTEXT, Sp); |
777 | case ICorDebugInfo::REGNUM_PC: return offsetof(T_CONTEXT, Pc); |
778 | case ICorDebugInfo::REGNUM_AMBIENT_SP: return offsetof(T_CONTEXT, Sp); |
779 | default: _ASSERTE(!"Bad regNum" ); return (SIZE_T)(-1); |
780 | } |
781 | #else |
782 | PORTABILITY_ASSERT("GetRegOffsInCONTEXT is not implemented on this platform." ); |
783 | return (SIZE_T) -1; |
784 | #endif // _TARGET_X86_ |
785 | } |
786 | |
787 | SIZE_T DereferenceByRefVar(SIZE_T addr) |
788 | { |
789 | STATIC_CONTRACT_WRAPPER; |
790 | |
791 | SIZE_T result = NULL; |
792 | |
793 | #if defined(DACCESS_COMPILE) |
794 | HRESULT hr = DacReadAll(addr, &result, sizeof(result), false); |
795 | if (FAILED(hr)) |
796 | { |
797 | result = NULL; |
798 | } |
799 | |
800 | #else // !DACCESS_COMPILE |
801 | EX_TRY |
802 | { |
803 | AVInRuntimeImplOkayHolder AVOkay; |
804 | |
805 | result = *(SIZE_T*)addr; |
806 | } |
807 | EX_CATCH |
808 | { |
809 | } |
810 | EX_END_CATCH(SwallowAllExceptions); |
811 | |
812 | #endif // !DACCESS_COMPILE |
813 | |
814 | return result; |
815 | } |
816 | |
817 | // How are errors communicated to the caller? |
818 | ULONG NativeVarLocations(const ICorDebugInfo::VarLoc & varLoc, |
819 | PT_CONTEXT pCtx, |
820 | ULONG numLocs, |
821 | NativeVarLocation* locs) |
822 | { |
823 | STATIC_CONTRACT_NOTHROW; |
824 | STATIC_CONTRACT_GC_NOTRIGGER; |
825 | STATIC_CONTRACT_FORBID_FAULT; |
826 | |
827 | _ASSERTE(numLocs >= MAX_NATIVE_VAR_LOCS); |
828 | |
829 | bool fByRef = false; |
830 | switch(varLoc.vlType) |
831 | { |
832 | SIZE_T regOffs; |
833 | TADDR baseReg; |
834 | |
835 | case ICorDebugInfo::VLT_REG_BYREF: |
836 | fByRef = true; // fall through |
837 | case ICorDebugInfo::VLT_REG: |
838 | regOffs = GetRegOffsInCONTEXT(varLoc.vlReg.vlrReg); |
839 | locs->addr = (ULONG64)(ULONG_PTR)pCtx + regOffs; |
840 | if (fByRef) |
841 | { |
842 | locs->addr = (ULONG64)DereferenceByRefVar((SIZE_T)locs->addr); |
843 | } |
844 | locs->size = sizeof(SIZE_T); |
845 | { |
846 | locs->contextReg = true; |
847 | } |
848 | return 1; |
849 | |
850 | case ICorDebugInfo::VLT_STK_BYREF: |
851 | fByRef = true; // fall through |
852 | case ICorDebugInfo::VLT_STK: |
853 | regOffs = GetRegOffsInCONTEXT(varLoc.vlStk.vlsBaseReg); |
854 | baseReg = *(TADDR *)(regOffs + (BYTE*)pCtx); |
855 | locs->addr = baseReg + varLoc.vlStk.vlsOffset; |
856 | if (fByRef) |
857 | { |
858 | locs->addr = (ULONG64)DereferenceByRefVar((SIZE_T)locs->addr); |
859 | } |
860 | locs->size = sizeof(SIZE_T); |
861 | locs->contextReg = false; |
862 | return 1; |
863 | |
864 | case ICorDebugInfo::VLT_REG_REG: |
865 | regOffs = GetRegOffsInCONTEXT(varLoc.vlRegReg.vlrrReg1); |
866 | locs->addr = (ULONG64)(ULONG_PTR)pCtx + regOffs; |
867 | locs->size = sizeof(SIZE_T); |
868 | locs->contextReg = true; |
869 | locs++; |
870 | |
871 | regOffs = GetRegOffsInCONTEXT(varLoc.vlRegReg.vlrrReg2); |
872 | locs->addr = (ULONG64)(ULONG_PTR)pCtx + regOffs; |
873 | locs->size = sizeof(SIZE_T); |
874 | locs->contextReg = true; |
875 | return 2; |
876 | |
877 | case ICorDebugInfo::VLT_REG_STK: |
878 | regOffs = GetRegOffsInCONTEXT(varLoc.vlRegStk.vlrsReg); |
879 | locs->addr = (ULONG64)(ULONG_PTR)pCtx + regOffs; |
880 | locs->size = sizeof(SIZE_T); |
881 | locs->contextReg = true; |
882 | locs++; |
883 | |
884 | regOffs = GetRegOffsInCONTEXT(varLoc.vlRegStk.vlrsStk.vlrssBaseReg); |
885 | baseReg = *(TADDR *)(regOffs + (BYTE*)pCtx); |
886 | locs->addr = baseReg + varLoc.vlRegStk.vlrsStk.vlrssOffset; |
887 | locs->size = sizeof(SIZE_T); |
888 | locs->contextReg = false; |
889 | return 2; |
890 | |
891 | case ICorDebugInfo::VLT_STK_REG: |
892 | regOffs = GetRegOffsInCONTEXT(varLoc.vlStkReg.vlsrStk.vlsrsBaseReg); |
893 | baseReg = *(TADDR *)(regOffs + (BYTE*)pCtx); |
894 | locs->addr = baseReg + varLoc.vlStkReg.vlsrStk.vlsrsOffset; |
895 | locs->size = sizeof(SIZE_T); |
896 | locs->contextReg = false; |
897 | locs++; |
898 | |
899 | regOffs = GetRegOffsInCONTEXT(varLoc.vlStkReg.vlsrReg); |
900 | locs->addr = (ULONG64)(ULONG_PTR)pCtx + regOffs; |
901 | locs->size = sizeof(SIZE_T); |
902 | locs->contextReg = true; |
903 | return 2; |
904 | |
905 | case ICorDebugInfo::VLT_STK2: |
906 | regOffs = GetRegOffsInCONTEXT(varLoc.vlStk2.vls2BaseReg); |
907 | baseReg = *(TADDR *)(regOffs + (BYTE*)pCtx); |
908 | locs->addr = baseReg + varLoc.vlStk2.vls2Offset; |
909 | locs->size = 2 * sizeof(SIZE_T); |
910 | locs->contextReg = false; |
911 | return 1; |
912 | |
913 | case ICorDebugInfo::VLT_FPSTK: |
914 | _ASSERTE(!"NYI" ); |
915 | return 0; |
916 | |
917 | default: |
918 | _ASSERTE(!"Bad locType" ); |
919 | return 0; |
920 | } |
921 | } |
922 | |
923 | |
924 | BOOL CompareFiles(HANDLE hFile1,HANDLE hFile2) |
925 | { |
926 | |
927 | STATIC_CONTRACT_THROWS; |
928 | STATIC_CONTRACT_GC_NOTRIGGER; |
929 | BY_HANDLE_FILE_INFORMATION fileinfo1; |
930 | BY_HANDLE_FILE_INFORMATION fileinfo2; |
931 | if (!GetFileInformationByHandle(hFile1,&fileinfo1) || |
932 | !GetFileInformationByHandle(hFile2,&fileinfo2)) |
933 | ThrowLastError(); |
934 | return fileinfo1.nFileIndexLow == fileinfo2.nFileIndexLow && |
935 | fileinfo1.nFileIndexHigh == fileinfo2.nFileIndexHigh && |
936 | fileinfo1.dwVolumeSerialNumber==fileinfo2.dwVolumeSerialNumber; |
937 | } |
938 | |
939 | |
940 | #ifndef DACCESS_COMPILE |
941 | |
942 | // Returns the location at which the variable |
943 | // begins. Returns NULL for register vars. For reg-stack |
944 | // split, it'll return the addr of the stack part. |
945 | // This also works for VLT_REG (a single register). |
946 | SIZE_T *NativeVarStackAddr(const ICorDebugInfo::VarLoc & varLoc, |
947 | PCONTEXT pCtx) |
948 | { |
949 | STATIC_CONTRACT_NOTHROW; |
950 | STATIC_CONTRACT_GC_NOTRIGGER; |
951 | STATIC_CONTRACT_FORBID_FAULT; |
952 | |
953 | SIZE_T *dwAddr = NULL; |
954 | |
955 | bool fByRef = false; |
956 | switch(varLoc.vlType) |
957 | { |
958 | SIZE_T regOffs; |
959 | const BYTE * baseReg; |
960 | |
961 | case ICorDebugInfo::VLT_REG_BYREF: |
962 | fByRef = true; // fall through |
963 | case ICorDebugInfo::VLT_REG: |
964 | regOffs = GetRegOffsInCONTEXT(varLoc.vlReg.vlrReg); |
965 | dwAddr = (SIZE_T *)(regOffs + (BYTE*)pCtx); |
966 | if (fByRef) |
967 | { |
968 | dwAddr = (SIZE_T*)(*dwAddr); |
969 | } |
970 | LOG((LF_CORDB, LL_INFO100, "NVSA: VLT_REG @ 0x%x (by ref = %d)\n" , dwAddr, fByRef)); |
971 | break; |
972 | |
973 | case ICorDebugInfo::VLT_STK_BYREF: |
974 | fByRef = true; // fall through |
975 | case ICorDebugInfo::VLT_STK: |
976 | regOffs = GetRegOffsInCONTEXT(varLoc.vlStk.vlsBaseReg); |
977 | baseReg = (const BYTE *)*(SIZE_T *)(regOffs + (BYTE*)pCtx); |
978 | dwAddr = (SIZE_T *)(baseReg + varLoc.vlStk.vlsOffset); |
979 | if (fByRef) |
980 | { |
981 | dwAddr = (SIZE_T*)(*dwAddr); |
982 | } |
983 | LOG((LF_CORDB, LL_INFO100, "NVSA: VLT_STK @ 0x%x (by ref = %d)\n" , dwAddr, fByRef)); |
984 | break; |
985 | |
986 | case ICorDebugInfo::VLT_STK2: |
987 | // <TODO>@TODO : VLT_STK2 is overloaded to also mean VLT_STK_n. |
988 | // return FALSE if n > 2;</TODO> |
989 | |
990 | regOffs = GetRegOffsInCONTEXT(varLoc.vlStk2.vls2BaseReg); |
991 | baseReg = (const BYTE *)*(SIZE_T *)(regOffs + (BYTE*)pCtx); |
992 | dwAddr = (SIZE_T *)(baseReg + varLoc.vlStk2.vls2Offset); |
993 | LOG((LF_CORDB, LL_INFO100, "NVSA: VLT_STK_2 @ 0x%x\n" ,dwAddr)); |
994 | break; |
995 | |
996 | case ICorDebugInfo::VLT_REG_STK: |
997 | regOffs = GetRegOffsInCONTEXT(varLoc.vlRegStk.vlrsStk.vlrssBaseReg); |
998 | baseReg = (const BYTE *)*(SIZE_T *)(regOffs + (BYTE*)pCtx); |
999 | dwAddr = (SIZE_T *)(baseReg + varLoc.vlRegStk.vlrsStk.vlrssOffset); |
1000 | LOG((LF_CORDB, LL_INFO100, "NVSA: REG_STK @ 0x%x\n" ,dwAddr)); |
1001 | break; |
1002 | |
1003 | case ICorDebugInfo::VLT_STK_REG: |
1004 | regOffs = GetRegOffsInCONTEXT(varLoc.vlStkReg.vlsrStk.vlsrsBaseReg); |
1005 | baseReg = (const BYTE *)*(SIZE_T *)(regOffs + (BYTE*)pCtx); |
1006 | dwAddr = (SIZE_T *)(baseReg + varLoc.vlStkReg.vlsrStk.vlsrsOffset); |
1007 | LOG((LF_CORDB, LL_INFO100, "NVSA: STK_REG @ 0x%x\n" ,dwAddr)); |
1008 | break; |
1009 | |
1010 | case ICorDebugInfo::VLT_REG_REG: |
1011 | case ICorDebugInfo::VLT_FPSTK: |
1012 | _ASSERTE(!"NYI" ); break; |
1013 | |
1014 | default: |
1015 | _ASSERTE(!"Bad locType" ); break; |
1016 | } |
1017 | |
1018 | return dwAddr; |
1019 | |
1020 | } |
1021 | |
1022 | |
1023 | #if defined(_WIN64) |
1024 | void GetNativeVarValHelper(SIZE_T* dstAddrLow, SIZE_T* dstAddrHigh, SIZE_T* srcAddr, SIZE_T size) |
1025 | { |
1026 | if (size == 1) |
1027 | *(BYTE*)dstAddrLow = *(BYTE*)srcAddr; |
1028 | else if (size == 2) |
1029 | *(USHORT*)dstAddrLow = *(USHORT*)srcAddr; |
1030 | else if (size == 4) |
1031 | *(ULONG*)dstAddrLow = *(ULONG*)srcAddr; |
1032 | else if (size == 8) |
1033 | *dstAddrLow = *srcAddr; |
1034 | else if (size == 16) |
1035 | { |
1036 | *dstAddrLow = *srcAddr; |
1037 | *dstAddrHigh = *(srcAddr+1); |
1038 | } |
1039 | else |
1040 | { |
1041 | _ASSERTE(!"util.cpp - unreachable code.\n" ); |
1042 | UNREACHABLE(); |
1043 | } |
1044 | } |
1045 | #endif // _WIN64 |
1046 | |
1047 | |
1048 | bool GetNativeVarVal(const ICorDebugInfo::VarLoc & varLoc, |
1049 | PCONTEXT pCtx, |
1050 | SIZE_T * pVal1, |
1051 | SIZE_T * pVal2 |
1052 | WIN64_ARG(SIZE_T cbSize)) |
1053 | { |
1054 | |
1055 | STATIC_CONTRACT_NOTHROW; |
1056 | STATIC_CONTRACT_GC_NOTRIGGER; |
1057 | STATIC_CONTRACT_FORBID_FAULT; |
1058 | |
1059 | switch(varLoc.vlType) |
1060 | { |
1061 | #if !defined(_WIN64) |
1062 | SIZE_T regOffs; |
1063 | |
1064 | case ICorDebugInfo::VLT_REG: |
1065 | *pVal1 = *NativeVarStackAddr(varLoc,pCtx); |
1066 | break; |
1067 | |
1068 | case ICorDebugInfo::VLT_STK: |
1069 | *pVal1 = *NativeVarStackAddr(varLoc,pCtx); |
1070 | break; |
1071 | |
1072 | case ICorDebugInfo::VLT_STK2: |
1073 | *pVal1 = *NativeVarStackAddr(varLoc,pCtx); |
1074 | *pVal2 = *(NativeVarStackAddr(varLoc,pCtx)+ 1); |
1075 | break; |
1076 | |
1077 | case ICorDebugInfo::VLT_REG_REG: |
1078 | regOffs = GetRegOffsInCONTEXT(varLoc.vlRegReg.vlrrReg1); |
1079 | *pVal1 = *(SIZE_T *)(regOffs + (BYTE*)pCtx); |
1080 | LOG((LF_CORDB, LL_INFO100, "GNVV: STK_REG_REG 1 @ 0x%x\n" , |
1081 | (SIZE_T *)(regOffs + (BYTE*)pCtx))); |
1082 | |
1083 | regOffs = GetRegOffsInCONTEXT(varLoc.vlRegReg.vlrrReg2); |
1084 | *pVal2 = *(SIZE_T *)(regOffs + (BYTE*)pCtx); |
1085 | LOG((LF_CORDB, LL_INFO100, "GNVV: STK_REG_REG 2 @ 0x%x\n" , |
1086 | (SIZE_T *)(regOffs + (BYTE*)pCtx))); |
1087 | break; |
1088 | |
1089 | case ICorDebugInfo::VLT_REG_STK: |
1090 | regOffs = GetRegOffsInCONTEXT(varLoc.vlRegStk.vlrsReg); |
1091 | *pVal1 = *(SIZE_T *)(regOffs + (BYTE*)pCtx); |
1092 | LOG((LF_CORDB, LL_INFO100, "GNVV: STK_REG_STK reg @ 0x%x\n" , |
1093 | (SIZE_T *)(regOffs + (BYTE*)pCtx))); |
1094 | *pVal2 = *NativeVarStackAddr(varLoc,pCtx); |
1095 | break; |
1096 | |
1097 | case ICorDebugInfo::VLT_STK_REG: |
1098 | *pVal1 = *NativeVarStackAddr(varLoc,pCtx); |
1099 | regOffs = GetRegOffsInCONTEXT(varLoc.vlStkReg.vlsrReg); |
1100 | *pVal2 = *(SIZE_T *)(regOffs + (BYTE*)pCtx); |
1101 | LOG((LF_CORDB, LL_INFO100, "GNVV: STK_STK_REG reg @ 0x%x\n" , |
1102 | (SIZE_T *)(regOffs + (BYTE*)pCtx))); |
1103 | break; |
1104 | |
1105 | case ICorDebugInfo::VLT_FPSTK: |
1106 | _ASSERTE(!"NYI" ); break; |
1107 | |
1108 | #else // _WIN64 |
1109 | case ICorDebugInfo::VLT_REG: |
1110 | case ICorDebugInfo::VLT_REG_FP: |
1111 | case ICorDebugInfo::VLT_STK: |
1112 | GetNativeVarValHelper(pVal1, pVal2, NativeVarStackAddr(varLoc, pCtx), cbSize); |
1113 | break; |
1114 | |
1115 | case ICorDebugInfo::VLT_REG_BYREF: // fall through |
1116 | case ICorDebugInfo::VLT_STK_BYREF: |
1117 | _ASSERTE(!"GNVV: This function should not be called for value types" ); |
1118 | break; |
1119 | |
1120 | #endif // _WIN64 |
1121 | |
1122 | default: |
1123 | _ASSERTE(!"Bad locType" ); break; |
1124 | } |
1125 | |
1126 | return true; |
1127 | } |
1128 | |
1129 | |
1130 | #if defined(_WIN64) |
1131 | void SetNativeVarValHelper(SIZE_T* dstAddr, SIZE_T valueLow, SIZE_T valueHigh, SIZE_T size) |
1132 | { |
1133 | if (size == 1) |
1134 | *(BYTE*)dstAddr = (BYTE)valueLow; |
1135 | else if (size == 2) |
1136 | *(USHORT*)dstAddr = (USHORT)valueLow; |
1137 | else if (size == 4) |
1138 | *(ULONG*)dstAddr = (ULONG)valueLow; |
1139 | else if (size == 8) |
1140 | *dstAddr = valueLow; |
1141 | else if (size == 16) |
1142 | { |
1143 | *dstAddr = valueLow; |
1144 | *(dstAddr+1) = valueHigh; |
1145 | } |
1146 | else |
1147 | { |
1148 | _ASSERTE(!"util.cpp - unreachable code.\n" ); |
1149 | UNREACHABLE(); |
1150 | } |
1151 | } |
1152 | #endif // _WIN64 |
1153 | |
1154 | |
1155 | bool SetNativeVarVal(const ICorDebugInfo::VarLoc & varLoc, |
1156 | PCONTEXT pCtx, |
1157 | SIZE_T val1, |
1158 | SIZE_T val2 |
1159 | WIN64_ARG(SIZE_T cbSize)) |
1160 | { |
1161 | STATIC_CONTRACT_NOTHROW; |
1162 | STATIC_CONTRACT_GC_NOTRIGGER; |
1163 | STATIC_CONTRACT_FORBID_FAULT; |
1164 | |
1165 | switch(varLoc.vlType) |
1166 | { |
1167 | #if !defined(_WIN64) |
1168 | SIZE_T regOffs; |
1169 | |
1170 | case ICorDebugInfo::VLT_REG: |
1171 | *NativeVarStackAddr(varLoc,pCtx) = val1; |
1172 | break; |
1173 | |
1174 | case ICorDebugInfo::VLT_STK: |
1175 | *NativeVarStackAddr(varLoc,pCtx) = val1; |
1176 | break; |
1177 | |
1178 | case ICorDebugInfo::VLT_STK2: |
1179 | *NativeVarStackAddr(varLoc,pCtx) = val1; |
1180 | *(NativeVarStackAddr(varLoc,pCtx)+ 1) = val2; |
1181 | break; |
1182 | |
1183 | case ICorDebugInfo::VLT_REG_REG: |
1184 | regOffs = GetRegOffsInCONTEXT(varLoc.vlRegReg.vlrrReg1); |
1185 | *(SIZE_T *)(regOffs + (BYTE*)pCtx) = val1; |
1186 | LOG((LF_CORDB, LL_INFO100, "SNVV: STK_REG_REG 1 @ 0x%x\n" , |
1187 | (SIZE_T *)(regOffs + (BYTE*)pCtx))); |
1188 | |
1189 | regOffs = GetRegOffsInCONTEXT(varLoc.vlRegReg.vlrrReg2); |
1190 | *(SIZE_T *)(regOffs + (BYTE*)pCtx) = val2; |
1191 | LOG((LF_CORDB, LL_INFO100, "SNVV: STK_REG_REG 2 @ 0x%x\n" , |
1192 | (SIZE_T *)(regOffs + (BYTE*)pCtx))); |
1193 | break; |
1194 | |
1195 | case ICorDebugInfo::VLT_REG_STK: |
1196 | regOffs = GetRegOffsInCONTEXT(varLoc.vlRegStk.vlrsReg); |
1197 | *(SIZE_T *)(regOffs + (BYTE*)pCtx) = val1; |
1198 | LOG((LF_CORDB, LL_INFO100, "SNVV: STK_REG_STK reg @ 0x%x\n" , |
1199 | (SIZE_T *)(regOffs + (BYTE*)pCtx))); |
1200 | *NativeVarStackAddr(varLoc,pCtx) = val2; |
1201 | break; |
1202 | |
1203 | case ICorDebugInfo::VLT_STK_REG: |
1204 | *NativeVarStackAddr(varLoc,pCtx) = val1; |
1205 | regOffs = GetRegOffsInCONTEXT(varLoc.vlStkReg.vlsrReg); |
1206 | *(SIZE_T *)(regOffs + (BYTE*)pCtx) = val2; |
1207 | LOG((LF_CORDB, LL_INFO100, "SNVV: STK_STK_REG reg @ 0x%x\n" , |
1208 | (SIZE_T *)(regOffs + (BYTE*)pCtx))); |
1209 | break; |
1210 | |
1211 | case ICorDebugInfo::VLT_FPSTK: |
1212 | _ASSERTE(!"NYI" ); break; |
1213 | |
1214 | #else // _WIN64 |
1215 | case ICorDebugInfo::VLT_REG: |
1216 | case ICorDebugInfo::VLT_REG_FP: |
1217 | case ICorDebugInfo::VLT_STK: |
1218 | SetNativeVarValHelper(NativeVarStackAddr(varLoc, pCtx), val1, val2, cbSize); |
1219 | break; |
1220 | |
1221 | case ICorDebugInfo::VLT_REG_BYREF: // fall through |
1222 | case ICorDebugInfo::VLT_STK_BYREF: |
1223 | _ASSERTE(!"GNVV: This function should not be called for value types" ); |
1224 | break; |
1225 | |
1226 | #endif // _WIN64 |
1227 | |
1228 | default: |
1229 | _ASSERTE(!"Bad locType" ); break; |
1230 | } |
1231 | |
1232 | return true; |
1233 | } |
1234 | |
1235 | HRESULT VMPostError( // Returned error. |
1236 | HRESULT hrRpt, // Reported error. |
1237 | ...) // Error arguments. |
1238 | { |
1239 | CONTRACTL |
1240 | { |
1241 | NOTHROW; |
1242 | GC_TRIGGERS; |
1243 | MODE_ANY; |
1244 | } |
1245 | CONTRACTL_END; |
1246 | |
1247 | GCX_PREEMP(); |
1248 | |
1249 | va_list marker; // User text. |
1250 | va_start(marker, hrRpt); |
1251 | hrRpt = PostErrorVA(hrRpt, marker); |
1252 | va_end(marker); |
1253 | |
1254 | return hrRpt; |
1255 | } |
1256 | |
1257 | #ifndef CROSSGEN_COMPILE |
1258 | void VMDumpCOMErrors(HRESULT hrErr) |
1259 | { |
1260 | CONTRACTL |
1261 | { |
1262 | NOTHROW; |
1263 | GC_TRIGGERS; |
1264 | MODE_PREEMPTIVE; |
1265 | PRECONDITION(FAILED(hrErr)); |
1266 | } |
1267 | CONTRACTL_END; |
1268 | |
1269 | SafeComHolderPreemp<IErrorInfo> pIErr(NULL);// Error interface. |
1270 | BSTRHolder bstrDesc(NULL); // Description text. |
1271 | |
1272 | // Try to get an error info object and display the message. |
1273 | if (SafeGetErrorInfo(&pIErr) == S_OK && pIErr->GetDescription(&bstrDesc) == S_OK) |
1274 | { |
1275 | EEMessageBoxCatastrophic(IDS_EE_GENERIC, IDS_FATAL_ERROR, (BSTR)bstrDesc); |
1276 | } |
1277 | else |
1278 | { |
1279 | // Just give out the failed hr return code. |
1280 | EEMessageBoxCatastrophic(IDS_COMPLUS_ERROR, IDS_FATAL_ERROR, hrErr); |
1281 | } |
1282 | } |
1283 | |
1284 | //----------------------------------------------------------------------------- |
1285 | #ifndef FEATURE_PAL |
1286 | |
1287 | // Wrap registry functions to use CQuickWSTR to allocate space. This does it |
1288 | // in a stack friendly manner. |
1289 | //----------------------------------------------------------------------------- |
1290 | LONG UtilRegEnumKey(HKEY hKey, // handle to key to query |
1291 | DWORD dwIndex, // index of subkey to query |
1292 | CQuickWSTR* lpName) // buffer for subkey name |
1293 | { |
1294 | CONTRACTL |
1295 | { |
1296 | NOTHROW; |
1297 | GC_NOTRIGGER; |
1298 | INJECT_FAULT(return ERROR_NOT_ENOUGH_MEMORY;); |
1299 | } |
1300 | CONTRACTL_END; |
1301 | |
1302 | DWORD size = (DWORD)lpName->MaxSize(); |
1303 | LONG result = WszRegEnumKeyEx(hKey, |
1304 | dwIndex, |
1305 | lpName->Ptr(), |
1306 | &size, |
1307 | NULL, |
1308 | NULL, |
1309 | NULL, |
1310 | NULL); |
1311 | |
1312 | if (result == ERROR_SUCCESS || result == ERROR_MORE_DATA) { |
1313 | |
1314 | // Grow or shrink buffer to correct size |
1315 | if (lpName->ReSizeNoThrow(size+1) != NOERROR) |
1316 | result = ERROR_NOT_ENOUGH_MEMORY; |
1317 | |
1318 | if (result == ERROR_MORE_DATA) { |
1319 | size = (DWORD)lpName->MaxSize(); |
1320 | result = WszRegEnumKeyEx(hKey, |
1321 | dwIndex, |
1322 | lpName->Ptr(), |
1323 | &size, |
1324 | NULL, |
1325 | NULL, |
1326 | NULL, |
1327 | NULL); |
1328 | } |
1329 | } |
1330 | |
1331 | return result; |
1332 | } |
1333 | |
1334 | LONG UtilRegQueryStringValueEx(HKEY hKey, // handle to key to query |
1335 | LPCWSTR lpValueName, // address of name of value to query |
1336 | LPDWORD lpReserved, // reserved |
1337 | LPDWORD lpType, // address of buffer for value type |
1338 | CQuickWSTR* lpData)// data buffer |
1339 | { |
1340 | CONTRACTL |
1341 | { |
1342 | NOTHROW; |
1343 | GC_NOTRIGGER; |
1344 | INJECT_FAULT(return ERROR_NOT_ENOUGH_MEMORY;); |
1345 | } |
1346 | CONTRACTL_END; |
1347 | |
1348 | DWORD size = (DWORD)lpData->MaxSize(); |
1349 | LONG result = WszRegQueryValueEx(hKey, |
1350 | lpValueName, |
1351 | lpReserved, |
1352 | lpType, |
1353 | (LPBYTE) lpData->Ptr(), |
1354 | &size); |
1355 | |
1356 | if (result == ERROR_SUCCESS || result == ERROR_MORE_DATA) { |
1357 | |
1358 | // Grow or shrink buffer to correct size |
1359 | if (lpData->ReSizeNoThrow(size+1) != NOERROR) |
1360 | result = ERROR_NOT_ENOUGH_MEMORY; |
1361 | |
1362 | if (result == ERROR_MORE_DATA) { |
1363 | size = (DWORD)lpData->MaxSize(); |
1364 | result = WszRegQueryValueEx(hKey, |
1365 | lpValueName, |
1366 | lpReserved, |
1367 | lpType, |
1368 | (LPBYTE) lpData->Ptr(), |
1369 | &size); |
1370 | } |
1371 | } |
1372 | |
1373 | return result; |
1374 | } |
1375 | |
1376 | BOOL ReportEventCLR( |
1377 | WORD wType, |
1378 | WORD wCategory, |
1379 | DWORD dwEventID, |
1380 | PSID lpUserSid, |
1381 | SString * message) |
1382 | { |
1383 | CONTRACTL { |
1384 | THROWS; |
1385 | GC_TRIGGERS; |
1386 | MODE_ANY; |
1387 | } CONTRACTL_END; |
1388 | |
1389 | GCX_PREEMP(); |
1390 | |
1391 | SString buff; |
1392 | buff.Printf(W(".NET Runtime version %s - %s" ), VER_FILEVERSION_STR_L, message->GetUnicode()); |
1393 | |
1394 | DWORD dwRetVal = ClrReportEvent(W(".NET Runtime" ), |
1395 | wType, // event type |
1396 | wCategory, // category |
1397 | dwEventID, // event identifier |
1398 | lpUserSid, // user security identifier |
1399 | buff.GetUnicode()); // one substitution string |
1400 | |
1401 | // Return BOOLEAN based upon return code |
1402 | return (dwRetVal == ERROR_SUCCESS)?TRUE:FALSE; |
1403 | } |
1404 | |
1405 | // This function checks to see if GetLogicalProcessorInformation API is supported. |
1406 | // On success, this function allocates a SLPI array, sets nEntries to number |
1407 | // of elements in the SLPI array and returns a pointer to the SLPI array after filling it with information. |
1408 | // |
1409 | // Note: If successful, IsGLPISupported allocates memory for the SLPI array and expects the caller to |
1410 | // free the memory once the caller is done using the information in the SLPI array. |
1411 | // |
1412 | // If the API is not supported or any failure, returns NULL |
1413 | // |
1414 | SYSTEM_LOGICAL_PROCESSOR_INFORMATION *IsGLPISupported( PDWORD nEntries ) |
1415 | { |
1416 | DWORD cbslpi = 0; |
1417 | DWORD dwNumElements = 0; |
1418 | SYSTEM_LOGICAL_PROCESSOR_INFORMATION *pslpi = NULL; |
1419 | |
1420 | // We setup the first call to GetLogicalProcessorInformation to fail so that we can obtain |
1421 | // the size of the buffer required to allocate for the SLPI array that is returned |
1422 | |
1423 | if (!GetLogicalProcessorInformation(pslpi, &cbslpi) && |
1424 | GetLastError() != ERROR_INSUFFICIENT_BUFFER) |
1425 | { |
1426 | // If we fail with anything other than an ERROR_INSUFFICIENT_BUFFER here, we punt with failure. |
1427 | return NULL; |
1428 | } |
1429 | |
1430 | _ASSERTE(cbslpi); |
1431 | |
1432 | // compute the number of SLPI entries required to hold the information returned from GLPI |
1433 | |
1434 | dwNumElements = cbslpi / sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION); |
1435 | |
1436 | // allocate a buffer in the free heap to hold an array of SLPI entries from GLPI, number of elements in the array is dwNumElements |
1437 | |
1438 | pslpi = new (nothrow) SYSTEM_LOGICAL_PROCESSOR_INFORMATION[ dwNumElements ]; |
1439 | |
1440 | if(pslpi == NULL) |
1441 | { |
1442 | // the memory allocation failed |
1443 | return NULL; |
1444 | } |
1445 | |
1446 | // Make call to GetLogicalProcessorInformation. Returns array of SLPI structures |
1447 | |
1448 | if (!GetLogicalProcessorInformation(pslpi, &cbslpi)) |
1449 | { |
1450 | // GetLogicalProcessorInformation failed |
1451 | delete[] pslpi ; //Allocation was fine but the API call itself failed and so we are releasing the memory before the return NULL. |
1452 | return NULL ; |
1453 | } |
1454 | |
1455 | // GetLogicalProcessorInformation successful, set nEntries to number of entries in the SLPI array |
1456 | *nEntries = dwNumElements; |
1457 | |
1458 | return pslpi; // return pointer to SLPI array |
1459 | |
1460 | }//IsGLPISupported |
1461 | |
1462 | // This function returns the size of highest level cache on the physical chip. If it cannot |
1463 | // determine the cachesize this function returns 0. |
1464 | size_t GetLogicalProcessorCacheSizeFromOS() |
1465 | { |
1466 | size_t cache_size = 0; |
1467 | DWORD nEntries = 0; |
1468 | |
1469 | // Try to use GetLogicalProcessorInformation API and get a valid pointer to the SLPI array if successful. Returns NULL |
1470 | // if API not present or on failure. |
1471 | |
1472 | SYSTEM_LOGICAL_PROCESSOR_INFORMATION *pslpi = IsGLPISupported(&nEntries) ; |
1473 | |
1474 | if (pslpi == NULL) |
1475 | { |
1476 | // GetLogicalProcessorInformation not supported or failed. |
1477 | goto Exit; |
1478 | } |
1479 | |
1480 | // Crack the information. Iterate through all the SLPI array entries for all processors in system. |
1481 | // Will return the greatest of all the processor cache sizes or zero |
1482 | { |
1483 | size_t last_cache_size = 0; |
1484 | |
1485 | for (DWORD i=0; i < nEntries; i++) |
1486 | { |
1487 | if (pslpi[i].Relationship == RelationCache) |
1488 | { |
1489 | last_cache_size = max(last_cache_size, pslpi[i].Cache.Size); |
1490 | } |
1491 | } |
1492 | cache_size = last_cache_size; |
1493 | } |
1494 | Exit: |
1495 | |
1496 | if(pslpi) |
1497 | delete[] pslpi; // release the memory allocated for the SLPI array. |
1498 | |
1499 | return cache_size; |
1500 | } |
1501 | |
1502 | #endif // !FEATURE_PAL |
1503 | |
1504 | // This function returns the number of logical processors on a given physical chip. If it cannot |
1505 | // determine the number of logical cpus, or the machine is not populated uniformly with the same |
1506 | // type of processors, this function returns 0. |
1507 | |
1508 | DWORD GetLogicalCpuCountFromOS() |
1509 | { |
1510 | // No CONTRACT possible because GetLogicalCpuCount uses SEH |
1511 | |
1512 | STATIC_CONTRACT_THROWS; |
1513 | STATIC_CONTRACT_GC_NOTRIGGER; |
1514 | |
1515 | static DWORD val = 0; |
1516 | DWORD retVal = 0; |
1517 | |
1518 | #ifdef FEATURE_PAL |
1519 | retVal = PAL_GetLogicalCpuCountFromOS(); |
1520 | #else // FEATURE_PAL |
1521 | |
1522 | DWORD nEntries = 0; |
1523 | |
1524 | DWORD prevcount = 0; |
1525 | DWORD count = 1; |
1526 | |
1527 | // Try to use GetLogicalProcessorInformation API and get a valid pointer to the SLPI array if successful. Returns NULL |
1528 | // if API not present or on failure. |
1529 | SYSTEM_LOGICAL_PROCESSOR_INFORMATION *pslpi = IsGLPISupported(&nEntries) ; |
1530 | |
1531 | if (pslpi == NULL) |
1532 | { |
1533 | // GetLogicalProcessorInformation no supported |
1534 | goto lDone; |
1535 | } |
1536 | |
1537 | for (DWORD j = 0; j < nEntries; j++) |
1538 | { |
1539 | if (pslpi[j].Relationship == RelationProcessorCore) |
1540 | { |
1541 | // LTP_PC_SMT indicates HT or SMT |
1542 | if (pslpi[j].ProcessorCore.Flags == LTP_PC_SMT) |
1543 | { |
1544 | SIZE_T pmask = pslpi[j].ProcessorMask; |
1545 | |
1546 | // Count the processors in the mask |
1547 | // |
1548 | // These are not the fastest bit counters. There may be processor intrinsics |
1549 | // (which would be best), but there are variants faster than these: |
1550 | // See http://en.wikipedia.org/wiki/Hamming_weight. |
1551 | // This is the naive implementation. |
1552 | #if !_WIN64 |
1553 | count = (pmask & 0x55555555) + ((pmask >> 1) & 0x55555555); |
1554 | count = (count & 0x33333333) + ((count >> 2) & 0x33333333); |
1555 | count = (count & 0x0F0F0F0F) + ((count >> 4) & 0x0F0F0F0F); |
1556 | count = (count & 0x00FF00FF) + ((count >> 8) & 0x00FF00FF); |
1557 | count = (count & 0x0000FFFF) + ((count >> 16)& 0x0000FFFF); |
1558 | #else |
1559 | pmask = (pmask & 0x5555555555555555ull) + ((pmask >> 1) & 0x5555555555555555ull); |
1560 | pmask = (pmask & 0x3333333333333333ull) + ((pmask >> 2) & 0x3333333333333333ull); |
1561 | pmask = (pmask & 0x0f0f0f0f0f0f0f0full) + ((pmask >> 4) & 0x0f0f0f0f0f0f0f0full); |
1562 | pmask = (pmask & 0x00ff00ff00ff00ffull) + ((pmask >> 8) & 0x00ff00ff00ff00ffull); |
1563 | pmask = (pmask & 0x0000ffff0000ffffull) + ((pmask >> 16) & 0x0000ffff0000ffffull); |
1564 | pmask = (pmask & 0x00000000ffffffffull) + ((pmask >> 32) & 0x00000000ffffffffull); |
1565 | count = static_cast<DWORD>(pmask); |
1566 | #endif // !_WIN64 else |
1567 | assert (count > 0); |
1568 | |
1569 | if (prevcount) |
1570 | { |
1571 | if (count != prevcount) |
1572 | { |
1573 | retVal = 1; // masks are not symmetric |
1574 | goto lDone; |
1575 | } |
1576 | } |
1577 | |
1578 | prevcount = count; |
1579 | } |
1580 | } |
1581 | } |
1582 | |
1583 | retVal = count; |
1584 | |
1585 | lDone: |
1586 | |
1587 | if(pslpi) |
1588 | { |
1589 | delete[] pslpi; // release the memory allocated for the SLPI array |
1590 | } |
1591 | #endif // FEATURE_PAL |
1592 | |
1593 | return retVal; |
1594 | } |
1595 | |
1596 | #if defined(_TARGET_X86_) || defined(_TARGET_AMD64_) |
1597 | |
1598 | #define CACHE_WAY_BITS 0xFFC00000 // number of cache WAYS-Associativity is returned in EBX[31:22] (10 bits) using cpuid function 4 |
1599 | #define CACHE_PARTITION_BITS 0x003FF000 // number of cache Physical Partitions is returned in EBX[21:12] (10 bits) using cpuid function 4 |
1600 | #define CACHE_LINESIZE_BITS 0x00000FFF // Linesize returned in EBX[11:0] (12 bits) using cpuid function 4 |
1601 | |
1602 | // these are defined in src\VM\AMD64\asmhelpers.asm / cgenx86.cpp |
1603 | extern "C" DWORD __stdcall getcpuid(DWORD arg1, unsigned char result[16]); |
1604 | extern "C" DWORD __stdcall getextcpuid(DWORD arg1, DWORD arg2, unsigned char result[16]); |
1605 | |
1606 | // The following function uses a deterministic mechanism for enumerating/calculating the details of the cache hierarychy at runtime |
1607 | // by using deterministic cache parameter leafs on Prescott and higher processors. |
1608 | // If successful, this function returns the cache size in bytes of the highest level on-die cache. Returns 0 on failure. |
1609 | |
1610 | size_t GetIntelDeterministicCacheEnum() |
1611 | { |
1612 | LIMITED_METHOD_CONTRACT; |
1613 | size_t retVal = 0; |
1614 | unsigned char buffer[16]; |
1615 | size_t buflen = ARRAYSIZE(buffer); |
1616 | |
1617 | DWORD maxCpuid = getextcpuid(0,0,buffer); |
1618 | DWORD dwBuffer[4]; |
1619 | memcpy(dwBuffer, buffer, buflen); |
1620 | |
1621 | if( (maxCpuid > 3) && (maxCpuid < 0x80000000) ) // Deterministic Cache Enum is Supported |
1622 | { |
1623 | DWORD dwCacheWays, dwCachePartitions, dwLineSize, dwSets; |
1624 | DWORD retEAX = 0; |
1625 | DWORD loopECX = 0; |
1626 | size_t maxSize = 0; |
1627 | size_t curSize = 0; |
1628 | |
1629 | // Make First call to getextcpuid with loopECX=0. loopECX provides an index indicating which level to return information about. |
1630 | // The second parameter is input EAX=4, to specify we want deterministic cache parameter leaf information. |
1631 | // getextcpuid with EAX=4 should be executed with loopECX = 0,1, ... until retEAX [4:0] contains 00000b, indicating no more |
1632 | // cache levels are supported. |
1633 | |
1634 | getextcpuid(loopECX, 4, buffer); |
1635 | memcpy(dwBuffer, buffer, buflen); |
1636 | retEAX = dwBuffer[0]; // get EAX |
1637 | |
1638 | int i = 0; |
1639 | while(retEAX & 0x1f) // Crack cache enums and loop while EAX > 0 |
1640 | { |
1641 | |
1642 | dwCacheWays = (dwBuffer[1] & CACHE_WAY_BITS) >> 22; |
1643 | dwCachePartitions = (dwBuffer[1] & CACHE_PARTITION_BITS) >> 12; |
1644 | dwLineSize = dwBuffer[1] & CACHE_LINESIZE_BITS; |
1645 | dwSets = dwBuffer[2]; // ECX |
1646 | |
1647 | curSize = (dwCacheWays+1)*(dwCachePartitions+1)*(dwLineSize+1)*(dwSets+1); |
1648 | |
1649 | if (maxSize < curSize) |
1650 | maxSize = curSize; |
1651 | |
1652 | loopECX++; |
1653 | getextcpuid(loopECX, 4, buffer); |
1654 | memcpy(dwBuffer, buffer, buflen); |
1655 | retEAX = dwBuffer[0] ; // get EAX[4:0]; |
1656 | i++; |
1657 | if (i > 16) { // prevent infinite looping |
1658 | return 0; |
1659 | } |
1660 | } |
1661 | retVal = maxSize; |
1662 | } |
1663 | return retVal ; |
1664 | } |
1665 | |
1666 | // The following function uses CPUID function 2 with descriptor values to determine the cache size. This requires a-priori |
1667 | // knowledge of the descriptor values. This works on gallatin and prior processors (already released processors). |
1668 | // If successful, this function returns the cache size in bytes of the highest level on-die cache. Returns 0 on failure. |
1669 | |
1670 | size_t GetIntelDescriptorValuesCache() |
1671 | { |
1672 | LIMITED_METHOD_CONTRACT; |
1673 | size_t size = 0; |
1674 | size_t maxSize = 0; |
1675 | unsigned char buffer[16]; |
1676 | |
1677 | getextcpuid(0,2, buffer); // call CPUID with EAX function 2H to obtain cache descriptor values |
1678 | |
1679 | for (int i = buffer[0]; --i >= 0; ) |
1680 | { |
1681 | int j; |
1682 | for (j = 3; j < 16; j += 4) |
1683 | { |
1684 | // if the information in a register is marked invalid, set to null descriptors |
1685 | if (buffer[j] & 0x80) |
1686 | { |
1687 | buffer[j-3] = 0; |
1688 | buffer[j-2] = 0; |
1689 | buffer[j-1] = 0; |
1690 | buffer[j-0] = 0; |
1691 | } |
1692 | } |
1693 | |
1694 | for (j = 1; j < 16; j++) |
1695 | { |
1696 | switch (buffer[j]) // need to add descriptor values for 8M and 12M when they become known |
1697 | { |
1698 | case 0x41: |
1699 | case 0x79: |
1700 | size = 128*1024; |
1701 | break; |
1702 | |
1703 | case 0x42: |
1704 | case 0x7A: |
1705 | case 0x82: |
1706 | size = 256*1024; |
1707 | break; |
1708 | |
1709 | case 0x22: |
1710 | case 0x43: |
1711 | case 0x7B: |
1712 | case 0x83: |
1713 | case 0x86: |
1714 | size = 512*1024; |
1715 | break; |
1716 | |
1717 | case 0x23: |
1718 | case 0x44: |
1719 | case 0x7C: |
1720 | case 0x84: |
1721 | case 0x87: |
1722 | size = 1024*1024; |
1723 | break; |
1724 | |
1725 | case 0x25: |
1726 | case 0x45: |
1727 | case 0x85: |
1728 | size = 2*1024*1024; |
1729 | break; |
1730 | |
1731 | case 0x29: |
1732 | size = 4*1024*1024; |
1733 | break; |
1734 | } |
1735 | if (maxSize < size) |
1736 | maxSize = size; |
1737 | } |
1738 | |
1739 | if (i > 0) |
1740 | getextcpuid(0,2, buffer); |
1741 | } |
1742 | return maxSize; |
1743 | } |
1744 | |
1745 | |
1746 | |
1747 | #define NUM_LOGICAL_BITS 0x00FF0000 // EBX[23:16] Bit 16-23 in ebx contains the number of logical |
1748 | // processors per physical processor (using cpuid function 1) |
1749 | #define INITIAL_APIC_ID_BITS 0xFF000000 // EBX[31:24] Bits 24-31 (8 bits) return the 8-bit unique |
1750 | // initial APIC ID for the processor this code is running on. |
1751 | // Default value = 0xff if HT is not supported |
1752 | |
1753 | // This function uses CPUID function 1 to return the number of logical processors on a given physical chip. |
1754 | // It returns the number of logicals processors on a physical chip. |
1755 | |
1756 | DWORD GetLogicalCpuCountFallback() |
1757 | { |
1758 | BYTE LogicalNum = 0; |
1759 | BYTE PhysicalNum = 0; |
1760 | DWORD lProcCounter = 0; |
1761 | unsigned char buffer[16]; |
1762 | |
1763 | DWORD* dwBuffer = (DWORD*)buffer; |
1764 | DWORD retVal = 1; |
1765 | |
1766 | getextcpuid(0,1, buffer); //call CPUID with EAX=1 |
1767 | |
1768 | if (dwBuffer[3] & (1<<28)) // edx:bit 28 is HT bit |
1769 | { |
1770 | PhysicalNum = (BYTE) g_SystemInfo.dwNumberOfProcessors ; // total # of processors |
1771 | LogicalNum = (BYTE) ((dwBuffer[1] & NUM_LOGICAL_BITS) >> 16); // # of logical per physical |
1772 | |
1773 | if(LogicalNum > 1) |
1774 | { |
1775 | #ifdef FEATURE_CORESYSTEM |
1776 | // CoreSystem doesn't expose GetProcessAffinityMask or SetProcessAffinityMask or anything |
1777 | // functionally equivalent. Just assume 1:1 mapping if we get here (in reality we shouldn't since |
1778 | // all CoreSystems support GetLogicalProcessorInformation so GetLogicalCpuCountFromOS should have |
1779 | // taken care of everything. |
1780 | goto fDone; |
1781 | #else // FEATURE_CORESYSTEM |
1782 | HANDLE hCurrentProcessHandle; |
1783 | DWORD_PTR dwProcessAffinity; |
1784 | DWORD_PTR dwSystemAffinity; |
1785 | DWORD_PTR dwAffinityMask; |
1786 | |
1787 | // Calculate the appropriate shifts and mask based on the |
1788 | // number of logical processors. |
1789 | |
1790 | BYTE i = 1, PHY_ID_MASK = 0xFF, PHY_ID_SHIFT = 0; |
1791 | while (i < LogicalNum) |
1792 | { |
1793 | i *= 2; |
1794 | PHY_ID_MASK <<= 1; |
1795 | PHY_ID_SHIFT++; |
1796 | } |
1797 | hCurrentProcessHandle = GetCurrentProcess(); |
1798 | |
1799 | GetProcessAffinityMask(hCurrentProcessHandle, &dwProcessAffinity, &dwSystemAffinity); |
1800 | |
1801 | // Check if available process affinity mask is equal to the available system affinity mask |
1802 | // If the masks are equal, then all the processors the OS utilizes are available to the |
1803 | // application. |
1804 | |
1805 | if (dwProcessAffinity != dwSystemAffinity) |
1806 | { |
1807 | retVal = 0; |
1808 | goto fDone; |
1809 | } |
1810 | |
1811 | dwAffinityMask = 1; |
1812 | |
1813 | // loop over all processors, running APIC ID retrieval code starting |
1814 | // with the first one by setting process affinity. |
1815 | while (dwAffinityMask != 0 && dwAffinityMask <= dwProcessAffinity) |
1816 | { |
1817 | // Check if this CPU is available |
1818 | if (dwAffinityMask & dwProcessAffinity) |
1819 | { |
1820 | if (SetProcessAffinityMask(hCurrentProcessHandle, dwAffinityMask)) |
1821 | { |
1822 | BYTE APIC_ID, LOG_ID, PHY_ID; |
1823 | __SwitchToThread(0, CALLER_LIMITS_SPINNING); // Give OS time to switch CPU |
1824 | |
1825 | getextcpuid(0,1, buffer); //call cpuid with EAX=1 |
1826 | |
1827 | APIC_ID = (dwBuffer[1] & INITIAL_APIC_ID_BITS) >> 24; |
1828 | LOG_ID = APIC_ID & ~PHY_ID_MASK; |
1829 | PHY_ID = APIC_ID >> PHY_ID_SHIFT; |
1830 | if (LOG_ID != 0) |
1831 | lProcCounter++; |
1832 | } |
1833 | } |
1834 | dwAffinityMask = dwAffinityMask << 1; |
1835 | } |
1836 | // Reset the processor affinity |
1837 | |
1838 | SetProcessAffinityMask(hCurrentProcessHandle, dwProcessAffinity); |
1839 | |
1840 | // Check if HT is enabled on all the processors |
1841 | if(lProcCounter > 0 && (lProcCounter == (DWORD)(PhysicalNum / LogicalNum))) |
1842 | { |
1843 | retVal = lProcCounter; |
1844 | goto fDone; |
1845 | } |
1846 | #endif // FEATURE_CORESYSTEM |
1847 | } |
1848 | } |
1849 | fDone: |
1850 | |
1851 | return retVal; |
1852 | } |
1853 | |
1854 | #endif // _TARGET_X86_ || _TARGET_AMD64_ |
1855 | |
1856 | // fix this if/when AMD does multicore or SMT |
1857 | size_t GetCacheSizePerLogicalCpu(BOOL bTrueSize) |
1858 | { |
1859 | // No CONTRACT possible because GetCacheSizePerLogicalCpu uses SEH |
1860 | |
1861 | STATIC_CONTRACT_NOTHROW; |
1862 | STATIC_CONTRACT_GC_NOTRIGGER; |
1863 | |
1864 | static size_t maxSize; |
1865 | static size_t maxTrueSize; |
1866 | |
1867 | if (maxSize) |
1868 | { |
1869 | // maxSize and maxTrueSize cached |
1870 | if (bTrueSize) |
1871 | { |
1872 | return maxTrueSize; |
1873 | } |
1874 | else |
1875 | { |
1876 | return maxSize; |
1877 | } |
1878 | } |
1879 | |
1880 | #if defined(_TARGET_AMD64_) || defined (_TARGET_X86_) |
1881 | DefaultCatchFilterParam param; |
1882 | param.pv = COMPLUS_EXCEPTION_EXECUTE_HANDLER; |
1883 | |
1884 | PAL_TRY(DefaultCatchFilterParam *, pParam, ¶m) |
1885 | { |
1886 | unsigned char buffer[16]; |
1887 | DWORD* dwBuffer = (DWORD*)buffer; |
1888 | |
1889 | DWORD maxCpuId = getcpuid(0, buffer); |
1890 | |
1891 | if (dwBuffer[1] == 'uneG') |
1892 | { |
1893 | if (dwBuffer[3] == 'Ieni') |
1894 | { |
1895 | if (dwBuffer[2] == 'letn') |
1896 | { |
1897 | /* |
1898 | //The following lines are commented because the OS API on Windows 2003 SP1 is not returning the Cache Relation information on x86. |
1899 | //Once the OS API (LH and above) is updated with this information, we should start using the OS API to get the cache enumeration by |
1900 | //uncommenting the lines below. |
1901 | |
1902 | tempSize = GetLogicalProcessorCacheSizeFromOS(); //use OS API for cache enumeration on LH and above |
1903 | */ |
1904 | size_t tempSize = 0; |
1905 | if (maxCpuId >= 2) // cpuid support for cache size determination is available |
1906 | { |
1907 | tempSize = GetIntelDeterministicCacheEnum(); // try to use use deterministic cache size enumeration |
1908 | if (!tempSize) |
1909 | { // deterministic enumeration failed, fallback to legacy enumeration using descriptor values |
1910 | tempSize = GetIntelDescriptorValuesCache(); |
1911 | } |
1912 | } |
1913 | |
1914 | // TODO: Currently GetLogicalCpuCountFromOS() and GetLogicalCpuCountFallback() are broken on |
1915 | // multi-core processor, but we never call into those two functions since we don't halve the |
1916 | // gen0size when it's prescott and above processor. We keep the old version here for earlier |
1917 | // generation system(Northwood based), perf data suggests on those systems, halve gen0 size |
1918 | // still boost the performance(ex:Biztalk boosts about 17%). So on earlier systems(Northwood) |
1919 | // based, we still go ahead and halve gen0 size. The logic in GetLogicalCpuCountFromOS() |
1920 | // and GetLogicalCpuCountFallback() works fine for those earlier generation systems. |
1921 | // If it's a Prescott and above processor or Multi-core, perf data suggests not to halve gen0 |
1922 | // size at all gives us overall better performance. |
1923 | // This is going to be fixed with a new version in orcas time frame. |
1924 | if (maxCpuId >= 2 && !((maxCpuId > 3) && (maxCpuId < 0x80000000))) |
1925 | { |
1926 | DWORD logicalProcessorCount = GetLogicalCpuCountFromOS(); //try to obtain HT enumeration from OS API |
1927 | |
1928 | if (!logicalProcessorCount) |
1929 | { |
1930 | logicalProcessorCount = GetLogicalCpuCountFallback(); // OS API failed, Fallback to HT enumeration using CPUID |
1931 | } |
1932 | |
1933 | if (logicalProcessorCount) |
1934 | { |
1935 | tempSize = tempSize / logicalProcessorCount; |
1936 | } |
1937 | } |
1938 | |
1939 | // update maxSize once with final value |
1940 | maxTrueSize = tempSize; |
1941 | |
1942 | #ifdef _WIN64 |
1943 | if (maxCpuId >= 2) |
1944 | { |
1945 | // If we're running on a Prescott or greater core, EM64T tests |
1946 | // show that starting with a gen0 larger than LLC improves performance. |
1947 | // Thus, start with a gen0 size that is larger than the cache. The value of |
1948 | // 3 is a reasonable tradeoff between workingset and performance. |
1949 | maxSize = maxTrueSize * 3; |
1950 | } |
1951 | else |
1952 | #endif |
1953 | { |
1954 | maxSize = maxTrueSize; |
1955 | } |
1956 | } |
1957 | } |
1958 | } |
1959 | |
1960 | if (dwBuffer[1] == 'htuA') { |
1961 | if (dwBuffer[3] == 'itne') { |
1962 | if (dwBuffer[2] == 'DMAc') { |
1963 | |
1964 | if (getcpuid(0x80000000, buffer) >= 0x80000006) |
1965 | { |
1966 | getcpuid(0x80000006, buffer); |
1967 | |
1968 | DWORD dwL2CacheBits = dwBuffer[2]; |
1969 | DWORD dwL3CacheBits = dwBuffer[3]; |
1970 | |
1971 | maxTrueSize = (size_t)((dwL2CacheBits >> 16) * 1024); // L2 cache size in ECX bits 31-16 |
1972 | |
1973 | getcpuid(0x1, buffer); |
1974 | DWORD dwBaseFamily = (dwBuffer[0] & (0xF << 8)) >> 8; |
1975 | DWORD dwExtFamily = (dwBuffer[0] & (0xFF << 20)) >> 20; |
1976 | DWORD dwFamily = dwBaseFamily >= 0xF ? dwBaseFamily + dwExtFamily : dwBaseFamily; |
1977 | |
1978 | if (dwFamily >= 0x10) |
1979 | { |
1980 | BOOL bSkipAMDL3 = FALSE; |
1981 | |
1982 | if (dwFamily == 0x10) // are we running on a Barcelona (Family 10h) processor? |
1983 | { |
1984 | // check model |
1985 | DWORD dwBaseModel = (dwBuffer[0] & (0xF << 4)) >> 4 ; |
1986 | DWORD dwExtModel = (dwBuffer[0] & (0xF << 16)) >> 16; |
1987 | DWORD dwModel = dwBaseFamily >= 0xF ? (dwExtModel << 4) | dwBaseModel : dwBaseModel; |
1988 | |
1989 | switch (dwModel) |
1990 | { |
1991 | case 0x2: |
1992 | // 65nm parts do not benefit from larger Gen0 |
1993 | bSkipAMDL3 = TRUE; |
1994 | break; |
1995 | |
1996 | case 0x4: |
1997 | default: |
1998 | bSkipAMDL3 = FALSE; |
1999 | } |
2000 | } |
2001 | |
2002 | if (!bSkipAMDL3) |
2003 | { |
2004 | // 45nm Greyhound parts (and future parts based on newer northbridge) benefit |
2005 | // from increased gen0 size, taking L3 into account |
2006 | getcpuid(0x80000008, buffer); |
2007 | DWORD dwNumberOfCores = (dwBuffer[2] & (0xFF)) + 1; // NC is in ECX bits 7-0 |
2008 | |
2009 | DWORD dwL3CacheSize = (size_t)((dwL3CacheBits >> 18) * 512 * 1024); // L3 size in EDX bits 31-18 * 512KB |
2010 | // L3 is shared between cores |
2011 | dwL3CacheSize = dwL3CacheSize / dwNumberOfCores; |
2012 | maxTrueSize += dwL3CacheSize; // due to exclusive caches, add L3 size (possibly zero) to L2 |
2013 | // L1 is too small to worry about, so ignore it |
2014 | } |
2015 | } |
2016 | |
2017 | |
2018 | maxSize = maxTrueSize; |
2019 | } |
2020 | } |
2021 | } |
2022 | } |
2023 | } |
2024 | PAL_EXCEPT_FILTER(DefaultCatchFilter) |
2025 | { |
2026 | } |
2027 | PAL_ENDTRY |
2028 | #else |
2029 | maxSize = maxTrueSize = GetLogicalProcessorCacheSizeFromOS() ; // Returns the size of the highest level processor cache |
2030 | #endif |
2031 | |
2032 | #if defined(_TARGET_ARM64_) |
2033 | // Bigger gen0 size helps arm64 targets |
2034 | maxSize = maxTrueSize * 3; |
2035 | #endif |
2036 | |
2037 | // printf("GetCacheSizePerLogicalCpu returns %d, adjusted size %d\n", maxSize, maxTrueSize); |
2038 | if (bTrueSize) |
2039 | return maxTrueSize; |
2040 | else |
2041 | return maxSize; |
2042 | } |
2043 | |
2044 | //--------------------------------------------------------------------- |
2045 | |
2046 | #ifndef FEATURE_PAL |
2047 | ThreadLocaleHolder::~ThreadLocaleHolder() |
2048 | { |
2049 | SetThreadLocale(m_locale); |
2050 | } |
2051 | |
2052 | HMODULE CLRGetModuleHandle(LPCWSTR lpModuleFileName) |
2053 | { |
2054 | // Don't use dynamic contract: will override GetLastError value |
2055 | STATIC_CONTRACT_NOTHROW; |
2056 | STATIC_CONTRACT_GC_NOTRIGGER; |
2057 | STATIC_CONTRACT_FORBID_FAULT; |
2058 | STATIC_CONTRACT_SO_TOLERANT; |
2059 | |
2060 | HMODULE hMod = WszGetModuleHandle(lpModuleFileName); |
2061 | return hMod; |
2062 | } |
2063 | |
2064 | |
2065 | HMODULE CLRGetCurrentModuleHandle() |
2066 | { |
2067 | // Don't use dynamic contract: will override GetLastError value |
2068 | STATIC_CONTRACT_NOTHROW; |
2069 | STATIC_CONTRACT_GC_NOTRIGGER; |
2070 | STATIC_CONTRACT_FORBID_FAULT; |
2071 | STATIC_CONTRACT_SO_TOLERANT; |
2072 | |
2073 | HMODULE hMod = WszGetModuleHandle(NULL); |
2074 | return hMod; |
2075 | } |
2076 | |
2077 | |
2078 | #endif // !FEATURE_PAL |
2079 | |
2080 | LPVOID EEHeapAllocInProcessHeap(DWORD dwFlags, SIZE_T dwBytes); |
2081 | BOOL EEHeapFreeInProcessHeap(DWORD dwFlags, LPVOID lpMem); |
2082 | void ShutdownRuntimeWithoutExiting(int exitCode); |
2083 | BOOL IsRuntimeStarted(DWORD *pdwStartupFlags); |
2084 | |
2085 | void *GetCLRFunction(LPCSTR FunctionName) |
2086 | { |
2087 | |
2088 | void* func = NULL; |
2089 | BEGIN_ENTRYPOINT_VOIDRET; |
2090 | |
2091 | LIMITED_METHOD_CONTRACT; |
2092 | |
2093 | if (strcmp(FunctionName, "EEHeapAllocInProcessHeap" ) == 0) |
2094 | { |
2095 | func = (void*)EEHeapAllocInProcessHeap; |
2096 | } |
2097 | else if (strcmp(FunctionName, "EEHeapFreeInProcessHeap" ) == 0) |
2098 | { |
2099 | func = (void*)EEHeapFreeInProcessHeap; |
2100 | } |
2101 | else if (strcmp(FunctionName, "ShutdownRuntimeWithoutExiting" ) == 0) |
2102 | { |
2103 | func = (void*)ShutdownRuntimeWithoutExiting; |
2104 | } |
2105 | else if (strcmp(FunctionName, "IsRuntimeStarted" ) == 0) |
2106 | { |
2107 | func = (void*)IsRuntimeStarted; |
2108 | } |
2109 | else { |
2110 | _ASSERTE ("Unknown function name" ); |
2111 | func = NULL; |
2112 | } |
2113 | END_ENTRYPOINT_VOIDRET; |
2114 | |
2115 | return func; |
2116 | } |
2117 | |
2118 | #endif // CROSSGEN_COMPILE |
2119 | |
2120 | LPVOID |
2121 | CLRMapViewOfFileEx( |
2122 | IN HANDLE hFileMappingObject, |
2123 | IN DWORD dwDesiredAccess, |
2124 | IN DWORD dwFileOffsetHigh, |
2125 | IN DWORD dwFileOffsetLow, |
2126 | IN SIZE_T dwNumberOfBytesToMap, |
2127 | IN LPVOID lpBaseAddress |
2128 | ) |
2129 | { |
2130 | #ifdef _DEBUG |
2131 | #ifdef _TARGET_X86_ |
2132 | |
2133 | char *tmp = new (nothrow) char; |
2134 | if (!tmp) |
2135 | { |
2136 | SetLastError(ERROR_OUTOFMEMORY); |
2137 | return NULL; |
2138 | } |
2139 | delete tmp; |
2140 | |
2141 | #endif // _TARGET_X86_ |
2142 | #endif // _DEBUG |
2143 | |
2144 | LPVOID pv = MapViewOfFileEx(hFileMappingObject,dwDesiredAccess,dwFileOffsetHigh,dwFileOffsetLow,dwNumberOfBytesToMap,lpBaseAddress); |
2145 | |
2146 | |
2147 | if (!pv) |
2148 | { |
2149 | if(GetLastError()==ERROR_SUCCESS) |
2150 | SetLastError(ERROR_OUTOFMEMORY); |
2151 | return NULL; |
2152 | } |
2153 | |
2154 | #ifdef _DEBUG |
2155 | #ifdef _TARGET_X86_ |
2156 | if (pv && g_pConfig && g_pConfig->ShouldInjectFault(INJECTFAULT_MAPVIEWOFFILE)) |
2157 | { |
2158 | MEMORY_BASIC_INFORMATION mbi; |
2159 | memset(&mbi, 0, sizeof(mbi)); |
2160 | if (!ClrVirtualQuery(pv, &mbi, sizeof(mbi))) |
2161 | { |
2162 | if(GetLastError()==ERROR_SUCCESS) |
2163 | SetLastError(ERROR_OUTOFMEMORY); |
2164 | return NULL; |
2165 | } |
2166 | UnmapViewOfFile(pv); |
2167 | pv = ClrVirtualAlloc(lpBaseAddress, mbi.RegionSize, MEM_RESERVE, PAGE_NOACCESS); |
2168 | } |
2169 | else |
2170 | #endif // _TARGET_X86_ |
2171 | #endif // _DEBUG |
2172 | { |
2173 | } |
2174 | |
2175 | if (!pv && GetLastError()==ERROR_SUCCESS) |
2176 | SetLastError(ERROR_OUTOFMEMORY); |
2177 | |
2178 | return pv; |
2179 | } |
2180 | |
2181 | LPVOID |
2182 | CLRMapViewOfFile( |
2183 | IN HANDLE hFileMappingObject, |
2184 | IN DWORD dwDesiredAccess, |
2185 | IN DWORD dwFileOffsetHigh, |
2186 | IN DWORD dwFileOffsetLow, |
2187 | IN SIZE_T dwNumberOfBytesToMap |
2188 | ) |
2189 | { |
2190 | WRAPPER_NO_CONTRACT; |
2191 | return CLRMapViewOfFileEx(hFileMappingObject,dwDesiredAccess,dwFileOffsetHigh,dwFileOffsetLow,dwNumberOfBytesToMap,NULL); |
2192 | } |
2193 | |
2194 | |
2195 | BOOL |
2196 | CLRUnmapViewOfFile( |
2197 | IN LPVOID lpBaseAddress |
2198 | ) |
2199 | { |
2200 | STATIC_CONTRACT_ENTRY_POINT; |
2201 | |
2202 | #ifdef _DEBUG |
2203 | #ifdef _TARGET_X86_ |
2204 | if (g_pConfig && g_pConfig->ShouldInjectFault(INJECTFAULT_MAPVIEWOFFILE)) |
2205 | { |
2206 | return ClrVirtualFree((LPVOID)lpBaseAddress, 0, MEM_RELEASE); |
2207 | } |
2208 | else |
2209 | #endif // _TARGET_X86_ |
2210 | #endif // _DEBUG |
2211 | { |
2212 | BOOL result = UnmapViewOfFile(lpBaseAddress); |
2213 | if (result) |
2214 | { |
2215 | } |
2216 | return result; |
2217 | } |
2218 | } |
2219 | |
2220 | |
2221 | #ifndef CROSSGEN_COMPILE |
2222 | |
2223 | static HMODULE CLRLoadLibraryWorker(LPCWSTR lpLibFileName, DWORD *pLastError) |
2224 | { |
2225 | // Don't use dynamic contract: will override GetLastError value |
2226 | STATIC_CONTRACT_NOTHROW; |
2227 | STATIC_CONTRACT_GC_TRIGGERS; |
2228 | STATIC_CONTRACT_FAULT; |
2229 | STATIC_CONTRACT_SO_TOLERANT; |
2230 | |
2231 | HMODULE hMod; |
2232 | UINT last = SetErrorMode(SEM_NOOPENFILEERRORBOX|SEM_FAILCRITICALERRORS); |
2233 | { |
2234 | INDEBUG(PEDecoder::ForceRelocForDLL(lpLibFileName)); |
2235 | hMod = WszLoadLibrary(lpLibFileName); |
2236 | *pLastError = GetLastError(); |
2237 | } |
2238 | SetErrorMode(last); |
2239 | return hMod; |
2240 | } |
2241 | |
2242 | HMODULE CLRLoadLibrary(LPCWSTR lpLibFileName) |
2243 | { |
2244 | // Don't use dynamic contract: will override GetLastError value |
2245 | STATIC_CONTRACT_NOTHROW; |
2246 | STATIC_CONTRACT_GC_TRIGGERS; |
2247 | STATIC_CONTRACT_FAULT; |
2248 | |
2249 | DWORD dwLastError = 0; |
2250 | HMODULE hmod = 0; |
2251 | |
2252 | // This method should be marked "throws" due to the probe here. |
2253 | STATIC_CONTRACT_VIOLATION(ThrowsViolation); |
2254 | |
2255 | BEGIN_SO_TOLERANT_CODE(GetThread()); |
2256 | hmod = CLRLoadLibraryWorker(lpLibFileName, &dwLastError); |
2257 | END_SO_TOLERANT_CODE; |
2258 | |
2259 | SetLastError(dwLastError); |
2260 | return hmod; |
2261 | } |
2262 | |
2263 | #ifndef FEATURE_PAL |
2264 | |
2265 | static HMODULE CLRLoadLibraryExWorker(LPCWSTR lpLibFileName, HANDLE hFile, DWORD dwFlags, DWORD *pLastError) |
2266 | |
2267 | { |
2268 | // Don't use dynamic contract: will override GetLastError value |
2269 | STATIC_CONTRACT_NOTHROW; |
2270 | STATIC_CONTRACT_GC_TRIGGERS; |
2271 | STATIC_CONTRACT_FAULT; |
2272 | STATIC_CONTRACT_SO_TOLERANT; |
2273 | |
2274 | HMODULE hMod; |
2275 | UINT last = SetErrorMode(SEM_NOOPENFILEERRORBOX|SEM_FAILCRITICALERRORS); |
2276 | { |
2277 | INDEBUG(PEDecoder::ForceRelocForDLL(lpLibFileName)); |
2278 | hMod = WszLoadLibraryEx(lpLibFileName, hFile, dwFlags); |
2279 | *pLastError = GetLastError(); |
2280 | } |
2281 | SetErrorMode(last); |
2282 | return hMod; |
2283 | } |
2284 | |
2285 | HMODULE CLRLoadLibraryEx(LPCWSTR lpLibFileName, HANDLE hFile, DWORD dwFlags) |
2286 | { |
2287 | // Don't use dynamic contract: will override GetLastError value |
2288 | |
2289 | // This will throw in the case of SO |
2290 | //STATIC_CONTRACT_NOTHROW; |
2291 | STATIC_CONTRACT_GC_TRIGGERS; |
2292 | STATIC_CONTRACT_FAULT; |
2293 | |
2294 | DWORD lastError = ERROR_SUCCESS; |
2295 | HMODULE hmod = NULL; |
2296 | |
2297 | BEGIN_SO_TOLERANT_CODE(GetThread()); |
2298 | hmod = CLRLoadLibraryExWorker(lpLibFileName, hFile, dwFlags, &lastError); |
2299 | END_SO_TOLERANT_CODE; |
2300 | |
2301 | SetLastError(lastError); |
2302 | return hmod; |
2303 | } |
2304 | |
2305 | #endif // !FEATURE_PAL |
2306 | |
2307 | BOOL CLRFreeLibrary(HMODULE hModule) |
2308 | { |
2309 | // Don't use dynamic contract: will override GetLastError value |
2310 | STATIC_CONTRACT_NOTHROW; |
2311 | STATIC_CONTRACT_GC_TRIGGERS; |
2312 | STATIC_CONTRACT_FORBID_FAULT; |
2313 | STATIC_CONTRACT_SO_TOLERANT; |
2314 | |
2315 | return FreeLibrary(hModule); |
2316 | } |
2317 | |
2318 | VOID CLRFreeLibraryAndExitThread(HMODULE hModule,DWORD dwExitCode) |
2319 | { |
2320 | // Don't use dynamic contract: will override GetLastError value |
2321 | STATIC_CONTRACT_NOTHROW; |
2322 | STATIC_CONTRACT_GC_TRIGGERS; |
2323 | STATIC_CONTRACT_FORBID_FAULT; |
2324 | STATIC_CONTRACT_SO_TOLERANT; |
2325 | |
2326 | // This is no-return |
2327 | FreeLibraryAndExitThread(hModule,dwExitCode); |
2328 | } |
2329 | |
2330 | #endif // CROSSGEN_COMPILE |
2331 | |
2332 | #endif // #ifndef DACCESS_COMPILE |
2333 | |
2334 | GPTR_IMPL(JITNotification, g_pNotificationTable); |
2335 | GVAL_IMPL(ULONG32, g_dacNotificationFlags); |
2336 | |
2337 | BOOL IsValidMethodCodeNotification(USHORT Notification) |
2338 | { |
2339 | // If any bit is on other than that given by a valid combination of flags, no good. |
2340 | if (Notification & ~( |
2341 | CLRDATA_METHNOTIFY_NONE | |
2342 | CLRDATA_METHNOTIFY_GENERATED | |
2343 | CLRDATA_METHNOTIFY_DISCARDED)) |
2344 | { |
2345 | return FALSE; |
2346 | } |
2347 | |
2348 | return TRUE; |
2349 | } |
2350 | |
2351 | JITNotifications::JITNotifications(JITNotification *jitTable) |
2352 | { |
2353 | LIMITED_METHOD_CONTRACT; |
2354 | if (jitTable) |
2355 | { |
2356 | // Bookkeeping info is held in the first slot |
2357 | m_jitTable = jitTable + 1; |
2358 | } |
2359 | else |
2360 | { |
2361 | m_jitTable = NULL; |
2362 | } |
2363 | } |
2364 | |
2365 | BOOL JITNotifications::FindItem(TADDR clrModule, mdToken token, UINT *indexOut) |
2366 | { |
2367 | LIMITED_METHOD_CONTRACT; |
2368 | if (m_jitTable == NULL) |
2369 | { |
2370 | return FALSE; |
2371 | } |
2372 | |
2373 | if (indexOut == NULL) |
2374 | { |
2375 | return FALSE; |
2376 | } |
2377 | |
2378 | UINT Length = GetLength(); |
2379 | for(UINT i=0; i < Length; i++) |
2380 | { |
2381 | JITNotification *pCurrent = m_jitTable + i; |
2382 | if (!pCurrent->IsFree() && |
2383 | pCurrent->clrModule == clrModule && |
2384 | pCurrent->methodToken == token) |
2385 | { |
2386 | *indexOut = i; |
2387 | return TRUE; |
2388 | } |
2389 | } |
2390 | |
2391 | return FALSE; |
2392 | } |
2393 | |
2394 | // if clrModule is NULL, all active notifications are changed to NType |
2395 | BOOL JITNotifications::SetAllNotifications(TADDR clrModule,USHORT NType,BOOL *changedOut) |
2396 | { |
2397 | if (m_jitTable == NULL) |
2398 | { |
2399 | return FALSE; |
2400 | } |
2401 | |
2402 | if (changedOut == NULL) |
2403 | { |
2404 | return FALSE; |
2405 | } |
2406 | |
2407 | *changedOut = FALSE; |
2408 | |
2409 | UINT Length = GetLength(); |
2410 | for(UINT i=0; i < Length; i++) |
2411 | { |
2412 | JITNotification *pCurrent = m_jitTable + i; |
2413 | if (!pCurrent->IsFree() && |
2414 | ((clrModule == NULL) || (pCurrent->clrModule == clrModule))&& |
2415 | pCurrent->state != NType) |
2416 | { |
2417 | pCurrent->state = NType; |
2418 | *changedOut = TRUE; |
2419 | } |
2420 | } |
2421 | |
2422 | if (*changedOut && NType == CLRDATA_METHNOTIFY_NONE) |
2423 | { |
2424 | // Need to recompute length if we removed notifications |
2425 | for (UINT iCurrent=Length; iCurrent > 0; iCurrent--) |
2426 | { |
2427 | JITNotification *pCurrent = m_jitTable + (iCurrent - 1); |
2428 | if (pCurrent->IsFree()) |
2429 | { |
2430 | DecrementLength(); |
2431 | } |
2432 | } |
2433 | } |
2434 | return TRUE; |
2435 | } |
2436 | |
2437 | BOOL JITNotifications::SetNotification(TADDR clrModule, mdToken token, USHORT NType) |
2438 | { |
2439 | UINT iIndex; |
2440 | |
2441 | if (!IsActive()) |
2442 | { |
2443 | return FALSE; |
2444 | } |
2445 | |
2446 | if (clrModule == NULL) |
2447 | { |
2448 | return FALSE; |
2449 | } |
2450 | |
2451 | if (NType == CLRDATA_METHNOTIFY_NONE) |
2452 | { |
2453 | // Remove an item if it exists |
2454 | if (FindItem(clrModule, token, &iIndex)) |
2455 | { |
2456 | JITNotification *pItem = m_jitTable + iIndex; |
2457 | pItem->SetFree(); |
2458 | _ASSERTE(iIndex < GetLength()); |
2459 | // Update highest? |
2460 | if (iIndex == (GetLength()-1)) |
2461 | { |
2462 | DecrementLength(); |
2463 | } |
2464 | } |
2465 | return TRUE; |
2466 | } |
2467 | |
2468 | if (FindItem(clrModule, token, &iIndex)) |
2469 | { |
2470 | JITNotification *pItem = m_jitTable + iIndex; |
2471 | _ASSERTE(pItem->IsFree() == FALSE); |
2472 | pItem->state = NType; |
2473 | return TRUE; |
2474 | } |
2475 | |
2476 | // Find first free item |
2477 | UINT iFirstFree = GetLength(); |
2478 | for (UINT i = 0; i < iFirstFree; i++) |
2479 | { |
2480 | JITNotification *pCurrent = m_jitTable + i; |
2481 | if (pCurrent->state == CLRDATA_METHNOTIFY_NONE) |
2482 | { |
2483 | iFirstFree = i; |
2484 | break; |
2485 | } |
2486 | } |
2487 | |
2488 | if (iFirstFree == GetLength() && |
2489 | iFirstFree == GetTableSize()) |
2490 | { |
2491 | // No more room |
2492 | return FALSE; |
2493 | } |
2494 | |
2495 | JITNotification *pCurrent = m_jitTable + iFirstFree; |
2496 | pCurrent->SetState(clrModule, token, NType); |
2497 | if (iFirstFree == GetLength()) |
2498 | { |
2499 | IncrementLength(); |
2500 | } |
2501 | |
2502 | return TRUE; |
2503 | } |
2504 | |
2505 | UINT JITNotifications::GetLength() |
2506 | { |
2507 | LIMITED_METHOD_CONTRACT; |
2508 | _ASSERTE(IsActive()); |
2509 | |
2510 | if (!IsActive()) |
2511 | { |
2512 | return 0; |
2513 | } |
2514 | |
2515 | return (UINT) (m_jitTable - 1)->methodToken; |
2516 | } |
2517 | |
2518 | void JITNotifications::IncrementLength() |
2519 | { |
2520 | _ASSERTE(IsActive()); |
2521 | |
2522 | if (!IsActive()) |
2523 | { |
2524 | return; |
2525 | } |
2526 | |
2527 | UINT *pShort = (UINT *) &((m_jitTable - 1)->methodToken); |
2528 | (*pShort)++; |
2529 | } |
2530 | |
2531 | void JITNotifications::DecrementLength() |
2532 | { |
2533 | _ASSERTE(IsActive()); |
2534 | |
2535 | if (!IsActive()) |
2536 | { |
2537 | return; |
2538 | } |
2539 | |
2540 | UINT *pShort = (UINT *) &((m_jitTable - 1)->methodToken); |
2541 | (*pShort)--; |
2542 | } |
2543 | |
2544 | UINT JITNotifications::GetTableSize() |
2545 | { |
2546 | _ASSERTE(IsActive()); |
2547 | |
2548 | if (!IsActive()) |
2549 | { |
2550 | return 0; |
2551 | } |
2552 | |
2553 | return ((UINT) (m_jitTable - 1)->clrModule); |
2554 | } |
2555 | |
2556 | USHORT JITNotifications::Requested(TADDR clrModule, mdToken token) |
2557 | { |
2558 | LIMITED_METHOD_CONTRACT; |
2559 | UINT iIndex; |
2560 | if (FindItem(clrModule, token, &iIndex)) |
2561 | { |
2562 | JITNotification *pItem = m_jitTable + iIndex; |
2563 | _ASSERTE(pItem->IsFree() == FALSE); |
2564 | return pItem->state; |
2565 | } |
2566 | |
2567 | return CLRDATA_METHNOTIFY_NONE; |
2568 | } |
2569 | |
2570 | #ifdef DACCESS_COMPILE |
2571 | |
2572 | JITNotification *JITNotifications::InitializeNotificationTable(UINT TableSize) |
2573 | { |
2574 | // We use the first entry in the table for recordkeeping info. |
2575 | |
2576 | JITNotification *retTable = new (nothrow) JITNotification[TableSize+1]; |
2577 | if (retTable) |
2578 | { |
2579 | // Set the length |
2580 | UINT *pUint = (UINT *) &(retTable->methodToken); |
2581 | *pUint = 0; |
2582 | // Set the table size |
2583 | pUint = (UINT *) &(retTable->clrModule); |
2584 | *pUint = TableSize; |
2585 | } |
2586 | return retTable; |
2587 | } |
2588 | |
2589 | template <class NotificationClass> |
2590 | BOOL UpdateOutOfProcTable(__GlobalPtr<NotificationClass*, DPTR(NotificationClass)> pHostTable, NotificationClass* copyFrom, UINT tableSize) |
2591 | { |
2592 | |
2593 | ClrSafeInt<ULONG32> allocSize = S_SIZE_T(sizeof(NotificationClass)) * ClrSafeInt<UINT>(tableSize); |
2594 | if (allocSize.IsOverflow()) |
2595 | { |
2596 | return FALSE; |
2597 | } |
2598 | |
2599 | if (dac_cast<TADDR>(pHostTable) == NULL) |
2600 | { |
2601 | // The table has not been initialized in the target. Allocate space for it and update the pointer |
2602 | // in the target so that we'll use this allocated memory from now on. Note that we never free this |
2603 | // memory, but this isn't a big deal because it's only a single allocation. |
2604 | TADDR Location; |
2605 | |
2606 | if (DacAllocVirtual(0, allocSize.Value(), |
2607 | MEM_COMMIT, PAGE_READWRITE, false, |
2608 | &Location) != S_OK) |
2609 | { |
2610 | return FALSE; |
2611 | } |
2612 | |
2613 | DPTR(DPTR(NotificationClass)) ppTable = &pHostTable; |
2614 | *ppTable = DPTR(NotificationClass)(Location); |
2615 | if (DacWriteHostInstance(ppTable,false) != S_OK) |
2616 | { |
2617 | return FALSE; |
2618 | } |
2619 | } |
2620 | |
2621 | // We store recordkeeping info right before the m_jitTable pointer, that must be written as well. |
2622 | if (DacWriteAll(dac_cast<TADDR>(pHostTable), copyFrom, |
2623 | allocSize.Value(), false) != S_OK) |
2624 | { |
2625 | return FALSE; |
2626 | } |
2627 | |
2628 | return TRUE; |
2629 | } |
2630 | |
2631 | BOOL JITNotifications::UpdateOutOfProcTable() |
2632 | { |
2633 | return ::UpdateOutOfProcTable<JITNotification>(g_pNotificationTable, m_jitTable - 1, GetTableSize() + 1); |
2634 | } |
2635 | #endif // DACCESS_COMPILE |
2636 | |
2637 | GPTR_IMPL(GcNotification, g_pGcNotificationTable); |
2638 | |
2639 | GcNotifications::GcNotifications(GcNotification *gcTable) |
2640 | { |
2641 | LIMITED_METHOD_CONTRACT; |
2642 | if (gcTable) |
2643 | { |
2644 | // Bookkeeping info is held in the first slot |
2645 | m_gcTable = gcTable + 1; |
2646 | } |
2647 | else |
2648 | { |
2649 | m_gcTable = NULL; |
2650 | } |
2651 | } |
2652 | |
2653 | BOOL GcNotifications::FindItem(GcEvtArgs ev_, UINT *indexOut) |
2654 | { |
2655 | LIMITED_METHOD_CONTRACT; |
2656 | if (m_gcTable == NULL) |
2657 | { |
2658 | return FALSE; |
2659 | } |
2660 | |
2661 | if (indexOut == NULL) |
2662 | { |
2663 | return FALSE; |
2664 | } |
2665 | |
2666 | UINT length = Length(); |
2667 | for (UINT i = 0; i < length; i++) |
2668 | { |
2669 | if (m_gcTable[i].IsMatch(ev_)) |
2670 | { |
2671 | *indexOut = i; |
2672 | return TRUE; |
2673 | } |
2674 | } |
2675 | |
2676 | return FALSE; |
2677 | } |
2678 | |
2679 | |
2680 | BOOL GcNotifications::SetNotification(GcEvtArgs ev) |
2681 | { |
2682 | if (!IsActive()) |
2683 | { |
2684 | return FALSE; |
2685 | } |
2686 | |
2687 | if (ev.typ < 0 || ev.typ >= GC_EVENT_TYPE_MAX) |
2688 | { |
2689 | return FALSE; |
2690 | } |
2691 | |
2692 | // build the "match" event |
2693 | GcEvtArgs evStar = { ev.typ }; |
2694 | switch (ev.typ) |
2695 | { |
2696 | case GC_MARK_END: |
2697 | // specify mark event matching all generations |
2698 | evStar.condemnedGeneration = -1; |
2699 | break; |
2700 | default: |
2701 | break; |
2702 | } |
2703 | |
2704 | // look for the entry that matches the evStar argument |
2705 | UINT idx; |
2706 | if (!FindItem(evStar, &idx)) |
2707 | { |
2708 | // Find first free item |
2709 | UINT iFirstFree = Length(); |
2710 | for (UINT i = 0; i < iFirstFree; i++) |
2711 | { |
2712 | GcNotification *pCurrent = m_gcTable + i; |
2713 | if (pCurrent->IsFree()) |
2714 | { |
2715 | iFirstFree = i; |
2716 | break; |
2717 | } |
2718 | } |
2719 | |
2720 | if (iFirstFree == Length() && |
2721 | iFirstFree == GetTableSize()) |
2722 | { |
2723 | // No more room |
2724 | return FALSE; |
2725 | } |
2726 | |
2727 | // guarantee the free cell is zeroed out |
2728 | m_gcTable[iFirstFree].SetFree(); |
2729 | idx = iFirstFree; |
2730 | } |
2731 | |
2732 | // Now update the state |
2733 | m_gcTable[idx].ev.typ = ev.typ; |
2734 | switch (ev.typ) |
2735 | { |
2736 | case GC_MARK_END: |
2737 | if (ev.condemnedGeneration == 0) |
2738 | { |
2739 | m_gcTable[idx].SetFree(); |
2740 | } |
2741 | else |
2742 | { |
2743 | m_gcTable[idx].ev.condemnedGeneration |= ev.condemnedGeneration; |
2744 | } |
2745 | break; |
2746 | default: |
2747 | break; |
2748 | } |
2749 | |
2750 | // and if needed, update the array's length |
2751 | if (idx == Length()) |
2752 | { |
2753 | IncrementLength(); |
2754 | } |
2755 | |
2756 | return TRUE; |
2757 | } |
2758 | |
2759 | GARY_IMPL(size_t, g_clrNotificationArguments, MAX_CLR_NOTIFICATION_ARGS); |
2760 | |
2761 | #ifdef DACCESS_COMPILE |
2762 | |
2763 | GcNotification *GcNotifications::InitializeNotificationTable(UINT TableSize) |
2764 | { |
2765 | // We use the first entry in the table for recordkeeping info. |
2766 | |
2767 | GcNotification *retTable = new (nothrow) GcNotification[TableSize+1]; |
2768 | if (retTable) |
2769 | { |
2770 | // Set the length |
2771 | UINT *pUint = (UINT *) &(retTable[0].ev.typ); |
2772 | *pUint = 0; |
2773 | // Set the table size |
2774 | ++pUint; |
2775 | *pUint = TableSize; |
2776 | } |
2777 | return retTable; |
2778 | } |
2779 | |
2780 | BOOL GcNotifications::UpdateOutOfProcTable() |
2781 | { |
2782 | return ::UpdateOutOfProcTable<GcNotification>(g_pGcNotificationTable, m_gcTable - 1, GetTableSize() + 1); |
2783 | } |
2784 | |
2785 | #else // DACCESS_COMPILE |
2786 | |
2787 | static CrstStatic g_clrNotificationCrst; |
2788 | |
2789 | void DACRaiseException(TADDR *args, UINT argCount) |
2790 | { |
2791 | STATIC_CONTRACT_NOTHROW; |
2792 | STATIC_CONTRACT_GC_NOTRIGGER; |
2793 | STATIC_CONTRACT_MODE_ANY; |
2794 | STATIC_CONTRACT_SO_TOLERANT; |
2795 | |
2796 | struct Param |
2797 | { |
2798 | TADDR *args; |
2799 | UINT argCount; |
2800 | } param; |
2801 | param.args = args; |
2802 | param.argCount = argCount; |
2803 | |
2804 | PAL_TRY(Param *, pParam, ¶m) |
2805 | { |
2806 | RaiseException(CLRDATA_NOTIFY_EXCEPTION, 0, pParam->argCount, (ULONG_PTR *)pParam->args); |
2807 | } |
2808 | PAL_EXCEPT(EXCEPTION_EXECUTE_HANDLER) |
2809 | { |
2810 | } |
2811 | PAL_ENDTRY |
2812 | } |
2813 | |
2814 | void DACNotifyExceptionHelper(TADDR *args, UINT argCount) |
2815 | { |
2816 | CONTRACTL |
2817 | { |
2818 | NOTHROW; |
2819 | GC_NOTRIGGER; |
2820 | SO_INTOLERANT; |
2821 | MODE_ANY; |
2822 | } |
2823 | CONTRACTL_END; |
2824 | |
2825 | _ASSERTE(argCount <= MAX_CLR_NOTIFICATION_ARGS); |
2826 | |
2827 | if (IsDebuggerPresent() && !CORDebuggerAttached()) |
2828 | { |
2829 | CrstHolder lh(&g_clrNotificationCrst); |
2830 | |
2831 | for (UINT i = 0; i < argCount; i++) |
2832 | { |
2833 | g_clrNotificationArguments[i] = args[i]; |
2834 | } |
2835 | |
2836 | DACRaiseException(args, argCount); |
2837 | |
2838 | g_clrNotificationArguments[0] = NULL; |
2839 | } |
2840 | } |
2841 | |
2842 | void InitializeClrNotifications() |
2843 | { |
2844 | g_clrNotificationCrst.Init(CrstClrNotification, CRST_UNSAFE_ANYMODE); |
2845 | g_clrNotificationArguments[0] = NULL; |
2846 | } |
2847 | |
2848 | // <TODO> FIX IN BETA 2 |
2849 | // |
2850 | // g_dacNotificationFlags is only modified by the DAC and therefore the |
2851 | // optmizer can assume that it will always be its default value and has |
2852 | // been seen to eliminate the code in DoModuleLoadNotification, |
2853 | // etc... such that DAC notifications are no longer sent. |
2854 | // |
2855 | // TODO: fix this in Beta 2 |
2856 | // the RIGHT fix is to make g_dacNotificationFlags volatile, but currently |
2857 | // we don't have DAC macros to do that. Additionally, there are a number |
2858 | // of other places we should look at DAC definitions to determine if they |
2859 | // should be also declared volatile. |
2860 | // |
2861 | // for now we just turn off optimization for these guys |
2862 | #ifdef _MSC_VER |
2863 | #pragma warning(push) |
2864 | #pragma warning(disable: 4748) |
2865 | #pragma optimize("", off) |
2866 | #endif // _MSC_VER |
2867 | |
2868 | #if defined(FEATURE_GDBJIT) |
2869 | #include "gdbjit.h" |
2870 | #endif // FEATURE_GDBJIT |
2871 | |
2872 | // called from the runtime |
2873 | void DACNotify::DoJITNotification(MethodDesc *MethodDescPtr, TADDR NativeCodeLocation) |
2874 | { |
2875 | CONTRACTL |
2876 | { |
2877 | NOTHROW; |
2878 | GC_NOTRIGGER; |
2879 | SO_INTOLERANT; |
2880 | MODE_PREEMPTIVE; |
2881 | } |
2882 | CONTRACTL_END; |
2883 | |
2884 | TADDR Args[3] = { JIT_NOTIFICATION2, (TADDR) MethodDescPtr, NativeCodeLocation }; |
2885 | DACNotifyExceptionHelper(Args, 3); |
2886 | } |
2887 | |
2888 | void DACNotify::DoJITPitchingNotification(MethodDesc *MethodDescPtr) |
2889 | { |
2890 | CONTRACTL |
2891 | { |
2892 | NOTHROW; |
2893 | GC_NOTRIGGER; |
2894 | SO_INTOLERANT; |
2895 | MODE_PREEMPTIVE; |
2896 | } |
2897 | CONTRACTL_END; |
2898 | |
2899 | #if defined(FEATURE_GDBJIT) && defined(FEATURE_PAL) && !defined(CROSSGEN_COMPILE) |
2900 | NotifyGdb::MethodPitched(MethodDescPtr); |
2901 | #endif |
2902 | TADDR Args[2] = { JIT_PITCHING_NOTIFICATION, (TADDR) MethodDescPtr }; |
2903 | DACNotifyExceptionHelper(Args, 2); |
2904 | } |
2905 | |
2906 | void DACNotify::DoModuleLoadNotification(Module *ModulePtr) |
2907 | { |
2908 | CONTRACTL |
2909 | { |
2910 | NOTHROW; |
2911 | GC_NOTRIGGER; |
2912 | SO_INTOLERANT; |
2913 | MODE_PREEMPTIVE; |
2914 | } |
2915 | CONTRACTL_END; |
2916 | |
2917 | if ((g_dacNotificationFlags & CLRDATA_NOTIFY_ON_MODULE_LOAD) != 0) |
2918 | { |
2919 | TADDR Args[2] = { MODULE_LOAD_NOTIFICATION, (TADDR) ModulePtr}; |
2920 | DACNotifyExceptionHelper(Args, 2); |
2921 | } |
2922 | } |
2923 | |
2924 | void DACNotify::DoModuleUnloadNotification(Module *ModulePtr) |
2925 | { |
2926 | CONTRACTL |
2927 | { |
2928 | NOTHROW; |
2929 | GC_NOTRIGGER; |
2930 | SO_INTOLERANT; |
2931 | MODE_PREEMPTIVE; |
2932 | } |
2933 | CONTRACTL_END; |
2934 | |
2935 | if ((g_dacNotificationFlags & CLRDATA_NOTIFY_ON_MODULE_UNLOAD) != 0) |
2936 | { |
2937 | TADDR Args[2] = { MODULE_UNLOAD_NOTIFICATION, (TADDR) ModulePtr}; |
2938 | DACNotifyExceptionHelper(Args, 2); |
2939 | } |
2940 | } |
2941 | |
2942 | void DACNotify::DoExceptionNotification(Thread* ThreadPtr) |
2943 | { |
2944 | CONTRACTL |
2945 | { |
2946 | NOTHROW; |
2947 | GC_NOTRIGGER; |
2948 | SO_INTOLERANT; |
2949 | MODE_PREEMPTIVE; |
2950 | } |
2951 | CONTRACTL_END; |
2952 | |
2953 | if ((g_dacNotificationFlags & CLRDATA_NOTIFY_ON_EXCEPTION) != 0) |
2954 | { |
2955 | TADDR Args[2] = { EXCEPTION_NOTIFICATION, (TADDR) ThreadPtr}; |
2956 | DACNotifyExceptionHelper(Args, 2); |
2957 | } |
2958 | } |
2959 | |
2960 | void DACNotify::DoGCNotification(const GcEvtArgs& args) |
2961 | { |
2962 | CONTRACTL |
2963 | { |
2964 | NOTHROW; |
2965 | GC_NOTRIGGER; |
2966 | SO_INTOLERANT; |
2967 | MODE_COOPERATIVE; |
2968 | } |
2969 | CONTRACTL_END; |
2970 | |
2971 | if (args.typ == GC_MARK_END) |
2972 | { |
2973 | TADDR Args[3] = { GC_NOTIFICATION, (TADDR) args.typ, args.condemnedGeneration }; |
2974 | DACNotifyExceptionHelper(Args, 3); |
2975 | } |
2976 | } |
2977 | |
2978 | void DACNotify::DoExceptionCatcherEnterNotification(MethodDesc *MethodDescPtr, DWORD nativeOffset) |
2979 | { |
2980 | CONTRACTL |
2981 | { |
2982 | NOTHROW; |
2983 | GC_NOTRIGGER; |
2984 | SO_INTOLERANT; |
2985 | MODE_COOPERATIVE; |
2986 | } |
2987 | CONTRACTL_END; |
2988 | |
2989 | if ((g_dacNotificationFlags & CLRDATA_NOTIFY_ON_EXCEPTION_CATCH_ENTER) != 0) |
2990 | { |
2991 | TADDR Args[3] = { CATCH_ENTER_NOTIFICATION, (TADDR) MethodDescPtr, (TADDR)nativeOffset }; |
2992 | DACNotifyExceptionHelper(Args, 3); |
2993 | } |
2994 | } |
2995 | |
2996 | #ifdef _MSC_VER |
2997 | #pragma optimize("", on) |
2998 | #pragma warning(pop) |
2999 | #endif // _MSC_VER |
3000 | // </TODO> |
3001 | |
3002 | #endif // DACCESS_COMPILE |
3003 | |
3004 | // called from the DAC |
3005 | int DACNotify::GetType(TADDR Args[]) |
3006 | { |
3007 | // Type is an enum, and will thus fit into an int. |
3008 | return static_cast<int>(Args[0]); |
3009 | } |
3010 | |
3011 | BOOL DACNotify::ParseJITNotification(TADDR Args[], TADDR& MethodDescPtr, TADDR& NativeCodeLocation) |
3012 | { |
3013 | _ASSERTE(Args[0] == JIT_NOTIFICATION2); |
3014 | if (Args[0] != JIT_NOTIFICATION2) |
3015 | { |
3016 | return FALSE; |
3017 | } |
3018 | |
3019 | MethodDescPtr = Args[1]; |
3020 | NativeCodeLocation = Args[2]; |
3021 | |
3022 | return TRUE; |
3023 | } |
3024 | |
3025 | BOOL DACNotify::ParseJITPitchingNotification(TADDR Args[], TADDR& MethodDescPtr) |
3026 | { |
3027 | _ASSERTE(Args[0] == JIT_PITCHING_NOTIFICATION); |
3028 | if (Args[0] != JIT_PITCHING_NOTIFICATION) |
3029 | { |
3030 | return FALSE; |
3031 | } |
3032 | |
3033 | MethodDescPtr = Args[1]; |
3034 | |
3035 | return TRUE; |
3036 | } |
3037 | |
3038 | BOOL DACNotify::ParseModuleLoadNotification(TADDR Args[], TADDR& Module) |
3039 | { |
3040 | _ASSERTE(Args[0] == MODULE_LOAD_NOTIFICATION); |
3041 | if (Args[0] != MODULE_LOAD_NOTIFICATION) |
3042 | { |
3043 | return FALSE; |
3044 | } |
3045 | |
3046 | Module = Args[1]; |
3047 | |
3048 | return TRUE; |
3049 | } |
3050 | |
3051 | BOOL DACNotify::ParseModuleUnloadNotification(TADDR Args[], TADDR& Module) |
3052 | { |
3053 | _ASSERTE(Args[0] == MODULE_UNLOAD_NOTIFICATION); |
3054 | if (Args[0] != MODULE_UNLOAD_NOTIFICATION) |
3055 | { |
3056 | return FALSE; |
3057 | } |
3058 | |
3059 | Module = Args[1]; |
3060 | |
3061 | return TRUE; |
3062 | } |
3063 | |
3064 | BOOL DACNotify::ParseExceptionNotification(TADDR Args[], TADDR& ThreadPtr) |
3065 | { |
3066 | _ASSERTE(Args[0] == EXCEPTION_NOTIFICATION); |
3067 | if (Args[0] != EXCEPTION_NOTIFICATION) |
3068 | { |
3069 | return FALSE; |
3070 | } |
3071 | |
3072 | ThreadPtr = Args[1]; |
3073 | |
3074 | return TRUE; |
3075 | } |
3076 | |
3077 | |
3078 | BOOL DACNotify::ParseGCNotification(TADDR Args[], GcEvtArgs& args) |
3079 | { |
3080 | _ASSERTE(Args[0] == GC_NOTIFICATION); |
3081 | if (Args[0] != GC_NOTIFICATION) |
3082 | { |
3083 | return FALSE; |
3084 | } |
3085 | |
3086 | BOOL bRet = FALSE; |
3087 | |
3088 | args.typ = (GcEvt_t) Args[1]; |
3089 | switch (args.typ) |
3090 | { |
3091 | case GC_MARK_END: |
3092 | { |
3093 | // The condemnedGeneration is an int. |
3094 | args.condemnedGeneration = static_cast<int>(Args[2]); |
3095 | bRet = TRUE; |
3096 | break; |
3097 | } |
3098 | default: |
3099 | bRet = FALSE; |
3100 | break; |
3101 | } |
3102 | |
3103 | return bRet; |
3104 | } |
3105 | |
3106 | BOOL DACNotify::ParseExceptionCatcherEnterNotification(TADDR Args[], TADDR& MethodDescPtr, DWORD& nativeOffset) |
3107 | { |
3108 | _ASSERTE(Args[0] == CATCH_ENTER_NOTIFICATION); |
3109 | if (Args[0] != CATCH_ENTER_NOTIFICATION) |
3110 | { |
3111 | return FALSE; |
3112 | } |
3113 | |
3114 | MethodDescPtr = Args[1]; |
3115 | nativeOffset = (DWORD) Args[2]; |
3116 | return TRUE; |
3117 | } |
3118 | |
3119 | |
3120 | #if !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE) |
3121 | |
3122 | |
3123 | #if defined(_DEBUG) && !defined(FEATURE_PAL) |
3124 | |
3125 | typedef USHORT |
3126 | (__stdcall *PFNRtlCaptureStackBackTrace)( |
3127 | IN ULONG FramesToSkip, |
3128 | IN ULONG FramesToCapture, |
3129 | OUT PVOID * BackTrace, |
3130 | OUT PULONG BackTraceHash); |
3131 | |
3132 | static PFNRtlCaptureStackBackTrace s_RtlCaptureStackBackTrace = NULL; |
3133 | |
3134 | WORD UtilCaptureStackBackTrace( |
3135 | ULONG FramesToSkip, |
3136 | ULONG FramesToCapture, |
3137 | PVOID * BackTrace, |
3138 | OUT PULONG BackTraceHash) |
3139 | { |
3140 | WRAPPER_NO_CONTRACT; |
3141 | |
3142 | #ifdef _DEBUG |
3143 | Thread* t = GetThread(); |
3144 | if (t != NULL) { |
3145 | // the thread should not have a hijack set up or we can't walk the stack. |
3146 | _ASSERTE(!(t->m_State & Thread::TS_Hijacked)); |
3147 | } |
3148 | #endif |
3149 | |
3150 | if(!s_RtlCaptureStackBackTrace) |
3151 | { |
3152 | // Don't need to worry about race conditions here since it will be the same value |
3153 | HMODULE hModNtdll = GetModuleHandleA("ntdll.dll" ); |
3154 | s_RtlCaptureStackBackTrace = reinterpret_cast<PFNRtlCaptureStackBackTrace>( |
3155 | GetProcAddress(hModNtdll, "RtlCaptureStackBackTrace" )); |
3156 | } |
3157 | if (!s_RtlCaptureStackBackTrace) { |
3158 | return 0; |
3159 | } |
3160 | ULONG hash; |
3161 | if (BackTraceHash == NULL) { |
3162 | BackTraceHash = &hash; |
3163 | } |
3164 | return s_RtlCaptureStackBackTrace(FramesToSkip, FramesToCapture, BackTrace, BackTraceHash); |
3165 | } |
3166 | |
3167 | #endif // #if _DEBUG && !FEATURE_PAL |
3168 | |
3169 | |
3170 | #ifdef _DEBUG |
3171 | DisableDelayLoadCheckForOleaut32::DisableDelayLoadCheckForOleaut32() |
3172 | { |
3173 | GetThread()->SetThreadStateNC(Thread::TSNC_DisableOleaut32Check); |
3174 | } |
3175 | |
3176 | DisableDelayLoadCheckForOleaut32::~DisableDelayLoadCheckForOleaut32() |
3177 | { |
3178 | GetThread()->ResetThreadStateNC(Thread::TSNC_DisableOleaut32Check); |
3179 | } |
3180 | |
3181 | BOOL DelayLoadOleaut32CheckDisabled() |
3182 | { |
3183 | Thread *pThread = GetThread(); |
3184 | if (pThread && pThread->HasThreadStateNC(Thread::TSNC_DisableOleaut32Check)) |
3185 | { |
3186 | return TRUE; |
3187 | } |
3188 | |
3189 | return FALSE; |
3190 | } |
3191 | #endif |
3192 | |
3193 | BOOL EnableARM() |
3194 | { |
3195 | #ifdef FEATURE_APPDOMAIN_RESOURCE_MONITORING |
3196 | CONTRACTL |
3197 | { |
3198 | NOTHROW; |
3199 | // TODO: this should really be GC_TRIGGERS so we wouldn't need the |
3200 | // CONTRACT_VIOLATION below but the hosting API that calls this |
3201 | // can be called on a COOP thread and it has a GC_NOTRIGGER contract. |
3202 | // We should use the AD unload thread to call this function on. |
3203 | GC_NOTRIGGER; |
3204 | SO_TOLERANT; |
3205 | } |
3206 | CONTRACTL_END; |
3207 | |
3208 | BOOL fARMEnabled = g_fEnableARM; |
3209 | |
3210 | if (!fARMEnabled) |
3211 | { |
3212 | if (ThreadStore::s_pThreadStore) |
3213 | { |
3214 | // We need to establish the baselines for the CPU usage counting. |
3215 | Thread *pThread = NULL; |
3216 | CONTRACT_VIOLATION(GCViolation); |
3217 | |
3218 | // I am returning TRUE here so the caller will NOT enable |
3219 | // ARM - if we can't take the thread store lock, something |
3220 | // is already kind of messed up so no need to proceed with |
3221 | // enabling ARM. |
3222 | BEGIN_SO_INTOLERANT_CODE_NOTHROW(GetThread(), return TRUE); |
3223 | // Take the thread store lock while we enumerate threads. |
3224 | ThreadStoreLockHolder tsl ; |
3225 | |
3226 | while ((pThread = ThreadStore::GetThreadList(pThread)) != NULL) |
3227 | { |
3228 | if (pThread->IsUnstarted() || pThread->IsDead()) |
3229 | continue; |
3230 | pThread->QueryThreadProcessorUsage(); |
3231 | } |
3232 | |
3233 | END_SO_INTOLERANT_CODE; |
3234 | } |
3235 | g_fEnableARM = TRUE; |
3236 | } |
3237 | |
3238 | return fARMEnabled; |
3239 | #else // FEATURE_APPDOMAIN_RESOURCE_MONITORING |
3240 | return FALSE; |
3241 | #endif // FEATURE_APPDOMAIN_RESOURCE_MONITORING |
3242 | } |
3243 | |
3244 | #endif // !DACCESS_COMPILE && !CROSSGEN_COMPILE |
3245 | |
3246 | |
3247 | static BOOL TrustMeIAmSafe(void *pLock) |
3248 | { |
3249 | LIMITED_METHOD_CONTRACT; |
3250 | return TRUE; |
3251 | } |
3252 | |
3253 | LockOwner g_lockTrustMeIAmThreadSafe = { NULL, TrustMeIAmSafe }; |
3254 | |
3255 | |
3256 | DangerousNonHostedSpinLock g_randomLock; |
3257 | CLRRandom g_random; |
3258 | |
3259 | |
3260 | int GetRandomInt(int maxVal) |
3261 | { |
3262 | #ifndef CROSSGEN_COMPILE |
3263 | // Use the thread-local Random instance if possible |
3264 | Thread* pThread = GetThread(); |
3265 | if (pThread) |
3266 | return pThread->GetRandom()->Next(maxVal); |
3267 | #endif |
3268 | |
3269 | // No Thread object - need to fall back to the global generator. |
3270 | // In DAC builds we don't need the lock (DAC is single-threaded) and can't get it anyway (DNHSL isn't supported) |
3271 | #ifndef DACCESS_COMPILE |
3272 | DangerousNonHostedSpinLockHolder lh(&g_randomLock); |
3273 | #endif |
3274 | if (!g_random.IsInitialized()) |
3275 | g_random.Init(); |
3276 | return g_random.Next(maxVal); |
3277 | } |
3278 | |
3279 | // These wrap the SString:L:CompareCaseInsenstive function in a way that makes it |
3280 | // easy to fix code that uses _stricmp. _stricmp should be avoided as it uses the current |
3281 | // C-runtime locale rather than the invariance culture. |
3282 | // |
3283 | // Note that unlike the real _stricmp, these functions unavoidably have a throws/gc_triggers/inject_fault |
3284 | // contract. So if need a case-insensitive comparison in a place where you can't tolerate this contract, |
3285 | // you've got a problem. |
3286 | int __cdecl stricmpUTF8(const char* szStr1, const char* szStr2) |
3287 | { |
3288 | CONTRACTL |
3289 | { |
3290 | THROWS; |
3291 | GC_TRIGGERS; |
3292 | INJECT_FAULT(COMPlusThrowOM()); |
3293 | } |
3294 | CONTRACTL_END |
3295 | |
3296 | SString sStr1 (SString::Utf8, szStr1); |
3297 | SString sStr2 (SString::Utf8, szStr2); |
3298 | return sStr1.CompareCaseInsensitive(sStr2); |
3299 | |
3300 | } |
3301 | |
3302 | #ifndef DACCESS_COMPILE |
3303 | // |
3304 | // Casing Table Helpers for use in the EE. |
3305 | // |
3306 | |
3307 | // // Convert szIn to lower case in the Invariant locale. |
3308 | INT32 InternalCasingHelper::InvariantToLower(__out_bcount_opt(cMaxBytes) LPUTF8 szOut, int cMaxBytes, __in_z LPCUTF8 szIn) |
3309 | { |
3310 | CONTRACTL { |
3311 | THROWS; |
3312 | GC_TRIGGERS; |
3313 | MODE_ANY; |
3314 | INJECT_FAULT(COMPlusThrowOM()); |
3315 | } CONTRACTL_END |
3316 | |
3317 | return InvariantToLowerHelper(szOut, cMaxBytes, szIn, TRUE /*fAllowThrow*/); |
3318 | } |
3319 | |
3320 | // Convert szIn to lower case in the Invariant locale. |
3321 | INT32 InternalCasingHelper::InvariantToLowerNoThrow(__out_bcount_opt(cMaxBytes) LPUTF8 szOut, int cMaxBytes, __in_z LPCUTF8 szIn) |
3322 | { |
3323 | CONTRACTL { |
3324 | NOTHROW; |
3325 | GC_NOTRIGGER; |
3326 | MODE_ANY; |
3327 | INJECT_FAULT(return 0;); |
3328 | } CONTRACTL_END |
3329 | |
3330 | |
3331 | return InvariantToLowerHelper(szOut, cMaxBytes, szIn, FALSE /*fAllowThrow*/); |
3332 | } |
3333 | |
3334 | // Convert szIn to lower case in the Invariant locale. |
3335 | INT32 InternalCasingHelper::InvariantToLowerHelper(__out_bcount_opt(cMaxBytes) LPUTF8 szOut, int cMaxBytes, __in_z LPCUTF8 szIn, BOOL fAllowThrow) |
3336 | { |
3337 | |
3338 | CONTRACTL { |
3339 | // This fcn can trigger a lazy load of the TextInfo class. |
3340 | if (fAllowThrow) THROWS; else NOTHROW; |
3341 | if (fAllowThrow) GC_TRIGGERS; else GC_NOTRIGGER; |
3342 | if (fAllowThrow) {INJECT_FAULT(COMPlusThrowOM());} else {INJECT_FAULT(return 0);} |
3343 | MODE_ANY; |
3344 | |
3345 | PRECONDITION((cMaxBytes == 0) || CheckPointer(szOut)); |
3346 | PRECONDITION(CheckPointer(szIn)); |
3347 | } CONTRACTL_END |
3348 | |
3349 | int inLength = (int)(strlen(szIn)+1); |
3350 | INT32 result = 0; |
3351 | |
3352 | LPCUTF8 szInSave = szIn; |
3353 | LPUTF8 szOutSave = szOut; |
3354 | BOOL bFoundHighChars=FALSE; |
3355 | //Compute our end point. |
3356 | LPCUTF8 szEnd; |
3357 | INT32 wideCopyLen; |
3358 | |
3359 | CQuickBytes qbOut; |
3360 | LPWSTR szWideOut; |
3361 | |
3362 | if (cMaxBytes != 0 && szOut == NULL) { |
3363 | if (fAllowThrow) { |
3364 | COMPlusThrowHR(ERROR_INVALID_PARAMETER); |
3365 | } |
3366 | SetLastError(ERROR_INVALID_PARAMETER); |
3367 | result = 0; |
3368 | goto Exit; |
3369 | } |
3370 | |
3371 | if (cMaxBytes) { |
3372 | szEnd = szOut + min(inLength, cMaxBytes); |
3373 | //Walk the string copying the characters. Change the case on |
3374 | //any character between A-Z. |
3375 | for (; szOut<szEnd; szOut++, szIn++) { |
3376 | if (*szIn>='A' && *szIn<='Z') { |
3377 | *szOut = *szIn | 0x20; |
3378 | } |
3379 | else { |
3380 | if (((UINT32)(*szIn))>((UINT32)0x80)) { |
3381 | bFoundHighChars = TRUE; |
3382 | break; |
3383 | } |
3384 | *szOut = *szIn; |
3385 | } |
3386 | } |
3387 | |
3388 | if (!bFoundHighChars) { |
3389 | //If we copied everything, tell them how many bytes we copied, |
3390 | //and arrange it so that the original position of the string + the returned |
3391 | //length gives us the position of the null (useful if we're appending). |
3392 | if (--inLength > cMaxBytes) { |
3393 | if (fAllowThrow) { |
3394 | COMPlusThrowHR(HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER)); |
3395 | } |
3396 | SetLastError(ERROR_INSUFFICIENT_BUFFER); |
3397 | result = 0; |
3398 | goto Exit; |
3399 | } |
3400 | |
3401 | result = inLength; |
3402 | goto Exit; |
3403 | } |
3404 | } |
3405 | else { |
3406 | szEnd = szIn + inLength; |
3407 | for (; szIn<szEnd; szIn++) { |
3408 | if (((UINT32)(*szIn))>((UINT32)0x80)) { |
3409 | bFoundHighChars = TRUE; |
3410 | break; |
3411 | } |
3412 | } |
3413 | |
3414 | if (!bFoundHighChars) { |
3415 | result = inLength; |
3416 | goto Exit; |
3417 | } |
3418 | } |
3419 | |
3420 | szOut = szOutSave; |
3421 | |
3422 | #ifndef FEATURE_PAL |
3423 | |
3424 | //convert the UTF8 to Unicode |
3425 | //MAKE_WIDEPTR_FROMUTF8(szInWide, szInSave); |
3426 | |
3427 | int __lszInWide; |
3428 | LPWSTR szInWide; |
3429 | __lszInWide = WszMultiByteToWideChar(CP_UTF8, 0, szInSave, -1, 0, 0); |
3430 | if (__lszInWide > MAKE_MAX_LENGTH) |
3431 | RaiseException(EXCEPTION_INT_OVERFLOW, EXCEPTION_NONCONTINUABLE, 0, 0); |
3432 | szInWide = (LPWSTR) alloca(__lszInWide*sizeof(WCHAR)); |
3433 | if (szInWide == NULL) { |
3434 | if (fAllowThrow) { |
3435 | COMPlusThrowOM(); |
3436 | } else { |
3437 | SetLastError(ERROR_NOT_ENOUGH_MEMORY); |
3438 | result = 0; |
3439 | goto Exit; |
3440 | } |
3441 | } |
3442 | if (0==WszMultiByteToWideChar(CP_UTF8, 0, szInSave, -1, szInWide, __lszInWide)) { |
3443 | RaiseException(ERROR_NO_UNICODE_TRANSLATION, EXCEPTION_NONCONTINUABLE, 0, 0); |
3444 | } |
3445 | |
3446 | |
3447 | wideCopyLen = (INT32)wcslen(szInWide)+1; |
3448 | if (fAllowThrow) { |
3449 | szWideOut = (LPWSTR)qbOut.AllocThrows(wideCopyLen * sizeof(WCHAR)); |
3450 | } |
3451 | else { |
3452 | szWideOut = (LPWSTR)qbOut.AllocNoThrow(wideCopyLen * sizeof(WCHAR)); |
3453 | if (!szWideOut) { |
3454 | SetLastError(ERROR_NOT_ENOUGH_MEMORY); |
3455 | result = 0; |
3456 | goto Exit; |
3457 | } |
3458 | } |
3459 | |
3460 | //Do the casing operation |
3461 | ::LCMapStringEx(W("" ), LCMAP_LOWERCASE, szInWide, wideCopyLen, szWideOut, wideCopyLen, NULL, NULL, 0); |
3462 | |
3463 | //Convert the Unicode back to UTF8 |
3464 | result = WszWideCharToMultiByte(CP_UTF8, 0, szWideOut, wideCopyLen, szOut, cMaxBytes, NULL, NULL); |
3465 | |
3466 | if ((result == 0) && fAllowThrow) { |
3467 | COMPlusThrowWin32(); |
3468 | } |
3469 | |
3470 | #endif // !FEATURE_PAL |
3471 | |
3472 | Exit: |
3473 | return result; |
3474 | } |
3475 | |
3476 | // |
3477 | // |
3478 | // COMCharacter and Helper functions |
3479 | // |
3480 | // |
3481 | |
3482 | #ifndef FEATURE_PAL |
3483 | /*============================GetCharacterInfoHelper============================ |
3484 | **Determines character type info (digit, whitespace, etc) for the given char. |
3485 | **Args: c is the character on which to operate. |
3486 | ** CharInfoType is one of CT_CTYPE1, CT_CTYPE2, CT_CTYPE3 and specifies the type |
3487 | ** of information being requested. |
3488 | **Returns: The bitmask returned by GetStringTypeEx. The caller needs to know |
3489 | ** how to interpret this. |
3490 | **Exceptions: ArgumentException if GetStringTypeEx fails. |
3491 | ==============================================================================*/ |
3492 | INT32 GetCharacterInfoHelper(WCHAR c, INT32 CharInfoType) |
3493 | { |
3494 | WRAPPER_NO_CONTRACT; |
3495 | |
3496 | unsigned short result=0; |
3497 | if (!GetStringTypeEx(LOCALE_USER_DEFAULT, CharInfoType, &(c), 1, &result)) { |
3498 | _ASSERTE(!"This should not happen, verify the arguments passed to GetStringTypeEx()" ); |
3499 | } |
3500 | return(INT32)result; |
3501 | } |
3502 | #endif // !FEATURE_PAL |
3503 | |
3504 | /*==============================nativeIsWhiteSpace============================== |
3505 | **The locally available version of IsWhiteSpace. Designed to be called by other |
3506 | **native methods. The work is mostly done by GetCharacterInfoHelper |
3507 | **Args: c -- the character to check. |
3508 | **Returns: true if c is whitespace, false otherwise. |
3509 | **Exceptions: Only those thrown by GetCharacterInfoHelper. |
3510 | ==============================================================================*/ |
3511 | BOOL COMCharacter::nativeIsWhiteSpace(WCHAR c) |
3512 | { |
3513 | WRAPPER_NO_CONTRACT; |
3514 | |
3515 | #ifndef FEATURE_PAL |
3516 | if (c <= (WCHAR) 0x7F) // common case |
3517 | { |
3518 | BOOL result = (c == ' ') || (c == '\r') || (c == '\n') || (c == '\t') || (c == '\f') || (c == (WCHAR) 0x0B); |
3519 | |
3520 | ASSERT(result == ((GetCharacterInfoHelper(c, CT_CTYPE1) & C1_SPACE)!=0)); |
3521 | |
3522 | return result; |
3523 | } |
3524 | |
3525 | // GetCharacterInfoHelper costs around 160 instructions |
3526 | return((GetCharacterInfoHelper(c, CT_CTYPE1) & C1_SPACE)!=0); |
3527 | #else // !FEATURE_PAL |
3528 | return iswspace(c); |
3529 | #endif // !FEATURE_PAL |
3530 | } |
3531 | |
3532 | /*================================nativeIsDigit================================= |
3533 | **The locally available version of IsDigit. Designed to be called by other |
3534 | **native methods. The work is mostly done by GetCharacterInfoHelper |
3535 | **Args: c -- the character to check. |
3536 | **Returns: true if c is whitespace, false otherwise. |
3537 | **Exceptions: Only those thrown by GetCharacterInfoHelper. |
3538 | ==============================================================================*/ |
3539 | BOOL COMCharacter::nativeIsDigit(WCHAR c) |
3540 | { |
3541 | WRAPPER_NO_CONTRACT; |
3542 | #ifndef FEATURE_PAL |
3543 | return((GetCharacterInfoHelper(c, CT_CTYPE1) & C1_DIGIT)!=0); |
3544 | #else // !FEATURE_PAL |
3545 | return iswdigit(c); |
3546 | #endif // !FEATURE_PAL |
3547 | } |
3548 | |
3549 | BOOL RuntimeFileNotFound(HRESULT hr) |
3550 | { |
3551 | LIMITED_METHOD_CONTRACT; |
3552 | return Assembly::FileNotFound(hr); |
3553 | } |
3554 | |
3555 | #ifndef FEATURE_PAL |
3556 | HRESULT GetFileVersion( // S_OK or error |
3557 | LPCWSTR wszFilePath, // Path to the executable. |
3558 | ULARGE_INTEGER* pFileVersion) // Put file version here. |
3559 | { |
3560 | CONTRACTL |
3561 | { |
3562 | NOTHROW; |
3563 | GC_NOTRIGGER; |
3564 | MODE_ANY; |
3565 | } |
3566 | CONTRACTL_END; |
3567 | |
3568 | // |
3569 | // Note that this code is equivalent to FusionGetFileVersionInfo, found in fusion\asmcache\asmcache.cpp |
3570 | // |
3571 | |
3572 | // Avoid confusion. |
3573 | pFileVersion->QuadPart = 0; |
3574 | |
3575 | DWORD ret; |
3576 | |
3577 | DWORD dwHandle = 0; |
3578 | DWORD bufSize = GetFileVersionInfoSizeW(wszFilePath, &dwHandle); |
3579 | if (!bufSize) |
3580 | { |
3581 | return HRESULT_FROM_GetLastErrorNA(); |
3582 | } |
3583 | |
3584 | // Allocate the buffer for the version info structure |
3585 | // _alloca() can't return NULL -- raises STATUS_STACK_OVERFLOW. |
3586 | BYTE* pVersionInfoBuffer = reinterpret_cast< BYTE* >(_alloca(bufSize)); |
3587 | |
3588 | ret = GetFileVersionInfoW(wszFilePath, dwHandle, bufSize, pVersionInfoBuffer); |
3589 | if (!ret) |
3590 | { |
3591 | return HRESULT_FROM_GetLastErrorNA(); |
3592 | } |
3593 | |
3594 | // Extract the actual File Version number that we care about. |
3595 | UINT versionInfoSize = 0; |
3596 | VS_FIXEDFILEINFO* pVSFileInfo; |
3597 | ret = VerQueryValueW(pVersionInfoBuffer, W("\\" ), |
3598 | reinterpret_cast< void **>(&pVSFileInfo), &versionInfoSize); |
3599 | if (!ret || versionInfoSize == 0) |
3600 | { |
3601 | return HRESULT_FROM_GetLastErrorNA(); |
3602 | } |
3603 | |
3604 | pFileVersion->HighPart = pVSFileInfo->dwFileVersionMS; |
3605 | pFileVersion->LowPart = pVSFileInfo->dwFileVersionLS; |
3606 | |
3607 | return S_OK; |
3608 | } |
3609 | #endif // !FEATURE_PAL |
3610 | |
3611 | #endif // !DACCESS_COMPILE |
3612 | |