tools/profiler/LulMain.cpp

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

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

mercurial