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// CeeFileGenWriterTokens.cpp
6//
7// This code will walk the byte codes for all methods before they are saved
8// to disk and apply the token fix-ups if they have moved.
9//
10//*****************************************************************************
11#include "stdafx.h"
12#include "ceegen.h"
13#include "../../ildasm/dasmenum.hpp"
14#define MAX_CLASSNAME_LENGTH 1024
15
16//********** Locals. **********************************************************
17OPCODE DecodeOpcode(const BYTE *pCode, DWORD *pdwLen);
18
19
20
21//********** Code. ************************************************************
22
23
24//*****************************************************************************
25// Method bodies are kept in the text section. The meta data contains the
26// RVA of each method body in the image. The TextSection object contains the
27// actual raw bytes where the method bodies are located. This code will
28// determine the raw offset of each method based on the RVA kept in the meta
29// data.
30//*****************************************************************************
31
32HRESULT CeeFileGenWriter::MapTokens(
33 CeeGenTokenMapper *pMapper,
34 IMetaDataImport *pImport)
35{
36 mdTypeDef td;
37 mdMethodDef md;
38 ULONG count;
39 ULONG MethodRVA;
40 ULONG codeOffset;
41 ULONG BaseRVA;
42 DWORD dwFlags;
43 DWORD iFlags;
44 HCORENUM hTypeDefs = 0, hEnum = 0;
45 WCHAR rcwName[MAX_CLASSNAME_LENGTH];
46 HRESULT hr;
47 CeeSection TextSection = getTextSection();
48
49 // Ask for the base RVA of the first method in the stream. All other
50 // method RVA's are >= that value, and will give us the raw offset required.
51
52 hr = getMethodRVA(0, &BaseRVA);
53 _ASSERTE(SUCCEEDED(hr));
54 // do globals first
55 while ((hr = pImport->EnumMethods(&hEnum, mdTokenNil, &md, 1, &count)) == S_OK)
56 {
57 hr = pImport->GetMethodProps(md, NULL,
58 rcwName, lengthof(rcwName), NULL,
59 &dwFlags, NULL, NULL,
60 &MethodRVA, &iFlags);
61 _ASSERTE(SUCCEEDED(hr));
62
63 if (MethodRVA == 0 || ((IsMdAbstract(dwFlags) || IsMiInternalCall(iFlags)) ||
64 (! IsMiIL(iFlags) && ! IsMiOPTIL(iFlags))))
65 continue;
66
67 // The raw offset of the method is the RVA in the image minus
68 // the first method in the text section.
69 codeOffset = MethodRVA - BaseRVA;
70 hr = MapTokensForMethod(pMapper,
71 (BYTE *) TextSection.computePointer(codeOffset),
72 rcwName);
73 if (FAILED(hr))
74 goto ErrExit;
75 }
76 if (hEnum) pImport->CloseEnum(hEnum);
77 hEnum = 0;
78
79 while ((hr = pImport->EnumTypeDefs(&hTypeDefs, &td, 1, &count)) == S_OK)
80 {
81 while ((hr = pImport->EnumMethods(&hEnum, td, &md, 1, &count)) == S_OK)
82 {
83 hr = pImport->GetMethodProps(md, NULL,
84 rcwName, lengthof(rcwName), NULL,
85 &dwFlags, NULL, NULL,
86 &MethodRVA, &iFlags);
87 _ASSERTE(SUCCEEDED(hr));
88
89 if (MethodRVA == 0 || ((IsMdAbstract(dwFlags) || IsMiInternalCall(iFlags)) ||
90 (! IsMiIL(iFlags) && ! IsMiOPTIL(iFlags))))
91 continue;
92
93
94 // The raw offset of the method is the RVA in the image minus
95 // the first method in the text section.
96 codeOffset = MethodRVA - BaseRVA;
97 hr = MapTokensForMethod(pMapper,
98 (BYTE *) TextSection.computePointer(codeOffset),
99 rcwName);
100 if (FAILED(hr))
101 goto ErrExit;
102 }
103
104 if (hEnum) pImport->CloseEnum(hEnum);
105 hEnum = 0;
106 }
107
108ErrExit:
109 if (hTypeDefs) pImport->CloseEnum(hTypeDefs);
110 if (hEnum) pImport->CloseEnum(hEnum);
111 return (hr);
112}
113
114
115//*****************************************************************************
116// This method will walk the byte codes of an IL method looking for tokens.
117// For each token found, it will check to see if it has been moved, and if
118// so, apply the new token value in its place.
119//*****************************************************************************
120HRESULT CeeFileGenWriter::MapTokensForMethod(
121 CeeGenTokenMapper *pMapper,
122 BYTE *pCode,
123 LPCWSTR szMethodName)
124{
125 mdToken tkTo;
126 DWORD PC;
127
128 COR_ILMETHOD_DECODER method((COR_ILMETHOD*) pCode);
129
130 // If compressed IL is being emitted, this routine will have no idea how to walk the tokens,
131 // so don't do it
132 if (m_dwMacroDefinitionSize != 0)
133 return S_OK;
134
135 pCode = const_cast<BYTE*>(method.Code);
136
137 PC = 0;
138 while (PC < method.GetCodeSize())
139 {
140 DWORD Len;
141 DWORD i;
142 OPCODE instr;
143
144 instr = DecodeOpcode(&pCode[PC], &Len);
145
146 if (instr == CEE_COUNT)
147 {
148 _ASSERTE(0 && "Instruction decoding error\n");
149 return E_FAIL;
150 }
151
152
153 PC += Len;
154
155 switch (OpcodeInfo[instr].Type)
156 {
157 DWORD tk;
158
159 default:
160 {
161 _ASSERTE(0 && "Unknown instruction\n");
162 return E_FAIL;
163 }
164
165 case InlineNone:
166 break;
167
168 case ShortInlineI:
169 case ShortInlineVar:
170 case ShortInlineBrTarget:
171 PC++;
172 break;
173
174 case InlineVar:
175 PC += 2;
176 break;
177
178 case InlineI:
179 case ShortInlineR:
180 case InlineBrTarget:
181 case InlineRVA:
182 PC += 4;
183 break;
184
185 case InlineI8:
186 case InlineR:
187 PC += 8;
188 break;
189
190 case InlinePhi:
191 {
192 DWORD cases = pCode[PC];
193 PC += 2 * cases + 1;
194 break;
195 }
196
197 case InlineSwitch:
198 {
199 DWORD cases = pCode[PC] + (pCode[PC+1] << 8) + (pCode[PC+2] << 16) + (pCode[PC+3] << 24);
200
201 PC += 4;
202 for (i = 0; i < cases; i++)
203 {
204 PC += 4;
205 }
206
207 // skip bottom of loop which prints szString
208 continue;
209 }
210
211 case InlineTok:
212 case InlineSig:
213 case InlineMethod:
214 case InlineField:
215 case InlineType:
216 case InlineString:
217 {
218 tk = GET_UNALIGNED_VAL32(&pCode[PC]);
219
220 if (pMapper->HasTokenMoved(tk, tkTo))
221 {
222 SET_UNALIGNED_VAL32(&pCode[PC], tkTo);
223 }
224
225 PC += 4;
226 break;
227 }
228 }
229 }
230
231 return S_OK;
232}
233
234
235
236
237OPCODE DecodeOpcode(const BYTE *pCode, DWORD *pdwLen)
238{
239 OPCODE opcode;
240
241 *pdwLen = 1;
242 opcode = OPCODE(pCode[0]);
243 switch(opcode) {
244 case CEE_PREFIX1:
245 opcode = OPCODE(pCode[1] + 256);
246 if (opcode < 0 || opcode >= CEE_COUNT)
247 opcode = CEE_COUNT;
248 *pdwLen = 2;
249 break;
250 case CEE_PREFIXREF:
251 case CEE_PREFIX2:
252 case CEE_PREFIX3:
253 case CEE_PREFIX4:
254 case CEE_PREFIX5:
255 case CEE_PREFIX6:
256 case CEE_PREFIX7:
257 *pdwLen = 3;
258 return CEE_COUNT;
259 default:
260 break;
261 }
262 return opcode;
263}
264