| 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: methodimpl.cpp | 
| 6 | // | 
| 7 |  | 
| 8 |  | 
| 9 | // | 
| 10 |  | 
| 11 | // | 
| 12 | // ============================================================================ | 
| 13 |  | 
| 14 | #include "common.h" | 
| 15 | #include "methodimpl.h" | 
| 16 |  | 
| 17 | DWORD MethodImpl::FindSlotIndex(DWORD slot) | 
| 18 | { | 
| 19 |     CONTRACTL { | 
| 20 |         NOTHROW; | 
| 21 |         GC_NOTRIGGER; | 
| 22 |         MODE_ANY; | 
| 23 |         PRECONDITION(CheckPointer(GetSlots())); | 
| 24 |     } CONTRACTL_END; | 
| 25 |  | 
| 26 |     DWORD dwSize = GetSize(); | 
| 27 |     if(dwSize == 0) { | 
| 28 |         return INVALID_INDEX; | 
| 29 |     } | 
| 30 |  | 
| 31 |     // Simple binary search | 
| 32 |     PTR_DWORD rgSlots = GetSlots(); | 
| 33 |     INT32     l       = 0; | 
| 34 |     INT32     r       = dwSize - 1; | 
| 35 |     INT32     pivot; | 
| 36 |  | 
| 37 |     while(1) { | 
| 38 |         pivot =  (l + r) / 2; | 
| 39 |  | 
| 40 |         if(rgSlots[pivot] == slot) { | 
| 41 |             break; // found it | 
| 42 |         } | 
| 43 |         else if(rgSlots[pivot] < slot) { | 
| 44 |             l = pivot + 1; | 
| 45 |         } | 
| 46 |         else { | 
| 47 |             r = pivot - 1; | 
| 48 |         } | 
| 49 |  | 
| 50 |         if(l > r) { | 
| 51 |             return INVALID_INDEX; // Not here | 
| 52 |         } | 
| 53 |     } | 
| 54 |  | 
| 55 |     CONSISTENCY_CHECK(pivot >= 0); | 
| 56 |     return (DWORD)pivot; | 
| 57 | } | 
| 58 |  | 
| 59 | PTR_MethodDesc MethodImpl::FindMethodDesc(DWORD slot, PTR_MethodDesc defaultReturn) | 
| 60 | { | 
| 61 |     CONTRACTL | 
| 62 |     { | 
| 63 |         if (FORBIDGC_LOADER_USE_ENABLED()) NOTHROW; else THROWS; | 
| 64 |         if (FORBIDGC_LOADER_USE_ENABLED()) GC_NOTRIGGER; else GC_TRIGGERS; | 
| 65 |         if (FORBIDGC_LOADER_USE_ENABLED()) FORBID_FAULT; else { INJECT_FAULT(COMPlusThrowOM()); } | 
| 66 |         MODE_ANY; | 
| 67 |     } | 
| 68 |     CONTRACTL_END | 
| 69 |  | 
| 70 |     DWORD slotIndex = FindSlotIndex(slot); | 
| 71 |     if (slotIndex == INVALID_INDEX) { | 
| 72 |         return defaultReturn; | 
| 73 |     } | 
| 74 |  | 
| 75 |     return GetMethodDesc(slotIndex, defaultReturn); | 
| 76 | } | 
| 77 |  | 
| 78 | PTR_MethodDesc MethodImpl::GetMethodDesc(DWORD slotIndex, PTR_MethodDesc defaultReturn) | 
| 79 | { | 
| 80 |     CONTRACTL | 
| 81 |     { | 
| 82 |         if (FORBIDGC_LOADER_USE_ENABLED()) NOTHROW; else THROWS; | 
| 83 |         if (FORBIDGC_LOADER_USE_ENABLED()) GC_NOTRIGGER; else GC_TRIGGERS; | 
| 84 |         if (FORBIDGC_LOADER_USE_ENABLED()) FORBID_FAULT; else { INJECT_FAULT(COMPlusThrowOM()); } | 
| 85 |         MODE_ANY; | 
| 86 |     } | 
| 87 |     CONTRACTL_END | 
| 88 |  | 
| 89 |     DPTR(RelativePointer<PTR_MethodDesc>) pRelPtrForSlot = GetImpMDsNonNull(); | 
| 90 |     // The method descs are not offset by one | 
| 91 |     TADDR base = dac_cast<TADDR>(pRelPtrForSlot) + slotIndex * sizeof(RelativePointer<MethodDesc *>); | 
| 92 |     PTR_MethodDesc result = RelativePointer<PTR_MethodDesc>::GetValueMaybeNullAtPtr(base); | 
| 93 |  | 
| 94 |     // Prejitted images may leave NULL in this table if | 
| 95 |     // the methoddesc is declared in another module. | 
| 96 |     // In this case we need to manually compute & restore it | 
| 97 |     // from the slot number. | 
| 98 |  | 
| 99 |     if (result == NULL) | 
| 100 | #ifndef DACCESS_COMPILE  | 
| 101 |         result = RestoreSlot(slotIndex, defaultReturn->GetMethodTable()); | 
| 102 | #else // DACCESS_COMPILE | 
| 103 |         DacNotImpl(); | 
| 104 | #endif // DACCESS_COMPILE | 
| 105 |  | 
| 106 |     return result; | 
| 107 | } | 
| 108 |  | 
| 109 | #ifndef DACCESS_COMPILE  | 
| 110 |  | 
| 111 | MethodDesc *MethodImpl::RestoreSlot(DWORD index, MethodTable *pMT) | 
| 112 | { | 
| 113 |     CONTRACTL | 
| 114 |     { | 
| 115 |         NOTHROW; | 
| 116 |         GC_NOTRIGGER; | 
| 117 |         FORBID_FAULT; | 
| 118 |         PRECONDITION(!pdwSlots.IsNull()); | 
| 119 |     } | 
| 120 |     CONTRACTL_END | 
| 121 |  | 
| 122 |     MethodDesc *result; | 
| 123 |  | 
| 124 |     PREFIX_ASSUME(!pdwSlots.IsNull()); | 
| 125 |     DWORD slot = GetSlots()[index]; | 
| 126 |  | 
| 127 |     // Since the overridden method is in a different module, we | 
| 128 |     // are guaranteed that it is from a different class.  It is | 
| 129 |     // either an override of a parent virtual method or parent-implemented | 
| 130 |     // interface, or of an interface that this class has introduced. | 
| 131 |  | 
| 132 |     // In the former 2 cases, the slot number will be in the parent's | 
| 133 |     // vtable section, and we can retrieve the implemented MethodDesc from | 
| 134 |     // there.  In the latter case, we can search through our interface | 
| 135 |     // map to determine which interface it is from. | 
| 136 |  | 
| 137 |     MethodTable *pParentMT = pMT->GetParentMethodTable(); | 
| 138 |     CONSISTENCY_CHECK(pParentMT != NULL && slot < pParentMT->GetNumVirtuals()); | 
| 139 |     { | 
| 140 |         result = pParentMT->GetMethodDescForSlot(slot); | 
| 141 |     } | 
| 142 |  | 
| 143 |     _ASSERTE(result != NULL); | 
| 144 |  | 
| 145 |     // Don't worry about races since we would all be setting the same result | 
| 146 |     if (EnsureWritableExecutablePagesNoThrow(&pImplementedMD.GetValue()[index], | 
| 147 |                                              sizeof(pImplementedMD.GetValue()[index]))) | 
| 148 |         pImplementedMD.GetValue()[index].SetValue(result); | 
| 149 |  | 
| 150 |     return result; | 
| 151 | } | 
| 152 |  | 
| 153 | /////////////////////////////////////////////////////////////////////////////////////// | 
| 154 | void MethodImpl::SetSize(LoaderHeap *pHeap, AllocMemTracker *pamTracker, DWORD size) | 
| 155 | { | 
| 156 |     CONTRACTL { | 
| 157 |         THROWS; | 
| 158 |         GC_NOTRIGGER; | 
| 159 |         PRECONDITION(CheckPointer(this)); | 
| 160 |         PRECONDITION(pdwSlots.GetValueMaybeNull()==NULL && pImplementedMD.GetValueMaybeNull()==NULL); | 
| 161 |         INJECT_FAULT(ThrowOutOfMemory()); | 
| 162 |     } CONTRACTL_END; | 
| 163 |  | 
| 164 |     if(size > 0) { | 
| 165 |         // An array of DWORDs, the first entry representing count, and the rest representing slot numbers | 
| 166 |         S_SIZE_T cbCountAndSlots = S_SIZE_T(sizeof(DWORD)) +        // DWORD for the total count of slots | 
| 167 |                                     S_SIZE_T(size) * S_SIZE_T(sizeof(DWORD)) + // DWORD each for the slot numbers | 
| 168 |                                     S_SIZE_T(size) * S_SIZE_T(sizeof(mdToken)); // Token each for the method tokens | 
| 169 |  | 
| 170 |         // MethodDesc* for each of the implemented methods | 
| 171 |         S_SIZE_T cbMethodDescs = S_SIZE_T(size) * S_SIZE_T(sizeof(RelativePointer<MethodDesc *>)); | 
| 172 |  | 
| 173 |         // Need to align-up the slot entries so that the MethodDesc* array starts on a pointer boundary. | 
| 174 |         cbCountAndSlots.AlignUp(sizeof(MethodDesc*)); | 
| 175 |         S_SIZE_T cbTotal =  cbCountAndSlots + cbMethodDescs; | 
| 176 |         if(cbCountAndSlots.IsOverflow()) | 
| 177 |             ThrowOutOfMemory(); | 
| 178 |  | 
| 179 |         // Allocate the memory. | 
| 180 |         LPBYTE pAllocData = (BYTE*)pamTracker->Track(pHeap->AllocMem(cbTotal)); | 
| 181 |  | 
| 182 |         // Set the count and slot array | 
| 183 |         pdwSlots.SetValue((DWORD*)pAllocData); | 
| 184 |  | 
| 185 |         // Set the MethodDesc* array. Make sure to adjust for alignment. | 
| 186 |         pImplementedMD.SetValue((RelativePointer<MethodDesc*> *)ALIGN_UP(pAllocData + cbCountAndSlots.Value(), sizeof(RelativePointer <MethodDesc*>))); | 
| 187 |  | 
| 188 |         // Store the count in the first entry | 
| 189 |         *pdwSlots.GetValue() = size; | 
| 190 |     } | 
| 191 | } | 
| 192 |  | 
| 193 | /////////////////////////////////////////////////////////////////////////////////////// | 
| 194 | void MethodImpl::SetData(DWORD* slots, mdToken* tokens, RelativePointer<MethodDesc*>* md) | 
| 195 | { | 
| 196 |     CONTRACTL { | 
| 197 |         NOTHROW; | 
| 198 |         GC_NOTRIGGER; | 
| 199 |         PRECONDITION(CheckPointer(this)); | 
| 200 |         PRECONDITION(!pdwSlots.IsNull()); | 
| 201 |     } CONTRACTL_END; | 
| 202 |  | 
| 203 |     DWORD *pdwSize = pdwSlots.GetValue(); | 
| 204 |     DWORD dwSize = *pdwSize; | 
| 205 |     memcpy(&(pdwSize[1]), slots, dwSize*sizeof(DWORD)); | 
| 206 |      | 
| 207 |     // Copy tokens that correspond to the slots above | 
| 208 |     memcpy(&(pdwSize[1 + dwSize]), tokens, dwSize*sizeof(mdToken)); | 
| 209 |  | 
| 210 |     RelativePointer<MethodDesc *> *pImplMD = pImplementedMD.GetValue(); | 
| 211 |  | 
| 212 |     for (uint32_t i = 0; i < dwSize; ++i) | 
| 213 |     { | 
| 214 |         pImplMD[i].SetValue(md[i].GetValue()); | 
| 215 |     } | 
| 216 | } | 
| 217 |  | 
| 218 | #ifdef FEATURE_NATIVE_IMAGE_GENERATION | 
| 219 | void MethodImpl::Save(DataImage *image) | 
| 220 | { | 
| 221 |     STANDARD_VM_CONTRACT; | 
| 222 |  | 
| 223 |     DWORD size = GetSize(); | 
| 224 |     _ASSERTE(size > 0); | 
| 225 |  | 
| 226 |     image->StoreStructure(pdwSlots.GetValue(), (size+1)*sizeof(DWORD)+size*sizeof(mdToken), | 
| 227 |                                     DataImage::ITEM_METHOD_DESC_COLD, | 
| 228 |                                     sizeof(DWORD)); | 
| 229 |     image->StoreStructure(pImplementedMD.GetValue(), size*sizeof(RelativePointer<MethodDesc*>), | 
| 230 |                                     DataImage::ITEM_METHOD_DESC_COLD, | 
| 231 |                                     sizeof(MethodDesc*)); | 
| 232 | } | 
| 233 |  | 
| 234 | void MethodImpl::Fixup(DataImage *image, PVOID p, SSIZE_T offset) | 
| 235 | { | 
| 236 |     STANDARD_VM_CONTRACT; | 
| 237 |  | 
| 238 |     DWORD size = GetSize(); | 
| 239 |     _ASSERTE(size > 0); | 
| 240 |  | 
| 241 |     for (DWORD iMD = 0; iMD < size; iMD++) | 
| 242 |     { | 
| 243 |         // <TODO> Why not use FixupMethodDescPointer? </TODO> | 
| 244 |         // <TODO> Does it matter if the MethodDesc needs a restore? </TODO>               | 
| 245 |  | 
| 246 |         RelativePointer<MethodDesc *> *pRelPtr = pImplementedMD.GetValue(); | 
| 247 |         MethodDesc * pMD = pRelPtr[iMD].GetValueMaybeNull(); | 
| 248 |  | 
| 249 |         if (image->CanEagerBindToMethodDesc(pMD) && | 
| 250 |             image->CanHardBindToZapModule(pMD->GetLoaderModule())) | 
| 251 |         { | 
| 252 |             image->FixupRelativePointerField(pImplementedMD.GetValue(), iMD * sizeof(RelativePointer<MethodDesc *>)); | 
| 253 |         } | 
| 254 |         else | 
| 255 |         { | 
| 256 |             image->ZeroPointerField(pImplementedMD.GetValue(), iMD * sizeof(RelativePointer<MethodDesc *>)); | 
| 257 |         } | 
| 258 |     } | 
| 259 |  | 
| 260 |     image->FixupRelativePointerField(p, offset + offsetof(MethodImpl, pdwSlots)); | 
| 261 |     image->FixupRelativePointerField(p, offset + offsetof(MethodImpl, pImplementedMD)); | 
| 262 | } | 
| 263 |  | 
| 264 | #endif // FEATURE_NATIVE_IMAGE_GENERATION | 
| 265 |  | 
| 266 | #endif //!DACCESS_COMPILE | 
| 267 |  | 
| 268 | #ifdef DACCESS_COMPILE  | 
| 269 |  | 
| 270 | void | 
| 271 | MethodImpl::EnumMemoryRegions(CLRDataEnumMemoryFlags flags) | 
| 272 | { | 
| 273 |     SUPPORTS_DAC; | 
| 274 | #ifndef STUB_DISPATCH_ALL  | 
| 275 |     CONSISTENCY_CHECK_MSG(FALSE, "Stub Dispatch forbidden code" ); | 
| 276 | #else // STUB_DISPATCH_ALL | 
| 277 |     // 'this' memory should already be enumerated as | 
| 278 |     // part of the base MethodDesc. | 
| 279 |  | 
| 280 |     if (GetSlotsRaw().IsValid() && GetSize()) | 
| 281 |     { | 
| 282 |         ULONG32 numSlots = GetSize(); | 
| 283 |         DacEnumMemoryRegion(dac_cast<TADDR>(GetSlotsRawNonNull()), | 
| 284 |                             (numSlots + 1) * sizeof(DWORD) + numSlots * sizeof(mdToken)); | 
| 285 |  | 
| 286 |         if (GetImpMDs().IsValid()) | 
| 287 |         { | 
| 288 |             DacEnumMemoryRegion(dac_cast<TADDR>(GetImpMDsNonNull()), | 
| 289 |                                 numSlots * sizeof(RelativePointer<MethodDesc *>)); | 
| 290 |             for (DWORD i = 0; i < numSlots; i++) | 
| 291 |             { | 
| 292 |                 DPTR(RelativePointer<PTR_MethodDesc>) pRelPtr = GetImpMDsNonNull(); | 
| 293 |                 PTR_MethodDesc methodDesc = pRelPtr[i].GetValueMaybeNull(); | 
| 294 |                 if (methodDesc.IsValid()) | 
| 295 |                 { | 
| 296 |                     methodDesc->EnumMemoryRegions(flags); | 
| 297 |                 } | 
| 298 |             } | 
| 299 |         } | 
| 300 |     } | 
| 301 | #endif // STUB_DISPATCH_ALL | 
| 302 | } | 
| 303 |  | 
| 304 | #endif //DACCESS_COMPILE | 
| 305 |  | 
| 306 | #ifndef DACCESS_COMPILE  | 
| 307 | MethodImpl::Iterator::Iterator(MethodDesc *pMD) : m_pMD(pMD), m_pImpl(NULL), m_iCur(0) | 
| 308 | { | 
| 309 |     LIMITED_METHOD_CONTRACT; | 
| 310 |     if (pMD->IsMethodImpl()) | 
| 311 |     { | 
| 312 |         m_pImpl = pMD->GetMethodImpl(); | 
| 313 |     } | 
| 314 | } | 
| 315 | #endif //!DACCESS_COMPILE | 
| 316 |  | 
| 317 |  |