mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Merge mozilla-central and inbound
This commit is contained in:
commit
c87b3f9147
@ -766,7 +766,9 @@ public:
|
||||
* Get this document's inline style sheet. May return null if there
|
||||
* isn't one
|
||||
*/
|
||||
virtual nsHTMLCSSStyleSheet* GetInlineStyleSheet() const = 0;
|
||||
nsHTMLCSSStyleSheet* GetInlineStyleSheet() const {
|
||||
return mStyleAttrStyleSheet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get/set the object from which a document can get a script context
|
||||
@ -2178,6 +2180,7 @@ protected:
|
||||
nsRefPtr<mozilla::css::Loader> mCSSLoader;
|
||||
nsRefPtr<mozilla::css::ImageLoader> mStyleImageLoader;
|
||||
nsRefPtr<nsHTMLStyleSheet> mAttrStyleSheet;
|
||||
nsRefPtr<nsHTMLCSSStyleSheet> mStyleAttrStyleSheet;
|
||||
|
||||
// The set of all object, embed, applet, video and audio elements for
|
||||
// which this is the owner document. (They might not be in the document.)
|
||||
|
@ -1478,8 +1478,6 @@ nsDocument::~nsDocument()
|
||||
if (mAttrStyleSheet) {
|
||||
mAttrStyleSheet->SetOwningDocument(nullptr);
|
||||
}
|
||||
if (mStyleAttrStyleSheet)
|
||||
mStyleAttrStyleSheet->SetOwningDocument(nullptr);
|
||||
|
||||
if (mListenerManager) {
|
||||
mListenerManager->Disconnect();
|
||||
@ -2234,37 +2232,16 @@ nsDocument::ResetStylesheetsToURI(nsIURI* aURI)
|
||||
|
||||
// Now reset our inline style and attribute sheets.
|
||||
if (mAttrStyleSheet) {
|
||||
// Remove this sheet from all style sets
|
||||
nsCOMPtr<nsIPresShell> shell = GetShell();
|
||||
if (shell) {
|
||||
shell->StyleSet()->RemoveStyleSheet(nsStyleSet::ePresHintSheet,
|
||||
mAttrStyleSheet);
|
||||
}
|
||||
mAttrStyleSheet->Reset(aURI);
|
||||
mAttrStyleSheet->Reset();
|
||||
mAttrStyleSheet->SetOwningDocument(this);
|
||||
} else {
|
||||
mAttrStyleSheet = new nsHTMLStyleSheet(aURI, this);
|
||||
mAttrStyleSheet = new nsHTMLStyleSheet(this);
|
||||
}
|
||||
|
||||
// Don't use AddStyleSheet, since it'll put the sheet into style
|
||||
// sets in the document level, which is not desirable here.
|
||||
mAttrStyleSheet->SetOwningDocument(this);
|
||||
|
||||
if (mStyleAttrStyleSheet) {
|
||||
// Remove this sheet from all style sets
|
||||
nsCOMPtr<nsIPresShell> shell = GetShell();
|
||||
if (shell) {
|
||||
shell->StyleSet()->
|
||||
RemoveStyleSheet(nsStyleSet::eStyleAttrSheet, mStyleAttrStyleSheet);
|
||||
}
|
||||
mStyleAttrStyleSheet->Reset(aURI);
|
||||
} else {
|
||||
mStyleAttrStyleSheet = new nsHTMLCSSStyleSheet(aURI, this);
|
||||
if (!mStyleAttrStyleSheet) {
|
||||
mStyleAttrStyleSheet = new nsHTMLCSSStyleSheet();
|
||||
}
|
||||
|
||||
// The loop over style sets below will handle putting this sheet
|
||||
// into style sets as needed.
|
||||
mStyleAttrStyleSheet->SetOwningDocument(this);
|
||||
|
||||
// Now set up our style sets
|
||||
nsCOMPtr<nsIPresShell> shell = GetShell();
|
||||
if (shell) {
|
||||
@ -2295,19 +2272,13 @@ void
|
||||
nsDocument::FillStyleSet(nsStyleSet* aStyleSet)
|
||||
{
|
||||
NS_PRECONDITION(aStyleSet, "Must have a style set");
|
||||
NS_PRECONDITION(aStyleSet->SheetCount(nsStyleSet::ePresHintSheet) == 0,
|
||||
"Style set already has a preshint sheet?");
|
||||
NS_PRECONDITION(aStyleSet->SheetCount(nsStyleSet::eDocSheet) == 0,
|
||||
"Style set already has document sheets?");
|
||||
NS_PRECONDITION(aStyleSet->SheetCount(nsStyleSet::eStyleAttrSheet) == 0,
|
||||
"Style set already has style attr sheets?");
|
||||
NS_PRECONDITION(mStyleAttrStyleSheet, "No style attr stylesheet?");
|
||||
NS_PRECONDITION(mAttrStyleSheet, "No attr stylesheet?");
|
||||
|
||||
aStyleSet->AppendStyleSheet(nsStyleSet::ePresHintSheet, mAttrStyleSheet);
|
||||
|
||||
aStyleSet->AppendStyleSheet(nsStyleSet::eStyleAttrSheet,
|
||||
mStyleAttrStyleSheet);
|
||||
// We could consider moving this to nsStyleSet::Init, to match its
|
||||
// handling of the eAnimationSheet and eTransitionSheet levels.
|
||||
aStyleSet->DirtyRuleProcessors(nsStyleSet::ePresHintSheet);
|
||||
aStyleSet->DirtyRuleProcessors(nsStyleSet::eStyleAttrSheet);
|
||||
|
||||
int32_t i;
|
||||
for (i = mStyleSheets.Count() - 1; i >= 0; --i) {
|
||||
|
@ -641,14 +641,6 @@ public:
|
||||
return mChannel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get this document's inline style sheet. May return null if there
|
||||
* isn't one
|
||||
*/
|
||||
virtual nsHTMLCSSStyleSheet* GetInlineStyleSheet() const MOZ_OVERRIDE {
|
||||
return mStyleAttrStyleSheet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the object from which a document can get a script context.
|
||||
* This is the context within which all scripts (during document
|
||||
@ -1308,7 +1300,6 @@ protected:
|
||||
|
||||
// The channel that got passed to StartDocumentLoad(), if any
|
||||
nsCOMPtr<nsIChannel> mChannel;
|
||||
nsRefPtr<nsHTMLCSSStyleSheet> mStyleAttrStyleSheet;
|
||||
|
||||
// A document "without a browsing context" that owns the content of
|
||||
// HTMLTemplateElement.
|
||||
|
@ -68,7 +68,6 @@ TEST(TiledLayerBuffer, TileStart) {
|
||||
TEST(TiledLayerBuffer, EmptyUpdate) {
|
||||
TestTiledLayerBuffer buffer;
|
||||
|
||||
nsRegion::InitStatic();
|
||||
nsIntRegion validRegion(nsIntRect(0, 0, 10, 10));
|
||||
buffer.TestUpdate(validRegion, validRegion);
|
||||
|
||||
|
@ -280,6 +280,9 @@ js::ValueToCallable(JSContext *cx, const Value &v, int numToSkip, MaybeConstruct
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static JS_NEVER_INLINE bool
|
||||
Interpret(JSContext *cx, StackFrame *entryFrame);
|
||||
|
||||
bool
|
||||
js::RunScript(JSContext *cx, StackFrame *fp)
|
||||
{
|
||||
@ -342,7 +345,7 @@ js::RunScript(JSContext *cx, StackFrame *fp)
|
||||
}
|
||||
#endif
|
||||
|
||||
return Interpret(cx, fp) != Interpret_Error;
|
||||
return Interpret(cx, fp);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -985,13 +988,12 @@ js::IteratorNext(JSContext *cx, HandleObject iterobj, MutableHandleValue rval)
|
||||
return js_IteratorNext(cx, iterobj, rval);
|
||||
}
|
||||
|
||||
JS_NEVER_INLINE InterpretStatus
|
||||
js::Interpret(JSContext *cx, StackFrame *entryFrame, InterpMode interpMode, bool useNewType)
|
||||
static JS_NEVER_INLINE bool
|
||||
Interpret(JSContext *cx, StackFrame *entryFrame)
|
||||
{
|
||||
JSAutoResolveFlags rf(cx, RESOLVE_INFER);
|
||||
|
||||
if (interpMode == JSINTERP_NORMAL)
|
||||
gc::MaybeVerifyBarriers(cx, true);
|
||||
gc::MaybeVerifyBarriers(cx, true);
|
||||
|
||||
JS_ASSERT(!cx->compartment()->activeAnalysis);
|
||||
|
||||
@ -1060,8 +1062,6 @@ js::Interpret(JSContext *cx, StackFrame *entryFrame, InterpMode interpMode, bool
|
||||
script = (s); \
|
||||
if (script->hasAnyBreakpointsOrStepMode() || script->hasScriptCounts) \
|
||||
interrupts.enable(); \
|
||||
JS_ASSERT_IF(interpMode == JSINTERP_SKIP_TRAP, \
|
||||
script->hasAnyBreakpointsOrStepMode()); \
|
||||
JS_END_MACRO
|
||||
|
||||
/* Repoint cx->regs to a local variable for faster access. */
|
||||
@ -1128,43 +1128,28 @@ js::Interpret(JSContext *cx, StackFrame *entryFrame, InterpMode interpMode, bool
|
||||
/* State communicated between non-local jumps: */
|
||||
bool interpReturnOK;
|
||||
|
||||
/* Don't call the script prologue if executing between Method and Trace JIT. */
|
||||
if (interpMode == JSINTERP_NORMAL) {
|
||||
StackFrame *fp = regs.fp();
|
||||
if (!fp->isGeneratorFrame()) {
|
||||
if (!fp->prologue(cx))
|
||||
goto error;
|
||||
} else {
|
||||
Probes::enterScript(cx, script, script->function(), fp);
|
||||
}
|
||||
if (cx->compartment()->debugMode()) {
|
||||
JSTrapStatus status = ScriptDebugPrologue(cx, fp);
|
||||
switch (status) {
|
||||
case JSTRAP_CONTINUE:
|
||||
break;
|
||||
case JSTRAP_RETURN:
|
||||
interpReturnOK = true;
|
||||
goto forced_return;
|
||||
case JSTRAP_THROW:
|
||||
case JSTRAP_ERROR:
|
||||
goto error;
|
||||
default:
|
||||
JS_NOT_REACHED("bad ScriptDebugPrologue status");
|
||||
}
|
||||
if (!entryFrame->isGeneratorFrame()) {
|
||||
if (!entryFrame->prologue(cx))
|
||||
goto error;
|
||||
} else {
|
||||
Probes::enterScript(cx, script, script->function(), entryFrame);
|
||||
}
|
||||
if (cx->compartment()->debugMode()) {
|
||||
JSTrapStatus status = ScriptDebugPrologue(cx, entryFrame);
|
||||
switch (status) {
|
||||
case JSTRAP_CONTINUE:
|
||||
break;
|
||||
case JSTRAP_RETURN:
|
||||
interpReturnOK = true;
|
||||
goto forced_return;
|
||||
case JSTRAP_THROW:
|
||||
case JSTRAP_ERROR:
|
||||
goto error;
|
||||
default:
|
||||
JS_NOT_REACHED("bad ScriptDebugPrologue status");
|
||||
}
|
||||
}
|
||||
|
||||
/* The REJOIN mode acts like the normal mode, except the prologue is skipped. */
|
||||
if (interpMode == JSINTERP_REJOIN)
|
||||
interpMode = JSINTERP_NORMAL;
|
||||
|
||||
/*
|
||||
* The RETHROW mode acts like a bailout mode, except that it resume an
|
||||
* exception instead of resuming the script.
|
||||
*/
|
||||
if (interpMode == JSINTERP_RETHROW)
|
||||
goto error;
|
||||
|
||||
/*
|
||||
* It is important that "op" be initialized before calling DO_OP because
|
||||
* it is possible for "op" to be specially assigned during the normal
|
||||
@ -1240,7 +1225,7 @@ js::Interpret(JSContext *cx, StackFrame *entryFrame, InterpMode interpMode, bool
|
||||
if (script->hasAnyBreakpointsOrStepMode())
|
||||
moreInterrupts = true;
|
||||
|
||||
if (script->hasBreakpointsAt(regs.pc) && interpMode != JSINTERP_SKIP_TRAP) {
|
||||
if (script->hasBreakpointsAt(regs.pc)) {
|
||||
RootedValue rval(cx);
|
||||
JSTrapStatus status = Debugger::onTrap(cx, &rval);
|
||||
switch (status) {
|
||||
@ -1260,8 +1245,6 @@ js::Interpret(JSContext *cx, StackFrame *entryFrame, InterpMode interpMode, bool
|
||||
JS_ASSERT(rval.isInt32() && rval.toInt32() == op);
|
||||
}
|
||||
|
||||
interpMode = JSINTERP_NORMAL;
|
||||
|
||||
switchMask = moreInterrupts ? -1 : 0;
|
||||
switchOp = int(op);
|
||||
goto do_switch;
|
||||
@ -3022,9 +3005,6 @@ END_CASE(JSOP_ARRAYPUSH)
|
||||
JS_ASSERT(&cx->regs() == ®s);
|
||||
JS_ASSERT(uint32_t(regs.pc - script->code) < script->length);
|
||||
|
||||
/* When rejoining, we must not err before finishing Interpret's prologue. */
|
||||
JS_ASSERT(interpMode != JSINTERP_REJOIN);
|
||||
|
||||
if (cx->isExceptionPending()) {
|
||||
/* Call debugger throw hooks. */
|
||||
if (cx->compartment()->debugMode()) {
|
||||
@ -3149,7 +3129,7 @@ END_CASE(JSOP_ARRAYPUSH)
|
||||
leave_on_safe_point:
|
||||
#endif
|
||||
|
||||
return interpReturnOK ? Interpret_Ok : Interpret_Error;
|
||||
return interpReturnOK;
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -164,31 +164,6 @@ ExecuteKernel(JSContext *cx, HandleScript script, JSObject &scopeChain, const Va
|
||||
extern bool
|
||||
Execute(JSContext *cx, HandleScript script, JSObject &scopeChain, Value *rval);
|
||||
|
||||
/* Flags to toggle js::Interpret() execution. */
|
||||
enum InterpMode
|
||||
{
|
||||
JSINTERP_NORMAL = 0, /* interpreter is running normally */
|
||||
JSINTERP_REJOIN = 1, /* as normal, but the frame has already started */
|
||||
JSINTERP_SKIP_TRAP = 2, /* as REJOIN, but skip trap at first opcode */
|
||||
JSINTERP_BAILOUT = 3, /* interpreter is running from an Ion bailout */
|
||||
JSINTERP_RETHROW = 4 /* as BAILOUT, but unwind all frames */
|
||||
};
|
||||
|
||||
enum InterpretStatus
|
||||
{
|
||||
Interpret_Error = 0, /* interpreter had an error */
|
||||
Interpret_Ok = 1, /* interpreter executed successfully */
|
||||
Interpret_OSR = 2 /* when mode=BAILOUT and we should OSR into Ion */
|
||||
};
|
||||
|
||||
/*
|
||||
* Execute the caller-initialized frame for a user-defined script or function
|
||||
* pointed to by cx->fp until completion or error.
|
||||
*/
|
||||
extern JS_NEVER_INLINE InterpretStatus
|
||||
Interpret(JSContext *cx, StackFrame *stopFp, InterpMode mode = JSINTERP_NORMAL,
|
||||
bool useNewType = false);
|
||||
|
||||
extern bool
|
||||
RunScript(JSContext *cx, StackFrame *fp);
|
||||
|
||||
|
@ -39,12 +39,8 @@ ClearAttrCache(const nsAString& aKey, MiscContainer*& aValue, void*)
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
nsHTMLCSSStyleSheet::nsHTMLCSSStyleSheet(nsIURI* aURL, nsIDocument* aDocument)
|
||||
: mURL(aURL)
|
||||
, mDocument(aDocument) // not refcounted!
|
||||
nsHTMLCSSStyleSheet::nsHTMLCSSStyleSheet()
|
||||
{
|
||||
MOZ_ASSERT(aURL);
|
||||
MOZ_ASSERT(aDocument);
|
||||
mCachedStyleAttrs.Init();
|
||||
}
|
||||
|
||||
@ -55,9 +51,7 @@ nsHTMLCSSStyleSheet::~nsHTMLCSSStyleSheet()
|
||||
mCachedStyleAttrs.Enumerate(ClearAttrCache, nullptr);
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS2(nsHTMLCSSStyleSheet,
|
||||
nsIStyleSheet,
|
||||
nsIStyleRuleProcessor)
|
||||
NS_IMPL_ISUPPORTS1(nsHTMLCSSStyleSheet, nsIStyleRuleProcessor)
|
||||
|
||||
/* virtual */ void
|
||||
nsHTMLCSSStyleSheet::RulesMatching(ElementRuleProcessorData* aData)
|
||||
@ -175,98 +169,3 @@ nsHTMLCSSStyleSheet::LookupStyleAttr(const nsAString& aSerialized)
|
||||
{
|
||||
return mCachedStyleAttrs.Get(aSerialized);
|
||||
}
|
||||
|
||||
void
|
||||
nsHTMLCSSStyleSheet::Reset(nsIURI* aURL)
|
||||
{
|
||||
mURL = aURL;
|
||||
}
|
||||
|
||||
/* virtual */ nsIURI*
|
||||
nsHTMLCSSStyleSheet::GetSheetURI() const
|
||||
{
|
||||
return mURL;
|
||||
}
|
||||
|
||||
/* virtual */ nsIURI*
|
||||
nsHTMLCSSStyleSheet::GetBaseURI() const
|
||||
{
|
||||
return mURL;
|
||||
}
|
||||
|
||||
/* virtual */ void
|
||||
nsHTMLCSSStyleSheet::GetTitle(nsString& aTitle) const
|
||||
{
|
||||
aTitle.AssignLiteral("Internal HTML/CSS Style Sheet");
|
||||
}
|
||||
|
||||
/* virtual */ void
|
||||
nsHTMLCSSStyleSheet::GetType(nsString& aType) const
|
||||
{
|
||||
aType.AssignLiteral("text/html");
|
||||
}
|
||||
|
||||
/* virtual */ bool
|
||||
nsHTMLCSSStyleSheet::HasRules() const
|
||||
{
|
||||
// Say we always have rules, since we don't know.
|
||||
return true;
|
||||
}
|
||||
|
||||
/* virtual */ bool
|
||||
nsHTMLCSSStyleSheet::IsApplicable() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/* virtual */ void
|
||||
nsHTMLCSSStyleSheet::SetEnabled(bool aEnabled)
|
||||
{ // these can't be disabled
|
||||
}
|
||||
|
||||
/* virtual */ bool
|
||||
nsHTMLCSSStyleSheet::IsComplete() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/* virtual */ void
|
||||
nsHTMLCSSStyleSheet::SetComplete()
|
||||
{
|
||||
}
|
||||
|
||||
// style sheet owner info
|
||||
/* virtual */ nsIStyleSheet*
|
||||
nsHTMLCSSStyleSheet::GetParentSheet() const
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/* virtual */ nsIDocument*
|
||||
nsHTMLCSSStyleSheet::GetOwningDocument() const
|
||||
{
|
||||
return mDocument;
|
||||
}
|
||||
|
||||
/* virtual */ void
|
||||
nsHTMLCSSStyleSheet::SetOwningDocument(nsIDocument* aDocument)
|
||||
{
|
||||
mDocument = aDocument;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
/* virtual */ void
|
||||
nsHTMLCSSStyleSheet::List(FILE* out, int32_t aIndent) const
|
||||
{
|
||||
// Indent
|
||||
for (int32_t index = aIndent; --index >= 0; ) fputs(" ", out);
|
||||
|
||||
fputs("HTML CSS Style Sheet: ", out);
|
||||
nsAutoCString urlSpec;
|
||||
mURL->GetSpec(urlSpec);
|
||||
if (!urlSpec.IsEmpty()) {
|
||||
fputs(urlSpec.get(), out);
|
||||
}
|
||||
fputs("\n", out);
|
||||
}
|
||||
#endif
|
||||
|
@ -19,34 +19,14 @@
|
||||
|
||||
struct MiscContainer;
|
||||
|
||||
class nsHTMLCSSStyleSheet MOZ_FINAL : public nsIStyleSheet,
|
||||
public nsIStyleRuleProcessor
|
||||
class nsHTMLCSSStyleSheet MOZ_FINAL : public nsIStyleRuleProcessor
|
||||
{
|
||||
public:
|
||||
nsHTMLCSSStyleSheet(nsIURI* aURL, nsIDocument* aDocument);
|
||||
nsHTMLCSSStyleSheet();
|
||||
~nsHTMLCSSStyleSheet();
|
||||
|
||||
NS_DECL_ISUPPORTS
|
||||
|
||||
void Reset(nsIURI* aURL);
|
||||
|
||||
// nsIStyleSheet
|
||||
virtual nsIURI* GetSheetURI() const MOZ_OVERRIDE;
|
||||
virtual nsIURI* GetBaseURI() const MOZ_OVERRIDE;
|
||||
virtual void GetTitle(nsString& aTitle) const MOZ_OVERRIDE;
|
||||
virtual void GetType(nsString& aType) const MOZ_OVERRIDE;
|
||||
virtual bool HasRules() const MOZ_OVERRIDE;
|
||||
virtual bool IsApplicable() const MOZ_OVERRIDE;
|
||||
virtual void SetEnabled(bool aEnabled) MOZ_OVERRIDE;
|
||||
virtual bool IsComplete() const MOZ_OVERRIDE;
|
||||
virtual void SetComplete() MOZ_OVERRIDE;
|
||||
virtual nsIStyleSheet* GetParentSheet() const MOZ_OVERRIDE; // will be null
|
||||
virtual nsIDocument* GetOwningDocument() const MOZ_OVERRIDE;
|
||||
virtual void SetOwningDocument(nsIDocument* aDocument) MOZ_OVERRIDE;
|
||||
#ifdef DEBUG
|
||||
virtual void List(FILE* out = stdout, int32_t aIndent = 0) const MOZ_OVERRIDE;
|
||||
#endif
|
||||
|
||||
// nsIStyleRuleProcessor
|
||||
virtual void RulesMatching(ElementRuleProcessorData* aData) MOZ_OVERRIDE;
|
||||
virtual void RulesMatching(PseudoElementRuleProcessorData* aData) MOZ_OVERRIDE;
|
||||
@ -73,15 +53,7 @@ private:
|
||||
nsHTMLCSSStyleSheet& operator=(const nsHTMLCSSStyleSheet& aCopy) MOZ_DELETE;
|
||||
|
||||
protected:
|
||||
nsCOMPtr<nsIURI> mURL;
|
||||
nsIDocument* mDocument;
|
||||
nsDataHashtable<nsStringHashKey, MiscContainer*> mCachedStyleAttrs;
|
||||
};
|
||||
|
||||
inline nsISupports*
|
||||
ToSupports(nsHTMLCSSStyleSheet* aPointer)
|
||||
{
|
||||
return static_cast<nsIStyleSheet*>(aPointer);
|
||||
}
|
||||
|
||||
#endif /* !defined(nsHTMLCSSStyleSheet_h_) */
|
||||
|
@ -227,13 +227,11 @@ static PLDHashTableOps LangRuleTable_Ops = {
|
||||
|
||||
// -----------------------------------------------------------
|
||||
|
||||
nsHTMLStyleSheet::nsHTMLStyleSheet(nsIURI* aURL, nsIDocument* aDocument)
|
||||
: mURL(aURL)
|
||||
, mDocument(aDocument)
|
||||
nsHTMLStyleSheet::nsHTMLStyleSheet(nsIDocument* aDocument)
|
||||
: mDocument(aDocument)
|
||||
, mTableQuirkColorRule(new TableQuirkColorRule())
|
||||
, mTableTHRule(new TableTHRule())
|
||||
{
|
||||
MOZ_ASSERT(aURL);
|
||||
MOZ_ASSERT(aDocument);
|
||||
mMappedAttrTable.ops = nullptr;
|
||||
mLangRuleTable.ops = nullptr;
|
||||
@ -247,7 +245,7 @@ nsHTMLStyleSheet::~nsHTMLStyleSheet()
|
||||
PL_DHashTableFinish(&mMappedAttrTable);
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS2(nsHTMLStyleSheet, nsIStyleSheet, nsIStyleRuleProcessor)
|
||||
NS_IMPL_ISUPPORTS1(nsHTMLStyleSheet, nsIStyleRuleProcessor)
|
||||
|
||||
/* virtual */ void
|
||||
nsHTMLStyleSheet::RulesMatching(ElementRuleProcessorData* aData)
|
||||
@ -402,82 +400,15 @@ nsHTMLStyleSheet::RulesMatching(XULTreeRuleProcessorData* aData)
|
||||
}
|
||||
#endif
|
||||
|
||||
// nsIStyleSheet api
|
||||
/* virtual */ nsIURI*
|
||||
nsHTMLStyleSheet::GetSheetURI() const
|
||||
{
|
||||
return mURL;
|
||||
}
|
||||
|
||||
/* virtual */ nsIURI*
|
||||
nsHTMLStyleSheet::GetBaseURI() const
|
||||
{
|
||||
return mURL;
|
||||
}
|
||||
|
||||
/* virtual */ void
|
||||
nsHTMLStyleSheet::GetTitle(nsString& aTitle) const
|
||||
{
|
||||
aTitle.Truncate();
|
||||
}
|
||||
|
||||
/* virtual */ void
|
||||
nsHTMLStyleSheet::GetType(nsString& aType) const
|
||||
{
|
||||
aType.AssignLiteral("text/html");
|
||||
}
|
||||
|
||||
/* virtual */ bool
|
||||
nsHTMLStyleSheet::HasRules() const
|
||||
{
|
||||
return true; // We have rules at all reasonable times
|
||||
}
|
||||
|
||||
/* virtual */ bool
|
||||
nsHTMLStyleSheet::IsApplicable() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/* virtual */ void
|
||||
nsHTMLStyleSheet::SetEnabled(bool aEnabled)
|
||||
{ // these can't be disabled
|
||||
}
|
||||
|
||||
/* virtual */ bool
|
||||
nsHTMLStyleSheet::IsComplete() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/* virtual */ void
|
||||
nsHTMLStyleSheet::SetComplete()
|
||||
{
|
||||
}
|
||||
|
||||
/* virtual */ nsIStyleSheet*
|
||||
nsHTMLStyleSheet::GetParentSheet() const
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/* virtual */ nsIDocument*
|
||||
nsHTMLStyleSheet::GetOwningDocument() const
|
||||
{
|
||||
return mDocument;
|
||||
}
|
||||
|
||||
/* virtual */ void
|
||||
void
|
||||
nsHTMLStyleSheet::SetOwningDocument(nsIDocument* aDocument)
|
||||
{
|
||||
mDocument = aDocument; // not refcounted
|
||||
}
|
||||
|
||||
void
|
||||
nsHTMLStyleSheet::Reset(nsIURI* aURL)
|
||||
nsHTMLStyleSheet::Reset()
|
||||
{
|
||||
mURL = aURL;
|
||||
|
||||
mLinkRule = nullptr;
|
||||
mVisitedRule = nullptr;
|
||||
mActiveRule = nullptr;
|
||||
@ -594,23 +525,6 @@ nsHTMLStyleSheet::LangRuleFor(const nsString& aLanguage)
|
||||
return entry->mRule;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
/* virtual */ void
|
||||
nsHTMLStyleSheet::List(FILE* out, int32_t aIndent) const
|
||||
{
|
||||
// Indent
|
||||
for (int32_t index = aIndent; --index >= 0; ) fputs(" ", out);
|
||||
|
||||
fputs("HTML Style Sheet: ", out);
|
||||
nsAutoCString urlSpec;
|
||||
mURL->GetSpec(urlSpec);
|
||||
if (!urlSpec.IsEmpty()) {
|
||||
fputs(urlSpec.get(), out);
|
||||
}
|
||||
fputs("\n", out);
|
||||
}
|
||||
#endif
|
||||
|
||||
static size_t
|
||||
SizeOfAttributesEntryExcludingThis(PLDHashEntryHdr* aEntry,
|
||||
nsMallocSizeOfFun aMallocSizeOf,
|
||||
|
@ -24,31 +24,15 @@
|
||||
|
||||
class nsMappedAttributes;
|
||||
|
||||
class nsHTMLStyleSheet MOZ_FINAL : public nsIStyleSheet,
|
||||
public nsIStyleRuleProcessor
|
||||
class nsHTMLStyleSheet MOZ_FINAL : public nsIStyleRuleProcessor
|
||||
{
|
||||
public:
|
||||
nsHTMLStyleSheet(nsIURI* aURL, nsIDocument* aDocument);
|
||||
nsHTMLStyleSheet(nsIDocument* aDocument);
|
||||
|
||||
void SetOwningDocument(nsIDocument* aDocument);
|
||||
|
||||
NS_DECL_ISUPPORTS
|
||||
|
||||
// nsIStyleSheet api
|
||||
virtual nsIURI* GetSheetURI() const MOZ_OVERRIDE;
|
||||
virtual nsIURI* GetBaseURI() const MOZ_OVERRIDE;
|
||||
virtual void GetTitle(nsString& aTitle) const MOZ_OVERRIDE;
|
||||
virtual void GetType(nsString& aType) const MOZ_OVERRIDE;
|
||||
virtual bool HasRules() const MOZ_OVERRIDE;
|
||||
virtual bool IsApplicable() const MOZ_OVERRIDE;
|
||||
virtual void SetEnabled(bool aEnabled) MOZ_OVERRIDE;
|
||||
virtual bool IsComplete() const MOZ_OVERRIDE;
|
||||
virtual void SetComplete() MOZ_OVERRIDE;
|
||||
virtual nsIStyleSheet* GetParentSheet() const MOZ_OVERRIDE; // will be null
|
||||
virtual nsIDocument* GetOwningDocument() const MOZ_OVERRIDE;
|
||||
virtual void SetOwningDocument(nsIDocument* aDocumemt) MOZ_OVERRIDE;
|
||||
#ifdef DEBUG
|
||||
virtual void List(FILE* out = stdout, int32_t aIndent = 0) const MOZ_OVERRIDE;
|
||||
#endif
|
||||
|
||||
// nsIStyleRuleProcessor API
|
||||
virtual void RulesMatching(ElementRuleProcessorData* aData) MOZ_OVERRIDE;
|
||||
virtual void RulesMatching(PseudoElementRuleProcessorData* aData) MOZ_OVERRIDE;
|
||||
@ -67,7 +51,7 @@ public:
|
||||
const MOZ_MUST_OVERRIDE MOZ_OVERRIDE;
|
||||
size_t DOMSizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf) const;
|
||||
|
||||
void Reset(nsIURI* aURL);
|
||||
void Reset();
|
||||
nsresult SetLinkColor(nscolor aColor);
|
||||
nsresult SetActiveLinkColor(nscolor aColor);
|
||||
nsresult SetVisitedLinkColor(nscolor aColor);
|
||||
@ -159,7 +143,6 @@ public: // for mLangRuleTable structures only
|
||||
};
|
||||
|
||||
private:
|
||||
nsCOMPtr<nsIURI> mURL;
|
||||
nsIDocument* mDocument;
|
||||
nsRefPtr<HTMLColorRule> mLinkRule;
|
||||
nsRefPtr<HTMLColorRule> mVisitedRule;
|
||||
|
@ -30,6 +30,8 @@
|
||||
#include "nsStyleSheetService.h"
|
||||
#include "mozilla/dom/Element.h"
|
||||
#include "GeckoProfiler.h"
|
||||
#include "nsHTMLCSSStyleSheet.h"
|
||||
#include "nsHTMLStyleSheet.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::dom;
|
||||
@ -318,19 +320,28 @@ nsStyleSet::GatherRuleProcessors(sheetType aType)
|
||||
// elements later if mAuthorStyleDisabled.
|
||||
return NS_OK;
|
||||
}
|
||||
if (aType == eAnimationSheet) {
|
||||
// We have no sheet for the animations level; just a rule
|
||||
// processor. (XXX: We should probably do this for the other
|
||||
// non-CSS levels too!)
|
||||
mRuleProcessors[aType] = PresContext()->AnimationManager();
|
||||
return NS_OK;
|
||||
}
|
||||
if (aType == eTransitionSheet) {
|
||||
// We have no sheet for the transitions level; just a rule
|
||||
// processor. (XXX: We should probably do this for the other
|
||||
// non-CSS levels too!)
|
||||
mRuleProcessors[aType] = PresContext()->TransitionManager();
|
||||
return NS_OK;
|
||||
switch (aType) {
|
||||
// handle the types for which have a rule processor that does not
|
||||
// implement the style sheet interface.
|
||||
case eAnimationSheet:
|
||||
MOZ_ASSERT(mSheets[aType].Count() == 0);
|
||||
mRuleProcessors[aType] = PresContext()->AnimationManager();
|
||||
return NS_OK;
|
||||
case eTransitionSheet:
|
||||
MOZ_ASSERT(mSheets[aType].Count() == 0);
|
||||
mRuleProcessors[aType] = PresContext()->TransitionManager();
|
||||
return NS_OK;
|
||||
case eStyleAttrSheet:
|
||||
MOZ_ASSERT(mSheets[aType].Count() == 0);
|
||||
mRuleProcessors[aType] = PresContext()->Document()->GetInlineStyleSheet();
|
||||
return NS_OK;
|
||||
case ePresHintSheet:
|
||||
MOZ_ASSERT(mSheets[aType].Count() == 0);
|
||||
mRuleProcessors[aType] = PresContext()->Document()->GetAttributeStyleSheet();
|
||||
return NS_OK;
|
||||
default:
|
||||
// keep going
|
||||
break;
|
||||
}
|
||||
if (aType == eScopedDocSheet) {
|
||||
// Create a rule processor for each scope.
|
||||
@ -423,11 +434,7 @@ nsStyleSet::AppendStyleSheet(sheetType aType, nsIStyleSheet *aSheet)
|
||||
if (!mSheets[aType].AppendObject(aSheet))
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
|
||||
if (!mBatching)
|
||||
return GatherRuleProcessors(aType);
|
||||
|
||||
mDirty |= 1 << aType;
|
||||
return NS_OK;
|
||||
return DirtyRuleProcessors(aType);
|
||||
}
|
||||
|
||||
nsresult
|
||||
@ -440,11 +447,7 @@ nsStyleSet::PrependStyleSheet(sheetType aType, nsIStyleSheet *aSheet)
|
||||
if (!mSheets[aType].InsertObjectAt(aSheet, 0))
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
|
||||
if (!mBatching)
|
||||
return GatherRuleProcessors(aType);
|
||||
|
||||
mDirty |= 1 << aType;
|
||||
return NS_OK;
|
||||
return DirtyRuleProcessors(aType);
|
||||
}
|
||||
|
||||
nsresult
|
||||
@ -454,11 +457,8 @@ nsStyleSet::RemoveStyleSheet(sheetType aType, nsIStyleSheet *aSheet)
|
||||
NS_ASSERTION(aSheet->IsComplete(),
|
||||
"Incomplete sheet being removed from style set");
|
||||
mSheets[aType].RemoveObject(aSheet);
|
||||
if (!mBatching)
|
||||
return GatherRuleProcessors(aType);
|
||||
|
||||
mDirty |= 1 << aType;
|
||||
return NS_OK;
|
||||
return DirtyRuleProcessors(aType);
|
||||
}
|
||||
|
||||
nsresult
|
||||
@ -469,11 +469,7 @@ nsStyleSet::ReplaceSheets(sheetType aType,
|
||||
if (!mSheets[aType].AppendObjects(aNewSheets))
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
|
||||
if (!mBatching)
|
||||
return GatherRuleProcessors(aType);
|
||||
|
||||
mDirty |= 1 << aType;
|
||||
return NS_OK;
|
||||
return DirtyRuleProcessors(aType);
|
||||
}
|
||||
|
||||
nsresult
|
||||
@ -488,10 +484,16 @@ nsStyleSet::InsertStyleSheetBefore(sheetType aType, nsIStyleSheet *aNewSheet,
|
||||
int32_t idx = mSheets[aType].IndexOf(aReferenceSheet);
|
||||
if (idx < 0)
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
|
||||
|
||||
if (!mSheets[aType].InsertObjectAt(aNewSheet, idx))
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
|
||||
return DirtyRuleProcessors(aType);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsStyleSet::DirtyRuleProcessors(sheetType aType)
|
||||
{
|
||||
if (!mBatching)
|
||||
return GatherRuleProcessors(aType);
|
||||
|
||||
@ -559,11 +561,8 @@ nsStyleSet::AddDocStyleSheet(nsIStyleSheet* aSheet, nsIDocument* aDocument)
|
||||
}
|
||||
if (!sheets.InsertObjectAt(aSheet, index))
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
if (!mBatching)
|
||||
return GatherRuleProcessors(type);
|
||||
|
||||
mDirty |= 1 << type;
|
||||
return NS_OK;
|
||||
return DirtyRuleProcessors(type);
|
||||
}
|
||||
|
||||
nsresult
|
||||
|
@ -259,6 +259,8 @@ class nsStyleSet
|
||||
nsresult InsertStyleSheetBefore(sheetType aType, nsIStyleSheet *aNewSheet,
|
||||
nsIStyleSheet *aReferenceSheet);
|
||||
|
||||
nsresult DirtyRuleProcessors(sheetType aType);
|
||||
|
||||
// Enable/Disable entire author style level (Doc, ScopedDoc & PresHint levels)
|
||||
bool GetAuthorStyleDisabled();
|
||||
nsresult SetAuthorStyleDisabled(bool aStyleDisabled);
|
||||
@ -392,6 +394,9 @@ class nsStyleSet
|
||||
|
||||
// The sheets in each array in mSheets are stored with the most significant
|
||||
// sheet last.
|
||||
// The arrays for ePresHintSheet, eStyleAttrSheet, eTransitionSheet,
|
||||
// and eAnimationSheet are always empty. (FIXME: We should reduce
|
||||
// the storage needed for them.)
|
||||
nsCOMArray<nsIStyleSheet> mSheets[eSheetTypeCount];
|
||||
|
||||
// mRuleProcessors[eScopedDocSheet] is always null; rule processors
|
||||
|
@ -45,6 +45,7 @@ check gtest::
|
||||
ifeq (cocoa,$(MOZ_WIDGET_TOOLKIT))
|
||||
$(MAKE) -C $(DEPTH)/browser/app repackage
|
||||
endif
|
||||
$(PYTHON) $(topsrcdir)/testing/gtest/rungtests.py --xre-path=$(DIST)/bin --symbols-path=$(DIST)/crashreporter-symbols $(DIST)/bin/$(MOZ_APP_NAME)$(BIN_SUFFIX)
|
||||
endif
|
||||
endif
|
||||
|
||||
|
@ -7,6 +7,10 @@
|
||||
#include "gtest/gtest.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/NullPtr.h"
|
||||
#ifdef MOZ_CRASHREPORTER
|
||||
#include "nsICrashReporter.h"
|
||||
#endif
|
||||
#include "testing/TestHarness.h"
|
||||
#include "prenv.h"
|
||||
|
||||
using ::testing::EmptyTestEventListener;
|
||||
@ -78,6 +82,31 @@ int RunGTestFunc()
|
||||
|
||||
PR_SetEnv("XPCOM_DEBUG_BREAK=stack-and-abort");
|
||||
|
||||
ScopedXPCOM xpcom("AsyncPanZoomController");
|
||||
|
||||
#ifdef MOZ_CRASHREPORTER
|
||||
nsCOMPtr<nsICrashReporter> crashreporter;
|
||||
char *crashreporterStr = PR_GetEnv("MOZ_CRASHREPORTER");
|
||||
if (crashreporterStr && !strcmp(crashreporterStr, "1")) {
|
||||
//TODO: move this to an even-more-common location to use in all
|
||||
// C++ unittests
|
||||
crashreporter = do_GetService("@mozilla.org/toolkit/crash-reporter;1");
|
||||
if (crashreporter) {
|
||||
std::cerr << "Setting up crash reporting" << std::endl;
|
||||
|
||||
nsCOMPtr<nsIProperties> dirsvc =
|
||||
do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID);
|
||||
nsCOMPtr<nsIFile> cwd;
|
||||
nsresult rv = dirsvc->Get(NS_OS_CURRENT_WORKING_DIR,
|
||||
NS_GET_IID(nsIFile),
|
||||
getter_AddRefs(cwd));
|
||||
MOZ_ASSERT(NS_SUCCEEDED(rv));
|
||||
crashreporter->SetEnabled(true);
|
||||
crashreporter->SetMinidumpPath(cwd);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
|
||||
|
129
testing/gtest/rungtests.py
Normal file
129
testing/gtest/rungtests.py
Normal file
@ -0,0 +1,129 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# 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/.
|
||||
|
||||
from __future__ import with_statement
|
||||
import sys, os, tempfile, shutil
|
||||
from optparse import OptionParser
|
||||
import mozprocess, mozinfo, mozlog, mozcrash
|
||||
from contextlib import contextmanager
|
||||
|
||||
log = mozlog.getLogger('gtests')
|
||||
|
||||
class GTests(object):
|
||||
# Time (seconds) to wait for test process to complete
|
||||
TEST_PROC_TIMEOUT = 1200
|
||||
# Time (seconds) in which process will be killed if it produces no output.
|
||||
TEST_PROC_NO_OUTPUT_TIMEOUT = 300
|
||||
|
||||
def run_gtest(self, prog, xre_path, symbols_path=None):
|
||||
"""
|
||||
Run a single C++ unit test program.
|
||||
|
||||
Arguments:
|
||||
* prog: The path to the test program to run.
|
||||
* env: The environment to use for running the program.
|
||||
* symbols_path: A path to a directory containing Breakpad-formatted
|
||||
symbol files for producing stack traces on crash.
|
||||
|
||||
Return True if the program exits with a zero status, False otherwise.
|
||||
"""
|
||||
self.xre_path = xre_path
|
||||
env = self.build_environment()
|
||||
basename = os.path.basename(prog)
|
||||
log.info("Running test %s", basename)
|
||||
proc = mozprocess.ProcessHandler([prog, "-unittest"],
|
||||
cwd=os.getcwd(),
|
||||
env=env)
|
||||
#TODO: After bug 811320 is fixed, don't let .run() kill the process,
|
||||
# instead use a timeout in .wait() and then kill to get a stack.
|
||||
proc.run(timeout=GTests.TEST_PROC_TIMEOUT,
|
||||
outputTimeout=GTests.TEST_PROC_NO_OUTPUT_TIMEOUT)
|
||||
proc.wait()
|
||||
if proc.timedOut:
|
||||
log.testFail("%s | timed out after %d seconds",
|
||||
basename, GTests.TEST_PROC_TIMEOUT)
|
||||
return False
|
||||
if mozcrash.check_for_crashes(os.getcwd(), symbols_path,
|
||||
test_name=basename):
|
||||
log.testFail("%s | test crashed", basename)
|
||||
return False
|
||||
result = proc.proc.returncode == 0
|
||||
if not result:
|
||||
log.testFail("%s | test failed with return code %d",
|
||||
basename, proc.proc.returncode)
|
||||
return result
|
||||
|
||||
def build_core_environment(self, env = {}):
|
||||
"""
|
||||
Add environment variables likely to be used across all platforms, including remote systems.
|
||||
"""
|
||||
env["MOZILLA_FIVE_HOME"] = self.xre_path
|
||||
env["MOZ_XRE_DIR"] = self.xre_path
|
||||
env["XPCOM_DEBUG_BREAK"] = "stack-and-abort"
|
||||
env["MOZ_CRASHREPORTER_NO_REPORT"] = "1"
|
||||
env["MOZ_CRASHREPORTER"] = "1"
|
||||
env["MOZ_RUN_GTEST"] = "1"
|
||||
# Normally we run with GTest default output, override this to use the TBPL test format.
|
||||
env["MOZ_TBPL_PARSER"] = "1"
|
||||
return env
|
||||
|
||||
def build_environment(self):
|
||||
"""
|
||||
Create and return a dictionary of all the appropriate env variables and values.
|
||||
On a remote system, we overload this to set different values and are missing things like os.environ and PATH.
|
||||
"""
|
||||
if not os.path.isdir(self.xre_path):
|
||||
raise Exception("xre_path does not exist: %s", self.xre_path)
|
||||
env = dict(os.environ)
|
||||
env = self.build_core_environment(env)
|
||||
pathvar = ""
|
||||
if mozinfo.os == "linux":
|
||||
pathvar = "LD_LIBRARY_PATH"
|
||||
elif mozinfo.os == "mac":
|
||||
pathvar = "DYLD_LIBRARY_PATH"
|
||||
elif mozinfo.os == "win":
|
||||
pathvar = "PATH"
|
||||
if pathvar:
|
||||
if pathvar in env:
|
||||
env[pathvar] = "%s%s%s" % (self.xre_path, os.pathsep, env[pathvar])
|
||||
else:
|
||||
env[pathvar] = self.xre_path
|
||||
return env
|
||||
|
||||
class gtestOptions(OptionParser):
|
||||
def __init__(self):
|
||||
OptionParser.__init__(self)
|
||||
self.add_option("--xre-path",
|
||||
action = "store", type = "string", dest = "xre_path",
|
||||
default = None,
|
||||
help = "absolute path to directory containing XRE (probably xulrunner)")
|
||||
self.add_option("--symbols-path",
|
||||
action = "store", type = "string", dest = "symbols_path",
|
||||
default = None,
|
||||
help = "absolute path to directory containing breakpad symbols, or the URL of a zip file containing symbols")
|
||||
|
||||
def main():
|
||||
parser = gtestOptions()
|
||||
options, args = parser.parse_args()
|
||||
if not args:
|
||||
print >>sys.stderr, """Usage: %s <binary>""" % sys.argv[0]
|
||||
sys.exit(1)
|
||||
if not options.xre_path:
|
||||
print >>sys.stderr, """Error: --xre-path is required"""
|
||||
sys.exit(1)
|
||||
prog = os.path.abspath(args[0])
|
||||
options.xre_path = os.path.abspath(options.xre_path)
|
||||
tester = GTests()
|
||||
try:
|
||||
result = tester.run_gtest(prog, options.xre_path, options.symbols_path)
|
||||
except Exception, e:
|
||||
log.error(str(e))
|
||||
result = False
|
||||
sys.exit(0 if result else 1)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
@ -433,6 +433,8 @@ src_tools_linux_dump_syms_dump_syms_SOURCES = \
|
||||
src/common/dwarf/bytereader.cc \
|
||||
src/common/dwarf/dwarf2diehandler.cc \
|
||||
src/common/dwarf/dwarf2reader.cc \
|
||||
src/common/arm_ex_reader.cc \
|
||||
src/common/arm_ex_to_module.cc \
|
||||
src/common/linux/dump_symbols.cc \
|
||||
src/common/linux/elf_symbols_to_module.cc \
|
||||
src/common/linux/elfutils.cc \
|
||||
@ -1015,6 +1017,10 @@ EXTRA_DIST = \
|
||||
src/common/convert_UTF.h \
|
||||
src/common/linux/dump_symbols.cc \
|
||||
src/common/linux/dump_symbols.h \
|
||||
src/common/arm_ex_reader.cc \
|
||||
src/common/arm_ex_reader.h \
|
||||
src/common/arm_ex_to_module.cc \
|
||||
src/common/arm_ex_to_module.h \
|
||||
src/common/linux/elf_symbols_to_module.cc \
|
||||
src/common/linux/elf_symbols_to_module.h \
|
||||
src/common/linux/elfutils.cc \
|
||||
|
@ -32,6 +32,8 @@ HOST_CPPSRCS = \
|
||||
stabs_to_module.cc \
|
||||
stabs_reader.cc \
|
||||
dwarf_line_to_module.cc \
|
||||
arm_ex_reader.cc \
|
||||
arm_ex_to_module.cc \
|
||||
pathname_stripper.cc \
|
||||
logging.cc \
|
||||
$(NULL)
|
||||
|
@ -0,0 +1,502 @@
|
||||
|
||||
/* libunwind - a platform-independent unwind library
|
||||
Copyright 2011 Linaro Limited
|
||||
|
||||
This file is part of libunwind.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
|
||||
// Copyright (c) 2010 Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
// Derived from libunwind, with extensive modifications.
|
||||
|
||||
|
||||
#include "common/arm_ex_reader.h"
|
||||
#include "common/logging.h"
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
// This file, in conjunction with arm_ex_to_module.cc, translates
|
||||
// EXIDX unwind information into the same format that Breakpad uses
|
||||
// for CFI information. Hence Breakpad's CFI unwinding abilities
|
||||
// also become usable for EXIDX.
|
||||
//
|
||||
// See: "Exception Handling ABI for the ARM Architecture", ARM IHI 0038A
|
||||
// http://infocenter.arm.com/help/topic/com.arm.doc.ihi0038a/IHI0038A_ehabi.pdf
|
||||
|
||||
// EXIDX data is presented in two parts:
|
||||
//
|
||||
// * an index table. This contains two words per routine,
|
||||
// the first of which identifies the routine, and the second
|
||||
// of which is a reference to the unwind bytecode. If the
|
||||
// bytecode is very compact -- 3 bytes or less -- it can be
|
||||
// stored directly in the second word.
|
||||
//
|
||||
// * an area containing the unwind bytecodes.
|
||||
|
||||
// General flow is: ExceptionTableInfo::Start iterates over all
|
||||
// of the index table entries (pairs). For each entry, it:
|
||||
//
|
||||
// * calls ExceptionTableInfo::ExtabEntryExtract to copy the bytecode
|
||||
// out into an intermediate buffer.
|
||||
|
||||
// * uses ExceptionTableInfo::ExtabEntryDecode to parse the intermediate
|
||||
// buffer. Each bytecode instruction is bundled into a
|
||||
// arm_ex_to_module::extab_data structure, and handed to ..
|
||||
//
|
||||
// * .. ARMExToModule::ImproveStackFrame, which in turn hands it to
|
||||
// ARMExToModule::TranslateCmd, and that generates the pseudo-CFI
|
||||
// records that Breakpad stores.
|
||||
|
||||
#define ARM_EXIDX_CANT_UNWIND 0x00000001
|
||||
#define ARM_EXIDX_COMPACT 0x80000000
|
||||
#define ARM_EXTBL_OP_FINISH 0xb0
|
||||
#define ARM_EXIDX_TABLE_LIMIT (255*4)
|
||||
|
||||
namespace arm_ex_reader {
|
||||
|
||||
using arm_ex_to_module::ARM_EXIDX_CMD_FINISH;
|
||||
using arm_ex_to_module::ARM_EXIDX_CMD_SUB_FROM_VSP;
|
||||
using arm_ex_to_module::ARM_EXIDX_CMD_ADD_TO_VSP;
|
||||
using arm_ex_to_module::ARM_EXIDX_CMD_REG_POP;
|
||||
using arm_ex_to_module::ARM_EXIDX_CMD_REG_TO_SP;
|
||||
using arm_ex_to_module::ARM_EXIDX_CMD_VFP_POP;
|
||||
using arm_ex_to_module::ARM_EXIDX_CMD_WREG_POP;
|
||||
using arm_ex_to_module::ARM_EXIDX_CMD_WCGR_POP;
|
||||
using arm_ex_to_module::ARM_EXIDX_CMD_RESERVED;
|
||||
using arm_ex_to_module::ARM_EXIDX_CMD_REFUSED;
|
||||
using arm_ex_to_module::exidx_entry;
|
||||
using arm_ex_to_module::ARM_EXIDX_VFP_SHIFT_16;
|
||||
using arm_ex_to_module::ARM_EXIDX_VFP_FSTMD;
|
||||
using google_breakpad::MemoryRange;
|
||||
|
||||
|
||||
static void* Prel31ToAddr(const void* addr)
|
||||
{
|
||||
uint32_t offset32 = *reinterpret_cast<const uint32_t*>(addr);
|
||||
// sign extend offset32[30:0] to 64 bits -- copy bit 30 to positions
|
||||
// 63:31 inclusive.
|
||||
uint64_t offset64 = offset32;
|
||||
if (offset64 & (1ULL << 30))
|
||||
offset64 |= 0xFFFFFFFF80000000ULL;
|
||||
else
|
||||
offset64 &= 0x000000007FFFFFFFULL;
|
||||
return ((char*)addr) + (uintptr_t)offset64;
|
||||
}
|
||||
|
||||
|
||||
// Extract unwind bytecode for the function denoted by |entry| into |buf|,
|
||||
// and return the number of bytes of |buf| written, along with a code
|
||||
// indicating the outcome.
|
||||
|
||||
ExceptionTableInfo::ExExtractResult
|
||||
ExceptionTableInfo::ExtabEntryExtract(const struct exidx_entry* entry,
|
||||
uint8_t* buf, size_t buf_size,
|
||||
/*OUT*/size_t* buf_used)
|
||||
{
|
||||
MemoryRange mr_out(buf, buf_size);
|
||||
|
||||
*buf_used = 0;
|
||||
|
||||
# define PUT_BUF_U8(_byte) \
|
||||
do { if (!mr_out.Covers(*buf_used, 1)) return ExOutBufOverflow; \
|
||||
buf[(*buf_used)++] = (_byte); } while (0)
|
||||
|
||||
# define GET_EX_U32(_lval, _addr, _sec_mr) \
|
||||
do { if (!(_sec_mr).Covers(reinterpret_cast<const uint8_t*>(_addr) \
|
||||
- (_sec_mr).data(), 4)) \
|
||||
return ExInBufOverflow; \
|
||||
(_lval) = *(reinterpret_cast<const uint32_t*>(_addr)); } while (0)
|
||||
|
||||
# define GET_EXIDX_U32(_lval, _addr) \
|
||||
GET_EX_U32(_lval, _addr, mr_exidx_)
|
||||
# define GET_EXTAB_U32(_lval, _addr) \
|
||||
GET_EX_U32(_lval, _addr, mr_extab_)
|
||||
|
||||
uint32_t data;
|
||||
GET_EXIDX_U32(data, &entry->data);
|
||||
|
||||
// A function can be marked CANT_UNWIND if (eg) it is known to be
|
||||
// at the bottom of the stack.
|
||||
if (data == ARM_EXIDX_CANT_UNWIND)
|
||||
return ExCantUnwind;
|
||||
|
||||
uint32_t pers; // personality number
|
||||
uint32_t extra; // number of extra data words required
|
||||
uint32_t extra_allowed; // number of extra data words allowed
|
||||
uint32_t* extbl_data; // the handler entry, if not inlined
|
||||
|
||||
if (data & ARM_EXIDX_COMPACT) {
|
||||
// The handler table entry has been inlined into the index table entry.
|
||||
// In this case it can only be an ARM-defined compact model, since
|
||||
// bit 31 is 1. Only personalities 0, 1 and 2 are defined for the
|
||||
// ARM compact model, but 1 and 2 are "Long format" and may require
|
||||
// extra data words. Hence the allowable personalities here are:
|
||||
// personality 0, in which case 'extra' has no meaning
|
||||
// personality 1, with zero extra words
|
||||
// personality 2, with zero extra words
|
||||
extbl_data = NULL;
|
||||
pers = (data >> 24) & 0x0F;
|
||||
extra = (data >> 16) & 0xFF;
|
||||
extra_allowed = 0;
|
||||
}
|
||||
else {
|
||||
// The index table entry is a pointer to the handler entry. Note
|
||||
// that Prel31ToAddr will read the given address, but we already
|
||||
// range-checked above.
|
||||
extbl_data = reinterpret_cast<uint32_t*>(Prel31ToAddr(&entry->data));
|
||||
GET_EXTAB_U32(data, extbl_data);
|
||||
if (!(data & ARM_EXIDX_COMPACT)) {
|
||||
// This denotes a "generic model" handler. That will involve
|
||||
// executing arbitary machine code, which is something we
|
||||
// can't represent here; hence reject it.
|
||||
return ExCantRepresent;
|
||||
}
|
||||
// So we have a compact model representation. Again, 3 possible
|
||||
// personalities, but this time up to 255 allowable extra words.
|
||||
pers = (data >> 24) & 0x0F;
|
||||
extra = (data >> 16) & 0xFF;
|
||||
extra_allowed = 255;
|
||||
extbl_data++;
|
||||
}
|
||||
|
||||
// Now look at the the handler table entry. The first word is
|
||||
// |data| and subsequent words start at |*extbl_data|. The number
|
||||
// of extra words to use is |extra|, provided that the personality
|
||||
// allows extra words. Even if it does, none may be available --
|
||||
// extra_allowed is the maximum number of extra words allowed. */
|
||||
if (pers == 0) {
|
||||
// "Su16" in the documentation -- 3 unwinding insn bytes
|
||||
// |extra| has no meaning here; instead that byte is an unwind-info byte
|
||||
PUT_BUF_U8(data >> 16);
|
||||
PUT_BUF_U8(data >> 8);
|
||||
PUT_BUF_U8(data);
|
||||
}
|
||||
else if ((pers == 1 || pers == 2) && extra <= extra_allowed) {
|
||||
// "Lu16" or "Lu32" respectively -- 2 unwinding insn bytes,
|
||||
// and up to 255 extra words.
|
||||
PUT_BUF_U8(data >> 8);
|
||||
PUT_BUF_U8(data);
|
||||
for (uint32_t j = 0; j < extra; j++) {
|
||||
GET_EXTAB_U32(data, extbl_data);
|
||||
extbl_data++;
|
||||
PUT_BUF_U8(data >> 24);
|
||||
PUT_BUF_U8(data >> 16);
|
||||
PUT_BUF_U8(data >> 8);
|
||||
PUT_BUF_U8(data >> 0);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// The entry is invalid.
|
||||
return ExInvalid;
|
||||
}
|
||||
|
||||
// Make sure the entry is terminated with "FINISH"
|
||||
if (*buf_used > 0 && buf[(*buf_used) - 1] != ARM_EXTBL_OP_FINISH)
|
||||
PUT_BUF_U8(ARM_EXTBL_OP_FINISH);
|
||||
|
||||
return ExSuccess;
|
||||
|
||||
# undef GET_EXTAB_U32
|
||||
# undef GET_EXIDX_U32
|
||||
# undef GET_U32
|
||||
# undef PUT_BUF_U8
|
||||
}
|
||||
|
||||
|
||||
// Take the unwind information extracted by ExtabEntryExtract
|
||||
// and parse it into frame-unwind instructions. These are as
|
||||
// specified in "Table 4, ARM-defined frame-unwinding instructions"
|
||||
// in the specification document detailed in comments at the top
|
||||
// of this file.
|
||||
//
|
||||
// This reads from |buf[0, +data_size)|. It checks for overruns of
|
||||
// the input buffer and returns a negative value if that happens, or
|
||||
// for any other failure cases. It returns zero in case of success.
|
||||
int ExceptionTableInfo::ExtabEntryDecode(const uint8_t* buf, size_t buf_size)
|
||||
{
|
||||
if (buf == NULL || buf_size == 0)
|
||||
return -1;
|
||||
|
||||
MemoryRange mr_in(buf, buf_size);
|
||||
const uint8_t* buf_initially = buf;
|
||||
|
||||
# define GET_BUF_U8(_lval) \
|
||||
do { if (!mr_in.Covers(buf - buf_initially, 1)) return -1; \
|
||||
(_lval) = *(buf++); } while (0)
|
||||
|
||||
const uint8_t* end = buf + buf_size;
|
||||
|
||||
while (buf < end) {
|
||||
struct arm_ex_to_module::extab_data edata;
|
||||
memset(&edata, 0, sizeof(edata));
|
||||
|
||||
uint8_t op;
|
||||
GET_BUF_U8(op);
|
||||
if ((op & 0xc0) == 0x00) {
|
||||
// vsp = vsp + (xxxxxx << 2) + 4
|
||||
edata.cmd = ARM_EXIDX_CMD_ADD_TO_VSP;
|
||||
edata.data = (((int)op & 0x3f) << 2) + 4;
|
||||
}
|
||||
else if ((op & 0xc0) == 0x40) {
|
||||
// vsp = vsp - (xxxxxx << 2) - 4
|
||||
edata.cmd = ARM_EXIDX_CMD_SUB_FROM_VSP;
|
||||
edata.data = (((int)op & 0x3f) << 2) + 4;
|
||||
}
|
||||
else if ((op & 0xf0) == 0x80) {
|
||||
uint8_t op2;
|
||||
GET_BUF_U8(op2);
|
||||
if (op == 0x80 && op2 == 0x00) {
|
||||
// Refuse to unwind
|
||||
edata.cmd = ARM_EXIDX_CMD_REFUSED;
|
||||
} else {
|
||||
// Pop up to 12 integer registers under masks {r15-r12},{r11-r4}
|
||||
edata.cmd = ARM_EXIDX_CMD_REG_POP;
|
||||
edata.data = ((op & 0xf) << 8) | op2;
|
||||
edata.data = edata.data << 4;
|
||||
}
|
||||
}
|
||||
else if ((op & 0xf0) == 0x90) {
|
||||
if (op == 0x9d || op == 0x9f) {
|
||||
// 9d: Reserved as prefix for ARM register to register moves
|
||||
// 9f: Reserved as perfix for Intel Wireless MMX reg to reg moves
|
||||
edata.cmd = ARM_EXIDX_CMD_RESERVED;
|
||||
} else {
|
||||
// Set vsp = r[nnnn]
|
||||
edata.cmd = ARM_EXIDX_CMD_REG_TO_SP;
|
||||
edata.data = op & 0x0f;
|
||||
}
|
||||
}
|
||||
else if ((op & 0xf0) == 0xa0) {
|
||||
// Pop r4 to r[4+nnn], or
|
||||
// Pop r4 to r[4+nnn] and r14 or
|
||||
unsigned end = (op & 0x07);
|
||||
edata.data = (1 << (end + 1)) - 1;
|
||||
edata.data = edata.data << 4;
|
||||
if (op & 0x08) edata.data |= 1 << 14;
|
||||
edata.cmd = ARM_EXIDX_CMD_REG_POP;
|
||||
}
|
||||
else if (op == ARM_EXTBL_OP_FINISH) {
|
||||
// Finish
|
||||
edata.cmd = ARM_EXIDX_CMD_FINISH;
|
||||
buf = end;
|
||||
}
|
||||
else if (op == 0xb1) {
|
||||
uint8_t op2;
|
||||
GET_BUF_U8(op2);
|
||||
if (op2 == 0 || (op2 & 0xf0)) {
|
||||
// Spare
|
||||
edata.cmd = ARM_EXIDX_CMD_RESERVED;
|
||||
} else {
|
||||
// Pop integer registers under mask {r3,r2,r1,r0}
|
||||
edata.cmd = ARM_EXIDX_CMD_REG_POP;
|
||||
edata.data = op2 & 0x0f;
|
||||
}
|
||||
}
|
||||
else if (op == 0xb2) {
|
||||
// vsp = vsp + 0x204 + (uleb128 << 2)
|
||||
uint64_t offset = 0;
|
||||
uint8_t byte, shift = 0;
|
||||
do {
|
||||
GET_BUF_U8(byte);
|
||||
offset |= (byte & 0x7f) << shift;
|
||||
shift += 7;
|
||||
} while ((byte & 0x80) && buf < end);
|
||||
edata.data = offset * 4 + 0x204;
|
||||
edata.cmd = ARM_EXIDX_CMD_ADD_TO_VSP;
|
||||
}
|
||||
else if (op == 0xb3 || op == 0xc8 || op == 0xc9) {
|
||||
// b3: Pop VFP regs D[ssss] to D[ssss+cccc], FSTMFDX-ishly
|
||||
// c8: Pop VFP regs D[16+ssss] to D[16+ssss+cccc], FSTMFDD-ishly
|
||||
// c9: Pop VFP regs D[ssss] to D[ssss+cccc], FSTMFDD-ishly
|
||||
edata.cmd = ARM_EXIDX_CMD_VFP_POP;
|
||||
GET_BUF_U8(edata.data);
|
||||
if (op == 0xc8) edata.data |= ARM_EXIDX_VFP_SHIFT_16;
|
||||
if (op != 0xb3) edata.data |= ARM_EXIDX_VFP_FSTMD;
|
||||
}
|
||||
else if ((op & 0xf8) == 0xb8 || (op & 0xf8) == 0xd0) {
|
||||
// b8: Pop VFP regs D[8] to D[8+nnn], FSTMFDX-ishly
|
||||
// d0: Pop VFP regs D[8] to D[8+nnn], FSTMFDD-ishly
|
||||
edata.cmd = ARM_EXIDX_CMD_VFP_POP;
|
||||
edata.data = 0x80 | (op & 0x07);
|
||||
if ((op & 0xf8) == 0xd0) edata.data |= ARM_EXIDX_VFP_FSTMD;
|
||||
}
|
||||
else if (op >= 0xc0 && op <= 0xc5) {
|
||||
// Intel Wireless MMX pop wR[10]-wr[10+nnn], nnn != 6,7
|
||||
edata.cmd = ARM_EXIDX_CMD_WREG_POP;
|
||||
edata.data = 0xa0 | (op & 0x07);
|
||||
}
|
||||
else if (op == 0xc6) {
|
||||
// Intel Wireless MMX pop wR[ssss] to wR[ssss+cccc]
|
||||
edata.cmd = ARM_EXIDX_CMD_WREG_POP;
|
||||
GET_BUF_U8(edata.data);
|
||||
}
|
||||
else if (op == 0xc7) {
|
||||
uint8_t op2;
|
||||
GET_BUF_U8(op2);
|
||||
if (op2 == 0 || (op2 & 0xf0)) {
|
||||
// Spare
|
||||
edata.cmd = ARM_EXIDX_CMD_RESERVED;
|
||||
} else {
|
||||
// Intel Wireless MMX pop wCGR registers under mask {wCGR3,2,1,0}
|
||||
edata.cmd = ARM_EXIDX_CMD_WCGR_POP;
|
||||
edata.data = op2 & 0x0f;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Spare
|
||||
edata.cmd = ARM_EXIDX_CMD_RESERVED;
|
||||
}
|
||||
|
||||
int ret = handler_->ImproveStackFrame(&edata);
|
||||
if (ret < 0) return ret;
|
||||
}
|
||||
return 0;
|
||||
|
||||
# undef GET_BUF_U8
|
||||
}
|
||||
|
||||
void ExceptionTableInfo::Start()
|
||||
{
|
||||
const struct exidx_entry* start
|
||||
= reinterpret_cast<const struct exidx_entry*>(mr_exidx_.data());
|
||||
const struct exidx_entry* end
|
||||
= reinterpret_cast<const struct exidx_entry*>(mr_exidx_.data()
|
||||
+ mr_exidx_.length());
|
||||
|
||||
// Iterate over each of the EXIDX entries (pairs of 32-bit words).
|
||||
// These occupy the entire .exidx section.
|
||||
for (const struct exidx_entry* entry = start; entry < end; ++entry) {
|
||||
|
||||
// Figure out the code address range that this table entry is
|
||||
// associated with.
|
||||
uint32_t addr = (reinterpret_cast<char*>(Prel31ToAddr(&entry->addr))
|
||||
- mapping_addr_ + loading_addr_) & 0x7fffffff;
|
||||
uint32_t next_addr;
|
||||
if (entry < end - 1)
|
||||
next_addr = (reinterpret_cast<char*>(Prel31ToAddr(&((entry + 1)->addr)))
|
||||
- mapping_addr_ + loading_addr_) & 0x7fffffff;
|
||||
else {
|
||||
// This is the last EXIDX entry in the sequence, so we don't
|
||||
// have an address for the start of the next function, to limit
|
||||
// this one. Instead use the address of the last byte of the
|
||||
// text section associated with this .exidx section, that we
|
||||
// have been given. So as to avoid junking up the CFI unwind
|
||||
// tables with absurdly large address ranges in the case where
|
||||
// text_last_svma_ is wrong, only use the value if it is nonzero
|
||||
// and within one page of |addr|. Otherwise assume a length of 1.
|
||||
//
|
||||
// In some cases, gcc has been observed to finish the exidx
|
||||
// section with an entry of length 1 marked CANT_UNWIND,
|
||||
// presumably exactly for the purpose of giving a definite
|
||||
// length for the last real entry, without having to look at
|
||||
// text segment boundaries.
|
||||
bool plausible = false;
|
||||
next_addr = addr + 1;
|
||||
if (text_last_svma_ != 0) {
|
||||
uint32_t maybe_next_addr = text_last_svma_ + 1;
|
||||
if (maybe_next_addr > addr && maybe_next_addr - addr <= 4096) {
|
||||
next_addr = maybe_next_addr;
|
||||
plausible = true;
|
||||
}
|
||||
}
|
||||
if (!plausible)
|
||||
BPLOG(INFO) << "ExceptionTableInfo: implausible EXIDX last entry size "
|
||||
<< (int32_t)(text_last_svma_ - addr)
|
||||
<< "; using 1 instead.";
|
||||
}
|
||||
|
||||
// Extract the unwind info into |buf|. This might fail for
|
||||
// various reasons. It involves reading both the .exidx and
|
||||
// .extab sections. All accesses to those sections are
|
||||
// bounds-checked.
|
||||
uint8_t buf[ARM_EXIDX_TABLE_LIMIT];
|
||||
size_t buf_used = 0;
|
||||
ExExtractResult res = ExtabEntryExtract(entry, buf, sizeof(buf), &buf_used);
|
||||
if (res != ExSuccess) {
|
||||
// Couldn't extract the unwind info, for some reason. Move on.
|
||||
switch (res) {
|
||||
case ExInBufOverflow:
|
||||
BPLOG(INFO) << "ExtabEntryExtract: .exidx/.extab section overrun";
|
||||
break;
|
||||
case ExOutBufOverflow:
|
||||
BPLOG(INFO) << "ExtabEntryExtract: bytecode buffer overflow";
|
||||
break;
|
||||
case ExCantUnwind:
|
||||
BPLOG(INFO) << "ExtabEntryExtract: function is marked CANT_UNWIND";
|
||||
break;
|
||||
case ExCantRepresent:
|
||||
BPLOG(INFO) << "ExtabEntryExtract: bytecode can't be represented";
|
||||
break;
|
||||
case ExInvalid:
|
||||
BPLOG(INFO) << "ExtabEntryExtract: index table entry is invalid";
|
||||
break;
|
||||
default:
|
||||
BPLOG(INFO) << "ExtabEntryExtract: unknown error: " << (int)res;
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// Finally, work through the unwind instructions in |buf| and
|
||||
// create CFI entries that Breakpad can use. This can also fail.
|
||||
// First, add a new stack frame entry, into which ExtabEntryDecode
|
||||
// will write the CFI entries.
|
||||
handler_->AddStackFrame(addr, next_addr - addr);
|
||||
int ret = ExtabEntryDecode(buf, buf_used);
|
||||
if (ret < 0) {
|
||||
handler_->DeleteStackFrame();
|
||||
BPLOG(INFO) << "ExtabEntryDecode: failed with error code: " << ret;
|
||||
continue;
|
||||
}
|
||||
handler_->SubmitStackFrame();
|
||||
|
||||
} /* iterating over .exidx */
|
||||
}
|
||||
|
||||
} // arm_ex_reader
|
115
toolkit/crashreporter/google-breakpad/src/common/arm_ex_reader.h
Normal file
115
toolkit/crashreporter/google-breakpad/src/common/arm_ex_reader.h
Normal file
@ -0,0 +1,115 @@
|
||||
|
||||
/* libunwind - a platform-independent unwind library
|
||||
Copyright 2011 Linaro Limited
|
||||
|
||||
This file is part of libunwind.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
|
||||
// Copyright (c) 2010 Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
// Derived from libunwind, with extensive modifications.
|
||||
|
||||
#ifndef COMMON_ARM_EX_READER_H__
|
||||
#define COMMON_ARM_EX_READER_H__
|
||||
|
||||
#include "common/arm_ex_to_module.h"
|
||||
#include "common/memory_range.h"
|
||||
|
||||
namespace arm_ex_reader {
|
||||
|
||||
// This class is a reader for ARM unwind information
|
||||
// from .ARM.exidx and .ARM.extab sections.
|
||||
class ExceptionTableInfo {
|
||||
public:
|
||||
ExceptionTableInfo(const char* exidx, size_t exidx_size,
|
||||
const char* extab, size_t extab_size,
|
||||
uint32_t text_last_svma,
|
||||
arm_ex_to_module::ARMExToModule* handler,
|
||||
const char* mapping_addr,
|
||||
uint32_t loading_addr)
|
||||
: mr_exidx_(google_breakpad::MemoryRange(exidx, exidx_size)),
|
||||
mr_extab_(google_breakpad::MemoryRange(extab, extab_size)),
|
||||
text_last_svma_(text_last_svma),
|
||||
handler_(handler), mapping_addr_(mapping_addr),
|
||||
loading_addr_(loading_addr) { }
|
||||
|
||||
~ExceptionTableInfo() { }
|
||||
|
||||
// Parses the entries in .ARM.exidx and possibly
|
||||
// in .ARM.extab tables, reports what we find to
|
||||
// arm_ex_to_module::ARMExToModule.
|
||||
void Start();
|
||||
|
||||
private:
|
||||
google_breakpad::MemoryRange mr_exidx_;
|
||||
google_breakpad::MemoryRange mr_extab_;
|
||||
uint32_t text_last_svma_;
|
||||
arm_ex_to_module::ARMExToModule* handler_;
|
||||
const char* mapping_addr_;
|
||||
uint32_t loading_addr_;
|
||||
|
||||
enum ExExtractResult {
|
||||
ExSuccess, // success
|
||||
ExInBufOverflow, // out-of-range while reading .exidx
|
||||
ExOutBufOverflow, // output buffer is too small
|
||||
ExCantUnwind, // this function is marked CANT_UNWIND
|
||||
ExCantRepresent, // entry valid, but we can't represent it
|
||||
ExInvalid // entry is invalid
|
||||
};
|
||||
ExExtractResult
|
||||
ExtabEntryExtract(const struct arm_ex_to_module::exidx_entry* entry,
|
||||
uint8_t* buf, size_t buf_size,
|
||||
/*OUT*/size_t* buf_used);
|
||||
|
||||
int ExtabEntryDecode(const uint8_t* buf, size_t buf_size);
|
||||
};
|
||||
|
||||
} // namespace arm_ex_reader
|
||||
|
||||
#endif // COMMON_ARM_EX_READER_H__
|
@ -0,0 +1,206 @@
|
||||
|
||||
/* libunwind - a platform-independent unwind library
|
||||
Copyright 2011 Linaro Limited
|
||||
|
||||
This file is part of libunwind.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
|
||||
// Copyright (c) 2010 Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
// Derived from libunwind, with extensive modifications.
|
||||
|
||||
#include "common/unique_string.h"
|
||||
#include "common/arm_ex_to_module.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
|
||||
// For big-picture comments on how the EXIDX reader works,
|
||||
// see arm_ex_reader.cc.
|
||||
|
||||
#define ARM_EXBUF_START(x) (((x) >> 4) & 0x0f)
|
||||
#define ARM_EXBUF_COUNT(x) ((x) & 0x0f)
|
||||
#define ARM_EXBUF_END(x) (ARM_EXBUF_START(x) + ARM_EXBUF_COUNT(x))
|
||||
|
||||
using google_breakpad::ustr__pc;
|
||||
using google_breakpad::ustr__lr;
|
||||
using google_breakpad::ustr__sp;
|
||||
using google_breakpad::Module;
|
||||
using google_breakpad::ToUniqueString;
|
||||
using google_breakpad::UniqueString;
|
||||
|
||||
namespace arm_ex_to_module {
|
||||
|
||||
// Translate command from extab_data to command for Module.
|
||||
int ARMExToModule::TranslateCmd(const struct extab_data* edata,
|
||||
Module::StackFrameEntry* entry, string& vsp) {
|
||||
int ret = 0;
|
||||
switch (edata->cmd) {
|
||||
case ARM_EXIDX_CMD_FINISH:
|
||||
/* Copy LR to PC if there isn't currently a rule for PC in force. */
|
||||
if (entry->initial_rules.find(ustr__pc())
|
||||
== entry->initial_rules.end()) {
|
||||
if (entry->initial_rules.find(ustr__lr())
|
||||
== entry->initial_rules.end()) {
|
||||
entry->initial_rules[ustr__pc()] = Module::Expr("lr");
|
||||
} else {
|
||||
entry->initial_rules[ustr__pc()] = entry->initial_rules[ustr__lr()];
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ARM_EXIDX_CMD_SUB_FROM_VSP:
|
||||
{
|
||||
char c[16];
|
||||
sprintf(c, " %d -", edata->data);
|
||||
vsp += c;
|
||||
}
|
||||
break;
|
||||
case ARM_EXIDX_CMD_ADD_TO_VSP:
|
||||
{
|
||||
char c[16];
|
||||
sprintf(c, " %d +", edata->data);
|
||||
vsp += c;
|
||||
}
|
||||
break;
|
||||
case ARM_EXIDX_CMD_REG_POP:
|
||||
for (unsigned int i = 0; i < 16; i++) {
|
||||
if (edata->data & (1 << i)) {
|
||||
entry->initial_rules[ToUniqueString(regnames[i])]
|
||||
= Module::Expr(vsp + " ^");
|
||||
vsp += " 4 +";
|
||||
}
|
||||
}
|
||||
/* Set cfa in case the SP got popped. */
|
||||
if (edata->data & (1 << 13)) {
|
||||
Module::Expr& vsp_expr = entry->initial_rules[ustr__sp()];
|
||||
// It must be a postfix expression (we don't generate anything
|
||||
// else here), so return -1 to fail out if it isn't.
|
||||
if (!vsp_expr.isExprPostfix()) {
|
||||
ret = -1;
|
||||
break;
|
||||
};
|
||||
vsp = vsp_expr.getExprPostfix();
|
||||
}
|
||||
break;
|
||||
case ARM_EXIDX_CMD_REG_TO_SP: {
|
||||
assert (edata->data < 16);
|
||||
const char* const regname = regnames[edata->data];
|
||||
const UniqueString* regname_us = ToUniqueString(regname);
|
||||
if (entry->initial_rules.find(regname_us) == entry->initial_rules.end()) {
|
||||
entry->initial_rules[ustr__sp()] = Module::Expr(regname);
|
||||
} else {
|
||||
entry->initial_rules[ustr__sp()] = entry->initial_rules[regname_us];
|
||||
}
|
||||
Module::Expr& vsp_expr = entry->initial_rules[ustr__sp()];
|
||||
if (!vsp_expr.isExprPostfix()) {
|
||||
ret = -1;
|
||||
break;
|
||||
};
|
||||
vsp = vsp_expr.getExprPostfix();
|
||||
break;
|
||||
}
|
||||
case ARM_EXIDX_CMD_VFP_POP:
|
||||
/* Don't recover VFP registers, but be sure to adjust the stack
|
||||
pointer. */
|
||||
for (unsigned int i = ARM_EXBUF_START(edata->data);
|
||||
i <= ARM_EXBUF_END(edata->data); i++) {
|
||||
vsp += " 8 +";
|
||||
}
|
||||
if (!(edata->data & ARM_EXIDX_VFP_FSTMD)) {
|
||||
vsp += " 4 +";
|
||||
}
|
||||
break;
|
||||
case ARM_EXIDX_CMD_WREG_POP:
|
||||
for (unsigned int i = ARM_EXBUF_START(edata->data);
|
||||
i <= ARM_EXBUF_END(edata->data); i++) {
|
||||
vsp += " 8 +";
|
||||
}
|
||||
break;
|
||||
case ARM_EXIDX_CMD_WCGR_POP:
|
||||
// Pop wCGR registers under mask {wCGR3,2,1,0}, hence "i < 4"
|
||||
for (unsigned int i = 0; i < 4; i++) {
|
||||
if (edata->data & (1 << i)) {
|
||||
vsp += " 4 +";
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ARM_EXIDX_CMD_REFUSED:
|
||||
case ARM_EXIDX_CMD_RESERVED:
|
||||
ret = -1;
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void ARMExToModule::AddStackFrame(uintptr_t addr, size_t size) {
|
||||
stack_frame_entry_ = new Module::StackFrameEntry;
|
||||
stack_frame_entry_->address = addr;
|
||||
stack_frame_entry_->size = size;
|
||||
stack_frame_entry_->initial_rules[ToUniqueString(kCFA)] = Module::Expr("sp");
|
||||
vsp_ = "sp";
|
||||
}
|
||||
|
||||
int ARMExToModule::ImproveStackFrame(const struct extab_data* edata) {
|
||||
return TranslateCmd(edata, stack_frame_entry_, vsp_) ;
|
||||
}
|
||||
|
||||
void ARMExToModule::DeleteStackFrame() {
|
||||
delete stack_frame_entry_;
|
||||
}
|
||||
|
||||
void ARMExToModule::SubmitStackFrame() {
|
||||
// return address always winds up in pc
|
||||
stack_frame_entry_->initial_rules[ToUniqueString(kRA)]
|
||||
= stack_frame_entry_->initial_rules[ustr__pc()];
|
||||
// the final value of vsp is the new value of sp
|
||||
stack_frame_entry_->initial_rules[ustr__sp()] = vsp_;
|
||||
module_->AddStackFrameEntry(stack_frame_entry_);
|
||||
}
|
||||
|
||||
} // namespace arm_ex_to_module
|
@ -0,0 +1,129 @@
|
||||
|
||||
/* libunwind - a platform-independent unwind library
|
||||
Copyright 2011 Linaro Limited
|
||||
|
||||
This file is part of libunwind.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
|
||||
// Copyright (c) 2010 Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
// Derived from libunwind, with extensive modifications.
|
||||
|
||||
#ifndef COMMON_ARM_EX_TO_MODULE__
|
||||
#define COMMON_ARM_EX_TO_MODULE__
|
||||
|
||||
#include "common/module.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
namespace arm_ex_to_module {
|
||||
|
||||
using google_breakpad::Module;
|
||||
|
||||
typedef enum extab_cmd {
|
||||
ARM_EXIDX_CMD_FINISH,
|
||||
ARM_EXIDX_CMD_SUB_FROM_VSP,
|
||||
ARM_EXIDX_CMD_ADD_TO_VSP,
|
||||
ARM_EXIDX_CMD_REG_POP,
|
||||
ARM_EXIDX_CMD_REG_TO_SP,
|
||||
ARM_EXIDX_CMD_VFP_POP,
|
||||
ARM_EXIDX_CMD_WREG_POP,
|
||||
ARM_EXIDX_CMD_WCGR_POP,
|
||||
ARM_EXIDX_CMD_RESERVED,
|
||||
ARM_EXIDX_CMD_REFUSED,
|
||||
} extab_cmd_t;
|
||||
|
||||
struct exidx_entry {
|
||||
uint32_t addr;
|
||||
uint32_t data;
|
||||
};
|
||||
|
||||
struct extab_data {
|
||||
extab_cmd_t cmd;
|
||||
uint32_t data;
|
||||
};
|
||||
|
||||
enum extab_cmd_flags {
|
||||
ARM_EXIDX_VFP_SHIFT_16 = 1 << 16,
|
||||
ARM_EXIDX_VFP_FSTMD = 1 << 17, // distinguishes FSTMxxD from FSTMxxX
|
||||
};
|
||||
|
||||
const string kRA = ".ra";
|
||||
const string kCFA = ".cfa";
|
||||
|
||||
static const char* const regnames[] = {
|
||||
"r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7",
|
||||
"r8", "r9", "r10", "r11", "r12", "sp", "lr", "pc",
|
||||
"f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7",
|
||||
"fps", "cpsr"
|
||||
};
|
||||
|
||||
// Receives information from arm_ex_reader::ExceptionTableInfo
|
||||
// and adds it to the Module object
|
||||
class ARMExToModule {
|
||||
public:
|
||||
ARMExToModule(Module* module)
|
||||
: module_(module) { }
|
||||
~ARMExToModule() { }
|
||||
void AddStackFrame(uintptr_t addr, size_t size);
|
||||
int ImproveStackFrame(const struct extab_data* edata);
|
||||
void DeleteStackFrame();
|
||||
void SubmitStackFrame();
|
||||
private:
|
||||
Module* module_;
|
||||
Module::StackFrameEntry* stack_frame_entry_;
|
||||
string vsp_;
|
||||
int TranslateCmd(const struct extab_data* edata,
|
||||
Module::StackFrameEntry* entry,
|
||||
string& vsp);
|
||||
};
|
||||
|
||||
} // namespace arm_ex_to_module
|
||||
|
||||
#endif // COMMON_ARM_EX_TO_MODULE__
|
@ -52,6 +52,7 @@
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "common/arm_ex_reader.h"
|
||||
#include "common/dwarf/bytereader-inl.h"
|
||||
#include "common/dwarf/dwarf2diehandler.h"
|
||||
#include "common/dwarf_cfi_to_module.h"
|
||||
@ -70,6 +71,10 @@
|
||||
#include "common/using_std_string.h"
|
||||
#include "common/logging.h"
|
||||
|
||||
#if defined(__ANDROID__) && !defined(SHT_ARM_EXIDX)
|
||||
# define SHT_ARM_EXIDX (SHT_LOPROC + 1)
|
||||
#endif
|
||||
|
||||
// This namespace contains helper functions.
|
||||
namespace {
|
||||
|
||||
@ -345,6 +350,52 @@ bool LoadDwarfCFI(const string& dwarf_filename,
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename ElfClass>
|
||||
bool LoadARMexidx(const typename ElfClass::Ehdr* elf_header,
|
||||
const typename ElfClass::Shdr* exidx_section,
|
||||
const typename ElfClass::Shdr* extab_section,
|
||||
uint32_t loading_addr,
|
||||
Module* module) {
|
||||
// To do this properly we need to know:
|
||||
// * the bounds of the .ARM.exidx section in the mapped image
|
||||
// * the bounds of the .ARM.extab section in the mapped image
|
||||
// * the vma of the last byte in the text section associated with the .exidx
|
||||
// The first two are easy. The third is a bit tricky. If we can't
|
||||
// figure out what it is, just pass in zero.
|
||||
const char *exidx_img
|
||||
= GetOffset<ElfClass, char>(elf_header, exidx_section->sh_offset);
|
||||
size_t exidx_size = exidx_section->sh_size;
|
||||
const char *extab_img
|
||||
= GetOffset<ElfClass, char>(elf_header, extab_section->sh_offset);
|
||||
size_t extab_size = extab_section->sh_size;
|
||||
|
||||
// The sh_link field of the exidx section gives the section number
|
||||
// for the associated text section.
|
||||
uint32_t exidx_text_last_svma = 0;
|
||||
int exidx_text_sno = exidx_section->sh_link;
|
||||
typedef typename ElfClass::Shdr Shdr;
|
||||
// |sections| points to the section header table
|
||||
const Shdr* sections
|
||||
= GetOffset<ElfClass, Shdr>(elf_header, elf_header->e_shoff);
|
||||
const int num_sections = elf_header->e_shnum;
|
||||
if (exidx_text_sno >= 0 && exidx_text_sno < num_sections) {
|
||||
const Shdr* exidx_text_shdr = §ions[exidx_text_sno];
|
||||
if (exidx_text_shdr->sh_size > 0) {
|
||||
exidx_text_last_svma
|
||||
= exidx_text_shdr->sh_addr + exidx_text_shdr->sh_size - 1;
|
||||
}
|
||||
}
|
||||
|
||||
arm_ex_to_module::ARMExToModule handler(module);
|
||||
arm_ex_reader::ExceptionTableInfo
|
||||
parser(exidx_img, exidx_size, extab_img, extab_size, exidx_text_last_svma,
|
||||
&handler,
|
||||
reinterpret_cast<const char*>(elf_header),
|
||||
loading_addr);
|
||||
parser.Start();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LoadELF(const string& obj_file, MmapWrapper* map_wrapper,
|
||||
void** elf_header) {
|
||||
int obj_fd = open(obj_file.c_str(), O_RDONLY);
|
||||
@ -634,6 +685,29 @@ bool LoadSymbols(const string& obj_file,
|
||||
}
|
||||
}
|
||||
|
||||
// ARM has special unwind tables that can be used.
|
||||
const Shdr* arm_exidx_section =
|
||||
FindElfSectionByName<ElfClass>(".ARM.exidx", SHT_ARM_EXIDX,
|
||||
sections, names, names_end,
|
||||
elf_header->e_shnum);
|
||||
const Shdr* arm_extab_section =
|
||||
FindElfSectionByName<ElfClass>(".ARM.extab", SHT_PROGBITS,
|
||||
sections, names, names_end,
|
||||
elf_header->e_shnum);
|
||||
// Only load information from this section if there isn't a .debug_info
|
||||
// section.
|
||||
if (!found_debug_info_section
|
||||
&& arm_exidx_section && arm_extab_section && symbol_data != NO_CFI) {
|
||||
info->LoadedSection(".ARM.exidx");
|
||||
info->LoadedSection(".ARM.extab");
|
||||
bool result = LoadARMexidx<ElfClass>(elf_header,
|
||||
arm_exidx_section, arm_extab_section,
|
||||
loading_addr, module);
|
||||
found_usable_info = found_usable_info || result;
|
||||
if (result)
|
||||
BPLOG(INFO) << "LoadSymbols: read EXIDX from .ARM.{exidx,extab}";
|
||||
}
|
||||
|
||||
if (!found_debug_info_section && symbol_data != ONLY_CFI) {
|
||||
fprintf(stderr, "%s: file contains no debugging information"
|
||||
" (no \".stab\" or \".debug_info\" sections)\n",
|
||||
|
@ -258,7 +258,7 @@ bool Module::ReportError() {
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& stream, const Module::Expr& expr) {
|
||||
assert(!expr.invalid());
|
||||
assert(!expr.isExprInvalid());
|
||||
switch (expr.how_) {
|
||||
case Module::kExprSimple:
|
||||
stream << expr.ident_ << " " << expr.offset_ << " +";
|
||||
|
@ -165,7 +165,14 @@ class Module {
|
||||
offset_ = 0;
|
||||
how_ = kExprInvalid;
|
||||
}
|
||||
bool invalid() const { return how_ == kExprInvalid; }
|
||||
bool isExprInvalid() const { return how_ == kExprInvalid; }
|
||||
bool isExprPostfix() const { return how_ == kExprPostfix; }
|
||||
|
||||
// Return the postfix expression string. This is only
|
||||
// meaningful on Exprs for which isExprPostfix returns true.
|
||||
// In all other cases it returns an empty string.
|
||||
string getExprPostfix() const { return postfix_; }
|
||||
|
||||
bool operator==(const Expr& other) const {
|
||||
return how_ == other.how_ &&
|
||||
ident_ == other.ident_ &&
|
||||
|
@ -31,6 +31,8 @@ if CONFIG['OS_TARGET'] != 'WINNT':
|
||||
'language.cc',
|
||||
'dwarf/dwarf2diehandler.cc',
|
||||
'dwarf_line_to_module.cc',
|
||||
'arm_ex_reader.cc',
|
||||
'arm_ex_to_module.cc',
|
||||
]
|
||||
|
||||
if CONFIG['OS_ARCH'] == 'Linux':
|
||||
|
@ -235,6 +235,27 @@ inline static const UniqueString* ustr__ZDra() {
|
||||
return us;
|
||||
}
|
||||
|
||||
// "pc"
|
||||
inline static const UniqueString* ustr__pc() {
|
||||
static const UniqueString* us = NULL;
|
||||
if (!us) us = ToUniqueString("pc");
|
||||
return us;
|
||||
}
|
||||
|
||||
// "lr"
|
||||
inline static const UniqueString* ustr__lr() {
|
||||
static const UniqueString* us = NULL;
|
||||
if (!us) us = ToUniqueString("lr");
|
||||
return us;
|
||||
}
|
||||
|
||||
// "sp"
|
||||
inline static const UniqueString* ustr__sp() {
|
||||
static const UniqueString* us = NULL;
|
||||
if (!us) us = ToUniqueString("sp");
|
||||
return us;
|
||||
}
|
||||
|
||||
template <typename ValueType>
|
||||
class UniqueStringMap
|
||||
{
|
||||
|
@ -54,7 +54,7 @@ bool CFIFrameInfo::FindCallerRegs(const RegisterValueMap<V> ®isters,
|
||||
RegisterValueMap<V> *caller_registers) const {
|
||||
// If there are not rules for both .ra and .cfa in effect at this address,
|
||||
// don't use this CFI data for stack walking.
|
||||
if (cfa_rule_.invalid() || ra_rule_.invalid())
|
||||
if (cfa_rule_.isExprInvalid() || ra_rule_.isExprInvalid())
|
||||
return false;
|
||||
|
||||
RegisterValueMap<V> working;
|
||||
@ -105,10 +105,10 @@ template bool CFIFrameInfo::FindCallerRegs<uint64_t>(
|
||||
string CFIFrameInfo::Serialize() const {
|
||||
std::ostringstream stream;
|
||||
|
||||
if (!cfa_rule_.invalid()) {
|
||||
if (!cfa_rule_.isExprInvalid()) {
|
||||
stream << ".cfa: " << cfa_rule_;
|
||||
}
|
||||
if (!ra_rule_.invalid()) {
|
||||
if (!ra_rule_.isExprInvalid()) {
|
||||
if (static_cast<std::streamoff>(stream.tellp()) != 0)
|
||||
stream << " ";
|
||||
stream << ".ra: " << ra_rule_;
|
||||
|
Loading…
Reference in New Issue
Block a user