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