michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* vim: set ts=8 sts=2 et sw=2 tw=80: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "LulDwarfSummariser.h" michael@0: michael@0: #include "mozilla/Assertions.h" michael@0: michael@0: // Set this to 1 for verbose logging michael@0: #define DEBUG_SUMMARISER 0 michael@0: michael@0: namespace lul { michael@0: michael@0: Summariser::Summariser(SecMap* aSecMap, uintptr_t aTextBias, michael@0: void(*aLog)(const char*)) michael@0: : mSecMap(aSecMap) michael@0: , mTextBias(aTextBias) michael@0: , mLog(aLog) michael@0: { michael@0: mCurrAddr = 0; michael@0: mMax1Addr = 0; // Gives an empty range. michael@0: michael@0: // Initialise the running RuleSet to "haven't got a clue" status. michael@0: new (&mCurrRules) RuleSet(); michael@0: } michael@0: michael@0: void michael@0: Summariser::Entry(uintptr_t aAddress, uintptr_t aLength) michael@0: { michael@0: aAddress += mTextBias; michael@0: if (DEBUG_SUMMARISER) { michael@0: char buf[100]; michael@0: snprintf(buf, sizeof(buf), "LUL Entry(%llx, %llu)\n", michael@0: (unsigned long long int)aAddress, michael@0: (unsigned long long int)aLength); michael@0: buf[sizeof(buf)-1] = 0; michael@0: mLog(buf); michael@0: } michael@0: // This throws away any previous summary, that is, assumes michael@0: // that the previous summary, if any, has been properly finished michael@0: // by a call to End(). michael@0: mCurrAddr = aAddress; michael@0: mMax1Addr = aAddress + aLength; michael@0: new (&mCurrRules) RuleSet(); michael@0: } michael@0: michael@0: void michael@0: Summariser::Rule(uintptr_t aAddress, michael@0: int aNewReg, int aOldReg, intptr_t aOffset, bool aDeref) michael@0: { michael@0: aAddress += mTextBias; michael@0: if (DEBUG_SUMMARISER) { michael@0: char buf[100]; michael@0: snprintf(buf, sizeof(buf), michael@0: "LUL 0x%llx old-r%d = %sr%d + %ld%s\n", michael@0: (unsigned long long int)aAddress, aNewReg, michael@0: aDeref ? "*(" : "", aOldReg, (long)aOffset, aDeref ? ")" : ""); michael@0: buf[sizeof(buf)-1] = 0; michael@0: mLog(buf); michael@0: } michael@0: if (mCurrAddr < aAddress) { michael@0: // Flush the existing summary first. michael@0: mCurrRules.mAddr = mCurrAddr; michael@0: mCurrRules.mLen = aAddress - mCurrAddr; michael@0: mSecMap->AddRuleSet(&mCurrRules); michael@0: if (DEBUG_SUMMARISER) { michael@0: mLog("LUL "); mCurrRules.Print(mLog); michael@0: mLog("\n"); michael@0: } michael@0: mCurrAddr = aAddress; michael@0: } michael@0: michael@0: // FIXME: factor out common parts of the arch-dependent summarisers. michael@0: michael@0: #if defined(LUL_ARCH_arm) michael@0: michael@0: // ----------------- arm ----------------- // michael@0: michael@0: // Now, can we add the rule to our summary? This depends on whether michael@0: // the registers and the overall expression are representable. This michael@0: // is the heart of the summarisation process. michael@0: switch (aNewReg) { michael@0: michael@0: case DW_REG_CFA: michael@0: // This is a rule that defines the CFA. The only forms we michael@0: // choose to represent are: r7/11/12/13 + offset. The offset michael@0: // must fit into 32 bits since 'uintptr_t' is 32 bit on ARM, michael@0: // hence there is no need to check it for overflow. michael@0: if (aDeref) { michael@0: goto cant_summarise; michael@0: } michael@0: switch (aOldReg) { michael@0: case DW_REG_ARM_R7: case DW_REG_ARM_R11: michael@0: case DW_REG_ARM_R12: case DW_REG_ARM_R13: michael@0: break; michael@0: default: michael@0: goto cant_summarise; michael@0: } michael@0: mCurrRules.mCfaExpr = LExpr(LExpr::NODEREF, aOldReg, aOffset); michael@0: break; michael@0: michael@0: case DW_REG_ARM_R7: case DW_REG_ARM_R11: case DW_REG_ARM_R12: michael@0: case DW_REG_ARM_R13: case DW_REG_ARM_R14: case DW_REG_ARM_R15: { michael@0: // Check the aOldReg is valid. michael@0: switch (aOldReg) { michael@0: case DW_REG_CFA: michael@0: case DW_REG_ARM_R7: case DW_REG_ARM_R11: case DW_REG_ARM_R12: michael@0: case DW_REG_ARM_R13: case DW_REG_ARM_R14: case DW_REG_ARM_R15: michael@0: break; michael@0: default: michael@0: goto cant_summarise; michael@0: } michael@0: // This is a new rule for one of r{7,11,12,13,14,15} and has a michael@0: // representable offset. In particular the new value of r15 is michael@0: // going to be the return address. michael@0: LExpr expr = LExpr(aDeref ? LExpr::DEREF : LExpr::NODEREF, michael@0: aOldReg, aOffset); michael@0: switch (aNewReg) { michael@0: case DW_REG_ARM_R7: mCurrRules.mR7expr = expr; break; michael@0: case DW_REG_ARM_R11: mCurrRules.mR11expr = expr; break; michael@0: case DW_REG_ARM_R12: mCurrRules.mR12expr = expr; break; michael@0: case DW_REG_ARM_R13: mCurrRules.mR13expr = expr; break; michael@0: case DW_REG_ARM_R14: mCurrRules.mR14expr = expr; break; michael@0: case DW_REG_ARM_R15: mCurrRules.mR15expr = expr; break; michael@0: default: MOZ_ASSERT(0); michael@0: } michael@0: break; michael@0: } michael@0: michael@0: default: michael@0: goto cant_summarise; michael@0: } michael@0: michael@0: // Mark callee-saved registers (r4 .. r11) as unchanged, if there is michael@0: // no other information about them. FIXME: do this just once, at michael@0: // the point where the ruleset is committed. michael@0: if (mCurrRules.mR7expr.mHow == LExpr::UNKNOWN) { michael@0: mCurrRules.mR7expr = LExpr(LExpr::NODEREF, DW_REG_ARM_R7, 0); michael@0: } michael@0: if (mCurrRules.mR11expr.mHow == LExpr::UNKNOWN) { michael@0: mCurrRules.mR11expr = LExpr(LExpr::NODEREF, DW_REG_ARM_R11, 0); michael@0: } michael@0: if (mCurrRules.mR12expr.mHow == LExpr::UNKNOWN) { michael@0: mCurrRules.mR12expr = LExpr(LExpr::NODEREF, DW_REG_ARM_R12, 0); michael@0: } michael@0: michael@0: // The old r13 (SP) value before the call is always the same as the michael@0: // CFA. michael@0: mCurrRules.mR13expr = LExpr(LExpr::NODEREF, DW_REG_CFA, 0); michael@0: michael@0: // If there's no information about R15 (the return address), say michael@0: // it's a copy of R14 (the link register). michael@0: if (mCurrRules.mR15expr.mHow == LExpr::UNKNOWN) { michael@0: mCurrRules.mR15expr = LExpr(LExpr::NODEREF, DW_REG_ARM_R14, 0); michael@0: } michael@0: michael@0: #elif defined(LUL_ARCH_x64) || defined(LUL_ARCH_x86) michael@0: michael@0: // ---------------- x64/x86 ---------------- // michael@0: michael@0: // Now, can we add the rule to our summary? This depends on whether michael@0: // the registers and the overall expression are representable. This michael@0: // is the heart of the summarisation process. In the 64 bit case michael@0: // we need to check that aOffset will fit into an int32_t. In the michael@0: // 32 bit case it is expected that the compiler will fold out the michael@0: // test since it always succeeds. michael@0: if (aNewReg == DW_REG_CFA) { michael@0: // This is a rule that defines the CFA. The only forms we can michael@0: // represent are: = SP+offset or = FP+offset. michael@0: if (!aDeref && aOffset == (intptr_t)(int32_t)aOffset && michael@0: (aOldReg == DW_REG_INTEL_XSP || aOldReg == DW_REG_INTEL_XBP)) { michael@0: mCurrRules.mCfaExpr = LExpr(LExpr::NODEREF, aOldReg, aOffset); michael@0: } else { michael@0: goto cant_summarise; michael@0: } michael@0: } michael@0: else michael@0: if ((aNewReg == DW_REG_INTEL_XSP || michael@0: aNewReg == DW_REG_INTEL_XBP || aNewReg == DW_REG_INTEL_XIP) && michael@0: (aOldReg == DW_REG_CFA || michael@0: aOldReg == DW_REG_INTEL_XSP || michael@0: aOldReg == DW_REG_INTEL_XBP || aOldReg == DW_REG_INTEL_XIP) && michael@0: aOffset == (intptr_t)(int32_t)aOffset) { michael@0: // This is a new rule for SP, BP or the return address michael@0: // respectively, and has a representable offset. michael@0: LExpr expr = LExpr(aDeref ? LExpr::DEREF : LExpr::NODEREF, michael@0: aOldReg, aOffset); michael@0: switch (aNewReg) { michael@0: case DW_REG_INTEL_XBP: mCurrRules.mXbpExpr = expr; break; michael@0: case DW_REG_INTEL_XSP: mCurrRules.mXspExpr = expr; break; michael@0: case DW_REG_INTEL_XIP: mCurrRules.mXipExpr = expr; break; michael@0: default: MOZ_CRASH("impossible value for aNewReg"); michael@0: } michael@0: } michael@0: else { michael@0: goto cant_summarise; michael@0: } michael@0: michael@0: // On Intel, it seems the old SP value before the call is always the michael@0: // same as the CFA. Therefore, in the absence of any other way to michael@0: // recover the SP, specify that the CFA should be copied. michael@0: if (mCurrRules.mXspExpr.mHow == LExpr::UNKNOWN) { michael@0: mCurrRules.mXspExpr = LExpr(LExpr::NODEREF, DW_REG_CFA, 0); michael@0: } michael@0: michael@0: // Also, gcc says "Undef" for BP when it is unchanged. michael@0: if (mCurrRules.mXbpExpr.mHow == LExpr::UNKNOWN) { michael@0: mCurrRules.mXbpExpr = LExpr(LExpr::NODEREF, DW_REG_INTEL_XBP, 0); michael@0: } michael@0: michael@0: #else michael@0: michael@0: # error "Unsupported arch" michael@0: #endif michael@0: michael@0: return; michael@0: cant_summarise: michael@0: if (0) { michael@0: mLog("LUL can't summarise\n"); michael@0: } michael@0: } michael@0: michael@0: void michael@0: Summariser::End() michael@0: { michael@0: if (DEBUG_SUMMARISER) { michael@0: mLog("LUL End\n"); michael@0: } michael@0: if (mCurrAddr < mMax1Addr) { michael@0: mCurrRules.mAddr = mCurrAddr; michael@0: mCurrRules.mLen = mMax1Addr - mCurrAddr; michael@0: mSecMap->AddRuleSet(&mCurrRules); michael@0: if (DEBUG_SUMMARISER) { michael@0: mLog("LUL "); mCurrRules.Print(mLog); michael@0: mLog("\n"); michael@0: } michael@0: } michael@0: } michael@0: michael@0: } // namespace lul