Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 : /* This Source Code Form is subject to the terms of the Mozilla Public
4 : * License, v. 2.0. If a copy of the MPL was not distributed with this
5 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : #include "LulDwarfSummariser.h"
8 :
9 : #include "mozilla/Assertions.h"
10 :
11 : // Set this to 1 for verbose logging
12 : #define DEBUG_SUMMARISER 0
13 :
14 : namespace lul {
15 :
16 : // Do |s64|'s lowest 32 bits sign extend back to |s64| itself?
17 0 : static inline bool fitsIn32Bits(int64 s64) {
18 0 : return s64 == ((s64 & 0xffffffff) ^ 0x80000000) - 0x80000000;
19 : }
20 :
21 : // Check a LExpr prefix expression, starting at pfxInstrs[start] up to
22 : // the next PX_End instruction, to ensure that:
23 : // * It only mentions registers that are tracked on this target
24 : // * The start point is sane
25 : // If the expression is ok, return NULL. Else return a pointer
26 : // a const char* holding a bit of text describing the problem.
27 : static const char*
28 0 : checkPfxExpr(const vector<PfxInstr>* pfxInstrs, int64_t start)
29 : {
30 0 : size_t nInstrs = pfxInstrs->size();
31 0 : if (start < 0 || start >= (ssize_t)nInstrs) {
32 0 : return "bogus start point";
33 : }
34 : size_t i;
35 0 : for (i = start; i < nInstrs; i++) {
36 0 : PfxInstr pxi = (*pfxInstrs)[i];
37 0 : if (pxi.mOpcode == PX_End)
38 0 : break;
39 0 : if (pxi.mOpcode == PX_DwReg &&
40 0 : !registerIsTracked((DW_REG_NUMBER)pxi.mOperand)) {
41 0 : return "uses untracked reg";
42 : }
43 : }
44 0 : return nullptr; // success
45 : }
46 :
47 :
48 0 : Summariser::Summariser(SecMap* aSecMap, uintptr_t aTextBias,
49 0 : void(*aLog)(const char*))
50 : : mSecMap(aSecMap)
51 : , mTextBias(aTextBias)
52 0 : , mLog(aLog)
53 : {
54 0 : mCurrAddr = 0;
55 0 : mMax1Addr = 0; // Gives an empty range.
56 :
57 : // Initialise the running RuleSet to "haven't got a clue" status.
58 0 : new (&mCurrRules) RuleSet();
59 0 : }
60 :
61 : void
62 0 : Summariser::Entry(uintptr_t aAddress, uintptr_t aLength)
63 : {
64 0 : aAddress += mTextBias;
65 : if (DEBUG_SUMMARISER) {
66 : char buf[100];
67 : SprintfLiteral(buf,
68 : "LUL Entry(%llx, %llu)\n",
69 : (unsigned long long int)aAddress,
70 : (unsigned long long int)aLength);
71 : mLog(buf);
72 : }
73 : // This throws away any previous summary, that is, assumes
74 : // that the previous summary, if any, has been properly finished
75 : // by a call to End().
76 0 : mCurrAddr = aAddress;
77 0 : mMax1Addr = aAddress + aLength;
78 0 : new (&mCurrRules) RuleSet();
79 0 : }
80 :
81 : void
82 0 : Summariser::Rule(uintptr_t aAddress, int aNewReg,
83 : LExprHow how, int16_t oldReg, int64_t offset)
84 : {
85 0 : aAddress += mTextBias;
86 : if (DEBUG_SUMMARISER) {
87 : char buf[100];
88 : if (how == NODEREF || how == DEREF) {
89 : bool deref = how == DEREF;
90 : SprintfLiteral(buf,
91 : "LUL 0x%llx old-r%d = %sr%d + %lld%s\n",
92 : (unsigned long long int)aAddress, aNewReg,
93 : deref ? "*(" : "", (int)oldReg, (long long int)offset,
94 : deref ? ")" : "");
95 : } else if (how == PFXEXPR) {
96 : SprintfLiteral(buf,
97 : "LUL 0x%llx old-r%d = pfx-expr-at %lld\n",
98 : (unsigned long long int)aAddress, aNewReg,
99 : (long long int)offset);
100 : } else {
101 : SprintfLiteral(buf,
102 : "LUL 0x%llx old-r%d = (invalid LExpr!)\n",
103 : (unsigned long long int)aAddress, aNewReg);
104 : }
105 : mLog(buf);
106 : }
107 :
108 0 : if (mCurrAddr < aAddress) {
109 : // Flush the existing summary first.
110 0 : mCurrRules.mAddr = mCurrAddr;
111 0 : mCurrRules.mLen = aAddress - mCurrAddr;
112 0 : mSecMap->AddRuleSet(&mCurrRules);
113 : if (DEBUG_SUMMARISER) {
114 : mLog("LUL "); mCurrRules.Print(mLog);
115 : mLog("\n");
116 : }
117 0 : mCurrAddr = aAddress;
118 : }
119 :
120 : // If for some reason summarisation fails, either or both of these
121 : // become non-null and point at constant text describing the
122 : // problem. Using two rather than just one avoids complications of
123 : // having to concatenate two strings to produce a complete error message.
124 0 : const char* reason1 = nullptr;
125 0 : const char* reason2 = nullptr;
126 :
127 : // |offset| needs to be a 32 bit value that sign extends to 64 bits
128 : // on a 64 bit target. We will need to incorporate |offset| into
129 : // any LExpr made here. So we may as well check it right now.
130 0 : if (!fitsIn32Bits(offset)) {
131 0 : reason1 = "offset not in signed 32-bit range";
132 0 : goto cant_summarise;
133 : }
134 :
135 : // FIXME: factor out common parts of the arch-dependent summarisers.
136 :
137 : #if defined(GP_ARCH_arm)
138 :
139 : // ----------------- arm ----------------- //
140 :
141 : // Now, can we add the rule to our summary? This depends on whether
142 : // the registers and the overall expression are representable. This
143 : // is the heart of the summarisation process.
144 : switch (aNewReg) {
145 :
146 : case DW_REG_CFA:
147 : // This is a rule that defines the CFA. The only forms we
148 : // choose to represent are: r7/11/12/13 + offset. The offset
149 : // must fit into 32 bits since 'uintptr_t' is 32 bit on ARM,
150 : // hence there is no need to check it for overflow.
151 : if (how != NODEREF) {
152 : reason1 = "rule for DW_REG_CFA: invalid |how|";
153 : goto cant_summarise;
154 : }
155 : switch (oldReg) {
156 : case DW_REG_ARM_R7: case DW_REG_ARM_R11:
157 : case DW_REG_ARM_R12: case DW_REG_ARM_R13:
158 : break;
159 : default:
160 : reason1 = "rule for DW_REG_CFA: invalid |oldReg|";
161 : goto cant_summarise;
162 : }
163 : mCurrRules.mCfaExpr = LExpr(how, oldReg, offset);
164 : break;
165 :
166 : case DW_REG_ARM_R7: case DW_REG_ARM_R11: case DW_REG_ARM_R12:
167 : case DW_REG_ARM_R13: case DW_REG_ARM_R14: case DW_REG_ARM_R15: {
168 : // This is a new rule for R7, R11, R12, R13 (SP), R14 (LR) or
169 : // R15 (the return address).
170 : switch (how) {
171 : case NODEREF: case DEREF:
172 : // Check the old register is one we're tracking.
173 : if (!registerIsTracked((DW_REG_NUMBER)oldReg) &&
174 : oldReg != DW_REG_CFA) {
175 : reason1 = "rule for R7/11/12/13/14/15: uses untracked reg";
176 : goto cant_summarise;
177 : }
178 : break;
179 : case PFXEXPR: {
180 : // Check that the prefix expression only mentions tracked registers.
181 : const vector<PfxInstr>* pfxInstrs = mSecMap->GetPfxInstrs();
182 : reason2 = checkPfxExpr(pfxInstrs, offset);
183 : if (reason2) {
184 : reason1 = "rule for R7/11/12/13/14/15: ";
185 : goto cant_summarise;
186 : }
187 : break;
188 : }
189 : default:
190 : goto cant_summarise;
191 : }
192 : LExpr expr = LExpr(how, oldReg, offset);
193 : switch (aNewReg) {
194 : case DW_REG_ARM_R7: mCurrRules.mR7expr = expr; break;
195 : case DW_REG_ARM_R11: mCurrRules.mR11expr = expr; break;
196 : case DW_REG_ARM_R12: mCurrRules.mR12expr = expr; break;
197 : case DW_REG_ARM_R13: mCurrRules.mR13expr = expr; break;
198 : case DW_REG_ARM_R14: mCurrRules.mR14expr = expr; break;
199 : case DW_REG_ARM_R15: mCurrRules.mR15expr = expr; break;
200 : default: MOZ_ASSERT(0);
201 : }
202 : break;
203 : }
204 :
205 : default:
206 : // Leave |reason1| and |reason2| unset here. This program point
207 : // is reached so often that it causes a flood of "Can't
208 : // summarise" messages. In any case, we don't really care about
209 : // the fact that this summary would produce a new value for a
210 : // register that we're not tracking. We do on the other hand
211 : // care if the summary's expression *uses* a register that we're
212 : // not tracking. But in that case one of the above failures
213 : // should tell us which.
214 : goto cant_summarise;
215 : }
216 :
217 : // Mark callee-saved registers (r4 .. r11) as unchanged, if there is
218 : // no other information about them. FIXME: do this just once, at
219 : // the point where the ruleset is committed.
220 : if (mCurrRules.mR7expr.mHow == UNKNOWN) {
221 : mCurrRules.mR7expr = LExpr(NODEREF, DW_REG_ARM_R7, 0);
222 : }
223 : if (mCurrRules.mR11expr.mHow == UNKNOWN) {
224 : mCurrRules.mR11expr = LExpr(NODEREF, DW_REG_ARM_R11, 0);
225 : }
226 : if (mCurrRules.mR12expr.mHow == UNKNOWN) {
227 : mCurrRules.mR12expr = LExpr(NODEREF, DW_REG_ARM_R12, 0);
228 : }
229 :
230 : // The old r13 (SP) value before the call is always the same as the
231 : // CFA.
232 : mCurrRules.mR13expr = LExpr(NODEREF, DW_REG_CFA, 0);
233 :
234 : // If there's no information about R15 (the return address), say
235 : // it's a copy of R14 (the link register).
236 : if (mCurrRules.mR15expr.mHow == UNKNOWN) {
237 : mCurrRules.mR15expr = LExpr(NODEREF, DW_REG_ARM_R14, 0);
238 : }
239 :
240 : #elif defined(GP_ARCH_amd64) || defined(GP_ARCH_x86)
241 :
242 : // ---------------- x64/x86 ---------------- //
243 :
244 : // Now, can we add the rule to our summary? This depends on whether
245 : // the registers and the overall expression are representable. This
246 : // is the heart of the summarisation process.
247 0 : switch (aNewReg) {
248 :
249 : case DW_REG_CFA: {
250 : // This is a rule that defines the CFA. The only forms we choose to
251 : // represent are: = SP+offset, = FP+offset, or =prefix-expr.
252 0 : switch (how) {
253 : case NODEREF:
254 0 : if (oldReg != DW_REG_INTEL_XSP && oldReg != DW_REG_INTEL_XBP) {
255 0 : reason1 = "rule for DW_REG_CFA: invalid |oldReg|";
256 0 : goto cant_summarise;
257 : }
258 0 : break;
259 : case DEREF:
260 0 : reason1 = "rule for DW_REG_CFA: invalid |how|";
261 0 : goto cant_summarise;
262 : case PFXEXPR: {
263 : // Check that the prefix expression only mentions tracked registers.
264 0 : const vector<PfxInstr>* pfxInstrs = mSecMap->GetPfxInstrs();
265 0 : reason2 = checkPfxExpr(pfxInstrs, offset);
266 0 : if (reason2) {
267 0 : reason1 = "rule for CFA: ";
268 0 : goto cant_summarise;
269 : }
270 0 : break;
271 : }
272 : default:
273 0 : goto cant_summarise;
274 : }
275 0 : mCurrRules.mCfaExpr = LExpr(how, oldReg, offset);
276 0 : break;
277 : }
278 :
279 : case DW_REG_INTEL_XSP: case DW_REG_INTEL_XBP: case DW_REG_INTEL_XIP: {
280 : // This is a new rule for XSP, XBP or XIP (the return address).
281 0 : switch (how) {
282 : case NODEREF: case DEREF:
283 : // Check the old register is one we're tracking.
284 0 : if (!registerIsTracked((DW_REG_NUMBER)oldReg) &&
285 : oldReg != DW_REG_CFA) {
286 0 : reason1 = "rule for XSP/XBP/XIP: uses untracked reg";
287 0 : goto cant_summarise;
288 : }
289 0 : break;
290 : case PFXEXPR: {
291 : // Check that the prefix expression only mentions tracked registers.
292 0 : const vector<PfxInstr>* pfxInstrs = mSecMap->GetPfxInstrs();
293 0 : reason2 = checkPfxExpr(pfxInstrs, offset);
294 0 : if (reason2) {
295 0 : reason1 = "rule for XSP/XBP/XIP: ";
296 0 : goto cant_summarise;
297 : }
298 0 : break;
299 : }
300 : default:
301 0 : goto cant_summarise;
302 : }
303 0 : LExpr expr = LExpr(how, oldReg, offset);
304 0 : switch (aNewReg) {
305 0 : case DW_REG_INTEL_XBP: mCurrRules.mXbpExpr = expr; break;
306 0 : case DW_REG_INTEL_XSP: mCurrRules.mXspExpr = expr; break;
307 0 : case DW_REG_INTEL_XIP: mCurrRules.mXipExpr = expr; break;
308 0 : default: MOZ_CRASH("impossible value for aNewReg");
309 : }
310 0 : break;
311 : }
312 :
313 : default:
314 : // Leave |reason1| and |reason2| unset here, for the reasons
315 : // explained in the analogous point in the ARM case just above.
316 0 : goto cant_summarise;
317 :
318 : }
319 :
320 : // On Intel, it seems the old SP value before the call is always the
321 : // same as the CFA. Therefore, in the absence of any other way to
322 : // recover the SP, specify that the CFA should be copied.
323 0 : if (mCurrRules.mXspExpr.mHow == UNKNOWN) {
324 0 : mCurrRules.mXspExpr = LExpr(NODEREF, DW_REG_CFA, 0);
325 : }
326 :
327 : // Also, gcc says "Undef" for BP when it is unchanged.
328 0 : if (mCurrRules.mXbpExpr.mHow == UNKNOWN) {
329 0 : mCurrRules.mXbpExpr = LExpr(NODEREF, DW_REG_INTEL_XBP, 0);
330 : }
331 :
332 : #else
333 :
334 : # error "Unsupported arch"
335 : #endif
336 :
337 0 : return;
338 :
339 : cant_summarise:
340 0 : if (reason1 || reason2) {
341 : char buf[200];
342 0 : SprintfLiteral(buf, "LUL can't summarise: "
343 : "SVMA=0x%llx: %s%s, expr=LExpr(%s,%u,%lld)\n",
344 0 : (unsigned long long int)(aAddress - mTextBias),
345 : reason1 ? reason1 : "", reason2 ? reason2 : "",
346 : NameOf_LExprHow(how),
347 0 : (unsigned int)oldReg, (long long int)offset);
348 0 : mLog(buf);
349 : }
350 : }
351 :
352 : uint32_t
353 0 : Summariser::AddPfxInstr(PfxInstr pfxi)
354 : {
355 0 : return mSecMap->AddPfxInstr(pfxi);
356 : }
357 :
358 : void
359 0 : Summariser::End()
360 : {
361 : if (DEBUG_SUMMARISER) {
362 : mLog("LUL End\n");
363 : }
364 0 : if (mCurrAddr < mMax1Addr) {
365 0 : mCurrRules.mAddr = mCurrAddr;
366 0 : mCurrRules.mLen = mMax1Addr - mCurrAddr;
367 0 : mSecMap->AddRuleSet(&mCurrRules);
368 : if (DEBUG_SUMMARISER) {
369 : mLog("LUL "); mCurrRules.Print(mLog);
370 : mLog("\n");
371 : }
372 : }
373 0 : }
374 :
375 : } // namespace lul
|