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