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 "LulDwarfExt.h"
10
11 #include "mozilla/Assertions.h"
12 #include "mozilla/Sprintf.h"
13
14 // Set this to 1 for verbose logging
15 #define DEBUG_SUMMARISER 0
16
17 namespace lul {
18
19 // Do |s64|'s lowest 32 bits sign extend back to |s64| itself?
fitsIn32Bits(int64 s64)20 static inline bool fitsIn32Bits(int64 s64) {
21 return s64 == ((s64 & 0xffffffff) ^ 0x80000000) - 0x80000000;
22 }
23
24 // Check a LExpr prefix expression, starting at pfxInstrs[start] up to
25 // the next PX_End instruction, to ensure that:
26 // * It only mentions registers that are tracked on this target
27 // * The start point is sane
28 // If the expression is ok, return NULL. Else return a pointer
29 // a const char* holding a bit of text describing the problem.
checkPfxExpr(const vector<PfxInstr> * pfxInstrs,int64_t start)30 static const char* checkPfxExpr(const vector<PfxInstr>* pfxInstrs,
31 int64_t start) {
32 size_t nInstrs = pfxInstrs->size();
33 if (start < 0 || start >= (ssize_t)nInstrs) {
34 return "bogus start point";
35 }
36 size_t i;
37 for (i = start; i < nInstrs; i++) {
38 PfxInstr pxi = (*pfxInstrs)[i];
39 if (pxi.mOpcode == PX_End) break;
40 if (pxi.mOpcode == PX_DwReg &&
41 !registerIsTracked((DW_REG_NUMBER)pxi.mOperand)) {
42 return "uses untracked reg";
43 }
44 }
45 return nullptr; // success
46 }
47
Summariser(SecMap * aSecMap,uintptr_t aTextBias,void (* aLog)(const char *))48 Summariser::Summariser(SecMap* aSecMap, uintptr_t aTextBias,
49 void (*aLog)(const char*))
50 : mSecMap(aSecMap), mTextBias(aTextBias), mLog(aLog) {
51 mCurrAddr = 0;
52 mMax1Addr = 0; // Gives an empty range.
53
54 // Initialise the running RuleSet to "haven't got a clue" status.
55 new (&mCurrRules) RuleSet();
56 }
57
Entry(uintptr_t aAddress,uintptr_t aLength)58 void Summariser::Entry(uintptr_t aAddress, uintptr_t aLength) {
59 aAddress += mTextBias;
60 if (DEBUG_SUMMARISER) {
61 char buf[100];
62 SprintfLiteral(buf, "LUL Entry(%llx, %llu)\n",
63 (unsigned long long int)aAddress,
64 (unsigned long long int)aLength);
65 mLog(buf);
66 }
67 // This throws away any previous summary, that is, assumes
68 // that the previous summary, if any, has been properly finished
69 // by a call to End().
70 mCurrAddr = aAddress;
71 mMax1Addr = aAddress + aLength;
72 new (&mCurrRules) RuleSet();
73 }
74
Rule(uintptr_t aAddress,int aNewReg,LExprHow how,int16_t oldReg,int64_t offset)75 void Summariser::Rule(uintptr_t aAddress, int aNewReg, LExprHow how,
76 int16_t oldReg, int64_t offset) {
77 aAddress += mTextBias;
78 if (DEBUG_SUMMARISER) {
79 char buf[100];
80 if (how == NODEREF || how == DEREF) {
81 bool deref = how == DEREF;
82 SprintfLiteral(buf, "LUL 0x%llx old-r%d = %sr%d + %lld%s\n",
83 (unsigned long long int)aAddress, aNewReg,
84 deref ? "*(" : "", (int)oldReg, (long long int)offset,
85 deref ? ")" : "");
86 } else if (how == PFXEXPR) {
87 SprintfLiteral(buf, "LUL 0x%llx old-r%d = pfx-expr-at %lld\n",
88 (unsigned long long int)aAddress, aNewReg,
89 (long long int)offset);
90 } else {
91 SprintfLiteral(buf, "LUL 0x%llx old-r%d = (invalid LExpr!)\n",
92 (unsigned long long int)aAddress, aNewReg);
93 }
94 mLog(buf);
95 }
96
97 if (mCurrAddr < aAddress) {
98 // Flush the existing summary first.
99 mSecMap->AddRuleSet(&mCurrRules, mCurrAddr, aAddress - mCurrAddr);
100 if (DEBUG_SUMMARISER) {
101 mLog("LUL ");
102 mCurrRules.Print(mCurrAddr, aAddress - mCurrAddr, mLog);
103 mLog("\n");
104 }
105 mCurrAddr = aAddress;
106 }
107
108 // If for some reason summarisation fails, either or both of these
109 // become non-null and point at constant text describing the
110 // problem. Using two rather than just one avoids complications of
111 // having to concatenate two strings to produce a complete error message.
112 const char* reason1 = nullptr;
113 const char* reason2 = nullptr;
114
115 // |offset| needs to be a 32 bit value that sign extends to 64 bits
116 // on a 64 bit target. We will need to incorporate |offset| into
117 // any LExpr made here. So we may as well check it right now.
118 if (!fitsIn32Bits(offset)) {
119 reason1 = "offset not in signed 32-bit range";
120 goto cant_summarise;
121 }
122
123 // FIXME: factor out common parts of the arch-dependent summarisers.
124
125 #if defined(GP_ARCH_arm)
126
127 // ----------------- arm ----------------- //
128
129 // Now, can we add the rule to our summary? This depends on whether
130 // the registers and the overall expression are representable. This
131 // is the heart of the summarisation process.
132 switch (aNewReg) {
133 case DW_REG_CFA:
134 // This is a rule that defines the CFA. The only forms we
135 // choose to represent are: r7/11/12/13 + offset. The offset
136 // must fit into 32 bits since 'uintptr_t' is 32 bit on ARM,
137 // hence there is no need to check it for overflow.
138 if (how != NODEREF) {
139 reason1 = "rule for DW_REG_CFA: invalid |how|";
140 goto cant_summarise;
141 }
142 switch (oldReg) {
143 case DW_REG_ARM_R7:
144 case DW_REG_ARM_R11:
145 case DW_REG_ARM_R12:
146 case DW_REG_ARM_R13:
147 break;
148 default:
149 reason1 = "rule for DW_REG_CFA: invalid |oldReg|";
150 goto cant_summarise;
151 }
152 mCurrRules.mCfaExpr = LExpr(how, oldReg, offset);
153 break;
154
155 case DW_REG_ARM_R7:
156 case DW_REG_ARM_R11:
157 case DW_REG_ARM_R12:
158 case DW_REG_ARM_R13:
159 case DW_REG_ARM_R14:
160 case DW_REG_ARM_R15: {
161 // This is a new rule for R7, R11, R12, R13 (SP), R14 (LR) or
162 // R15 (the return address).
163 switch (how) {
164 case NODEREF:
165 case DEREF:
166 // Check the old register is one we're tracking.
167 if (!registerIsTracked((DW_REG_NUMBER)oldReg) &&
168 oldReg != DW_REG_CFA) {
169 reason1 = "rule for R7/11/12/13/14/15: uses untracked reg";
170 goto cant_summarise;
171 }
172 break;
173 case PFXEXPR: {
174 // Check that the prefix expression only mentions tracked registers.
175 const vector<PfxInstr>* pfxInstrs = mSecMap->GetPfxInstrs();
176 reason2 = checkPfxExpr(pfxInstrs, offset);
177 if (reason2) {
178 reason1 = "rule for R7/11/12/13/14/15: ";
179 goto cant_summarise;
180 }
181 break;
182 }
183 default:
184 goto cant_summarise;
185 }
186 LExpr expr = LExpr(how, oldReg, offset);
187 switch (aNewReg) {
188 case DW_REG_ARM_R7:
189 mCurrRules.mR7expr = expr;
190 break;
191 case DW_REG_ARM_R11:
192 mCurrRules.mR11expr = expr;
193 break;
194 case DW_REG_ARM_R12:
195 mCurrRules.mR12expr = expr;
196 break;
197 case DW_REG_ARM_R13:
198 mCurrRules.mR13expr = expr;
199 break;
200 case DW_REG_ARM_R14:
201 mCurrRules.mR14expr = expr;
202 break;
203 case DW_REG_ARM_R15:
204 mCurrRules.mR15expr = expr;
205 break;
206 default:
207 MOZ_ASSERT(0);
208 }
209 break;
210 }
211
212 default:
213 // Leave |reason1| and |reason2| unset here. This program point
214 // is reached so often that it causes a flood of "Can't
215 // summarise" messages. In any case, we don't really care about
216 // the fact that this summary would produce a new value for a
217 // register that we're not tracking. We do on the other hand
218 // care if the summary's expression *uses* a register that we're
219 // not tracking. But in that case one of the above failures
220 // should tell us which.
221 goto cant_summarise;
222 }
223
224 // Mark callee-saved registers (r4 .. r11) as unchanged, if there is
225 // no other information about them. FIXME: do this just once, at
226 // the point where the ruleset is committed.
227 if (mCurrRules.mR7expr.mHow == UNKNOWN) {
228 mCurrRules.mR7expr = LExpr(NODEREF, DW_REG_ARM_R7, 0);
229 }
230 if (mCurrRules.mR11expr.mHow == UNKNOWN) {
231 mCurrRules.mR11expr = LExpr(NODEREF, DW_REG_ARM_R11, 0);
232 }
233 if (mCurrRules.mR12expr.mHow == UNKNOWN) {
234 mCurrRules.mR12expr = LExpr(NODEREF, DW_REG_ARM_R12, 0);
235 }
236
237 // The old r13 (SP) value before the call is always the same as the
238 // CFA.
239 mCurrRules.mR13expr = LExpr(NODEREF, DW_REG_CFA, 0);
240
241 // If there's no information about R15 (the return address), say
242 // it's a copy of R14 (the link register).
243 if (mCurrRules.mR15expr.mHow == UNKNOWN) {
244 mCurrRules.mR15expr = LExpr(NODEREF, DW_REG_ARM_R14, 0);
245 }
246
247 #elif defined(GP_ARCH_arm64)
248
249 // ----------------- arm64 ----------------- //
250
251 switch (aNewReg) {
252 case DW_REG_CFA:
253 if (how != NODEREF) {
254 reason1 = "rule for DW_REG_CFA: invalid |how|";
255 goto cant_summarise;
256 }
257 switch (oldReg) {
258 case DW_REG_AARCH64_X29:
259 case DW_REG_AARCH64_SP:
260 break;
261 default:
262 reason1 = "rule for DW_REG_CFA: invalid |oldReg|";
263 goto cant_summarise;
264 }
265 mCurrRules.mCfaExpr = LExpr(how, oldReg, offset);
266 break;
267
268 case DW_REG_AARCH64_X29:
269 case DW_REG_AARCH64_X30:
270 case DW_REG_AARCH64_SP: {
271 switch (how) {
272 case NODEREF:
273 case DEREF:
274 // Check the old register is one we're tracking.
275 if (!registerIsTracked((DW_REG_NUMBER)oldReg) &&
276 oldReg != DW_REG_CFA) {
277 reason1 = "rule for X29/X30/SP: uses untracked reg";
278 goto cant_summarise;
279 }
280 break;
281 case PFXEXPR: {
282 // Check that the prefix expression only mentions tracked registers.
283 const vector<PfxInstr>* pfxInstrs = mSecMap->GetPfxInstrs();
284 reason2 = checkPfxExpr(pfxInstrs, offset);
285 if (reason2) {
286 reason1 = "rule for X29/X30/SP: ";
287 goto cant_summarise;
288 }
289 break;
290 }
291 default:
292 goto cant_summarise;
293 }
294 LExpr expr = LExpr(how, oldReg, offset);
295 switch (aNewReg) {
296 case DW_REG_AARCH64_X29:
297 mCurrRules.mX29expr = expr;
298 break;
299 case DW_REG_AARCH64_X30:
300 mCurrRules.mX30expr = expr;
301 break;
302 case DW_REG_AARCH64_SP:
303 mCurrRules.mSPexpr = expr;
304 break;
305 default:
306 MOZ_ASSERT(0);
307 }
308 break;
309 }
310 default:
311 // Leave |reason1| and |reason2| unset here, for the reasons explained
312 // in the analogous point
313 goto cant_summarise;
314 }
315
316 if (mCurrRules.mX29expr.mHow == UNKNOWN) {
317 mCurrRules.mX29expr = LExpr(NODEREF, DW_REG_AARCH64_X29, 0);
318 }
319 if (mCurrRules.mX30expr.mHow == UNKNOWN) {
320 mCurrRules.mX30expr = LExpr(NODEREF, DW_REG_AARCH64_X30, 0);
321 }
322 // On aarch64, it seems the old SP value before the call is always the
323 // same as the CFA. Therefore, in the absence of any other way to
324 // recover the SP, specify that the CFA should be copied.
325 if (mCurrRules.mSPexpr.mHow == UNKNOWN) {
326 mCurrRules.mSPexpr = LExpr(NODEREF, DW_REG_CFA, 0);
327 }
328 #elif defined(GP_ARCH_amd64) || defined(GP_ARCH_x86)
329
330 // ---------------- x64/x86 ---------------- //
331
332 // Now, can we add the rule to our summary? This depends on whether
333 // the registers and the overall expression are representable. This
334 // is the heart of the summarisation process.
335 switch (aNewReg) {
336 case DW_REG_CFA: {
337 // This is a rule that defines the CFA. The only forms we choose to
338 // represent are: = SP+offset, = FP+offset, or =prefix-expr.
339 switch (how) {
340 case NODEREF:
341 if (oldReg != DW_REG_INTEL_XSP && oldReg != DW_REG_INTEL_XBP) {
342 reason1 = "rule for DW_REG_CFA: invalid |oldReg|";
343 goto cant_summarise;
344 }
345 break;
346 case DEREF:
347 reason1 = "rule for DW_REG_CFA: invalid |how|";
348 goto cant_summarise;
349 case PFXEXPR: {
350 // Check that the prefix expression only mentions tracked registers.
351 const vector<PfxInstr>* pfxInstrs = mSecMap->GetPfxInstrs();
352 reason2 = checkPfxExpr(pfxInstrs, offset);
353 if (reason2) {
354 reason1 = "rule for CFA: ";
355 goto cant_summarise;
356 }
357 break;
358 }
359 default:
360 goto cant_summarise;
361 }
362 mCurrRules.mCfaExpr = LExpr(how, oldReg, offset);
363 break;
364 }
365
366 case DW_REG_INTEL_XSP:
367 case DW_REG_INTEL_XBP:
368 case DW_REG_INTEL_XIP: {
369 // This is a new rule for XSP, XBP or XIP (the return address).
370 switch (how) {
371 case NODEREF:
372 case DEREF:
373 // Check the old register is one we're tracking.
374 if (!registerIsTracked((DW_REG_NUMBER)oldReg) &&
375 oldReg != DW_REG_CFA) {
376 reason1 = "rule for XSP/XBP/XIP: uses untracked reg";
377 goto cant_summarise;
378 }
379 break;
380 case PFXEXPR: {
381 // Check that the prefix expression only mentions tracked registers.
382 const vector<PfxInstr>* pfxInstrs = mSecMap->GetPfxInstrs();
383 reason2 = checkPfxExpr(pfxInstrs, offset);
384 if (reason2) {
385 reason1 = "rule for XSP/XBP/XIP: ";
386 goto cant_summarise;
387 }
388 break;
389 }
390 default:
391 goto cant_summarise;
392 }
393 LExpr expr = LExpr(how, oldReg, offset);
394 switch (aNewReg) {
395 case DW_REG_INTEL_XBP:
396 mCurrRules.mXbpExpr = expr;
397 break;
398 case DW_REG_INTEL_XSP:
399 mCurrRules.mXspExpr = expr;
400 break;
401 case DW_REG_INTEL_XIP:
402 mCurrRules.mXipExpr = expr;
403 break;
404 default:
405 MOZ_CRASH("impossible value for aNewReg");
406 }
407 break;
408 }
409
410 default:
411 // Leave |reason1| and |reason2| unset here, for the reasons
412 // explained in the analogous point in the ARM case just above.
413 goto cant_summarise;
414 }
415
416 // On Intel, it seems the old SP value before the call is always the
417 // same as the CFA. Therefore, in the absence of any other way to
418 // recover the SP, specify that the CFA should be copied.
419 if (mCurrRules.mXspExpr.mHow == UNKNOWN) {
420 mCurrRules.mXspExpr = LExpr(NODEREF, DW_REG_CFA, 0);
421 }
422
423 // Also, gcc says "Undef" for BP when it is unchanged.
424 if (mCurrRules.mXbpExpr.mHow == UNKNOWN) {
425 mCurrRules.mXbpExpr = LExpr(NODEREF, DW_REG_INTEL_XBP, 0);
426 }
427
428 #elif defined(GP_ARCH_mips64)
429 // ---------------- mips ---------------- //
430 //
431 // Now, can we add the rule to our summary? This depends on whether
432 // the registers and the overall expression are representable. This
433 // is the heart of the summarisation process.
434 switch (aNewReg) {
435 case DW_REG_CFA:
436 // This is a rule that defines the CFA. The only forms we can
437 // represent are: = SP+offset or = FP+offset.
438 if (how != NODEREF) {
439 reason1 = "rule for DW_REG_CFA: invalid |how|";
440 goto cant_summarise;
441 }
442 if (oldReg != DW_REG_MIPS_SP && oldReg != DW_REG_MIPS_FP) {
443 reason1 = "rule for DW_REG_CFA: invalid |oldReg|";
444 goto cant_summarise;
445 }
446 mCurrRules.mCfaExpr = LExpr(how, oldReg, offset);
447 break;
448
449 case DW_REG_MIPS_SP:
450 case DW_REG_MIPS_FP:
451 case DW_REG_MIPS_PC: {
452 // This is a new rule for SP, FP or PC (the return address).
453 switch (how) {
454 case NODEREF:
455 case DEREF:
456 // Check the old register is one we're tracking.
457 if (!registerIsTracked((DW_REG_NUMBER)oldReg) &&
458 oldReg != DW_REG_CFA) {
459 reason1 = "rule for SP/FP/PC: uses untracked reg";
460 goto cant_summarise;
461 }
462 break;
463 case PFXEXPR: {
464 // Check that the prefix expression only mentions tracked registers.
465 const vector<PfxInstr>* pfxInstrs = mSecMap->GetPfxInstrs();
466 reason2 = checkPfxExpr(pfxInstrs, offset);
467 if (reason2) {
468 reason1 = "rule for SP/FP/PC: ";
469 goto cant_summarise;
470 }
471 break;
472 }
473 default:
474 goto cant_summarise;
475 }
476 LExpr expr = LExpr(how, oldReg, offset);
477 switch (aNewReg) {
478 case DW_REG_MIPS_FP:
479 mCurrRules.mFPexpr = expr;
480 break;
481 case DW_REG_MIPS_SP:
482 mCurrRules.mSPexpr = expr;
483 break;
484 case DW_REG_MIPS_PC:
485 mCurrRules.mPCexpr = expr;
486 break;
487 default:
488 MOZ_CRASH("impossible value for aNewReg");
489 }
490 break;
491 }
492 default:
493 // Leave |reason1| and |reason2| unset here, for the reasons
494 // explained in the analogous point in the ARM case just above.
495 goto cant_summarise;
496 }
497
498 // On MIPS, it seems the old SP value before the call is always the
499 // same as the CFA. Therefore, in the absence of any other way to
500 // recover the SP, specify that the CFA should be copied.
501 if (mCurrRules.mSPexpr.mHow == UNKNOWN) {
502 mCurrRules.mSPexpr = LExpr(NODEREF, DW_REG_CFA, 0);
503 }
504
505 // Also, gcc says "Undef" for FP when it is unchanged.
506 if (mCurrRules.mFPexpr.mHow == UNKNOWN) {
507 mCurrRules.mFPexpr = LExpr(NODEREF, DW_REG_MIPS_FP, 0);
508 }
509
510 #else
511
512 # error "Unsupported arch"
513 #endif
514
515 return;
516
517 cant_summarise:
518 if (reason1 || reason2) {
519 char buf[200];
520 SprintfLiteral(buf,
521 "LUL can't summarise: "
522 "SVMA=0x%llx: %s%s, expr=LExpr(%s,%u,%lld)\n",
523 (unsigned long long int)(aAddress - mTextBias),
524 reason1 ? reason1 : "", reason2 ? reason2 : "",
525 NameOf_LExprHow(how), (unsigned int)oldReg,
526 (long long int)offset);
527 mLog(buf);
528 }
529 }
530
AddPfxInstr(PfxInstr pfxi)531 uint32_t Summariser::AddPfxInstr(PfxInstr pfxi) {
532 return mSecMap->AddPfxInstr(pfxi);
533 }
534
End()535 void Summariser::End() {
536 if (DEBUG_SUMMARISER) {
537 mLog("LUL End\n");
538 }
539 if (mCurrAddr < mMax1Addr) {
540 mSecMap->AddRuleSet(&mCurrRules, mCurrAddr, mMax1Addr - mCurrAddr);
541 if (DEBUG_SUMMARISER) {
542 mLog("LUL ");
543 mCurrRules.Print(mCurrAddr, mMax1Addr - mCurrAddr, mLog);
544 mLog("\n");
545 }
546 }
547 }
548
549 } // namespace lul
550