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