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 using System;
6 using System.Runtime;
7 using System.Collections.Generic;
8 using System.Runtime.InteropServices;
9 
10 using Internal.JitInterface;
11 using Internal.Runtime.Augments;
12 using Internal.Runtime.TypeLoader;
13 using Internal.TypeSystem;
14 
15 using ILCompiler;
16 using ILCompiler.DependencyAnalysisFramework;
17 using ILCompiler.DependencyAnalysis;
18 
19 using Debug = System.Diagnostics.Debug;
20 
21 namespace Internal.Runtime.JitSupport
22 {
23     public class RyuJitExecutionStrategy : MethodExecutionStrategy
24     {
25         private CorInfoImpl _corInfoImpl;
26         private TypeSystemContext _context;
27         private NodeFactory _nodeFactory;
28 
UpdateBytesUsed(ObjectNode.ObjectData nodeData, ref int bytesUsed)29         private void UpdateBytesUsed(ObjectNode.ObjectData nodeData, ref int bytesUsed)
30         {
31             bytesUsed = bytesUsed.AlignUp(nodeData.Alignment);
32             bytesUsed += nodeData.Data.Length;
33             return;
34         }
35 
36         [DllImport("jitinterface")]
AllocJittedCode(UInt32 cbCode, UInt32 align, out IntPtr pCodeManager)37         static extern IntPtr AllocJittedCode(UInt32 cbCode, UInt32 align, out IntPtr pCodeManager);
38 
39         [DllImport("jitinterface")]
SetEHInfoPtr(IntPtr pCodeManager, IntPtr pbCode, IntPtr ehInfo)40         static extern void SetEHInfoPtr(IntPtr pCodeManager, IntPtr pbCode, IntPtr ehInfo);
41 
42         [DllImport("jitinterface")]
PublishRuntimeFunction( IntPtr pCodeManager, IntPtr pbCode, IntPtr pMainRuntimeFunction, UInt32 startOffset, UInt32 endOffset, byte[] pUnwindInfo, UInt32 cbUnwindInfo, byte[] pGCData, UInt32 cbGCData)43         static extern unsafe IntPtr PublishRuntimeFunction(
44             IntPtr pCodeManager,
45             IntPtr pbCode,
46             IntPtr pMainRuntimeFunction,
47             UInt32 startOffset,
48             UInt32 endOffset,
49             byte[] pUnwindInfo,
50             UInt32 cbUnwindInfo,
51             byte[] pGCData,
52             UInt32 cbGCData);
53 
54         [DllImport("jitinterface")]
UpdateRuntimeFunctionTable(IntPtr pCodeManager)55         static extern void UpdateRuntimeFunctionTable(IntPtr pCodeManager);
56 
57         [DllImport("jitinterface")]
InitJitCodeManager(IntPtr mrtModule)58         static extern void InitJitCodeManager(IntPtr mrtModule);
59 
OnEntryPoint(MethodEntrypointPtr methodEntrypoint, IntPtr callerArgs)60         public override IntPtr OnEntryPoint(MethodEntrypointPtr methodEntrypoint, IntPtr callerArgs)
61         {
62             lock (this)
63             {
64                 if (_corInfoImpl == null)
65                 {
66                     InitJitCodeManager(RuntimeAugments.RhGetOSModuleForMrt ());
67 
68                     // TODO: Recycle jit interface object and TypeSystemContext
69                     _context = TypeSystemContextFactory.Create();
70 
71                     Compilation compilation = new Compilation(_context);
72                     _nodeFactory = compilation.NodeFactory;
73 
74                     JitConfigProvider configProvider = new JitConfigProvider(new CorJitFlag[] { CorJitFlag.CORJIT_FLAG_DEBUG_CODE }, Array.Empty<KeyValuePair<string, string>>());
75 
76                     _corInfoImpl = new CorInfoImpl(compilation, configProvider);
77                 }
78 
79                 MethodDesc methodToCompile = methodEntrypoint.MethodIdentifier.ToMethodDesc(_context);
80 
81                 JitMethodCodeNode codeNode = new JitMethodCodeNode(methodToCompile);
82                 _corInfoImpl.CompileMethod(codeNode);
83 
84                 ObjectNode.ObjectData codeData = codeNode.GetData(null, false);
85 
86                 List<ObjectNode> nodesToEmit = new List<ObjectNode>();
87                 Dictionary<DependencyNodeCore<NodeFactory>, object> relocTargets = new Dictionary<DependencyNodeCore<NodeFactory>, object>();
88                 int totalAllocSizeNeeded = 0;
89                 int nonObjectRelocTargets = 0;
90 
91                 nodesToEmit.Add(codeNode);
92                 UpdateBytesUsed(codeNode.GetData(_nodeFactory), ref totalAllocSizeNeeded);
93 
94                 int offsetOfEHData = totalAllocSizeNeeded;
95 
96                 if (codeNode.EHInfo != null)
97                 {
98                     Debug.Assert(codeNode.EHInfo.Alignment == 1); // Assert needed as otherwise offsetOfEHData will be wrong
99 
100                     UpdateBytesUsed(codeNode.EHInfo, ref totalAllocSizeNeeded);
101                     ComputeDependencySizeAndRelocData(codeNode.EHInfo, relocTargets, nodesToEmit, ref totalAllocSizeNeeded, ref nonObjectRelocTargets);
102                 }
103 
104                 for (int i = 0; i < nodesToEmit.Count; i++)
105                 {
106                     ObjectNode objNode = nodesToEmit[i];
107                     ComputeDependencySizeAndRelocData(objNode.GetData(_nodeFactory, true), relocTargets, nodesToEmit, ref totalAllocSizeNeeded, ref nonObjectRelocTargets);
108                 }
109 
110                 if (nonObjectRelocTargets != 0)
111                 {
112                     totalAllocSizeNeeded = totalAllocSizeNeeded.AlignUp(IntPtr.Size);
113                 }
114 
115                 int relocTargetOffsetStart = totalAllocSizeNeeded;
116 
117                 DependencyNodeCore<NodeFactory>[] relocTargetsArray = new DependencyNodeCore<NodeFactory>[nonObjectRelocTargets];
118                 {
119                     int iRelocTarget = 0;
120                     foreach (var relocTarget in relocTargets)
121                     {
122                         if (!(relocTarget.Key is ObjectNode))
123                         {
124                             relocTargetsArray[iRelocTarget] = relocTarget.Key;
125                             totalAllocSizeNeeded += IntPtr.Size;
126                             iRelocTarget++;
127                         }
128                     }
129                     Debug.Assert(iRelocTarget == nonObjectRelocTargets);
130                 }
131 
132                 GenericDictionaryCell[] genDictCells = new GenericDictionaryCell[relocTargetsArray.Length];
133                 for (int iRelocTarget = 0; iRelocTarget < relocTargetsArray.Length; iRelocTarget++)
134                 {
135                     DependencyNodeCore<NodeFactory> relocTarget = relocTargetsArray[iRelocTarget];
136                     GenericDictionaryCell newCell = null;
137 
138                     if (relocTarget is ExternObjectSymbolNode)
139                     {
140                         var externObjectSymbolNode = (ExternObjectSymbolNode)relocTarget;
141                         var newMethodCell = externObjectSymbolNode.GetDictionaryCell();
142                         newCell = newMethodCell;
143                     }
144 
145                     if (newCell == null)
146                     {
147                         Environment.FailFast("Unknown reloc target type");
148                     }
149                     genDictCells[iRelocTarget] = newCell;
150                 }
151 
152                 IntPtr[] relocTargetsAsIntPtr = null;
153 
154                 TypeLoaderEnvironment.Instance.RunUnderTypeLoaderLock(
155                     () =>
156                     {
157                         TypeBuilderApi.ResolveMultipleCells(genDictCells, out relocTargetsAsIntPtr);
158                     });
159 
160                 // Layout of allocated memory...
161                 // ObjectNodes (aligned as appropriate)
162                 IntPtr pCodeManager;
163                 IntPtr jittedCode = AllocJittedCode(checked((uint)totalAllocSizeNeeded), 8/* TODO, alignment calculation */, out pCodeManager);
164                 int currentOffset = 0;
165 
166                 foreach (var node in nodesToEmit)
167                 {
168                     ObjectNode.ObjectData objectData = node.GetData(_nodeFactory);
169                     EmitAndRelocData(objectData, jittedCode, relocTargetOffsetStart, ref currentOffset, relocTargetsArray, relocTargetsAsIntPtr);
170 
171                     // EHInfo doesn't get its own node, but it does get emitted into the stream.
172                     if ((node == codeNode) && (codeNode.EHInfo != null))
173                     {
174                         Debug.Assert(offsetOfEHData == currentOffset);
175                         EmitAndRelocData(codeNode.EHInfo, jittedCode, relocTargetOffsetStart, ref currentOffset, relocTargetsArray, relocTargetsAsIntPtr);
176                     }
177                 }
178 
179                 foreach (IntPtr ptr in relocTargetsAsIntPtr)
180                 {
181                     currentOffset = currentOffset.AlignUp(IntPtr.Size);
182                     Marshal.WriteIntPtr(jittedCode, currentOffset, ptr);
183                     currentOffset += IntPtr.Size;
184                 }
185 
186                 SetEHInfoPtr(pCodeManager, jittedCode, jittedCode + offsetOfEHData);
187 
188                 IntPtr mainRuntimeFunction = IntPtr.Zero;
189 
190                 for (int i = 0; i < codeNode.FrameInfos.Length; i++)
191                 {
192                     FrameInfo frame = codeNode.FrameInfos[i];
193                     byte[] frameData = frame.BlobData;
194                     byte[] gcInfoData = Array.Empty<byte>();
195                     byte[] gcInfoDataDeref = frameData;
196 
197                     if (i == 0)
198                     {
199                         // For main function, add the gc info to the data
200                         gcInfoDataDeref = gcInfoData = codeNode.GCInfo;
201                     }
202 
203                     IntPtr publishedFunction = PublishRuntimeFunction(pCodeManager,
204                                     jittedCode,
205                                     mainRuntimeFunction,
206                                     checked((uint)frame.StartOffset),
207                                     checked((uint)frame.EndOffset),
208                                     frameData,
209                                     checked((uint)frameData.Length),
210                                     gcInfoDataDeref,
211                                     checked((uint)gcInfoData.Length));
212 
213                     if (i == 0)
214                     {
215                         mainRuntimeFunction = publishedFunction;
216                     }
217                 }
218 
219                 if (mainRuntimeFunction != IntPtr.Zero)
220                 {
221                     UpdateRuntimeFunctionTable(pCodeManager);
222                 }
223 
224                 methodEntrypoint.MethodCode = jittedCode;
225 
226                 return jittedCode;
227             }
228         }
229 
ComputeDependencySizeAndRelocData(ObjectNode.ObjectData objectData, Dictionary<DependencyNodeCore<NodeFactory>, object> relocTargets, List<ObjectNode> nodesToEmit, ref int totalAllocSizeNeeded, ref int nonObjectRelocTargets)230         void ComputeDependencySizeAndRelocData(ObjectNode.ObjectData objectData, Dictionary<DependencyNodeCore<NodeFactory>, object> relocTargets, List<ObjectNode> nodesToEmit, ref int totalAllocSizeNeeded, ref int nonObjectRelocTargets)
231         {
232             foreach (var reloc in objectData.Relocs)
233             {
234                 DependencyNodeCore<NodeFactory> relocTargetAsNode = (DependencyNodeCore<NodeFactory>)reloc.Target;
235 
236                 if (!relocTargets.ContainsKey(relocTargetAsNode))
237                 {
238                     relocTargets.Add(relocTargetAsNode, null);
239                     ObjectNode relocTargetObjectNode = relocTargetAsNode as ObjectNode;
240                     if (relocTargetObjectNode != null)
241                     {
242                         UpdateBytesUsed(relocTargetObjectNode.GetData(_nodeFactory), ref totalAllocSizeNeeded);
243                         nodesToEmit.Add(relocTargetObjectNode);
244                     }
245                     else
246                     {
247                         nonObjectRelocTargets++;
248                     }
249                 }
250             }
251         }
252 
EmitAndRelocData(ObjectNode.ObjectData objectData, IntPtr jittedCode, int relocTargetOffsetStart, ref int currentOffset, DependencyNodeCore<NodeFactory>[] relocTargetsArray, IntPtr[] relocTargetsAsIntPtr)253         void EmitAndRelocData(ObjectNode.ObjectData objectData, IntPtr jittedCode, int relocTargetOffsetStart, ref int currentOffset, DependencyNodeCore<NodeFactory>[] relocTargetsArray, IntPtr[] relocTargetsAsIntPtr)
254         {
255             currentOffset = currentOffset.AlignUp(objectData.Alignment);
256             Marshal.Copy(objectData.Data, 0, jittedCode + currentOffset, objectData.Data.Length);
257             foreach (Relocation reloc in objectData.Relocs)
258             {
259                 switch (reloc.RelocType)
260                 {
261                     case RelocType.IMAGE_REL_BASED_REL32:
262                         // 4 byte offset from current pointer to target
263                         // ADD ROUTINE TO FIND Relocation
264                         for (int i = 0; i < relocTargetsArray.Length; i++)
265                         {
266                             if (relocTargetsArray[i] == reloc.Target)
267                             {
268                                 int relocTargetAddressOffset = relocTargetOffsetStart + i * IntPtr.Size;
269                                 int relocAddressOffset = currentOffset + reloc.Offset;
270 
271                                 int relocTargetAddressDelta = relocTargetAddressOffset - relocAddressOffset - 4;
272                                 Marshal.WriteInt32(jittedCode, relocAddressOffset, relocTargetAddressDelta);
273                                 break;
274                             }
275                         }
276                         break;
277 
278                     default:
279                         Environment.FailFast("Unknown RelocType");
280                         break;
281                 }
282             }
283 
284             currentOffset += objectData.Data.Length;
285         }
286     }
287 }
288