1// Licensed to the .NET Foundation under one or more agreements.
2// The .NET Foundation licenses this file to you under the MIT license.
3// See the LICENSE file in the project root for more information.
4
5/*****************************************************************************
6 ** **
7 ** Corhlprpriv.h - **
8 ** **
9 *****************************************************************************/
10
11#ifndef __CORHLPRPRIV_H__
12#define __CORHLPRPRIV_H__
13
14#include "corhlpr.h"
15#include "fstring.h"
16
17#if defined(_MSC_VER) && defined(_TARGET_X86_)
18#pragma optimize("y", on) // If routines don't get inlined, don't pay the EBP frame penalty
19#endif
20
21//*****************************************************************************
22//
23//***** Utility helpers
24//
25//*****************************************************************************
26
27#ifndef SOS_INCLUDE
28
29//*****************************************************************************
30//
31// **** CQuickBytes
32// This helper class is useful for cases where 90% of the time you allocate 512
33// or less bytes for a data structure. This class contains a 512 byte buffer.
34// Alloc() will return a pointer to this buffer if your allocation is small
35// enough, otherwise it asks the heap for a larger buffer which is freed for
36// you. No mutex locking is required for the small allocation case, making the
37// code run faster, less heap fragmentation, etc... Each instance will allocate
38// 520 bytes, so use accordinly.
39//
40//*****************************************************************************
41namespace NSQuickBytesHelper
42{
43 template <BOOL bThrow>
44 struct _AllocBytes;
45
46 template <>
47 struct _AllocBytes<TRUE>
48 {
49 static BYTE *Invoke(SIZE_T iItems)
50 {
51 return NEW_THROWS(iItems);
52 }
53 };
54
55 template <>
56 struct _AllocBytes<FALSE>
57 {
58 static BYTE *Invoke(SIZE_T iItems)
59 {
60 return NEW_NOTHROW(iItems);
61 }
62 };
63};
64
65void DECLSPEC_NORETURN ThrowHR(HRESULT hr);
66
67template <SIZE_T SIZE, SIZE_T INCREMENT>
68class CQuickMemoryBase
69{
70protected:
71 template <typename ELEM_T>
72 static ELEM_T Min(ELEM_T a, ELEM_T b)
73 { return a < b ? a : b; }
74
75 template <typename ELEM_T>
76 static ELEM_T Max(ELEM_T a, ELEM_T b)
77 { return a < b ? b : a; }
78
79 // bGrow - indicates that this is a resize and that the original data
80 // needs to be copied over.
81 // bThrow - indicates whether or not memory allocations will throw.
82 template <BOOL bGrow, BOOL bThrow>
83 void *_Alloc(SIZE_T iItems)
84 {
85#if defined(_BLD_CLR) && defined(_DEBUG)
86 { // Exercise heap for OOM-fault injection purposes
87 BYTE * pb = NSQuickBytesHelper::_AllocBytes<bThrow>::Invoke(iItems);
88 _ASSERTE(!bThrow || pb != NULL); // _AllocBytes would have thrown if bThrow == TRUE
89 if (pb == NULL) return NULL; // bThrow == FALSE and we failed to allocate memory
90 delete [] pb; // Success, delete allocated memory.
91 }
92#endif
93 if (iItems <= cbTotal)
94 { // Fits within existing memory allocation
95 iSize = iItems;
96 }
97 else if (iItems <= SIZE)
98 { // Will fit in internal buffer.
99 if (pbBuff == NULL)
100 { // Any previous allocation is in the internal buffer and the new
101 // allocation fits in the internal buffer, so just update the size.
102 iSize = iItems;
103 cbTotal = SIZE;
104 }
105 else
106 { // There was a previous allocation, sitting in pbBuff
107 if (bGrow)
108 { // If growing, need to copy any existing data over.
109 memcpy(&rgData[0], pbBuff, Min(cbTotal, SIZE));
110 }
111
112 delete [] pbBuff;
113 pbBuff = NULL;
114 iSize = iItems;
115 cbTotal = SIZE;
116 }
117 }
118 else
119 { // Need to allocate a new buffer
120 SIZE_T cbTotalNew = iItems + (bGrow ? INCREMENT : 0);
121 BYTE * pbBuffNew = NSQuickBytesHelper::_AllocBytes<bThrow>::Invoke(cbTotalNew);
122
123 if (!bThrow && pbBuffNew == NULL)
124 { // Allocation failed. Zero out structure.
125 if (pbBuff != NULL)
126 { // Delete old buffer
127 delete [] pbBuff;
128 }
129 pbBuff = NULL;
130 iSize = 0;
131 cbTotal = 0;
132 return NULL;
133 }
134
135 if (bGrow && cbTotal > 0)
136 { // If growing, need to copy any existing data over.
137 memcpy(pbBuffNew, (BYTE *)Ptr(), Min(cbTotal, cbTotalNew));
138 }
139
140 if (pbBuff != NULL)
141 { // Delete old pre-existing buffer
142 delete [] pbBuff;
143 pbBuff = NULL;
144 }
145
146 pbBuff = pbBuffNew;
147 cbTotal = cbTotalNew;
148 iSize = iItems;
149 }
150
151 return Ptr();
152 }
153
154public:
155 void Init()
156 {
157 pbBuff = 0;
158 iSize = 0;
159 cbTotal = SIZE;
160 }
161
162 void Destroy()
163 {
164 if (pbBuff)
165 {
166 delete [] pbBuff;
167 pbBuff = 0;
168 }
169 }
170
171 void *AllocThrows(SIZE_T iItems)
172 {
173 return _Alloc<FALSE /*bGrow*/, TRUE /*bThrow*/>(iItems);
174 }
175
176 void *AllocNoThrow(SIZE_T iItems)
177 {
178 return _Alloc<FALSE /*bGrow*/, FALSE /*bThrow*/>(iItems);
179 }
180
181 void ReSizeThrows(SIZE_T iItems)
182 {
183 _Alloc<TRUE /*bGrow*/, TRUE /*bThrow*/>(iItems);
184 }
185
186#ifdef __llvm__
187 // This makes sure that we will not get an undefined symbol
188 // when building a release version of libcoreclr using LLVM.
189 __attribute__((used))
190#endif // __llvm__
191 HRESULT ReSizeNoThrow(SIZE_T iItems);
192
193 void Shrink(SIZE_T iItems)
194 {
195 _ASSERTE(iItems <= cbTotal);
196 iSize = iItems;
197 }
198
199 operator PVOID()
200 {
201 return ((pbBuff) ? pbBuff : (PVOID)&rgData[0]);
202 }
203
204 void *Ptr()
205 {
206 return ((pbBuff) ? pbBuff : (PVOID)&rgData[0]);
207 }
208
209 const void *Ptr() const
210 {
211 return ((pbBuff) ? pbBuff : (PVOID)&rgData[0]);
212 }
213
214 SIZE_T Size() const
215 {
216 return (iSize);
217 }
218
219 SIZE_T MaxSize() const
220 {
221 return (cbTotal);
222 }
223
224 void Maximize()
225 {
226 iSize = cbTotal;
227 }
228
229
230 // Convert UTF8 string to UNICODE string, optimized for speed
231 HRESULT ConvertUtf8_UnicodeNoThrow(const char * utf8str)
232 {
233 bool allAscii;
234 DWORD length;
235
236 HRESULT hr = FString::Utf8_Unicode_Length(utf8str, & allAscii, & length);
237
238 if (SUCCEEDED(hr))
239 {
240 LPWSTR buffer = (LPWSTR) AllocNoThrow((length + 1) * sizeof(WCHAR));
241
242 if (buffer == NULL)
243 {
244 hr = E_OUTOFMEMORY;
245 }
246 else
247 {
248 hr = FString::Utf8_Unicode(utf8str, allAscii, buffer, length);
249 }
250 }
251
252 return hr;
253 }
254
255 // Convert UTF8 string to UNICODE string, optimized for speed
256 void ConvertUtf8_Unicode(const char * utf8str)
257 {
258 bool allAscii;
259 DWORD length;
260
261 HRESULT hr = FString::Utf8_Unicode_Length(utf8str, & allAscii, & length);
262
263 if (SUCCEEDED(hr))
264 {
265 LPWSTR buffer = (LPWSTR) AllocThrows((length + 1) * sizeof(WCHAR));
266
267 hr = FString::Utf8_Unicode(utf8str, allAscii, buffer, length);
268 }
269
270 if (FAILED(hr))
271 {
272 ThrowHR(hr);
273 }
274 }
275
276 // Convert UNICODE string to UTF8 string, optimized for speed
277 void ConvertUnicode_Utf8(const WCHAR * pString)
278 {
279 bool allAscii;
280 DWORD length;
281
282 HRESULT hr = FString::Unicode_Utf8_Length(pString, & allAscii, & length);
283
284 if (SUCCEEDED(hr))
285 {
286 LPSTR buffer = (LPSTR) AllocThrows((length + 1) * sizeof(char));
287
288 hr = FString::Unicode_Utf8(pString, allAscii, buffer, length);
289 }
290
291 if (FAILED(hr))
292 {
293 ThrowHR(hr);
294 }
295 }
296
297 // Copy single byte string and hold it
298 const char * SetStringNoThrow(const char * pStr, SIZE_T len)
299 {
300 LPSTR buffer = (LPSTR) AllocNoThrow(len + 1);
301
302 if (buffer != NULL)
303 {
304 memcpy(buffer, pStr, len);
305 buffer[len] = 0;
306 }
307
308 return buffer;
309 }
310
311#ifdef DACCESS_COMPILE
312 void
313 EnumMemoryRegions(CLRDataEnumMemoryFlags flags)
314 {
315 // Assume that 'this' is enumerated, either explicitly
316 // or because this class is embedded in another.
317 DacEnumMemoryRegion(dac_cast<TADDR>(pbBuff), iSize);
318 }
319#endif // DACCESS_COMPILE
320
321 BYTE *pbBuff;
322 SIZE_T iSize; // number of bytes used
323 SIZE_T cbTotal; // total bytes allocated in the buffer
324 // use UINT64 to enforce the alignment of the memory
325 UINT64 rgData[(SIZE+sizeof(UINT64)-1)/sizeof(UINT64)];
326};
327
328// These should be multiples of 8 so that data can be naturally aligned.
329#define CQUICKBYTES_BASE_SIZE 512
330#define CQUICKBYTES_INCREMENTAL_SIZE 128
331
332class CQuickBytesBase : public CQuickMemoryBase<CQUICKBYTES_BASE_SIZE, CQUICKBYTES_INCREMENTAL_SIZE>
333{
334};
335
336
337class CQuickBytes : public CQuickBytesBase
338{
339public:
340 CQuickBytes()
341 {
342 Init();
343 }
344
345 ~CQuickBytes()
346 {
347 Destroy();
348 }
349};
350
351/* to be used as static variable - no constructor/destructor, assumes zero
352 initialized memory */
353class CQuickBytesStatic : public CQuickBytesBase
354{
355};
356
357template <SIZE_T CQUICKBYTES_BASE_SPECIFY_SIZE>
358class CQuickBytesSpecifySizeBase : public CQuickMemoryBase<CQUICKBYTES_BASE_SPECIFY_SIZE, CQUICKBYTES_INCREMENTAL_SIZE>
359{
360};
361
362template <SIZE_T CQUICKBYTES_BASE_SPECIFY_SIZE>
363class CQuickBytesSpecifySize : public CQuickBytesSpecifySizeBase<CQUICKBYTES_BASE_SPECIFY_SIZE>
364{
365public:
366 CQuickBytesSpecifySize()
367 {
368 this->Init();
369 }
370
371 ~CQuickBytesSpecifySize()
372 {
373 this->Destroy();
374 }
375};
376
377/* to be used as static variable - no constructor/destructor, assumes zero
378 initialized memory */
379template <SIZE_T CQUICKBYTES_BASE_SPECIFY_SIZE>
380class CQuickBytesSpecifySizeStatic : public CQuickBytesSpecifySizeBase<CQUICKBYTES_BASE_SPECIFY_SIZE>
381{
382};
383
384template <class T> class CQuickArrayBase : public CQuickBytesBase
385{
386public:
387 T* AllocThrows(SIZE_T iItems)
388 {
389 CheckOverflowThrows(iItems);
390 return (T*)CQuickBytesBase::AllocThrows(iItems * sizeof(T));
391 }
392
393 void ReSizeThrows(SIZE_T iItems)
394 {
395 CheckOverflowThrows(iItems);
396 CQuickBytesBase::ReSizeThrows(iItems * sizeof(T));
397 }
398
399 T* AllocNoThrow(SIZE_T iItems)
400 {
401 if (!CheckOverflowNoThrow(iItems))
402 {
403 return NULL;
404 }
405 return (T*)CQuickBytesBase::AllocNoThrow(iItems * sizeof(T));
406 }
407
408 HRESULT ReSizeNoThrow(SIZE_T iItems)
409 {
410 if (!CheckOverflowNoThrow(iItems))
411 {
412 return E_OUTOFMEMORY;
413 }
414 return CQuickBytesBase::ReSizeNoThrow(iItems * sizeof(T));
415 }
416
417 void Shrink(SIZE_T iItems)
418 {
419 CQuickBytesBase::Shrink(iItems * sizeof(T));
420 }
421
422 T* Ptr()
423 {
424 return (T*) CQuickBytesBase::Ptr();
425 }
426
427 const T* Ptr() const
428 {
429 return (T*) CQuickBytesBase::Ptr();
430 }
431
432 SIZE_T Size() const
433 {
434 return CQuickBytesBase::Size() / sizeof(T);
435 }
436
437 SIZE_T MaxSize() const
438 {
439 return CQuickBytesBase::cbTotal / sizeof(T);
440 }
441
442 T& operator[] (SIZE_T ix)
443 {
444 _ASSERTE(ix < Size());
445 return *(Ptr() + ix);
446 }
447
448 const T& operator[] (SIZE_T ix) const
449 {
450 _ASSERTE(ix < Size());
451 return *(Ptr() + ix);
452 }
453
454private:
455 inline
456 BOOL CheckOverflowNoThrow(SIZE_T iItems)
457 {
458 SIZE_T totalSize = iItems * sizeof(T);
459
460 if (totalSize / sizeof(T) != iItems)
461 {
462 return FALSE;
463 }
464
465 return TRUE;
466 }
467
468 inline
469 void CheckOverflowThrows(SIZE_T iItems)
470 {
471 if (!CheckOverflowNoThrow(iItems))
472 {
473 THROW_OUT_OF_MEMORY();
474 }
475 }
476};
477
478template <class T> class CQuickArray : public CQuickArrayBase<T>
479{
480public:
481 CQuickArray<T>()
482 {
483 this->Init();
484 }
485
486 ~CQuickArray<T>()
487 {
488 this->Destroy();
489 }
490};
491
492// This is actually more of a stack with array access. Essentially, you can
493// only add elements through Push and remove them through Pop, but you can
494// access and modify any random element with the index operator. You cannot
495// access elements that have not been added.
496
497template <class T>
498class CQuickArrayList : protected CQuickArray<T>
499{
500private:
501 SIZE_T m_curSize;
502
503public:
504 // Make these specific functions public.
505 using CQuickArray<T>::AllocThrows;
506 using CQuickArray<T>::ReSizeThrows;
507 using CQuickArray<T>::AllocNoThrow;
508 using CQuickArray<T>::ReSizeNoThrow;
509 using CQuickArray<T>::MaxSize;
510
511 CQuickArrayList()
512 : m_curSize(0)
513 {
514 this->Init();
515 }
516
517 ~CQuickArrayList()
518 {
519 this->Destroy();
520 }
521
522 // Can only access values that have been pushed.
523 T& operator[] (SIZE_T ix)
524 {
525 _ASSERTE(ix < m_curSize);
526 return CQuickArray<T>::operator[](ix);
527 }
528
529 // Can only access values that have been pushed.
530 const T& operator[] (SIZE_T ix) const
531 {
532 _ASSERTE(ix < m_curSize);
533 return CQuickArray<T>::operator[](ix);
534 }
535
536 // THROWS: Resizes if necessary.
537 void Push(const T & value)
538 {
539 // Resize if necessary - thows.
540 if (m_curSize + 1 >= CQuickArray<T>::Size())
541 ReSizeThrows((m_curSize + 1) * 2);
542
543 // Append element to end of array.
544 _ASSERTE(m_curSize + 1 < CQuickArray<T>::Size());
545 SIZE_T ix = m_curSize++;
546 (*this)[ix] = value;
547 }
548
549 T Pop()
550 {
551 _ASSERTE(m_curSize > 0);
552 T retval = (*this)[m_curSize - 1];
553 INDEBUG(ZeroMemory(&(this->Ptr()[m_curSize - 1]), sizeof(T));)
554 --m_curSize;
555 return retval;
556 }
557
558 SIZE_T Size() const
559 {
560 return m_curSize;
561 }
562
563 void Shrink()
564 {
565 CQuickArray<T>::Shrink(m_curSize);
566 }
567};
568
569
570/* to be used as static variable - no constructor/destructor, assumes zero
571 initialized memory */
572template <class T> class CQuickArrayStatic : public CQuickArrayBase<T>
573{
574};
575
576typedef CQuickArrayBase<WCHAR> CQuickWSTRBase;
577typedef CQuickArray<WCHAR> CQuickWSTR;
578typedef CQuickArrayStatic<WCHAR> CQuickWSTRStatic;
579
580typedef CQuickArrayBase<CHAR> CQuickSTRBase;
581typedef CQuickArray<CHAR> CQuickSTR;
582typedef CQuickArrayStatic<CHAR> CQuickSTRStatic;
583
584class RidBitmap
585{
586public:
587 HRESULT InsertToken(mdToken token)
588 {
589 HRESULT hr = S_OK;
590 mdToken rid = RidFromToken(token);
591 SIZE_T index = rid / 8;
592 BYTE bit = (1 << (rid % 8));
593
594 if (index >= buffer.Size())
595 {
596 SIZE_T oldSize = buffer.Size();
597 SIZE_T newSize = index+1+oldSize/8;
598 IfFailRet(buffer.ReSizeNoThrow(newSize));
599 memset(&buffer[oldSize], 0, newSize-oldSize);
600 }
601
602 buffer[index] |= bit;
603 return hr;
604 }
605
606 bool IsTokenInBitmap(mdToken token)
607 {
608 mdToken rid = RidFromToken(token);
609 SIZE_T index = rid / 8;
610 BYTE bit = (1 << (rid % 8));
611
612 return ((index < buffer.Size()) && (buffer[index] & bit));
613 }
614
615 void Reset()
616 {
617 if (buffer.Size())
618 {
619 memset(&buffer[0], 0, buffer.Size());
620 }
621 }
622
623private:
624 CQuickArray<BYTE> buffer;
625};
626
627//*****************************************************************************
628//
629//***** Signature helpers
630//
631//*****************************************************************************
632
633HRESULT _CountBytesOfOneArg(
634 PCCOR_SIGNATURE pbSig,
635 ULONG *pcbTotal);
636
637HRESULT _GetFixedSigOfVarArg( // S_OK or error.
638 PCCOR_SIGNATURE pvSigBlob, // [IN] point to a blob of CLR signature
639 ULONG cbSigBlob, // [IN] size of signature
640 CQuickBytes *pqbSig, // [OUT] output buffer for fixed part of VarArg Signature
641 ULONG *pcbSigBlob); // [OUT] number of bytes written to the above output buffer
642
643#endif //!SOS_INCLUDE
644
645#if defined(_MSC_VER) && defined(_TARGET_X86_)
646#pragma optimize("", on) // restore command line default optimizations
647#endif
648
649
650//---------------------------------------------------------------------------------------
651//
652// Reads compressed integer from buffer pData, fills the result to *pnDataOut. Advances buffer pointer.
653// Doesn't read behind the end of the buffer (the end starts at pDataEnd).
654//
655inline
656__checkReturn
657HRESULT
658CorSigUncompressData_EndPtr(
659 PCCOR_SIGNATURE & pData, // [IN,OUT] Buffer
660 PCCOR_SIGNATURE pDataEnd, // End of buffer
661 DWORD * pnDataOut) // [OUT] Compressed integer read from the buffer
662{
663 _ASSERTE(pData <= pDataEnd);
664 HRESULT hr = S_OK;
665
666 INT_PTR cbDataSize = pDataEnd - pData;
667 if (cbDataSize > 4)
668 { // Compressed integer cannot be bigger than 4 bytes
669 cbDataSize = 4;
670 }
671 DWORD dwDataSize = (DWORD)cbDataSize;
672
673 ULONG cbDataOutLength;
674 IfFailRet(CorSigUncompressData(
675 pData,
676 dwDataSize,
677 pnDataOut,
678 &cbDataOutLength));
679 pData += cbDataOutLength;
680
681 return hr;
682} // CorSigUncompressData_EndPtr
683
684//---------------------------------------------------------------------------------------
685//
686// Reads CorElementType (1 byte) from buffer pData, fills the result to *pTypeOut. Advances buffer pointer.
687// Doesn't read behind the end of the buffer (the end starts at pDataEnd).
688//
689inline
690__checkReturn
691HRESULT
692CorSigUncompressElementType_EndPtr(
693 PCCOR_SIGNATURE & pData, // [IN,OUT] Buffer
694 PCCOR_SIGNATURE pDataEnd, // End of buffer
695 CorElementType * pTypeOut) // [OUT] ELEMENT_TYPE_* value read from the buffer
696{
697 _ASSERTE(pData <= pDataEnd);
698 // We don't expect pData > pDataEnd, but the runtime check doesn't cost much and it is more secure in
699 // case caller has a bug
700 if (pData >= pDataEnd)
701 { // No data
702 return META_E_BAD_SIGNATURE;
703 }
704 // Read 'type' as 1 byte
705 *pTypeOut = (CorElementType)*pData;
706 pData++;
707
708 return S_OK;
709} // CorSigUncompressElementType_EndPtr
710
711//---------------------------------------------------------------------------------------
712//
713// Reads pointer (4/8 bytes) from buffer pData, fills the result to *ppvPointerOut. Advances buffer pointer.
714// Doesn't read behind the end of the buffer (the end starts at pDataEnd).
715//
716inline
717__checkReturn
718HRESULT
719CorSigUncompressPointer_EndPtr(
720 PCCOR_SIGNATURE & pData, // [IN,OUT] Buffer
721 PCCOR_SIGNATURE pDataEnd, // End of buffer
722 void ** ppvPointerOut) // [OUT] Pointer value read from the buffer
723{
724 _ASSERTE(pData <= pDataEnd);
725 // We could just skip this check as pointers should be only in trusted (and therefore correct)
726 // signatures and we check for that on the caller side, but it won't hurt to have this check and it will
727 // make it easier to catch invalid signatures in trusted code (e.g. IL stubs, NGEN images, etc.)
728 if (pData + sizeof(void *) > pDataEnd)
729 { // Not enough data in the buffer
730 _ASSERTE(!"This signature is invalid. Note that caller should check that it is not comming from untrusted source!");
731 return META_E_BAD_SIGNATURE;
732 }
733 *ppvPointerOut = *(void * UNALIGNED *)pData;
734 pData += sizeof(void *);
735
736 return S_OK;
737} // CorSigUncompressPointer_EndPtr
738
739//---------------------------------------------------------------------------------------
740//
741// Reads compressed TypeDef/TypeRef/TypeSpec token, fills the result to *pnDataOut. Advances buffer pointer.
742// Doesn't read behind the end of the buffer (the end starts at pDataEnd).
743//
744inline
745__checkReturn
746HRESULT
747CorSigUncompressToken_EndPtr(
748 PCCOR_SIGNATURE & pData, // [IN,OUT] Buffer
749 PCCOR_SIGNATURE pDataEnd, // End of buffer
750 mdToken * ptkTokenOut) // [OUT] Token read from the buffer
751{
752 _ASSERTE(pData <= pDataEnd);
753 HRESULT hr = S_OK;
754
755 INT_PTR cbDataSize = pDataEnd - pData;
756 if (cbDataSize > 4)
757 { // Compressed token cannot be bigger than 4 bytes
758 cbDataSize = 4;
759 }
760 DWORD dwDataSize = (DWORD)cbDataSize;
761
762 ULONG cbTokenOutLength;
763 IfFailRet(CorSigUncompressToken(
764 pData,
765 dwDataSize,
766 ptkTokenOut,
767 &cbTokenOutLength));
768 pData += cbTokenOutLength;
769
770 return hr;
771} // CorSigUncompressToken_EndPtr
772
773#endif // __CORHLPRPRIV_H__
774