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