aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/common/linux/dump_symbols.cc19
-rw-r--r--src/google_breakpad/common/minidump_cpu_arm.h10
-rw-r--r--src/google_breakpad/processor/stack_frame_cpu.h47
-rw-r--r--src/processor/minidump_stackwalk.cc31
-rw-r--r--src/processor/stackwalker_arm.cc114
-rw-r--r--src/processor/stackwalker_arm.h14
-rw-r--r--src/processor/stackwalker_arm_unittest.cc440
7 files changed, 641 insertions, 34 deletions
diff --git a/src/common/linux/dump_symbols.cc b/src/common/linux/dump_symbols.cc
index 3df31372..3d3e75e0 100644
--- a/src/common/linux/dump_symbols.cc
+++ b/src/common/linux/dump_symbols.cc
@@ -255,16 +255,19 @@ static bool DwarfCFIRegisterNames(const ElfW(Ehdr) *elf_header,
NULL
};
+ static const char *const arm_names[] = {
+ "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",
+ NULL
+ };
+
const char * const *name_table;
switch (elf_header->e_machine) {
- case EM_386:
- name_table = i386_names;
- break;
-
- case EM_X86_64:
- name_table = x86_64_names;
- break;
-
+ case EM_386: name_table = i386_names; break;
+ case EM_ARM: name_table = arm_names; break;
+ case EM_X86_64: name_table = x86_64_names; break;
default:
return false;
}
diff --git a/src/google_breakpad/common/minidump_cpu_arm.h b/src/google_breakpad/common/minidump_cpu_arm.h
index bd3d934f..14d81460 100644
--- a/src/google_breakpad/common/minidump_cpu_arm.h
+++ b/src/google_breakpad/common/minidump_cpu_arm.h
@@ -116,6 +116,16 @@ typedef struct {
} MDRawContextARM;
+/* Indices into iregs for registers with a dedicated or conventional
+ * purpose.
+ */
+enum MDARMRegisterNumbers {
+ MD_CONTEXT_ARM_REG_FP = 11,
+ MD_CONTEXT_ARM_REG_SP = 13,
+ MD_CONTEXT_ARM_REG_LR = 14,
+ MD_CONTEXT_ARM_REG_PC = 15
+};
+
/* For (MDRawContextARM).context_flags. These values indicate the type of
* context stored in the structure. */
#define MD_CONTEXT_ARM_INTEGER (MD_CONTEXT_ARM | 0x00000002)
diff --git a/src/google_breakpad/processor/stack_frame_cpu.h b/src/google_breakpad/processor/stack_frame_cpu.h
index 8b88fdc6..c5bddffb 100644
--- a/src/google_breakpad/processor/stack_frame_cpu.h
+++ b/src/google_breakpad/processor/stack_frame_cpu.h
@@ -189,29 +189,54 @@ struct StackFrameSPARC : public StackFrame {
};
struct StackFrameARM : public StackFrame {
- // ContextValidity should eventually contain entries for the validity of
- // other nonvolatile (callee-save) registers as in
- // StackFrameX86::ContextValidity. I suspect this list is sufficient
- // for arm stackwalking.
+ // A flag for each register we might know.
enum ContextValidity {
CONTEXT_VALID_NONE = 0,
- CONTEXT_VALID_R13 = 1 << 0,
- CONTEXT_VALID_R14 = 1 << 1,
- CONTEXT_VALID_R15 = 1 << 2,
- CONTEXT_VALID_ALL = -1
+ CONTEXT_VALID_R0 = 1 << 0,
+ CONTEXT_VALID_R1 = 1 << 1,
+ CONTEXT_VALID_R2 = 1 << 2,
+ CONTEXT_VALID_R3 = 1 << 3,
+ CONTEXT_VALID_R4 = 1 << 4,
+ CONTEXT_VALID_R5 = 1 << 5,
+ CONTEXT_VALID_R6 = 1 << 6,
+ CONTEXT_VALID_R7 = 1 << 7,
+ CONTEXT_VALID_R8 = 1 << 8,
+ CONTEXT_VALID_R9 = 1 << 9,
+ CONTEXT_VALID_R10 = 1 << 10,
+ CONTEXT_VALID_R11 = 1 << 11,
+ CONTEXT_VALID_R12 = 1 << 12,
+ CONTEXT_VALID_R13 = 1 << 13,
+ CONTEXT_VALID_R14 = 1 << 14,
+ CONTEXT_VALID_R15 = 1 << 15,
+ CONTEXT_VALID_ALL = ~CONTEXT_VALID_NONE,
+
+ // Aliases for registers with dedicated or conventional roles.
+ CONTEXT_VALID_FP = CONTEXT_VALID_R11,
+ CONTEXT_VALID_SP = CONTEXT_VALID_R13,
+ CONTEXT_VALID_LR = CONTEXT_VALID_R14,
+ CONTEXT_VALID_PC = CONTEXT_VALID_R15
};
StackFrameARM() : context(), context_validity(CONTEXT_VALID_NONE) {}
+ // Return the ContextValidity flag for register rN.
+ static ContextValidity RegisterValidFlag(int n) {
+ return ContextValidity(1 << n);
+ }
+
// Register state. This is only fully valid for the topmost frame in a
// stack. In other frames, the values of nonvolatile registers may be
// present, given sufficient debugging information. Refer to
// context_validity.
MDRawContextARM context;
- // context_validity is actually ContextValidity, but int is used because
- // the OR operator doesn't work well with enumerated types. This indicates
- // which fields in context are valid.
+ // For each register in context whose value has been recovered, we set
+ // the corresponding CONTEXT_VALID_ bit in context_validity.
+ //
+ // context_validity's type should actually be ContextValidity, but
+ // we use int instead because the bitwise inclusive or operator
+ // yields an int when applied to enum values, and C++ doesn't
+ // silently convert from ints to enums.
int context_validity;
};
diff --git a/src/processor/minidump_stackwalk.cc b/src/processor/minidump_stackwalk.cc
index 1d0a913a..9fe3b547 100644
--- a/src/processor/minidump_stackwalk.cc
+++ b/src/processor/minidump_stackwalk.cc
@@ -216,12 +216,31 @@ static void PrintStack(const CallStack *stack, const string &cpu) {
const StackFrameARM *frame_arm =
reinterpret_cast<const StackFrameARM*>(frame);
- if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R13)
- sequence = PrintRegister("r13", frame_arm->context.iregs[13], sequence);
- if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R14)
- sequence = PrintRegister("r14", frame_arm->context.iregs[14], sequence);
- if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R15)
- sequence = PrintRegister("r15", frame_arm->context.iregs[15], sequence);
+ // General-purpose callee-saves registers.
+ if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R4)
+ sequence = PrintRegister("r4", frame_arm->context.iregs[4], sequence);
+ if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R5)
+ sequence = PrintRegister("r5", frame_arm->context.iregs[5], sequence);
+ if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R6)
+ sequence = PrintRegister("r6", frame_arm->context.iregs[6], sequence);
+ if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R7)
+ sequence = PrintRegister("r7", frame_arm->context.iregs[7], sequence);
+ if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R8)
+ sequence = PrintRegister("r8", frame_arm->context.iregs[8], sequence);
+ if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R9)
+ sequence = PrintRegister("r9", frame_arm->context.iregs[9], sequence);
+ if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R10)
+ sequence = PrintRegister("r10", frame_arm->context.iregs[10], sequence);
+
+ // Registers with a dedicated or conventional purpose.
+ if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_FP)
+ sequence = PrintRegister("fp", frame_arm->context.iregs[11], sequence);
+ if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_SP)
+ sequence = PrintRegister("sp", frame_arm->context.iregs[13], sequence);
+ if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_LR)
+ sequence = PrintRegister("lr", frame_arm->context.iregs[14], sequence);
+ if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_PC)
+ sequence = PrintRegister("pc", frame_arm->context.iregs[15], sequence);
}
printf("\n");
}
diff --git a/src/processor/stackwalker_arm.cc b/src/processor/stackwalker_arm.cc
index 4b15593d..0af27a32 100644
--- a/src/processor/stackwalker_arm.cc
+++ b/src/processor/stackwalker_arm.cc
@@ -31,14 +31,17 @@
//
// See stackwalker_arm.h for documentation.
//
-// Author: Mark Mentovai, Ted Mielczarek
+// Author: Mark Mentovai, Ted Mielczarek, Jim Blandy
-#include "processor/stackwalker_arm.h"
#include "google_breakpad/processor/call_stack.h"
#include "google_breakpad/processor/memory_region.h"
+#include "google_breakpad/processor/source_line_resolver_interface.h"
#include "google_breakpad/processor/stack_frame_cpu.h"
+#include "processor/cfi_frame_info.h"
#include "processor/logging.h"
+#include "processor/scoped_ptr.h"
+#include "processor/stackwalker_arm.h"
namespace google_breakpad {
@@ -50,8 +53,8 @@ StackwalkerARM::StackwalkerARM(const SystemInfo *system_info,
SymbolSupplier *supplier,
SourceLineResolverInterface *resolver)
: Stackwalker(system_info, memory, modules, supplier, resolver),
- context_(context) {
-}
+ context_(context),
+ context_frame_validity_(StackFrameARM::CONTEXT_VALID_ALL) { }
StackFrame* StackwalkerARM::GetContextFrame() {
@@ -65,7 +68,7 @@ StackFrame* StackwalkerARM::GetContextFrame() {
// The instruction pointer is stored directly in a register (r15), so pull it
// straight out of the CPU context structure.
frame->context = *context_;
- frame->context_validity = StackFrameARM::CONTEXT_VALID_ALL;
+ frame->context_validity = context_frame_validity_;
frame->instruction = frame->context.iregs[15];
return frame;
@@ -78,9 +81,104 @@ StackFrame* StackwalkerARM::GetCallerFrame(const CallStack *stack) {
return NULL;
}
- // TODO: Can't actually walk the stack on ARM without the CFI data.
- // Implement this when the CFI symbol dumper changes have landed.
- return NULL;
+ const vector<StackFrame *> &frames = *stack->frames();
+ StackFrameARM *last_frame = static_cast<StackFrameARM *>(frames.back());
+
+ // See if we have DWARF call frame information covering this address.
+ scoped_ptr<CFIFrameInfo> cfi_frame_info(resolver_
+ ->FindCFIFrameInfo(last_frame));
+ if (cfi_frame_info == NULL)
+ // Unfortunately, CFI is our only option on the ARM for now. If we
+ // add a second strategy, we should put each one in its own function.
+ return NULL;
+
+ static const char *register_names[] = {
+ "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",
+ NULL
+ };
+
+ // Populate a dictionary with the valid register values in last_frame.
+ CFIFrameInfo::RegisterValueMap<u_int32_t> callee_registers;
+ for (int i = 0; register_names[i]; i++)
+ if (last_frame->context_validity & StackFrameARM::RegisterValidFlag(i))
+ callee_registers[register_names[i]] = last_frame->context.iregs[i];
+
+ // Use the STACK CFI data to recover the caller's register values.
+ CFIFrameInfo::RegisterValueMap<u_int32_t> caller_registers;
+ if (!cfi_frame_info->FindCallerRegs(callee_registers, *memory_,
+ &caller_registers))
+ return NULL;
+
+ // Construct a new stack frame given the values the CFI recovered.
+ scoped_ptr<StackFrameARM> frame(new StackFrameARM());
+ for (int i = 0; register_names[i]; i++) {
+ CFIFrameInfo::RegisterValueMap<u_int32_t>::iterator entry =
+ caller_registers.find(register_names[i]);
+ if (entry != caller_registers.end()) {
+ // We recovered the value of this register; fill the context with the
+ // value from caller_registers.
+ frame->context_validity |= StackFrameARM::RegisterValidFlag(i);
+ frame->context.iregs[i] = entry->second;
+ } else if (4 <= i && i <= 11 && (last_frame->context_validity &
+ StackFrameARM::RegisterValidFlag(i))) {
+ // If the STACK CFI data doesn't mention some callee-saves register, and
+ // it is valid in the callee, assume the callee has not yet changed it.
+ // Registers r4 through r11 are callee-saves, according to the Procedure
+ // Call Standard for the ARM Architecture, which the Linux ABI follows.
+ frame->context_validity |= StackFrameARM::RegisterValidFlag(i);
+ frame->context.iregs[i] = last_frame->context.iregs[i];
+ }
+ }
+ // If the CFI doesn't recover the PC explicitly, then use .ra.
+ if (! (frame->context_validity & StackFrameARM::CONTEXT_VALID_PC)) {
+ CFIFrameInfo::RegisterValueMap<u_int32_t>::iterator entry =
+ caller_registers.find(".ra");
+ if (entry != caller_registers.end()) {
+ frame->context_validity |= StackFrameARM::CONTEXT_VALID_PC;
+ frame->context.iregs[MD_CONTEXT_ARM_REG_PC] = entry->second;
+ }
+ }
+ // If the CFI doesn't recover the SP explicitly, then use .cfa.
+ if (! (frame->context_validity & StackFrameARM::CONTEXT_VALID_SP)) {
+ CFIFrameInfo::RegisterValueMap<u_int32_t>::iterator entry =
+ caller_registers.find(".cfa");
+ if (entry != caller_registers.end()) {
+ frame->context_validity |= StackFrameARM::CONTEXT_VALID_SP;
+ frame->context.iregs[MD_CONTEXT_ARM_REG_SP] = entry->second;
+ }
+ }
+
+ // If we didn't recover the PC and the SP, then the frame isn't very useful.
+ static const int essentials = (StackFrameARM::CONTEXT_VALID_SP
+ | StackFrameARM::CONTEXT_VALID_PC);
+ if ((frame->context_validity & essentials) != essentials)
+ return NULL;
+
+ // An instruction address of zero marks the end of the stack.
+ if (frame->context.iregs[MD_CONTEXT_ARM_REG_PC] == 0)
+ return NULL;
+
+ // If the new stack pointer is at a lower address than the old, then
+ // that's clearly incorrect. Treat this as end-of-stack to enforce
+ // progress and avoid infinite loops.
+ if (frame->context.iregs[MD_CONTEXT_ARM_REG_SP]
+ < last_frame->context.iregs[MD_CONTEXT_ARM_REG_SP])
+ return NULL;
+
+ // The new frame's context's PC is the return address, which is one
+ // instruction past the instruction that caused us to arrive at the
+ // callee. Set new_frame->instruction to one less than the PC. This won't
+ // reference the beginning of the call instruction, but it's at least
+ // within it, which is sufficient to get the source line information to
+ // match up with the line that contains the function call. Callers that
+ // require the exact return address value may access
+ // frame->context.iregs[MD_CONTEXT_ARM_REG_PC].
+ frame->instruction = frame->context.iregs[MD_CONTEXT_ARM_REG_PC] - 1;
+
+ return frame.release();
}
diff --git a/src/processor/stackwalker_arm.h b/src/processor/stackwalker_arm.h
index 5dfde5ab..3768cc2c 100644
--- a/src/processor/stackwalker_arm.h
+++ b/src/processor/stackwalker_arm.h
@@ -1,3 +1,5 @@
+// -*- mode: C++ -*-
+
// Copyright (c) 2010 Google Inc.
// All rights reserved.
//
@@ -60,15 +62,25 @@ class StackwalkerARM : public Stackwalker {
SymbolSupplier *supplier,
SourceLineResolverInterface *resolver);
+ // Change the context validity mask of the frame returned by
+ // GetContextFrame to VALID. This is only for use by unit tests; the
+ // default behavior is correct for all application code.
+ void SetContextFrameValidity(int valid) { context_frame_validity_ = valid; }
+
private:
// Implementation of Stackwalker, using arm context and stack conventions.
// TODO: currently stubbed out, needs CFI symbol dumper support
virtual StackFrame* GetContextFrame();
virtual StackFrame* GetCallerFrame(const CallStack *stack);
- // Stores the CPU context corresponding to the innermost stack frame to
+ // Stores the CPU context corresponding to the youngest stack frame, to
// be returned by GetContextFrame.
const MDRawContextARM *context_;
+
+ // Validity mask for youngest stack frame. This is always
+ // CONTEXT_VALID_ALL in real use; it is only changeable for the sake of
+ // unit tests.
+ int context_frame_validity_;
};
diff --git a/src/processor/stackwalker_arm_unittest.cc b/src/processor/stackwalker_arm_unittest.cc
new file mode 100644
index 00000000..37103241
--- /dev/null
+++ b/src/processor/stackwalker_arm_unittest.cc
@@ -0,0 +1,440 @@
+// 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.
+
+// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
+
+// stackwalker_arm_unittest.cc: Unit tests for StackwalkerARM class.
+
+#include <string>
+#include <string.h>
+#include <vector>
+
+#include "breakpad_googletest_includes.h"
+#include "google_breakpad/common/minidump_format.h"
+#include "google_breakpad/processor/basic_source_line_resolver.h"
+#include "google_breakpad/processor/call_stack.h"
+#include "google_breakpad/processor/source_line_resolver_interface.h"
+#include "google_breakpad/processor/stack_frame_cpu.h"
+#include "processor/stackwalker_unittest_utils.h"
+#include "processor/stackwalker_arm.h"
+#include "processor/test_assembler.h"
+#include "processor/windows_frame_info.h"
+
+using google_breakpad::BasicSourceLineResolver;
+using google_breakpad::CallStack;
+using google_breakpad::StackFrame;
+using google_breakpad::StackFrameARM;
+using google_breakpad::StackwalkerARM;
+using google_breakpad::SystemInfo;
+using google_breakpad::WindowsFrameInfo;
+using google_breakpad::TestAssembler::kLittleEndian;
+using google_breakpad::TestAssembler::Label;
+using google_breakpad::TestAssembler::Section;
+using std::string;
+using std::vector;
+using testing::_;
+using testing::Return;
+using testing::SetArgumentPointee;
+using testing::Test;
+
+class StackwalkerARMFixture {
+ public:
+ StackwalkerARMFixture()
+ : stack_section(kLittleEndian),
+ // Give the two modules reasonable standard locations and names
+ // for tests to play with.
+ module1(0x40000000, 0x10000, "module1", "version1"),
+ module2(0x50000000, 0x10000, "module2", "version2") {
+ // Identify the system as a Linux system.
+ system_info.os = "Linux";
+ system_info.os_short = "linux";
+ system_info.os_version = "Lugubrious Labrador";
+ system_info.cpu = "arm";
+ system_info.cpu_info = "";
+
+ // Put distinctive values in the raw CPU context.
+ BrandContext(&raw_context);
+
+ // Create some modules with some stock debugging information.
+ modules.Add(&module1);
+ modules.Add(&module2);
+
+ // By default, none of the modules have symbol info; call
+ // SetModuleSymbols to override this.
+ EXPECT_CALL(supplier, GetSymbolFile(_, _, _, _))
+ .WillRepeatedly(Return(MockSymbolSupplier::NOT_FOUND));
+ }
+
+ // Set the Breakpad symbol information that supplier should return for
+ // MODULE to INFO.
+ void SetModuleSymbols(MockCodeModule *module, const string &info) {
+ EXPECT_CALL(supplier, GetSymbolFile(module, &system_info, _, _))
+ .WillRepeatedly(DoAll(SetArgumentPointee<3>(info),
+ Return(MockSymbolSupplier::FOUND)));
+ }
+
+ // Populate stack_region with the contents of stack_section. Use
+ // stack_section.start() as the region's starting address.
+ void RegionFromSection() {
+ string contents;
+ ASSERT_TRUE(stack_section.GetContents(&contents));
+ stack_region.Init(stack_section.start().Value(), contents);
+ }
+
+ // Fill RAW_CONTEXT with pseudo-random data, for round-trip checking.
+ void BrandContext(MDRawContextARM *raw_context) {
+ u_int8_t x = 173;
+ for (size_t i = 0; i < sizeof(*raw_context); i++)
+ reinterpret_cast<u_int8_t *>(raw_context)[i] = (x += 17);
+ }
+
+ SystemInfo system_info;
+ MDRawContextARM raw_context;
+ Section stack_section;
+ MockMemoryRegion stack_region;
+ MockCodeModule module1;
+ MockCodeModule module2;
+ MockCodeModules modules;
+ MockSymbolSupplier supplier;
+ BasicSourceLineResolver resolver;
+ CallStack call_stack;
+ const vector<StackFrame *> *frames;
+};
+
+class GetContextFrame: public StackwalkerARMFixture, public Test { };
+
+TEST_F(GetContextFrame, Simple) {
+ // Since we have no call frame information, and all unwinding
+ // requires call frame information, the stack walk will end after
+ // the first frame.
+ StackwalkerARM walker(&system_info, &raw_context, &stack_region, &modules,
+ &supplier, &resolver);
+ ASSERT_TRUE(walker.Walk(&call_stack));
+ frames = call_stack.frames();
+ ASSERT_EQ(1U, frames->size());
+ StackFrameARM *frame = static_cast<StackFrameARM *>(frames->at(0));
+ // Check that the values from the original raw context made it
+ // through to the context in the stack frame.
+ EXPECT_TRUE(memcmp(&raw_context, &frame->context, sizeof(raw_context)) == 0);
+}
+
+struct CFIFixture: public StackwalkerARMFixture {
+ CFIFixture() {
+ // Provide a bunch of STACK CFI records; we'll walk to the caller
+ // from every point in this series, expecting to find the same set
+ // of register values.
+ SetModuleSymbols(&module1,
+ // The youngest frame's function.
+ "FUNC 4000 1000 10 enchiridion\n"
+ // Initially, nothing has been pushed on the stack,
+ // and the return address is still in the link register.
+ "STACK CFI INIT 4000 100 .cfa: sp .ra: lr\n"
+ // Push r4, the frame pointer, and the link register.
+ "STACK CFI 4001 .cfa: sp 12 + r4: .cfa 12 - ^"
+ " r11: .cfa 8 - ^ .ra: .cfa 4 - ^\n"
+ // Save r4..r7 in r0..r3: verify that we populate
+ // the youngest frame with all the values we have.
+ "STACK CFI 4002 r4: r0 r5: r1 r6: r2 r7: r3\n"
+ // Restore r4..r7. Save the non-callee-saves register r1.
+ "STACK CFI 4003 .cfa: sp 16 + r1: .cfa 16 - ^"
+ " r4: r4 r5: r5 r6: r6 r7: r7\n"
+ // Move the .cfa back four bytes, to point at the return
+ // address, and restore the sp explicitly.
+ "STACK CFI 4005 .cfa: sp 12 + r1: .cfa 12 - ^"
+ " r11: .cfa 4 - ^ .ra: .cfa ^ sp: .cfa 4 +\n"
+ // Recover the PC explicitly from a new stack slot;
+ // provide garbage for the .ra.
+ "STACK CFI 4006 .cfa: sp 16 + pc: .cfa 16 - ^\n"
+
+ // The calling function.
+ "FUNC 5000 1000 10 epictetus\n"
+ // Mark it as end of stack.
+ "STACK CFI INIT 5000 1000 .cfa: 0 .ra: 0\n"
+
+ // A function whose CFI makes the stack pointer
+ // go backwards.
+ "FUNC 6000 1000 20 palinal\n"
+ "STACK CFI INIT 6000 1000 .cfa: sp 4 - .ra: lr\n"
+
+ // A function with CFI expressions that can't be
+ // evaluated.
+ "FUNC 7000 1000 20 rhetorical\n"
+ "STACK CFI INIT 7000 1000 .cfa: moot .ra: ambiguous\n");
+
+ // Provide some distinctive values for the caller's registers.
+ expected.iregs[MD_CONTEXT_ARM_REG_PC] = 0x40005510;
+ expected.iregs[MD_CONTEXT_ARM_REG_SP] = 0x80000000;
+ expected.iregs[4] = 0xb5d55e68;
+ expected.iregs[5] = 0xebd134f3;
+ expected.iregs[6] = 0xa31e74bc;
+ expected.iregs[7] = 0x2dcb16b3;
+ expected.iregs[8] = 0x2ada2137;
+ expected.iregs[9] = 0xbbbb557d;
+ expected.iregs[10] = 0x48bf8ca7;
+ expected.iregs[MD_CONTEXT_ARM_REG_FP] = 0x8112e110;
+
+ // Expect CFI to recover all callee-saves registers. Since CFI is the
+ // only stack frame construction technique we have, aside from the
+ // context frame itself, there's no way for us to have a set of valid
+ // registers smaller than this.
+ expected_validity = (StackFrameARM::CONTEXT_VALID_PC |
+ StackFrameARM::CONTEXT_VALID_SP |
+ StackFrameARM::CONTEXT_VALID_R4 |
+ StackFrameARM::CONTEXT_VALID_R5 |
+ StackFrameARM::CONTEXT_VALID_R6 |
+ StackFrameARM::CONTEXT_VALID_R7 |
+ StackFrameARM::CONTEXT_VALID_R8 |
+ StackFrameARM::CONTEXT_VALID_R9 |
+ StackFrameARM::CONTEXT_VALID_R10 |
+ StackFrameARM::CONTEXT_VALID_FP);
+
+ // By default, context frames provide all registers, as normal.
+ context_frame_validity = StackFrameARM::CONTEXT_VALID_ALL;
+
+ // By default, registers are unchanged.
+ raw_context = expected;
+ }
+
+ // Walk the stack, using stack_section as the contents of the stack
+ // and raw_context as the current register values. (Set the stack
+ // pointer to the stack's starting address.) Expect two stack
+ // frames; in the older frame, expect the callee-saves registers to
+ // have values matching those in 'expected'.
+ void CheckWalk() {
+ RegionFromSection();
+ raw_context.iregs[MD_CONTEXT_ARM_REG_SP] = stack_section.start().Value();
+
+ StackwalkerARM walker(&system_info, &raw_context, &stack_region, &modules,
+ &supplier, &resolver);
+ walker.SetContextFrameValidity(context_frame_validity);
+ ASSERT_TRUE(walker.Walk(&call_stack));
+ frames = call_stack.frames();
+ ASSERT_EQ(2U, frames->size());
+
+ StackFrameARM *frame0 = static_cast<StackFrameARM *>(frames->at(0));
+ ASSERT_EQ(context_frame_validity, frame0->context_validity);
+ EXPECT_EQ("enchiridion", frame0->function_name);
+ EXPECT_EQ(0x40004000U, frame0->function_base);
+
+ StackFrameARM *frame1 = static_cast<StackFrameARM *>(frames->at(1));
+ ASSERT_EQ(expected_validity, frame1->context_validity);
+ if (expected_validity & StackFrameARM::CONTEXT_VALID_R1)
+ EXPECT_EQ(expected.iregs[1], frame1->context.iregs[1]);
+ if (expected_validity & StackFrameARM::CONTEXT_VALID_R4)
+ EXPECT_EQ(expected.iregs[4], frame1->context.iregs[4]);
+ if (expected_validity & StackFrameARM::CONTEXT_VALID_R5)
+ EXPECT_EQ(expected.iregs[5], frame1->context.iregs[5]);
+ if (expected_validity & StackFrameARM::CONTEXT_VALID_R6)
+ EXPECT_EQ(expected.iregs[6], frame1->context.iregs[6]);
+ if (expected_validity & StackFrameARM::CONTEXT_VALID_R7)
+ EXPECT_EQ(expected.iregs[7], frame1->context.iregs[7]);
+ if (expected_validity & StackFrameARM::CONTEXT_VALID_R8)
+ EXPECT_EQ(expected.iregs[8], frame1->context.iregs[8]);
+ if (expected_validity & StackFrameARM::CONTEXT_VALID_R9)
+ EXPECT_EQ(expected.iregs[9], frame1->context.iregs[9]);
+ if (expected_validity & StackFrameARM::CONTEXT_VALID_R10)
+ EXPECT_EQ(expected.iregs[10], frame1->context.iregs[10]);
+ if (expected_validity & StackFrameARM::CONTEXT_VALID_FP)
+ EXPECT_EQ(expected.iregs[MD_CONTEXT_ARM_REG_FP],
+ frame1->context.iregs[MD_CONTEXT_ARM_REG_FP]);
+
+ // We would never have gotten a frame in the first place if the SP
+ // and PC weren't valid or ->instruction weren't set.
+ EXPECT_EQ(expected.iregs[MD_CONTEXT_ARM_REG_SP],
+ frame1->context.iregs[MD_CONTEXT_ARM_REG_SP]);
+ EXPECT_EQ(expected.iregs[MD_CONTEXT_ARM_REG_PC],
+ frame1->context.iregs[MD_CONTEXT_ARM_REG_PC]);
+ EXPECT_EQ(expected.iregs[MD_CONTEXT_ARM_REG_PC],
+ frame1->instruction + 1);
+ EXPECT_EQ("epictetus", frame1->function_name);
+ }
+
+ // The values we expect to find for the caller's registers.
+ MDRawContextARM expected;
+
+ // The validity mask for expected.
+ int expected_validity;
+
+ // The validity mask to impose on the context frame.
+ int context_frame_validity;
+};
+
+class CFI: public CFIFixture, public Test { };
+
+TEST_F(CFI, At4000) {
+ stack_section.start() = expected.iregs[MD_CONTEXT_ARM_REG_SP];
+ raw_context.iregs[MD_CONTEXT_ARM_REG_PC] = 0x40004000;
+ raw_context.iregs[MD_CONTEXT_ARM_REG_LR] = 0x40005510;
+ CheckWalk();
+}
+
+TEST_F(CFI, At4001) {
+ Label frame1_sp = expected.iregs[MD_CONTEXT_ARM_REG_SP];
+ stack_section
+ .D32(0xb5d55e68) // saved r4
+ .D32(0x8112e110) // saved fp
+ .D32(0x40005510) // return address
+ .Mark(&frame1_sp); // This effectively sets stack_section.start().
+ raw_context.iregs[MD_CONTEXT_ARM_REG_PC] = 0x40004001;
+ raw_context.iregs[4] = 0x635adc9f; // distinct callee r4
+ raw_context.iregs[MD_CONTEXT_ARM_REG_FP] = 0xbe145fc4; // distinct callee fp
+ CheckWalk();
+}
+
+// As above, but unwind from a context that has only the PC and SP.
+TEST_F(CFI, At4001LimitedValidity) {
+ context_frame_validity =
+ StackFrameARM::CONTEXT_VALID_PC | StackFrameARM::CONTEXT_VALID_SP;
+ raw_context.iregs[MD_CONTEXT_ARM_REG_PC] = 0x40004001;
+ raw_context.iregs[MD_CONTEXT_ARM_REG_FP] = 0xbe145fc4; // distinct callee fp
+ Label frame1_sp = expected.iregs[MD_CONTEXT_ARM_REG_SP];
+ stack_section
+ .D32(0xb5d55e68) // saved r4
+ .D32(0x8112e110) // saved fp
+ .D32(0x40005510) // return address
+ .Mark(&frame1_sp); // This effectively sets stack_section.start().
+ expected_validity = (StackFrameARM::CONTEXT_VALID_PC
+ | StackFrameARM::CONTEXT_VALID_SP
+ | StackFrameARM::CONTEXT_VALID_FP
+ | StackFrameARM::CONTEXT_VALID_R4);
+ CheckWalk();
+}
+
+TEST_F(CFI, At4002) {
+ Label frame1_sp = expected.iregs[MD_CONTEXT_ARM_REG_SP];
+ stack_section
+ .D32(0xfb81ff3d) // no longer saved r4
+ .D32(0x8112e110) // saved fp
+ .D32(0x40005510) // return address
+ .Mark(&frame1_sp); // This effectively sets stack_section.start().
+ raw_context.iregs[MD_CONTEXT_ARM_REG_PC] = 0x40004002;
+ raw_context.iregs[0] = 0xb5d55e68; // saved r4
+ raw_context.iregs[1] = 0xebd134f3; // saved r5
+ raw_context.iregs[2] = 0xa31e74bc; // saved r6
+ raw_context.iregs[3] = 0x2dcb16b3; // saved r7
+ raw_context.iregs[4] = 0xfdd35466; // distinct callee r4
+ raw_context.iregs[5] = 0xf18c946c; // distinct callee r5
+ raw_context.iregs[6] = 0xac2079e8; // distinct callee r6
+ raw_context.iregs[7] = 0xa449829f; // distinct callee r7
+ raw_context.iregs[MD_CONTEXT_ARM_REG_FP] = 0xbe145fc4; // distinct callee fp
+ CheckWalk();
+}
+
+TEST_F(CFI, At4003) {
+ Label frame1_sp = expected.iregs[MD_CONTEXT_ARM_REG_SP];
+ stack_section
+ .D32(0x48c8dd5a) // saved r1 (even though it's not callee-saves)
+ .D32(0xcb78040e) // no longer saved r4
+ .D32(0x8112e110) // saved fp
+ .D32(0x40005510) // return address
+ .Mark(&frame1_sp); // This effectively sets stack_section.start().
+ raw_context.iregs[MD_CONTEXT_ARM_REG_PC] = 0x40004003;
+ raw_context.iregs[1] = 0xfb756319; // distinct callee r1
+ raw_context.iregs[MD_CONTEXT_ARM_REG_FP] = 0x0a2857ea; // distinct callee fp
+ expected.iregs[1] = 0x48c8dd5a; // caller's r1
+ expected_validity |= StackFrameARM::CONTEXT_VALID_R1;
+ CheckWalk();
+}
+
+// We have no new rule at module offset 0x4004, so the results here should
+// be the same as those at module offset 0x4003.
+TEST_F(CFI, At4004) {
+ Label frame1_sp = expected.iregs[MD_CONTEXT_ARM_REG_SP];
+ stack_section
+ .D32(0x48c8dd5a) // saved r1 (even though it's not callee-saves)
+ .D32(0xcb78040e) // no longer saved r4
+ .D32(0x8112e110) // saved fp
+ .D32(0x40005510) // return address
+ .Mark(&frame1_sp); // This effectively sets stack_section.start().
+ raw_context.iregs[MD_CONTEXT_ARM_REG_PC] = 0x40004004;
+ raw_context.iregs[1] = 0xfb756319; // distinct callee r1
+ expected.iregs[1] = 0x48c8dd5a; // caller's r1
+ expected_validity |= StackFrameARM::CONTEXT_VALID_R1;
+ CheckWalk();
+}
+
+// Here we move the .cfa, but provide an explicit rule to recover the SP,
+// so again there should be no change in the registers recovered.
+TEST_F(CFI, At4005) {
+ Label frame1_sp = expected.iregs[MD_CONTEXT_ARM_REG_SP];
+ stack_section
+ .D32(0x48c8dd5a) // saved r1 (even though it's not callee-saves)
+ .D32(0xf013f841) // no longer saved r4
+ .D32(0x8112e110) // saved fp
+ .D32(0x40005510) // return address
+ .Mark(&frame1_sp); // This effectively sets stack_section.start().
+ raw_context.iregs[MD_CONTEXT_ARM_REG_PC] = 0x40004005;
+ raw_context.iregs[1] = 0xfb756319; // distinct callee r1
+ expected.iregs[1] = 0x48c8dd5a; // caller's r1
+ expected_validity |= StackFrameARM::CONTEXT_VALID_R1;
+ CheckWalk();
+}
+
+// Here we provide an explicit rule for the PC, and have the saved .ra be
+// bogus.
+TEST_F(CFI, At4006) {
+ Label frame1_sp = expected.iregs[MD_CONTEXT_ARM_REG_SP];
+ stack_section
+ .D32(0x40005510) // saved pc
+ .D32(0x48c8dd5a) // saved r1 (even though it's not callee-saves)
+ .D32(0xf013f841) // no longer saved r4
+ .D32(0x8112e110) // saved fp
+ .D32(0xf8d15783) // .ra rule recovers this, which is garbage
+ .Mark(&frame1_sp); // This effectively sets stack_section.start().
+ raw_context.iregs[MD_CONTEXT_ARM_REG_PC] = 0x40004006;
+ raw_context.iregs[1] = 0xfb756319; // callee's r1, different from caller's
+ expected.iregs[1] = 0x48c8dd5a; // caller's r1
+ expected_validity |= StackFrameARM::CONTEXT_VALID_R1;
+ CheckWalk();
+}
+
+// Check that we reject rules that would cause the stack pointer to
+// move in the wrong direction.
+TEST_F(CFI, RejectBackwards) {
+ raw_context.iregs[MD_CONTEXT_ARM_REG_PC] = 0x40006000;
+ raw_context.iregs[MD_CONTEXT_ARM_REG_SP] = 0x80000000;
+ raw_context.iregs[MD_CONTEXT_ARM_REG_LR] = 0x40005510;
+ StackwalkerARM walker(&system_info, &raw_context, &stack_region, &modules,
+ &supplier, &resolver);
+ ASSERT_TRUE(walker.Walk(&call_stack));
+ frames = call_stack.frames();
+ ASSERT_EQ(1U, frames->size());
+}
+
+// Check that we reject rules whose expressions' evaluation fails.
+TEST_F(CFI, RejectBadExpressions) {
+ raw_context.iregs[MD_CONTEXT_ARM_REG_PC] = 0x40007000;
+ raw_context.iregs[MD_CONTEXT_ARM_REG_SP] = 0x80000000;
+ StackwalkerARM walker(&system_info, &raw_context, &stack_region, &modules,
+ &supplier, &resolver);
+ ASSERT_TRUE(walker.Walk(&call_stack));
+ frames = call_stack.frames();
+ ASSERT_EQ(1U, frames->size());
+}
+