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 "LulMain.h"
8 :
9 : #include <string.h>
10 : #include <stdlib.h>
11 : #include <stdio.h>
12 :
13 : #include <algorithm> // std::sort
14 : #include <string>
15 :
16 : #include "mozilla/Assertions.h"
17 : #include "mozilla/ArrayUtils.h"
18 : #include "mozilla/CheckedInt.h"
19 : #include "mozilla/DebugOnly.h"
20 : #include "mozilla/MemoryChecking.h"
21 : #include "mozilla/Move.h"
22 : #include "mozilla/Sprintf.h"
23 : #include "mozilla/UniquePtr.h"
24 :
25 : #include "LulCommonExt.h"
26 : #include "LulElfExt.h"
27 :
28 : #include "LulMainInt.h"
29 :
30 : #include "platform-linux-lul.h" // for gettid()
31 :
32 : // Set this to 1 for verbose logging
33 : #define DEBUG_MAIN 0
34 :
35 : namespace lul {
36 :
37 : using std::string;
38 : using std::vector;
39 : using std::pair;
40 : using mozilla::CheckedInt;
41 : using mozilla::DebugOnly;
42 : using mozilla::MallocSizeOf;
43 :
44 :
45 : // WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
46 : //
47 : // Some functions in this file are marked RUNS IN NO-MALLOC CONTEXT.
48 : // Any such function -- and, hence, the transitive closure of those
49 : // reachable from it -- must not do any dynamic memory allocation.
50 : // Doing so risks deadlock. There is exactly one root function for
51 : // the transitive closure: Lul::Unwind.
52 : //
53 : // WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
54 :
55 :
56 : ////////////////////////////////////////////////////////////////
57 : // RuleSet //
58 : ////////////////////////////////////////////////////////////////
59 :
60 : static const char*
61 0 : NameOf_DW_REG(int16_t aReg)
62 : {
63 0 : switch (aReg) {
64 0 : case DW_REG_CFA: return "cfa";
65 : #if defined(GP_ARCH_amd64) || defined(GP_ARCH_x86)
66 0 : case DW_REG_INTEL_XBP: return "xbp";
67 0 : case DW_REG_INTEL_XSP: return "xsp";
68 0 : case DW_REG_INTEL_XIP: return "xip";
69 : #elif defined(GP_ARCH_arm)
70 : case DW_REG_ARM_R7: return "r7";
71 : case DW_REG_ARM_R11: return "r11";
72 : case DW_REG_ARM_R12: return "r12";
73 : case DW_REG_ARM_R13: return "r13";
74 : case DW_REG_ARM_R14: return "r14";
75 : case DW_REG_ARM_R15: return "r15";
76 : #else
77 : # error "Unsupported arch"
78 : #endif
79 0 : default: return "???";
80 : }
81 : }
82 :
83 : string
84 0 : LExpr::ShowRule(const char* aNewReg) const
85 : {
86 : char buf[64];
87 0 : string res = string(aNewReg) + "=";
88 0 : switch (mHow) {
89 : case UNKNOWN:
90 0 : res += "Unknown";
91 0 : break;
92 : case NODEREF:
93 0 : SprintfLiteral(buf, "%s+%d",
94 0 : NameOf_DW_REG(mReg), (int)mOffset);
95 0 : res += buf;
96 0 : break;
97 : case DEREF:
98 0 : SprintfLiteral(buf, "*(%s+%d)",
99 0 : NameOf_DW_REG(mReg), (int)mOffset);
100 0 : res += buf;
101 0 : break;
102 : case PFXEXPR:
103 0 : SprintfLiteral(buf, "PfxExpr-at-%d", (int)mOffset);
104 0 : res += buf;
105 0 : break;
106 : default:
107 0 : res += "???";
108 0 : break;
109 : }
110 0 : return res;
111 : }
112 :
113 : void
114 0 : RuleSet::Print(void(*aLog)(const char*)) const
115 : {
116 : char buf[96];
117 : SprintfLiteral(buf, "[%llx .. %llx]: let ",
118 0 : (unsigned long long int)mAddr,
119 0 : (unsigned long long int)(mAddr + mLen - 1));
120 0 : string res = string(buf);
121 0 : res += mCfaExpr.ShowRule("cfa");
122 0 : res += " in";
123 : // For each reg we care about, print the recovery expression.
124 : #if defined(GP_ARCH_amd64) || defined(GP_ARCH_x86)
125 0 : res += mXipExpr.ShowRule(" RA");
126 0 : res += mXspExpr.ShowRule(" SP");
127 0 : res += mXbpExpr.ShowRule(" BP");
128 : #elif defined(GP_ARCH_arm)
129 : res += mR15expr.ShowRule(" R15");
130 : res += mR7expr .ShowRule(" R7" );
131 : res += mR11expr.ShowRule(" R11");
132 : res += mR12expr.ShowRule(" R12");
133 : res += mR13expr.ShowRule(" R13");
134 : res += mR14expr.ShowRule(" R14");
135 : #else
136 : # error "Unsupported arch"
137 : #endif
138 0 : aLog(res.c_str());
139 0 : }
140 :
141 : LExpr*
142 0 : RuleSet::ExprForRegno(DW_REG_NUMBER aRegno) {
143 0 : switch (aRegno) {
144 0 : case DW_REG_CFA: return &mCfaExpr;
145 : # if defined(GP_ARCH_amd64) || defined(GP_ARCH_x86)
146 0 : case DW_REG_INTEL_XIP: return &mXipExpr;
147 0 : case DW_REG_INTEL_XSP: return &mXspExpr;
148 0 : case DW_REG_INTEL_XBP: return &mXbpExpr;
149 : # elif defined(GP_ARCH_arm)
150 : case DW_REG_ARM_R15: return &mR15expr;
151 : case DW_REG_ARM_R14: return &mR14expr;
152 : case DW_REG_ARM_R13: return &mR13expr;
153 : case DW_REG_ARM_R12: return &mR12expr;
154 : case DW_REG_ARM_R11: return &mR11expr;
155 : case DW_REG_ARM_R7: return &mR7expr;
156 : # else
157 : # error "Unknown arch"
158 : # endif
159 0 : default: return nullptr;
160 : }
161 : }
162 :
163 0 : RuleSet::RuleSet()
164 : {
165 0 : mAddr = 0;
166 0 : mLen = 0;
167 : // The only other fields are of type LExpr and those are initialised
168 : // by LExpr::LExpr().
169 0 : }
170 :
171 :
172 : ////////////////////////////////////////////////////////////////
173 : // SecMap //
174 : ////////////////////////////////////////////////////////////////
175 :
176 : // See header file LulMainInt.h for comments about invariants.
177 :
178 0 : SecMap::SecMap(void(*aLog)(const char*))
179 : : mSummaryMinAddr(1)
180 : , mSummaryMaxAddr(0)
181 : , mUsable(true)
182 0 : , mLog(aLog)
183 0 : {}
184 :
185 0 : SecMap::~SecMap() {
186 0 : mRuleSets.clear();
187 0 : }
188 :
189 : // RUNS IN NO-MALLOC CONTEXT
190 : RuleSet*
191 0 : SecMap::FindRuleSet(uintptr_t ia) {
192 : // Binary search mRuleSets to find one that brackets |ia|.
193 : // lo and hi need to be signed, else the loop termination tests
194 : // don't work properly. Note that this works correctly even when
195 : // mRuleSets.size() == 0.
196 :
197 : // Can't do this until the array has been sorted and preened.
198 0 : MOZ_ASSERT(mUsable);
199 :
200 0 : long int lo = 0;
201 0 : long int hi = (long int)mRuleSets.size() - 1;
202 : while (true) {
203 : // current unsearched space is from lo to hi, inclusive.
204 0 : if (lo > hi) {
205 : // not found
206 0 : return nullptr;
207 : }
208 0 : long int mid = lo + ((hi - lo) / 2);
209 0 : RuleSet* mid_ruleSet = &mRuleSets[mid];
210 0 : uintptr_t mid_minAddr = mid_ruleSet->mAddr;
211 0 : uintptr_t mid_maxAddr = mid_minAddr + mid_ruleSet->mLen - 1;
212 0 : if (ia < mid_minAddr) { hi = mid-1; continue; }
213 0 : if (ia > mid_maxAddr) { lo = mid+1; continue; }
214 0 : MOZ_ASSERT(mid_minAddr <= ia && ia <= mid_maxAddr);
215 0 : return mid_ruleSet;
216 0 : }
217 : // NOTREACHED
218 : }
219 :
220 : // Add a RuleSet to the collection. The rule is copied in. Calling
221 : // this makes the map non-searchable.
222 : void
223 0 : SecMap::AddRuleSet(const RuleSet* rs) {
224 0 : mUsable = false;
225 0 : mRuleSets.push_back(*rs);
226 0 : }
227 :
228 : // Add a PfxInstr to the vector of such instrs, and return the index
229 : // in the vector. Calling this makes the map non-searchable.
230 : uint32_t
231 0 : SecMap::AddPfxInstr(PfxInstr pfxi) {
232 0 : mUsable = false;
233 0 : mPfxInstrs.push_back(pfxi);
234 0 : return mPfxInstrs.size() - 1;
235 : }
236 :
237 :
238 : static bool
239 0 : CmpRuleSetsByAddrLE(const RuleSet& rs1, const RuleSet& rs2) {
240 0 : return rs1.mAddr < rs2.mAddr;
241 : }
242 :
243 : // Prepare the map for searching. Completely remove any which don't
244 : // fall inside the specified range [start, +len).
245 : void
246 0 : SecMap::PrepareRuleSets(uintptr_t aStart, size_t aLen)
247 : {
248 0 : if (mRuleSets.empty()) {
249 0 : return;
250 : }
251 :
252 0 : MOZ_ASSERT(aLen > 0);
253 0 : if (aLen == 0) {
254 : // This should never happen.
255 0 : mRuleSets.clear();
256 0 : return;
257 : }
258 :
259 : // Sort by start addresses.
260 0 : std::sort(mRuleSets.begin(), mRuleSets.end(), CmpRuleSetsByAddrLE);
261 :
262 : // Detect any entry not completely contained within [start, +len).
263 : // Set its length to zero, so that the next pass will remove it.
264 0 : for (size_t i = 0; i < mRuleSets.size(); ++i) {
265 0 : RuleSet* rs = &mRuleSets[i];
266 0 : if (rs->mLen > 0 &&
267 0 : (rs->mAddr < aStart || rs->mAddr + rs->mLen > aStart + aLen)) {
268 0 : rs->mLen = 0;
269 : }
270 : }
271 :
272 : // Iteratively truncate any overlaps and remove any zero length
273 : // entries that might result, or that may have been present
274 : // initially. Unless the input is seriously screwy, this is
275 : // expected to iterate only once.
276 : while (true) {
277 : size_t i;
278 0 : size_t n = mRuleSets.size();
279 0 : size_t nZeroLen = 0;
280 :
281 0 : if (n == 0) {
282 0 : break;
283 : }
284 :
285 0 : for (i = 1; i < n; ++i) {
286 0 : RuleSet* prev = &mRuleSets[i-1];
287 0 : RuleSet* here = &mRuleSets[i];
288 0 : MOZ_ASSERT(prev->mAddr <= here->mAddr);
289 0 : if (prev->mAddr + prev->mLen > here->mAddr) {
290 0 : prev->mLen = here->mAddr - prev->mAddr;
291 : }
292 0 : if (prev->mLen == 0)
293 0 : nZeroLen++;
294 : }
295 :
296 0 : if (mRuleSets[n-1].mLen == 0) {
297 0 : nZeroLen++;
298 : }
299 :
300 : // At this point, the entries are in-order and non-overlapping.
301 : // If none of them are zero-length, we are done.
302 0 : if (nZeroLen == 0) {
303 0 : break;
304 : }
305 :
306 : // Slide back the entries to remove the zero length ones.
307 0 : size_t j = 0; // The write-point.
308 0 : for (i = 0; i < n; ++i) {
309 0 : if (mRuleSets[i].mLen == 0) {
310 0 : continue;
311 : }
312 0 : if (j != i) mRuleSets[j] = mRuleSets[i];
313 0 : ++j;
314 : }
315 0 : MOZ_ASSERT(i == n);
316 0 : MOZ_ASSERT(nZeroLen <= n);
317 0 : MOZ_ASSERT(j == n - nZeroLen);
318 0 : while (nZeroLen > 0) {
319 0 : mRuleSets.pop_back();
320 0 : nZeroLen--;
321 : }
322 :
323 0 : MOZ_ASSERT(mRuleSets.size() == j);
324 0 : }
325 :
326 0 : size_t n = mRuleSets.size();
327 :
328 : #ifdef DEBUG
329 : // Do a final check on the rules: their address ranges must be
330 : // ascending, non overlapping, non zero sized.
331 0 : if (n > 0) {
332 0 : MOZ_ASSERT(mRuleSets[0].mLen > 0);
333 0 : for (size_t i = 1; i < n; ++i) {
334 0 : RuleSet* prev = &mRuleSets[i-1];
335 0 : RuleSet* here = &mRuleSets[i];
336 0 : MOZ_ASSERT(prev->mAddr < here->mAddr);
337 0 : MOZ_ASSERT(here->mLen > 0);
338 0 : MOZ_ASSERT(prev->mAddr + prev->mLen <= here->mAddr);
339 : }
340 : }
341 : #endif
342 :
343 : // Set the summary min and max address values.
344 0 : if (n == 0) {
345 : // Use the values defined in comments in the class declaration.
346 0 : mSummaryMinAddr = 1;
347 0 : mSummaryMaxAddr = 0;
348 : } else {
349 0 : mSummaryMinAddr = mRuleSets[0].mAddr;
350 0 : mSummaryMaxAddr = mRuleSets[n-1].mAddr + mRuleSets[n-1].mLen - 1;
351 : }
352 : char buf[150];
353 0 : SprintfLiteral(buf,
354 : "PrepareRuleSets: %d entries, smin/smax 0x%llx, 0x%llx\n",
355 0 : (int)n, (unsigned long long int)mSummaryMinAddr,
356 0 : (unsigned long long int)mSummaryMaxAddr);
357 0 : buf[sizeof(buf)-1] = 0;
358 0 : mLog(buf);
359 :
360 : // Is now usable for binary search.
361 0 : mUsable = true;
362 :
363 : if (0) {
364 : mLog("\nRulesets after preening\n");
365 : for (size_t i = 0; i < mRuleSets.size(); ++i) {
366 : mRuleSets[i].Print(mLog);
367 : mLog("\n");
368 : }
369 : mLog("\n");
370 : }
371 : }
372 :
373 0 : bool SecMap::IsEmpty() {
374 0 : return mRuleSets.empty();
375 : }
376 :
377 : size_t
378 0 : SecMap::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
379 : {
380 0 : size_t n = aMallocSizeOf(this);
381 :
382 : // It's conceivable that these calls would be unsafe with some
383 : // implementations of std::vector, but it seems to be working for now...
384 0 : n += aMallocSizeOf(mRuleSets.data());
385 0 : n += aMallocSizeOf(mPfxInstrs.data());
386 :
387 0 : return n;
388 : }
389 :
390 : ////////////////////////////////////////////////////////////////
391 : // SegArray //
392 : ////////////////////////////////////////////////////////////////
393 :
394 : // A SegArray holds a set of address ranges that together exactly
395 : // cover an address range, with no overlaps or holes. Each range has
396 : // an associated value, which in this case has been specialised to be
397 : // a simple boolean. The representation is kept to minimal canonical
398 : // form in which adjacent ranges with the same associated value are
399 : // merged together. Each range is represented by a |struct Seg|.
400 : //
401 : // SegArrays are used to keep track of which parts of the address
402 : // space are known to contain instructions.
403 0 : class SegArray {
404 :
405 : public:
406 0 : void add(uintptr_t lo, uintptr_t hi, bool val) {
407 0 : if (lo > hi) {
408 0 : return;
409 : }
410 0 : split_at(lo);
411 0 : if (hi < UINTPTR_MAX) {
412 0 : split_at(hi+1);
413 : }
414 : std::vector<Seg>::size_type iLo, iHi, i;
415 0 : iLo = find(lo);
416 0 : iHi = find(hi);
417 0 : for (i = iLo; i <= iHi; ++i) {
418 0 : mSegs[i].val = val;
419 : }
420 0 : preen();
421 : }
422 :
423 : // RUNS IN NO-MALLOC CONTEXT
424 : bool getBoundingCodeSegment(/*OUT*/uintptr_t* rx_min,
425 : /*OUT*/uintptr_t* rx_max, uintptr_t addr) {
426 : std::vector<Seg>::size_type i = find(addr);
427 : if (!mSegs[i].val) {
428 : return false;
429 : }
430 : *rx_min = mSegs[i].lo;
431 : *rx_max = mSegs[i].hi;
432 : return true;
433 : }
434 :
435 0 : SegArray() {
436 0 : Seg s(0, UINTPTR_MAX, false);
437 0 : mSegs.push_back(s);
438 0 : }
439 :
440 : private:
441 : struct Seg {
442 0 : Seg(uintptr_t lo, uintptr_t hi, bool val) : lo(lo), hi(hi), val(val) {}
443 : uintptr_t lo;
444 : uintptr_t hi;
445 : bool val;
446 : };
447 :
448 0 : void preen() {
449 0 : for (std::vector<Seg>::iterator iter = mSegs.begin();
450 0 : iter < mSegs.end()-1;
451 : ++iter) {
452 0 : if (iter[0].val != iter[1].val) {
453 0 : continue;
454 : }
455 0 : iter[0].hi = iter[1].hi;
456 0 : mSegs.erase(iter+1);
457 : // Back up one, so as not to miss an opportunity to merge
458 : // with the entry after this one.
459 0 : --iter;
460 : }
461 0 : }
462 :
463 : // RUNS IN NO-MALLOC CONTEXT
464 0 : std::vector<Seg>::size_type find(uintptr_t a) {
465 0 : long int lo = 0;
466 0 : long int hi = (long int)mSegs.size();
467 : while (true) {
468 : // The unsearched space is lo .. hi inclusive.
469 0 : if (lo > hi) {
470 : // Not found. This can't happen.
471 0 : return (std::vector<Seg>::size_type)(-1);
472 : }
473 0 : long int mid = lo + ((hi - lo) / 2);
474 0 : uintptr_t mid_lo = mSegs[mid].lo;
475 0 : uintptr_t mid_hi = mSegs[mid].hi;
476 0 : if (a < mid_lo) { hi = mid-1; continue; }
477 0 : if (a > mid_hi) { lo = mid+1; continue; }
478 0 : return (std::vector<Seg>::size_type)mid;
479 0 : }
480 : }
481 :
482 0 : void split_at(uintptr_t a) {
483 0 : std::vector<Seg>::size_type i = find(a);
484 0 : if (mSegs[i].lo == a) {
485 0 : return;
486 : }
487 0 : mSegs.insert( mSegs.begin()+i+1, mSegs[i] );
488 0 : mSegs[i].hi = a-1;
489 0 : mSegs[i+1].lo = a;
490 : }
491 :
492 : void show() {
493 : printf("<< %d entries:\n", (int)mSegs.size());
494 : for (std::vector<Seg>::iterator iter = mSegs.begin();
495 : iter < mSegs.end();
496 : ++iter) {
497 : printf(" %016llx %016llx %s\n",
498 : (unsigned long long int)(*iter).lo,
499 : (unsigned long long int)(*iter).hi,
500 : (*iter).val ? "true" : "false");
501 : }
502 : printf(">>\n");
503 : }
504 :
505 : std::vector<Seg> mSegs;
506 : };
507 :
508 :
509 : ////////////////////////////////////////////////////////////////
510 : // PriMap //
511 : ////////////////////////////////////////////////////////////////
512 :
513 0 : class PriMap {
514 : public:
515 0 : explicit PriMap(void (*aLog)(const char*))
516 0 : : mLog(aLog)
517 0 : {}
518 :
519 : // RUNS IN NO-MALLOC CONTEXT
520 : pair<const RuleSet*, const vector<PfxInstr>*>
521 0 : Lookup(uintptr_t ia)
522 : {
523 0 : SecMap* sm = FindSecMap(ia);
524 0 : return pair<const RuleSet*, const vector<PfxInstr>*>
525 0 : (sm ? sm->FindRuleSet(ia) : nullptr,
526 0 : sm ? sm->GetPfxInstrs() : nullptr);
527 : }
528 :
529 : // Add a secondary map. No overlaps allowed w.r.t. existing
530 : // secondary maps.
531 0 : void AddSecMap(mozilla::UniquePtr<SecMap>&& aSecMap) {
532 : // We can't add an empty SecMap to the PriMap. But that's OK
533 : // since we'd never be able to find anything in it anyway.
534 0 : if (aSecMap->IsEmpty()) {
535 0 : return;
536 : }
537 :
538 : // Iterate through the SecMaps and find the right place for this
539 : // one. At the same time, ensure that the in-order
540 : // non-overlapping invariant is preserved (and, generally, holds).
541 : // FIXME: this gives a cost that is O(N^2) in the total number of
542 : // shared objects in the system. ToDo: better.
543 0 : MOZ_ASSERT(aSecMap->mSummaryMinAddr <= aSecMap->mSummaryMaxAddr);
544 :
545 0 : size_t num_secMaps = mSecMaps.size();
546 : uintptr_t i;
547 0 : for (i = 0; i < num_secMaps; ++i) {
548 0 : mozilla::UniquePtr<SecMap>& sm_i = mSecMaps[i];
549 0 : MOZ_ASSERT(sm_i->mSummaryMinAddr <= sm_i->mSummaryMaxAddr);
550 0 : if (aSecMap->mSummaryMinAddr < sm_i->mSummaryMaxAddr) {
551 : // |aSecMap| needs to be inserted immediately before mSecMaps[i].
552 0 : break;
553 : }
554 : }
555 0 : MOZ_ASSERT(i <= num_secMaps);
556 0 : if (i == num_secMaps) {
557 : // It goes at the end.
558 0 : mSecMaps.push_back(mozilla::Move(aSecMap));
559 : } else {
560 0 : std::vector<mozilla::UniquePtr<SecMap>>::iterator iter = mSecMaps.begin() + i;
561 0 : mSecMaps.insert(iter, mozilla::Move(aSecMap));
562 : }
563 : char buf[100];
564 0 : SprintfLiteral(buf, "AddSecMap: now have %d SecMaps\n",
565 0 : (int)mSecMaps.size());
566 0 : buf[sizeof(buf)-1] = 0;
567 0 : mLog(buf);
568 : }
569 :
570 : // Remove and delete any SecMaps in the mapping, that intersect
571 : // with the specified address range.
572 0 : void RemoveSecMapsInRange(uintptr_t avma_min, uintptr_t avma_max) {
573 0 : MOZ_ASSERT(avma_min <= avma_max);
574 0 : size_t num_secMaps = mSecMaps.size();
575 0 : if (num_secMaps > 0) {
576 : intptr_t i;
577 : // Iterate from end to start over the vector, so as to ensure
578 : // that the special case where |avma_min| and |avma_max| denote
579 : // the entire address space, can be completed in time proportional
580 : // to the number of elements in the map.
581 0 : for (i = (intptr_t)num_secMaps-1; i >= 0; i--) {
582 0 : mozilla::UniquePtr<SecMap>& sm_i = mSecMaps[i];
583 0 : if (sm_i->mSummaryMaxAddr < avma_min ||
584 0 : avma_max < sm_i->mSummaryMinAddr) {
585 : // There's no overlap. Move on.
586 0 : continue;
587 : }
588 : // We need to remove mSecMaps[i] and slide all those above it
589 : // downwards to cover the hole.
590 0 : mSecMaps.erase(mSecMaps.begin() + i);
591 : }
592 : }
593 0 : }
594 :
595 : // Return the number of currently contained SecMaps.
596 0 : size_t CountSecMaps() {
597 0 : return mSecMaps.size();
598 : }
599 :
600 0 : size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const {
601 0 : size_t n = aMallocSizeOf(this);
602 :
603 : // It's conceivable that this call would be unsafe with some
604 : // implementations of std::vector, but it seems to be working for now...
605 0 : n += aMallocSizeOf(mSecMaps.data());
606 :
607 0 : for (size_t i = 0; i < mSecMaps.size(); i++) {
608 0 : n += mSecMaps[i]->SizeOfIncludingThis(aMallocSizeOf);
609 : }
610 :
611 0 : return n;
612 : }
613 :
614 : private:
615 : // RUNS IN NO-MALLOC CONTEXT
616 0 : SecMap* FindSecMap(uintptr_t ia) {
617 : // Binary search mSecMaps to find one that brackets |ia|.
618 : // lo and hi need to be signed, else the loop termination tests
619 : // don't work properly.
620 0 : long int lo = 0;
621 0 : long int hi = (long int)mSecMaps.size() - 1;
622 : while (true) {
623 : // current unsearched space is from lo to hi, inclusive.
624 0 : if (lo > hi) {
625 : // not found
626 0 : return nullptr;
627 : }
628 0 : long int mid = lo + ((hi - lo) / 2);
629 0 : mozilla::UniquePtr<SecMap>& mid_secMap = mSecMaps[mid];
630 0 : uintptr_t mid_minAddr = mid_secMap->mSummaryMinAddr;
631 0 : uintptr_t mid_maxAddr = mid_secMap->mSummaryMaxAddr;
632 0 : if (ia < mid_minAddr) { hi = mid-1; continue; }
633 0 : if (ia > mid_maxAddr) { lo = mid+1; continue; }
634 0 : MOZ_ASSERT(mid_minAddr <= ia && ia <= mid_maxAddr);
635 0 : return mid_secMap.get();
636 0 : }
637 : // NOTREACHED
638 : }
639 :
640 : private:
641 : // sorted array of per-object ranges, non overlapping, non empty
642 : std::vector<mozilla::UniquePtr<SecMap>> mSecMaps;
643 :
644 : // a logging sink, for debugging.
645 : void (*mLog)(const char*);
646 : };
647 :
648 :
649 : ////////////////////////////////////////////////////////////////
650 : // LUL //
651 : ////////////////////////////////////////////////////////////////
652 :
653 : #define LUL_LOG(_str) \
654 : do { \
655 : char buf[200]; \
656 : SprintfLiteral(buf, \
657 : "LUL: pid %d tid %d lul-obj %p: %s", \
658 : getpid(), gettid(), this, (_str)); \
659 : buf[sizeof(buf)-1] = 0; \
660 : mLog(buf); \
661 : } while (0)
662 :
663 0 : LUL::LUL(void (*aLog)(const char*))
664 : : mLog(aLog)
665 : , mAdminMode(true)
666 0 : , mAdminThreadId(gettid())
667 0 : , mPriMap(new PriMap(aLog))
668 0 : , mSegArray(new SegArray())
669 0 : , mUSU(new UniqueStringUniverse())
670 : {
671 0 : LUL_LOG("LUL::LUL: Created object");
672 0 : }
673 :
674 :
675 0 : LUL::~LUL()
676 : {
677 0 : LUL_LOG("LUL::~LUL: Destroyed object");
678 0 : delete mPriMap;
679 0 : delete mSegArray;
680 0 : mLog = nullptr;
681 0 : delete mUSU;
682 0 : }
683 :
684 :
685 : void
686 0 : LUL::MaybeShowStats()
687 : {
688 : // This is racey in the sense that it can't guarantee that
689 : // n_new == n_new_Context + n_new_CFI + n_new_Scanned
690 : // if it should happen that mStats is updated by some other thread
691 : // in between computation of n_new and n_new_{Context,CFI,FP}.
692 : // But it's just stats printing, so we don't really care.
693 0 : uint32_t n_new = mStats - mStatsPrevious;
694 0 : if (n_new >= 5000) {
695 0 : uint32_t n_new_Context = mStats.mContext - mStatsPrevious.mContext;
696 0 : uint32_t n_new_CFI = mStats.mCFI - mStatsPrevious.mCFI;
697 0 : uint32_t n_new_FP = mStats.mFP - mStatsPrevious.mFP;
698 0 : mStatsPrevious = mStats;
699 : char buf[200];
700 : SprintfLiteral(buf,
701 : "LUL frame stats: TOTAL %5u"
702 : " CTX %4u CFI %4u FP %4u",
703 0 : n_new, n_new_Context, n_new_CFI, n_new_FP);
704 0 : buf[sizeof(buf)-1] = 0;
705 0 : mLog(buf);
706 : }
707 0 : }
708 :
709 :
710 : size_t
711 0 : LUL::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
712 : {
713 0 : size_t n = aMallocSizeOf(this);
714 0 : n += mPriMap->SizeOfIncludingThis(aMallocSizeOf);
715 :
716 : // Measurement of the following members may be added later if DMD finds it
717 : // is worthwhile:
718 : // - mSegArray
719 : // - mUSU
720 :
721 0 : return n;
722 : }
723 :
724 :
725 : void
726 0 : LUL::EnableUnwinding()
727 : {
728 0 : LUL_LOG("LUL::EnableUnwinding");
729 : // Don't assert for Admin mode here. That is, tolerate a call here
730 : // if we are already in Unwinding mode.
731 0 : MOZ_ASSERT(gettid() == mAdminThreadId);
732 :
733 0 : mAdminMode = false;
734 0 : }
735 :
736 :
737 : void
738 0 : LUL::NotifyAfterMap(uintptr_t aRXavma, size_t aSize,
739 : const char* aFileName, const void* aMappedImage)
740 : {
741 0 : MOZ_ASSERT(mAdminMode);
742 0 : MOZ_ASSERT(gettid() == mAdminThreadId);
743 :
744 0 : mLog(":\n");
745 : char buf[200];
746 : SprintfLiteral(buf, "NotifyMap %llx %llu %s\n",
747 : (unsigned long long int)aRXavma, (unsigned long long int)aSize,
748 0 : aFileName);
749 0 : buf[sizeof(buf)-1] = 0;
750 0 : mLog(buf);
751 :
752 : // Ignore obviously-stupid notifications.
753 0 : if (aSize > 0) {
754 :
755 : // Here's a new mapping, for this object.
756 0 : mozilla::UniquePtr<SecMap> smap = mozilla::MakeUnique<SecMap>(mLog);
757 :
758 : // Read CFI or EXIDX unwind data into |smap|.
759 0 : if (!aMappedImage) {
760 0 : (void)lul::ReadSymbolData(
761 0 : string(aFileName), std::vector<string>(), smap.get(),
762 0 : (void*)aRXavma, aSize, mUSU, mLog);
763 : } else {
764 0 : (void)lul::ReadSymbolDataInternal(
765 : (const uint8_t*)aMappedImage,
766 0 : string(aFileName), std::vector<string>(), smap.get(),
767 0 : (void*)aRXavma, aSize, mUSU, mLog);
768 : }
769 :
770 0 : mLog("NotifyMap .. preparing entries\n");
771 :
772 0 : smap->PrepareRuleSets(aRXavma, aSize);
773 :
774 0 : SprintfLiteral(buf,
775 0 : "NotifyMap got %lld entries\n", (long long int)smap->Size());
776 0 : buf[sizeof(buf)-1] = 0;
777 0 : mLog(buf);
778 :
779 : // Add it to the primary map (the top level set of mapped objects).
780 0 : mPriMap->AddSecMap(mozilla::Move(smap));
781 :
782 : // Tell the segment array about the mapping, so that the stack
783 : // scan and __kernel_syscall mechanisms know where valid code is.
784 0 : mSegArray->add(aRXavma, aRXavma + aSize - 1, true);
785 : }
786 0 : }
787 :
788 :
789 : void
790 0 : LUL::NotifyExecutableArea(uintptr_t aRXavma, size_t aSize)
791 : {
792 0 : MOZ_ASSERT(mAdminMode);
793 0 : MOZ_ASSERT(gettid() == mAdminThreadId);
794 :
795 0 : mLog(":\n");
796 : char buf[200];
797 : SprintfLiteral(buf, "NotifyExecutableArea %llx %llu\n",
798 0 : (unsigned long long int)aRXavma, (unsigned long long int)aSize);
799 0 : buf[sizeof(buf)-1] = 0;
800 0 : mLog(buf);
801 :
802 : // Ignore obviously-stupid notifications.
803 0 : if (aSize > 0) {
804 : // Tell the segment array about the mapping, so that the stack
805 : // scan and __kernel_syscall mechanisms know where valid code is.
806 0 : mSegArray->add(aRXavma, aRXavma + aSize - 1, true);
807 : }
808 0 : }
809 :
810 :
811 : void
812 0 : LUL::NotifyBeforeUnmap(uintptr_t aRXavmaMin, uintptr_t aRXavmaMax)
813 : {
814 0 : MOZ_ASSERT(mAdminMode);
815 0 : MOZ_ASSERT(gettid() == mAdminThreadId);
816 :
817 0 : mLog(":\n");
818 : char buf[100];
819 : SprintfLiteral(buf, "NotifyUnmap %016llx-%016llx\n",
820 : (unsigned long long int)aRXavmaMin,
821 0 : (unsigned long long int)aRXavmaMax);
822 0 : buf[sizeof(buf)-1] = 0;
823 0 : mLog(buf);
824 :
825 0 : MOZ_ASSERT(aRXavmaMin <= aRXavmaMax);
826 :
827 : // Remove from the primary map, any secondary maps that intersect
828 : // with the address range. Also delete the secondary maps.
829 0 : mPriMap->RemoveSecMapsInRange(aRXavmaMin, aRXavmaMax);
830 :
831 : // Tell the segment array that the address range no longer
832 : // contains valid code.
833 0 : mSegArray->add(aRXavmaMin, aRXavmaMax, false);
834 :
835 0 : SprintfLiteral(buf, "NotifyUnmap: now have %d SecMaps\n",
836 0 : (int)mPriMap->CountSecMaps());
837 0 : buf[sizeof(buf)-1] = 0;
838 0 : mLog(buf);
839 0 : }
840 :
841 :
842 : size_t
843 0 : LUL::CountMappings()
844 : {
845 0 : MOZ_ASSERT(mAdminMode);
846 0 : MOZ_ASSERT(gettid() == mAdminThreadId);
847 :
848 0 : return mPriMap->CountSecMaps();
849 : }
850 :
851 :
852 : // RUNS IN NO-MALLOC CONTEXT
853 : static
854 0 : TaggedUWord DerefTUW(TaggedUWord aAddr, const StackImage* aStackImg)
855 : {
856 0 : if (!aAddr.Valid()) {
857 0 : return TaggedUWord();
858 : }
859 :
860 : // Lower limit check. |aAddr.Value()| is the lowest requested address
861 : // and |aStackImg->mStartAvma| is the lowest address we actually have,
862 : // so the comparison is straightforward.
863 0 : if (aAddr.Value() < aStackImg->mStartAvma) {
864 0 : return TaggedUWord();
865 : }
866 :
867 : // Upper limit check. We must compute the highest requested address
868 : // and the highest address we actually have, but being careful to
869 : // avoid overflow. In particular if |aAddr| is 0xFFF...FFF or the
870 : // 3/7 values below that, then we will get overflow. See bug #1245477.
871 : typedef CheckedInt<uintptr_t> CheckedUWord;
872 : CheckedUWord highest_requested_plus_one
873 0 : = CheckedUWord(aAddr.Value()) + CheckedUWord(sizeof(uintptr_t));
874 : CheckedUWord highest_available_plus_one
875 0 : = CheckedUWord(aStackImg->mStartAvma) + CheckedUWord(aStackImg->mLen);
876 0 : if (!highest_requested_plus_one.isValid() // overflow?
877 0 : || !highest_available_plus_one.isValid() // overflow?
878 0 : || (highest_requested_plus_one.value()
879 0 : > highest_available_plus_one.value())) { // in range?
880 0 : return TaggedUWord();
881 : }
882 :
883 0 : return TaggedUWord(*(uintptr_t*)(aStackImg->mContents + aAddr.Value()
884 0 : - aStackImg->mStartAvma));
885 : }
886 :
887 : // RUNS IN NO-MALLOC CONTEXT
888 : static
889 0 : TaggedUWord EvaluateReg(int16_t aReg, const UnwindRegs* aOldRegs,
890 : TaggedUWord aCFA)
891 : {
892 0 : switch (aReg) {
893 0 : case DW_REG_CFA: return aCFA;
894 : #if defined(GP_ARCH_amd64) || defined(GP_ARCH_x86)
895 0 : case DW_REG_INTEL_XBP: return aOldRegs->xbp;
896 0 : case DW_REG_INTEL_XSP: return aOldRegs->xsp;
897 0 : case DW_REG_INTEL_XIP: return aOldRegs->xip;
898 : #elif defined(GP_ARCH_arm)
899 : case DW_REG_ARM_R7: return aOldRegs->r7;
900 : case DW_REG_ARM_R11: return aOldRegs->r11;
901 : case DW_REG_ARM_R12: return aOldRegs->r12;
902 : case DW_REG_ARM_R13: return aOldRegs->r13;
903 : case DW_REG_ARM_R14: return aOldRegs->r14;
904 : case DW_REG_ARM_R15: return aOldRegs->r15;
905 : #else
906 : # error "Unsupported arch"
907 : #endif
908 0 : default: MOZ_ASSERT(0); return TaggedUWord();
909 : }
910 : }
911 :
912 : // RUNS IN NO-MALLOC CONTEXT
913 : // See prototype for comment.
914 0 : TaggedUWord EvaluatePfxExpr(int32_t start,
915 : const UnwindRegs* aOldRegs,
916 : TaggedUWord aCFA, const StackImage* aStackImg,
917 : const vector<PfxInstr>& aPfxInstrs)
918 : {
919 : // A small evaluation stack, and a stack pointer, which points to
920 : // the highest numbered in-use element.
921 0 : const int N_STACK = 10;
922 0 : TaggedUWord stack[N_STACK];
923 0 : int stackPointer = -1;
924 0 : for (int i = 0; i < N_STACK; i++)
925 0 : stack[i] = TaggedUWord();
926 :
927 : # define PUSH(_tuw) \
928 : do { \
929 : if (stackPointer >= N_STACK-1) goto fail; /* overflow */ \
930 : stack[++stackPointer] = (_tuw); \
931 : } while (0)
932 :
933 : # define POP(_lval) \
934 : do { \
935 : if (stackPointer < 0) goto fail; /* underflow */ \
936 : _lval = stack[stackPointer--]; \
937 : } while (0)
938 :
939 : // Cursor in the instruction sequence.
940 0 : size_t curr = start + 1;
941 :
942 : // Check the start point is sane.
943 0 : size_t nInstrs = aPfxInstrs.size();
944 0 : if (start < 0 || (size_t)start >= nInstrs)
945 : goto fail;
946 :
947 : {
948 : // The instruction sequence must start with PX_Start. If not,
949 : // something is seriously wrong.
950 0 : PfxInstr first = aPfxInstrs[start];
951 0 : if (first.mOpcode != PX_Start)
952 0 : goto fail;
953 :
954 : // Push the CFA on the stack to start with (or not), as required by
955 : // the original DW_OP_*expression* CFI.
956 0 : if (first.mOperand != 0)
957 0 : PUSH(aCFA);
958 : }
959 :
960 : while (true) {
961 0 : if (curr >= nInstrs)
962 0 : goto fail; // ran off the end of the sequence
963 :
964 0 : PfxInstr pfxi = aPfxInstrs[curr++];
965 0 : if (pfxi.mOpcode == PX_End)
966 0 : break; // we're done
967 :
968 0 : switch (pfxi.mOpcode) {
969 : case PX_Start:
970 : // This should appear only at the start of the sequence.
971 0 : goto fail;
972 : case PX_End:
973 : // We just took care of that, so we shouldn't see it again.
974 0 : MOZ_ASSERT(0);
975 : goto fail;
976 : case PX_SImm32:
977 0 : PUSH(TaggedUWord((intptr_t)pfxi.mOperand));
978 0 : break;
979 : case PX_DwReg: {
980 0 : DW_REG_NUMBER reg = (DW_REG_NUMBER)pfxi.mOperand;
981 0 : MOZ_ASSERT(reg != DW_REG_CFA);
982 0 : PUSH(EvaluateReg(reg, aOldRegs, aCFA));
983 0 : break;
984 : }
985 : case PX_Deref: {
986 0 : TaggedUWord addr;
987 0 : POP(addr);
988 0 : PUSH(DerefTUW(addr, aStackImg));
989 0 : break;
990 : }
991 : case PX_Add: {
992 0 : TaggedUWord x, y;
993 0 : POP(x); POP(y); PUSH(y + x);
994 0 : break;
995 : }
996 : case PX_Sub: {
997 0 : TaggedUWord x, y;
998 0 : POP(x); POP(y); PUSH(y - x);
999 0 : break;
1000 : }
1001 : case PX_And: {
1002 0 : TaggedUWord x, y;
1003 0 : POP(x); POP(y); PUSH(y & x);
1004 0 : break;
1005 : }
1006 : case PX_Or: {
1007 0 : TaggedUWord x, y;
1008 0 : POP(x); POP(y); PUSH(y | x);
1009 0 : break;
1010 : }
1011 : case PX_CmpGES: {
1012 0 : TaggedUWord x, y;
1013 0 : POP(x); POP(y); PUSH(y.CmpGEs(x));
1014 0 : break;
1015 : }
1016 : case PX_Shl: {
1017 0 : TaggedUWord x, y;
1018 0 : POP(x); POP(y); PUSH(y << x);
1019 0 : break;
1020 : }
1021 : default:
1022 0 : MOZ_ASSERT(0);
1023 : goto fail;
1024 : }
1025 0 : } // while (true)
1026 :
1027 : // Evaluation finished. The top value on the stack is the result.
1028 0 : if (stackPointer >= 0) {
1029 0 : return stack[stackPointer];
1030 : }
1031 : // Else fall through
1032 :
1033 : fail:
1034 0 : return TaggedUWord();
1035 :
1036 : # undef PUSH
1037 : # undef POP
1038 : }
1039 :
1040 : // RUNS IN NO-MALLOC CONTEXT
1041 0 : TaggedUWord LExpr::EvaluateExpr(const UnwindRegs* aOldRegs,
1042 : TaggedUWord aCFA, const StackImage* aStackImg,
1043 : const vector<PfxInstr>* aPfxInstrs) const
1044 : {
1045 0 : switch (mHow) {
1046 : case UNKNOWN:
1047 0 : return TaggedUWord();
1048 : case NODEREF: {
1049 0 : TaggedUWord tuw = EvaluateReg(mReg, aOldRegs, aCFA);
1050 0 : tuw = tuw + TaggedUWord((intptr_t)mOffset);
1051 0 : return tuw;
1052 : }
1053 : case DEREF: {
1054 0 : TaggedUWord tuw = EvaluateReg(mReg, aOldRegs, aCFA);
1055 0 : tuw = tuw + TaggedUWord((intptr_t)mOffset);
1056 0 : return DerefTUW(tuw, aStackImg);
1057 : }
1058 : case PFXEXPR: {
1059 0 : MOZ_ASSERT(aPfxInstrs);
1060 0 : if (!aPfxInstrs) {
1061 0 : return TaggedUWord();
1062 : }
1063 0 : return EvaluatePfxExpr(mOffset, aOldRegs, aCFA, aStackImg, *aPfxInstrs);
1064 : }
1065 : default:
1066 0 : MOZ_ASSERT(0);
1067 : return TaggedUWord();
1068 : }
1069 : }
1070 :
1071 : // RUNS IN NO-MALLOC CONTEXT
1072 : static
1073 0 : void UseRuleSet(/*MOD*/UnwindRegs* aRegs,
1074 : const StackImage* aStackImg, const RuleSet* aRS,
1075 : const vector<PfxInstr>* aPfxInstrs)
1076 : {
1077 : // Take a copy of regs, since we'll need to refer to the old values
1078 : // whilst computing the new ones.
1079 0 : UnwindRegs old_regs = *aRegs;
1080 :
1081 : // Mark all the current register values as invalid, so that the
1082 : // caller can see, on our return, which ones have been computed
1083 : // anew. If we don't even manage to compute a new PC value, then
1084 : // the caller will have to abandon the unwind.
1085 : // FIXME: Create and use instead: aRegs->SetAllInvalid();
1086 : #if defined(GP_ARCH_amd64) || defined(GP_ARCH_x86)
1087 0 : aRegs->xbp = TaggedUWord();
1088 0 : aRegs->xsp = TaggedUWord();
1089 0 : aRegs->xip = TaggedUWord();
1090 : #elif defined(GP_ARCH_arm)
1091 : aRegs->r7 = TaggedUWord();
1092 : aRegs->r11 = TaggedUWord();
1093 : aRegs->r12 = TaggedUWord();
1094 : aRegs->r13 = TaggedUWord();
1095 : aRegs->r14 = TaggedUWord();
1096 : aRegs->r15 = TaggedUWord();
1097 : #else
1098 : # error "Unsupported arch"
1099 : #endif
1100 :
1101 : // This is generally useful.
1102 0 : const TaggedUWord inval = TaggedUWord();
1103 :
1104 : // First, compute the CFA.
1105 : TaggedUWord cfa
1106 : = aRS->mCfaExpr.EvaluateExpr(&old_regs,
1107 0 : inval/*old cfa*/, aStackImg, aPfxInstrs);
1108 :
1109 : // If we didn't manage to compute the CFA, well .. that's ungood,
1110 : // but keep going anyway. It'll be OK provided none of the register
1111 : // value rules mention the CFA. In any case, compute the new values
1112 : // for each register that we're tracking.
1113 :
1114 : #if defined(GP_ARCH_amd64) || defined(GP_ARCH_x86)
1115 : aRegs->xbp
1116 0 : = aRS->mXbpExpr.EvaluateExpr(&old_regs, cfa, aStackImg, aPfxInstrs);
1117 : aRegs->xsp
1118 0 : = aRS->mXspExpr.EvaluateExpr(&old_regs, cfa, aStackImg, aPfxInstrs);
1119 : aRegs->xip
1120 0 : = aRS->mXipExpr.EvaluateExpr(&old_regs, cfa, aStackImg, aPfxInstrs);
1121 : #elif defined(GP_ARCH_arm)
1122 : aRegs->r7
1123 : = aRS->mR7expr .EvaluateExpr(&old_regs, cfa, aStackImg, aPfxInstrs);
1124 : aRegs->r11
1125 : = aRS->mR11expr.EvaluateExpr(&old_regs, cfa, aStackImg, aPfxInstrs);
1126 : aRegs->r12
1127 : = aRS->mR12expr.EvaluateExpr(&old_regs, cfa, aStackImg, aPfxInstrs);
1128 : aRegs->r13
1129 : = aRS->mR13expr.EvaluateExpr(&old_regs, cfa, aStackImg, aPfxInstrs);
1130 : aRegs->r14
1131 : = aRS->mR14expr.EvaluateExpr(&old_regs, cfa, aStackImg, aPfxInstrs);
1132 : aRegs->r15
1133 : = aRS->mR15expr.EvaluateExpr(&old_regs, cfa, aStackImg, aPfxInstrs);
1134 : #else
1135 : # error "Unsupported arch"
1136 : #endif
1137 :
1138 : // We're done. Any regs for which we didn't manage to compute a
1139 : // new value will now be marked as invalid.
1140 0 : }
1141 :
1142 : // RUNS IN NO-MALLOC CONTEXT
1143 : void
1144 0 : LUL::Unwind(/*OUT*/uintptr_t* aFramePCs,
1145 : /*OUT*/uintptr_t* aFrameSPs,
1146 : /*OUT*/size_t* aFramesUsed,
1147 : /*OUT*/size_t* aFramePointerFramesAcquired,
1148 : size_t aFramesAvail,
1149 : UnwindRegs* aStartRegs, StackImage* aStackImg)
1150 : {
1151 0 : MOZ_ASSERT(!mAdminMode);
1152 :
1153 : /////////////////////////////////////////////////////////
1154 : // BEGIN UNWIND
1155 :
1156 0 : *aFramesUsed = 0;
1157 :
1158 0 : UnwindRegs regs = *aStartRegs;
1159 0 : TaggedUWord last_valid_sp = TaggedUWord();
1160 :
1161 : while (true) {
1162 :
1163 : if (DEBUG_MAIN) {
1164 : char buf[300];
1165 : mLog("\n");
1166 : #if defined(GP_ARCH_amd64) || defined(GP_ARCH_x86)
1167 : SprintfLiteral(buf,
1168 : "LoopTop: rip %d/%llx rsp %d/%llx rbp %d/%llx\n",
1169 : (int)regs.xip.Valid(), (unsigned long long int)regs.xip.Value(),
1170 : (int)regs.xsp.Valid(), (unsigned long long int)regs.xsp.Value(),
1171 : (int)regs.xbp.Valid(), (unsigned long long int)regs.xbp.Value());
1172 : buf[sizeof(buf)-1] = 0;
1173 : mLog(buf);
1174 : #elif defined(GP_ARCH_arm)
1175 : SprintfLiteral(buf,
1176 : "LoopTop: r15 %d/%llx r7 %d/%llx r11 %d/%llx"
1177 : " r12 %d/%llx r13 %d/%llx r14 %d/%llx\n",
1178 : (int)regs.r15.Valid(), (unsigned long long int)regs.r15.Value(),
1179 : (int)regs.r7.Valid(), (unsigned long long int)regs.r7.Value(),
1180 : (int)regs.r11.Valid(), (unsigned long long int)regs.r11.Value(),
1181 : (int)regs.r12.Valid(), (unsigned long long int)regs.r12.Value(),
1182 : (int)regs.r13.Valid(), (unsigned long long int)regs.r13.Value(),
1183 : (int)regs.r14.Valid(), (unsigned long long int)regs.r14.Value());
1184 : buf[sizeof(buf)-1] = 0;
1185 : mLog(buf);
1186 : #else
1187 : # error "Unsupported arch"
1188 : #endif
1189 : }
1190 :
1191 : #if defined(GP_ARCH_amd64) || defined(GP_ARCH_x86)
1192 0 : TaggedUWord ia = regs.xip;
1193 0 : TaggedUWord sp = regs.xsp;
1194 : #elif defined(GP_ARCH_arm)
1195 : TaggedUWord ia = (*aFramesUsed == 0 ? regs.r15 : regs.r14);
1196 : TaggedUWord sp = regs.r13;
1197 : #else
1198 : # error "Unsupported arch"
1199 : #endif
1200 :
1201 0 : if (*aFramesUsed >= aFramesAvail) {
1202 0 : break;
1203 : }
1204 :
1205 : // If we don't have a valid value for the PC, give up.
1206 0 : if (!ia.Valid()) {
1207 0 : break;
1208 : }
1209 :
1210 : // If this is the innermost frame, record the SP value, which
1211 : // presumably is valid. If this isn't the innermost frame, and we
1212 : // have a valid SP value, check that its SP value isn't less that
1213 : // the one we've seen so far, so as to catch potential SP value
1214 : // cycles.
1215 0 : if (*aFramesUsed == 0) {
1216 0 : last_valid_sp = sp;
1217 : } else {
1218 0 : MOZ_ASSERT(last_valid_sp.Valid());
1219 0 : if (sp.Valid()) {
1220 0 : if (sp.Value() < last_valid_sp.Value()) {
1221 : // Hmm, SP going in the wrong direction. Let's stop.
1222 0 : break;
1223 : }
1224 : // Remember where we got to.
1225 0 : last_valid_sp = sp;
1226 : }
1227 : }
1228 :
1229 : // For the innermost frame, the IA value is what we need. For all
1230 : // other frames, it's actually the return address, so back up one
1231 : // byte so as to get it into the calling instruction.
1232 0 : aFramePCs[*aFramesUsed] = ia.Value() - (*aFramesUsed == 0 ? 0 : 1);
1233 0 : aFrameSPs[*aFramesUsed] = sp.Valid() ? sp.Value() : 0;
1234 0 : (*aFramesUsed)++;
1235 :
1236 : // Find the RuleSet for the current IA, if any. This will also
1237 : // query the backing (secondary) maps if it isn't found in the
1238 : // thread-local cache.
1239 :
1240 : // If this isn't the innermost frame, back up into the calling insn.
1241 0 : if (*aFramesUsed > 1) {
1242 0 : ia = ia + TaggedUWord((uintptr_t)(-1));
1243 : }
1244 :
1245 : pair<const RuleSet*, const vector<PfxInstr>*> ruleset_and_pfxinstrs
1246 0 : = mPriMap->Lookup(ia.Value());
1247 0 : const RuleSet* ruleset = ruleset_and_pfxinstrs.first;
1248 0 : const vector<PfxInstr>* pfxinstrs = ruleset_and_pfxinstrs.second;
1249 :
1250 : if (DEBUG_MAIN) {
1251 : char buf[100];
1252 : SprintfLiteral(buf, "ruleset for 0x%llx = %p\n",
1253 : (unsigned long long int)ia.Value(), ruleset);
1254 : buf[sizeof(buf)-1] = 0;
1255 : mLog(buf);
1256 : }
1257 :
1258 : #if defined(GP_PLAT_x86_android) || defined(GP_PLAT_x86_linux)
1259 : /////////////////////////////////////////////
1260 : ////
1261 : // On 32 bit x86-linux, syscalls are often done via the VDSO
1262 : // function __kernel_vsyscall, which doesn't have a corresponding
1263 : // object that we can read debuginfo from. That effectively kills
1264 : // off all stack traces for threads blocked in syscalls. Hence
1265 : // special-case by looking at the code surrounding the program
1266 : // counter.
1267 : //
1268 : // 0xf7757420 <__kernel_vsyscall+0>: push %ecx
1269 : // 0xf7757421 <__kernel_vsyscall+1>: push %edx
1270 : // 0xf7757422 <__kernel_vsyscall+2>: push %ebp
1271 : // 0xf7757423 <__kernel_vsyscall+3>: mov %esp,%ebp
1272 : // 0xf7757425 <__kernel_vsyscall+5>: sysenter
1273 : // 0xf7757427 <__kernel_vsyscall+7>: nop
1274 : // 0xf7757428 <__kernel_vsyscall+8>: nop
1275 : // 0xf7757429 <__kernel_vsyscall+9>: nop
1276 : // 0xf775742a <__kernel_vsyscall+10>: nop
1277 : // 0xf775742b <__kernel_vsyscall+11>: nop
1278 : // 0xf775742c <__kernel_vsyscall+12>: nop
1279 : // 0xf775742d <__kernel_vsyscall+13>: nop
1280 : // 0xf775742e <__kernel_vsyscall+14>: int $0x80
1281 : // 0xf7757430 <__kernel_vsyscall+16>: pop %ebp
1282 : // 0xf7757431 <__kernel_vsyscall+17>: pop %edx
1283 : // 0xf7757432 <__kernel_vsyscall+18>: pop %ecx
1284 : // 0xf7757433 <__kernel_vsyscall+19>: ret
1285 : //
1286 : // In cases where the sampled thread is blocked in a syscall, its
1287 : // program counter will point at "pop %ebp". Hence we look for
1288 : // the sequence "int $0x80; pop %ebp; pop %edx; pop %ecx; ret", and
1289 : // the corresponding register-recovery actions are:
1290 : // new_ebp = *(old_esp + 0)
1291 : // new eip = *(old_esp + 12)
1292 : // new_esp = old_esp + 16
1293 : //
1294 : // It may also be the case that the program counter points two
1295 : // nops before the "int $0x80", viz, is __kernel_vsyscall+12, in
1296 : // the case where the syscall has been restarted but the thread
1297 : // hasn't been rescheduled. The code below doesn't handle that;
1298 : // it could easily be made to.
1299 : //
1300 : if (!ruleset && *aFramesUsed == 1 && ia.Valid() && sp.Valid()) {
1301 : uintptr_t insns_min, insns_max;
1302 : uintptr_t eip = ia.Value();
1303 : bool b = mSegArray->getBoundingCodeSegment(&insns_min, &insns_max, eip);
1304 : if (b && eip - 2 >= insns_min && eip + 3 <= insns_max) {
1305 : uint8_t* eipC = (uint8_t*)eip;
1306 : if (eipC[-2] == 0xCD && eipC[-1] == 0x80 && eipC[0] == 0x5D &&
1307 : eipC[1] == 0x5A && eipC[2] == 0x59 && eipC[3] == 0xC3) {
1308 : TaggedUWord sp_plus_0 = sp;
1309 : TaggedUWord sp_plus_12 = sp;
1310 : TaggedUWord sp_plus_16 = sp;
1311 : sp_plus_12 = sp_plus_12 + TaggedUWord(12);
1312 : sp_plus_16 = sp_plus_16 + TaggedUWord(16);
1313 : TaggedUWord new_ebp = DerefTUW(sp_plus_0, aStackImg);
1314 : TaggedUWord new_eip = DerefTUW(sp_plus_12, aStackImg);
1315 : TaggedUWord new_esp = sp_plus_16;
1316 : if (new_ebp.Valid() && new_eip.Valid() && new_esp.Valid()) {
1317 : regs.xbp = new_ebp;
1318 : regs.xip = new_eip;
1319 : regs.xsp = new_esp;
1320 : continue;
1321 : }
1322 : }
1323 : }
1324 : }
1325 : ////
1326 : /////////////////////////////////////////////
1327 : #endif // defined(GP_PLAT_x86_android) || defined(GP_PLAT_x86_linux)
1328 :
1329 : // So, do we have a ruleset for this address? If so, use it now.
1330 0 : if (ruleset) {
1331 :
1332 : if (DEBUG_MAIN) {
1333 : ruleset->Print(mLog); mLog("\n");
1334 : }
1335 : // Use the RuleSet to compute the registers for the previous
1336 : // frame. |regs| is modified in-place.
1337 0 : UseRuleSet(®s, aStackImg, ruleset, pfxinstrs);
1338 0 : continue;
1339 :
1340 : }
1341 :
1342 : #if defined(GP_PLAT_amd64_linux)
1343 : // There's no RuleSet for the specified address. On amd64_linux, see if
1344 : // it's possible to recover the caller's frame by using the frame pointer.
1345 : // This would probably work for the 32-bit case too, but hasn't been
1346 : // tested for that case.
1347 :
1348 : // We seek to compute (new_IP, new_SP, new_BP) from (old_BP, stack image),
1349 : // and assume the following layout:
1350 : //
1351 : // <--- new_SP
1352 : // +----------+
1353 : // | new_IP | (return address)
1354 : // +----------+
1355 : // | new_BP | <--- old_BP
1356 : // +----------+
1357 : // | .... |
1358 : // | .... |
1359 : // | .... |
1360 : // +----------+ <---- old_SP (arbitrary, but must be <= old_BP)
1361 :
1362 0 : const size_t wordSzB = sizeof(uintptr_t);
1363 0 : TaggedUWord old_xsp = regs.xsp;
1364 :
1365 : // points at new_BP ?
1366 0 : TaggedUWord old_xbp = regs.xbp;
1367 : // points at new_IP ?
1368 0 : TaggedUWord old_xbp_plus1 = regs.xbp + TaggedUWord(1 * wordSzB);
1369 : // is the new_SP ?
1370 0 : TaggedUWord old_xbp_plus2 = regs.xbp + TaggedUWord(2 * wordSzB);
1371 :
1372 0 : if (old_xbp.Valid() && old_xbp.IsAligned() &&
1373 0 : old_xsp.Valid() && old_xsp.IsAligned() &&
1374 0 : old_xsp.Value() <= old_xbp.Value()) {
1375 : // We don't need to do any range, alignment or validity checks for
1376 : // addresses passed to DerefTUW, since that performs them itself, and
1377 : // returns an invalid value on failure. Any such value will poison
1378 : // subsequent uses, and we do a final check for validity before putting
1379 : // the computed values into |regs|.
1380 0 : TaggedUWord new_xbp = DerefTUW(old_xbp, aStackImg);
1381 0 : if (new_xbp.Valid() && new_xbp.IsAligned() &&
1382 0 : old_xbp.Value() < new_xbp.Value()) {
1383 0 : TaggedUWord new_xip = DerefTUW(old_xbp_plus1, aStackImg);
1384 0 : TaggedUWord new_xsp = old_xbp_plus2;
1385 0 : if (new_xbp.Valid() && new_xip.Valid() && new_xsp.Valid()) {
1386 0 : regs.xbp = new_xbp;
1387 0 : regs.xip = new_xip;
1388 0 : regs.xsp = new_xsp;
1389 0 : (*aFramePointerFramesAcquired)++;
1390 0 : continue;
1391 : }
1392 : }
1393 : }
1394 : #endif // defined(GP_PLAT_amd64_linux)
1395 :
1396 : // We failed to recover a frame either using CFI or FP chasing, and we
1397 : // have no other ways to recover the frame. So we have to give up.
1398 0 : break;
1399 :
1400 0 : } // top level unwind loop
1401 :
1402 : // END UNWIND
1403 : /////////////////////////////////////////////////////////
1404 0 : }
1405 :
1406 :
1407 : ////////////////////////////////////////////////////////////////
1408 : // LUL Unit Testing //
1409 : ////////////////////////////////////////////////////////////////
1410 :
1411 : static const int LUL_UNIT_TEST_STACK_SIZE = 16384;
1412 :
1413 : // This function is innermost in the test call sequence. It uses LUL
1414 : // to unwind, and compares the result with the sequence specified in
1415 : // the director string. These need to agree in order for the test to
1416 : // pass. In order not to screw up the results, this function needs
1417 : // to have a not-very big stack frame, since we're only presenting
1418 : // the innermost LUL_UNIT_TEST_STACK_SIZE bytes of stack to LUL, and
1419 : // that chunk unavoidably includes the frame for this function.
1420 : //
1421 : // This function must not be inlined into its callers. Doing so will
1422 : // cause the expected-vs-actual backtrace consistency checking to
1423 : // fail. Prints summary results to |aLUL|'s logging sink and also
1424 : // returns a boolean indicating whether or not the test passed.
1425 : static __attribute__((noinline))
1426 0 : bool GetAndCheckStackTrace(LUL* aLUL, const char* dstring)
1427 : {
1428 : // Get hold of the current unwind-start registers.
1429 0 : UnwindRegs startRegs;
1430 0 : memset(&startRegs, 0, sizeof(startRegs));
1431 : #if defined(GP_PLAT_amd64_linux)
1432 : volatile uintptr_t block[3];
1433 : MOZ_ASSERT(sizeof(block) == 24);
1434 : __asm__ __volatile__(
1435 : "leaq 0(%%rip), %%r15" "\n\t"
1436 : "movq %%r15, 0(%0)" "\n\t"
1437 : "movq %%rsp, 8(%0)" "\n\t"
1438 : "movq %%rbp, 16(%0)" "\n"
1439 : : : "r"(&block[0]) : "memory", "r15"
1440 0 : );
1441 0 : startRegs.xip = TaggedUWord(block[0]);
1442 0 : startRegs.xsp = TaggedUWord(block[1]);
1443 0 : startRegs.xbp = TaggedUWord(block[2]);
1444 0 : const uintptr_t REDZONE_SIZE = 128;
1445 0 : uintptr_t start = block[1] - REDZONE_SIZE;
1446 : #elif defined(GP_PLAT_x86_linux) || defined(GP_PLAT_x86_android)
1447 : volatile uintptr_t block[3];
1448 : MOZ_ASSERT(sizeof(block) == 12);
1449 : __asm__ __volatile__(
1450 : ".byte 0xE8,0x00,0x00,0x00,0x00"/*call next insn*/ "\n\t"
1451 : "popl %%edi" "\n\t"
1452 : "movl %%edi, 0(%0)" "\n\t"
1453 : "movl %%esp, 4(%0)" "\n\t"
1454 : "movl %%ebp, 8(%0)" "\n"
1455 : : : "r"(&block[0]) : "memory", "edi"
1456 : );
1457 : startRegs.xip = TaggedUWord(block[0]);
1458 : startRegs.xsp = TaggedUWord(block[1]);
1459 : startRegs.xbp = TaggedUWord(block[2]);
1460 : const uintptr_t REDZONE_SIZE = 0;
1461 : uintptr_t start = block[1] - REDZONE_SIZE;
1462 : #elif defined(GP_PLAT_arm_android)
1463 : volatile uintptr_t block[6];
1464 : MOZ_ASSERT(sizeof(block) == 24);
1465 : __asm__ __volatile__(
1466 : "mov r0, r15" "\n\t"
1467 : "str r0, [%0, #0]" "\n\t"
1468 : "str r14, [%0, #4]" "\n\t"
1469 : "str r13, [%0, #8]" "\n\t"
1470 : "str r12, [%0, #12]" "\n\t"
1471 : "str r11, [%0, #16]" "\n\t"
1472 : "str r7, [%0, #20]" "\n"
1473 : : : "r"(&block[0]) : "memory", "r0"
1474 : );
1475 : startRegs.r15 = TaggedUWord(block[0]);
1476 : startRegs.r14 = TaggedUWord(block[1]);
1477 : startRegs.r13 = TaggedUWord(block[2]);
1478 : startRegs.r12 = TaggedUWord(block[3]);
1479 : startRegs.r11 = TaggedUWord(block[4]);
1480 : startRegs.r7 = TaggedUWord(block[5]);
1481 : const uintptr_t REDZONE_SIZE = 0;
1482 : uintptr_t start = block[1] - REDZONE_SIZE;
1483 : #else
1484 : # error "Unsupported platform"
1485 : #endif
1486 :
1487 : // Get hold of the innermost LUL_UNIT_TEST_STACK_SIZE bytes of the
1488 : // stack.
1489 0 : uintptr_t end = start + LUL_UNIT_TEST_STACK_SIZE;
1490 0 : uintptr_t ws = sizeof(void*);
1491 0 : start &= ~(ws-1);
1492 0 : end &= ~(ws-1);
1493 0 : uintptr_t nToCopy = end - start;
1494 0 : if (nToCopy > lul::N_STACK_BYTES) {
1495 0 : nToCopy = lul::N_STACK_BYTES;
1496 : }
1497 0 : MOZ_ASSERT(nToCopy <= lul::N_STACK_BYTES);
1498 0 : StackImage* stackImg = new StackImage();
1499 0 : stackImg->mLen = nToCopy;
1500 0 : stackImg->mStartAvma = start;
1501 0 : if (nToCopy > 0) {
1502 : MOZ_MAKE_MEM_DEFINED((void*)start, nToCopy);
1503 0 : memcpy(&stackImg->mContents[0], (void*)start, nToCopy);
1504 : }
1505 :
1506 : // Unwind it.
1507 0 : const int MAX_TEST_FRAMES = 64;
1508 : uintptr_t framePCs[MAX_TEST_FRAMES];
1509 : uintptr_t frameSPs[MAX_TEST_FRAMES];
1510 0 : size_t framesAvail = mozilla::ArrayLength(framePCs);
1511 0 : size_t framesUsed = 0;
1512 0 : size_t framePointerFramesAcquired = 0;
1513 : aLUL->Unwind( &framePCs[0], &frameSPs[0],
1514 : &framesUsed, &framePointerFramesAcquired,
1515 0 : framesAvail, &startRegs, stackImg );
1516 :
1517 : delete stackImg;
1518 :
1519 : //if (0) {
1520 : // // Show what we have.
1521 : // fprintf(stderr, "Got %d frames:\n", (int)framesUsed);
1522 : // for (size_t i = 0; i < framesUsed; i++) {
1523 : // fprintf(stderr, " [%2d] SP %p PC %p\n",
1524 : // (int)i, (void*)frameSPs[i], (void*)framePCs[i]);
1525 : // }
1526 : // fprintf(stderr, "\n");
1527 : //}
1528 :
1529 : // Check to see if there's a consistent binding between digits in
1530 : // the director string ('1' .. '8') and the PC values acquired by
1531 : // the unwind. If there isn't, the unwinding has failed somehow.
1532 : uintptr_t binding[8]; // binding for '1' .. binding for '8'
1533 0 : memset((void*)binding, 0, sizeof(binding));
1534 :
1535 : // The general plan is to work backwards along the director string
1536 : // and forwards along the framePCs array. Doing so corresponds to
1537 : // working outwards from the innermost frame of the recursive test set.
1538 0 : const char* cursor = dstring;
1539 :
1540 : // Find the end. This leaves |cursor| two bytes past the first
1541 : // character we want to look at -- see comment below.
1542 0 : while (*cursor) cursor++;
1543 :
1544 : // Counts the number of consistent frames.
1545 0 : size_t nConsistent = 0;
1546 :
1547 : // Iterate back to the start of the director string. The starting
1548 : // points are a bit complex. We can't use framePCs[0] because that
1549 : // contains the PC in this frame (above). We can't use framePCs[1]
1550 : // because that will contain the PC at return point in the recursive
1551 : // test group (TestFn[1-8]) for their call "out" to this function,
1552 : // GetAndCheckStackTrace. Although LUL will compute a correct
1553 : // return address, that will not be the same return address as for a
1554 : // recursive call out of the the function to another function in the
1555 : // group. Hence we can only start consistency checking at
1556 : // framePCs[2].
1557 : //
1558 : // To be consistent, then, we must ignore the last element in the
1559 : // director string as that corresponds to framePCs[1]. Hence the
1560 : // start points are: framePCs[2] and the director string 2 bytes
1561 : // before the terminating zero.
1562 : //
1563 : // Also as a result of this, the number of consistent frames counted
1564 : // will always be one less than the length of the director string
1565 : // (not including its terminating zero).
1566 : size_t frameIx;
1567 0 : for (cursor = cursor-2, frameIx = 2;
1568 0 : cursor >= dstring && frameIx < framesUsed;
1569 : cursor--, frameIx++) {
1570 0 : char c = *cursor;
1571 0 : uintptr_t pc = framePCs[frameIx];
1572 : // If this doesn't hold, the director string is ill-formed.
1573 0 : MOZ_ASSERT(c >= '1' && c <= '8');
1574 0 : int n = ((int)c) - ((int)'1');
1575 0 : if (binding[n] == 0) {
1576 : // There's no binding for |c| yet, so install |pc| and carry on.
1577 0 : binding[n] = pc;
1578 0 : nConsistent++;
1579 0 : continue;
1580 : }
1581 : // There's a pre-existing binding for |c|. Check it's consistent.
1582 0 : if (binding[n] != pc) {
1583 : // Not consistent. Give up now.
1584 0 : break;
1585 : }
1586 : // Consistent. Keep going.
1587 0 : nConsistent++;
1588 : }
1589 :
1590 : // So, did we succeed?
1591 0 : bool passed = nConsistent+1 == strlen(dstring);
1592 :
1593 : // Show the results.
1594 : char buf[200];
1595 0 : SprintfLiteral(buf, "LULUnitTest: dstring = %s\n", dstring);
1596 0 : buf[sizeof(buf)-1] = 0;
1597 0 : aLUL->mLog(buf);
1598 0 : SprintfLiteral(buf,
1599 : "LULUnitTest: %d consistent, %d in dstring: %s\n",
1600 0 : (int)nConsistent, (int)strlen(dstring),
1601 0 : passed ? "PASS" : "FAIL");
1602 0 : buf[sizeof(buf)-1] = 0;
1603 0 : aLUL->mLog(buf);
1604 :
1605 0 : return passed;
1606 : }
1607 :
1608 :
1609 : // Macro magic to create a set of 8 mutually recursive functions with
1610 : // varying frame sizes. These will recurse amongst themselves as
1611 : // specified by |strP|, the directory string, and call
1612 : // GetAndCheckStackTrace when the string becomes empty, passing it the
1613 : // original value of the string. This checks the result, printing
1614 : // results on |aLUL|'s logging sink, and also returns a boolean
1615 : // indicating whether or not the results are acceptable (correct).
1616 :
1617 : #define DECL_TEST_FN(NAME) \
1618 : bool NAME(LUL* aLUL, const char* strPorig, const char* strP);
1619 :
1620 : #define GEN_TEST_FN(NAME, FRAMESIZE) \
1621 : bool NAME(LUL* aLUL, const char* strPorig, const char* strP) { \
1622 : volatile char space[FRAMESIZE]; \
1623 : memset((char*)&space[0], 0, sizeof(space)); \
1624 : if (*strP == '\0') { \
1625 : /* We've come to the end of the director string. */ \
1626 : /* Take a stack snapshot. */ \
1627 : return GetAndCheckStackTrace(aLUL, strPorig); \
1628 : } else { \
1629 : /* Recurse onwards. This is a bit subtle. The obvious */ \
1630 : /* thing to do here is call onwards directly, from within the */ \
1631 : /* arms of the case statement. That gives a problem in that */ \
1632 : /* there will be multiple return points inside each function when */ \
1633 : /* unwinding, so it will be difficult to check for consistency */ \
1634 : /* against the director string. Instead, we make an indirect */ \
1635 : /* call, so as to guarantee that there is only one call site */ \
1636 : /* within each function. This does assume that the compiler */ \
1637 : /* won't transform it back to the simple direct-call form. */ \
1638 : /* To discourage it from doing so, the call is bracketed with */ \
1639 : /* __asm__ __volatile__ sections so as to make it not-movable. */ \
1640 : bool (*nextFn)(LUL*, const char*, const char*) = NULL; \
1641 : switch (*strP) { \
1642 : case '1': nextFn = TestFn1; break; \
1643 : case '2': nextFn = TestFn2; break; \
1644 : case '3': nextFn = TestFn3; break; \
1645 : case '4': nextFn = TestFn4; break; \
1646 : case '5': nextFn = TestFn5; break; \
1647 : case '6': nextFn = TestFn6; break; \
1648 : case '7': nextFn = TestFn7; break; \
1649 : case '8': nextFn = TestFn8; break; \
1650 : default: nextFn = TestFn8; break; \
1651 : } \
1652 : __asm__ __volatile__("":::"cc","memory"); \
1653 : bool passed = nextFn(aLUL, strPorig, strP+1); \
1654 : __asm__ __volatile__("":::"cc","memory"); \
1655 : return passed; \
1656 : } \
1657 : }
1658 :
1659 : // The test functions are mutually recursive, so it is necessary to
1660 : // declare them before defining them.
1661 : DECL_TEST_FN(TestFn1)
1662 : DECL_TEST_FN(TestFn2)
1663 : DECL_TEST_FN(TestFn3)
1664 : DECL_TEST_FN(TestFn4)
1665 : DECL_TEST_FN(TestFn5)
1666 : DECL_TEST_FN(TestFn6)
1667 : DECL_TEST_FN(TestFn7)
1668 : DECL_TEST_FN(TestFn8)
1669 :
1670 0 : GEN_TEST_FN(TestFn1, 123)
1671 0 : GEN_TEST_FN(TestFn2, 456)
1672 0 : GEN_TEST_FN(TestFn3, 789)
1673 0 : GEN_TEST_FN(TestFn4, 23)
1674 0 : GEN_TEST_FN(TestFn5, 47)
1675 0 : GEN_TEST_FN(TestFn6, 117)
1676 0 : GEN_TEST_FN(TestFn7, 1)
1677 0 : GEN_TEST_FN(TestFn8, 99)
1678 :
1679 :
1680 : // This starts the test sequence going. Call here to generate a
1681 : // sequence of calls as directed by the string |dstring|. The call
1682 : // sequence will, from its innermost frame, finish by calling
1683 : // GetAndCheckStackTrace() and passing it |dstring|.
1684 : // GetAndCheckStackTrace() will unwind the stack, check consistency
1685 : // of those results against |dstring|, and print a pass/fail message
1686 : // to aLUL's logging sink. It also updates the counters in *aNTests
1687 : // and aNTestsPassed.
1688 : __attribute__((noinline)) void
1689 0 : TestUnw(/*OUT*/int* aNTests, /*OUT*/int*aNTestsPassed,
1690 : LUL* aLUL, const char* dstring)
1691 : {
1692 : // Ensure that the stack has at least this much space on it. This
1693 : // makes it safe to saw off the top LUL_UNIT_TEST_STACK_SIZE bytes
1694 : // and hand it to LUL. Safe in the sense that no segfault can
1695 : // happen because the stack is at least this big. This is all
1696 : // somewhat dubious in the sense that a sufficiently clever compiler
1697 : // (clang, for one) can figure out that space[] is unused and delete
1698 : // it from the frame. Hence the somewhat elaborate hoop jumping to
1699 : // fill it up before the call and to at least appear to use the
1700 : // value afterwards.
1701 : int i;
1702 : volatile char space[LUL_UNIT_TEST_STACK_SIZE];
1703 0 : for (i = 0; i < LUL_UNIT_TEST_STACK_SIZE; i++) {
1704 0 : space[i] = (char)(i & 0x7F);
1705 : }
1706 :
1707 : // Really run the test.
1708 0 : bool passed = TestFn1(aLUL, dstring, dstring);
1709 :
1710 : // Appear to use space[], by visiting the value to compute some kind
1711 : // of checksum, and then (apparently) using the checksum.
1712 0 : int sum = 0;
1713 0 : for (i = 0; i < LUL_UNIT_TEST_STACK_SIZE; i++) {
1714 : // If this doesn't fool LLVM, I don't know what will.
1715 0 : sum += space[i] - 3*i;
1716 : }
1717 0 : __asm__ __volatile__("" : : "r"(sum));
1718 :
1719 : // Update the counters.
1720 0 : (*aNTests)++;
1721 0 : if (passed) {
1722 0 : (*aNTestsPassed)++;
1723 : }
1724 0 : }
1725 :
1726 :
1727 : void
1728 0 : RunLulUnitTests(/*OUT*/int* aNTests, /*OUT*/int*aNTestsPassed, LUL* aLUL)
1729 : {
1730 0 : aLUL->mLog(":\n");
1731 0 : aLUL->mLog("LULUnitTest: BEGIN\n");
1732 0 : *aNTests = *aNTestsPassed = 0;
1733 0 : TestUnw(aNTests, aNTestsPassed, aLUL, "11111111");
1734 0 : TestUnw(aNTests, aNTestsPassed, aLUL, "11222211");
1735 0 : TestUnw(aNTests, aNTestsPassed, aLUL, "111222333");
1736 0 : TestUnw(aNTests, aNTestsPassed, aLUL, "1212121231212331212121212121212");
1737 0 : TestUnw(aNTests, aNTestsPassed, aLUL, "31415827271828325332173258");
1738 : TestUnw(aNTests, aNTestsPassed, aLUL,
1739 0 : "123456781122334455667788777777777777777777777");
1740 0 : aLUL->mLog("LULUnitTest: END\n");
1741 0 : aLUL->mLog(":\n");
1742 0 : }
1743 :
1744 :
1745 : } // namespace lul
|