Index: include/libunwind.h
===================================================================
--- include/libunwind.h	(revision 196555)
+++ include/libunwind.h	(working copy)
@@ -353,4 +353,61 @@
   UNW_ARM64_D31 = 95,
 };
 
+// 32-bit ARM64 registers
+enum {
+  UNW_ARM_R0  = 0,
+  UNW_ARM_R1  = 1,
+  UNW_ARM_R2  = 2,
+  UNW_ARM_R3  = 3,
+  UNW_ARM_R4  = 4,
+  UNW_ARM_R5  = 5,
+  UNW_ARM_R6  = 6,
+  UNW_ARM_R7  = 7,
+  UNW_ARM_R8  = 8,
+  UNW_ARM_R9  = 9,
+  UNW_ARM_R10 = 10,
+  UNW_ARM_R11 = 11,
+  UNW_ARM_R12 = 12,
+  UNW_ARM_SP  = 13,  // Logical alias for UNW_REG_SP
+  UNW_ARM_R13 = 13,
+  UNW_ARM_LR  = 14,
+  UNW_ARM_R14 = 14,
+  UNW_ARM_IP  = 15,  // Logical alias for UNW_REG_IP
+  UNW_ARM_R15 = 15,
+  // reserved block
+  UNW_ARM_D0  = 64,
+  UNW_ARM_D1  = 65,
+  UNW_ARM_D2  = 66,
+  UNW_ARM_D3  = 67,
+  UNW_ARM_D4  = 68,
+  UNW_ARM_D5  = 69,
+  UNW_ARM_D6  = 70,
+  UNW_ARM_D7  = 71,
+  UNW_ARM_D8  = 72,
+  UNW_ARM_D9  = 73,
+  UNW_ARM_D10 = 74,
+  UNW_ARM_D11 = 75,
+  UNW_ARM_D12 = 76,
+  UNW_ARM_D13 = 77,
+  UNW_ARM_D14 = 78,
+  UNW_ARM_D15 = 79,
+  UNW_ARM_D16 = 80,
+  UNW_ARM_D17 = 81,
+  UNW_ARM_D18 = 82,
+  UNW_ARM_D19 = 83,
+  UNW_ARM_D20 = 84,
+  UNW_ARM_D21 = 85,
+  UNW_ARM_D22 = 86,
+  UNW_ARM_D23 = 87,
+  UNW_ARM_D24 = 88,
+  UNW_ARM_D25 = 89,
+  UNW_ARM_D26 = 90,
+  UNW_ARM_D27 = 91,
+  UNW_ARM_D28 = 92,
+  UNW_ARM_D29 = 93,
+  UNW_ARM_D30 = 94,
+  UNW_ARM_D31 = 95,
+  UNW_ARM_MAX_REG,
+};
+
 #endif
Index: src/Unwind/Registers.hpp
===================================================================
--- src/Unwind/Registers.hpp	(revision 196555)
+++ src/Unwind/Registers.hpp	(working copy)
@@ -1273,6 +1273,229 @@
   _LIBUNWIND_ABORT("no arm64 vector register support yet");
 }
 
+/// Registers_arm holds the register state of a thread in a 32-bit arm
+/// process.
+///
+/// NOTE: Assuming cortex (armv7a) or newer with NEON.
+class _LIBUNWIND_HIDDEN Registers_arm {
+public:
+  Registers_arm();
+  Registers_arm(const void *registers);
+
+  bool        validRegister(int num) const;
+  uint32_t    getRegister(int num) const;
+  void        setRegister(int num, uint32_t value);
+  bool        validFloatRegister(int num) const;
+  double      getFloatRegister(int num) const;
+  void        setFloatRegister(int num, double value);
+  bool        validVectorRegister(int num) const;
+  v128        getVectorRegister(int num) const;
+  void        setVectorRegister(int num, v128 value);
+  const char *getRegisterName(int num);
+  void        jumpto();
+
+  uint32_t  getSP() const         { return _registers.__sp; }
+  void      setSP(uint32_t value) { _registers.__sp = value; }
+  uint32_t  getIP() const         { return _registers.__pc; }
+  void      setIP(uint32_t value) { _registers.__pc = value; }
+
+private:
+  struct GPRs {
+    uint32_t __r[13]; // r0-r12
+    uint32_t __sp;    // Stack pointer r13
+    uint32_t __lr;    // Link register r14
+    uint32_t __pc;    // Program counter r15
+  };
+
+  GPRs    _registers;
+  double  _vectorHalfRegisters[32];
+};
+
+inline Registers_arm::Registers_arm(const void *registers) {
+  static_assert(sizeof(Registers_arm) < sizeof(unw_context_t),
+                    "arm registers do not fit into unw_context_t");
+  memcpy(&_registers, registers, sizeof(_registers));
+  // FIXME: Should actually copy floating point registers?
+  bzero(&_vectorHalfRegisters, sizeof(_vectorHalfRegisters));
+}
+
+inline Registers_arm::Registers_arm() {
+  bzero(&_registers, sizeof(_registers));
+  bzero(&_vectorHalfRegisters, sizeof(_vectorHalfRegisters));
+}
+
+inline bool Registers_arm::validRegister(int regNum) const {
+  if (regNum == UNW_REG_IP)
+    return true;
+  if (regNum == UNW_REG_SP)
+    return true;
+  if (regNum < 0)
+    return false;
+  if (regNum >= UNW_ARM_MAX_REG)
+    return false;
+  // Above general purpose reg, but under neon reg indexes.
+  if ((regNum > UNW_ARM_R15) && (regNum < UNW_ARM_D0))
+    return false;
+  // FIXME Shouldn't this also fail for ranges UNW_ARM_D0 to UNW_ARM_D31?
+  return true;
+}
+
+inline uint32_t Registers_arm::getRegister(int regNum) const {
+  if (regNum == UNW_REG_SP || regNum == UNW_ARM_SP)
+    return _registers.__sp;
+  if (regNum == UNW_ARM_LR)
+    return _registers.__lr;
+  if (regNum == UNW_REG_IP || regNum == UNW_ARM_IP)
+    return _registers.__pc;
+  if ((regNum >= UNW_ARM_R0) && (regNum <= UNW_ARM_R12))
+    return _registers.__r[regNum];
+  _LIBUNWIND_ABORT("unsupported arm register");
+}
+
+inline void Registers_arm::setRegister(int regNum, uint32_t value) {
+  if (regNum == UNW_REG_SP || regNum == UNW_ARM_SP)
+    _registers.__sp = value;
+  else if (regNum == UNW_ARM_LR)
+    _registers.__lr = value;
+  else if (regNum == UNW_REG_IP || regNum == UNW_ARM_IP)
+    _registers.__pc = value;
+  else if ((regNum >= UNW_ARM_R0) && (regNum <= UNW_ARM_R12))
+    _registers.__r[regNum] = value;
+  else
+    _LIBUNWIND_ABORT("unsupported arm register");
+}
+
+inline const char *Registers_arm::getRegisterName(int regNum) {
+  switch (regNum) {
+  case UNW_REG_IP:
+  case UNW_ARM_IP: // UNW_ARM_R15 is alias
+    return "pc";
+  case UNW_ARM_LR: // UNW_ARM_R14 is alias
+    return "lr";
+  case UNW_REG_SP:
+  case UNW_ARM_SP: // UNW_ARM_R13 is alias
+    return "sp";
+  case UNW_ARM_R0:
+    return "r0";
+  case UNW_ARM_R1:
+    return "r1";
+  case UNW_ARM_R2:
+    return "r2";
+  case UNW_ARM_R3:
+    return "r3";
+  case UNW_ARM_R4:
+    return "r4";
+  case UNW_ARM_R5:
+    return "r5";
+  case UNW_ARM_R6:
+    return "r6";
+  case UNW_ARM_R7:
+    return "r7";
+  case UNW_ARM_R8:
+    return "r8";
+  case UNW_ARM_R9:
+    return "r9";
+  case UNW_ARM_R10:
+    return "r10";
+  case UNW_ARM_R11:
+    return "r11";
+  case UNW_ARM_R12:
+    return "r12";
+  case UNW_ARM_D0:
+    return "d0";
+  case UNW_ARM_D1:
+    return "d1";
+  case UNW_ARM_D2:
+    return "d2";
+  case UNW_ARM_D3:
+    return "d3";
+  case UNW_ARM_D4:
+    return "d4";
+  case UNW_ARM_D5:
+    return "d5";
+  case UNW_ARM_D6:
+    return "d6";
+  case UNW_ARM_D7:
+    return "d7";
+  case UNW_ARM_D8:
+    return "d8";
+  case UNW_ARM_D9:
+    return "d9";
+  case UNW_ARM_D10:
+    return "d10";
+  case UNW_ARM_D11:
+    return "d11";
+  case UNW_ARM_D12:
+    return "d12";
+  case UNW_ARM_D13:
+    return "d13";
+  case UNW_ARM_D14:
+    return "d14";
+  case UNW_ARM_D15:
+    return "d15";
+  case UNW_ARM_D16:
+    return "d16";
+  case UNW_ARM_D17:
+    return "d17";
+  case UNW_ARM_D18:
+    return "d18";
+  case UNW_ARM_D19:
+    return "d19";
+  case UNW_ARM_D20:
+    return "d20";
+  case UNW_ARM_D21:
+    return "d21";
+  case UNW_ARM_D22:
+    return "d22";
+  case UNW_ARM_D23:
+    return "d23";
+  case UNW_ARM_D24:
+    return "d24";
+  case UNW_ARM_D25:
+    return "d25";
+  case UNW_ARM_D26:
+    return "d26";
+  case UNW_ARM_D27:
+    return "d27";
+  case UNW_ARM_D28:
+    return "d28";
+  case UNW_ARM_D29:
+    return "d29";
+  case UNW_ARM_D30:
+    return "d30";
+  case UNW_ARM_D31:
+    return "d31";
+  default:
+    return "unknown register";
+  }
+}
+
+inline bool Registers_arm::validFloatRegister(int regNum) const {
+  return ((regNum >= UNW_ARM_D0) && (regNum <= UNW_ARM_D31));
+}
+
+inline double Registers_arm::getFloatRegister(int regNum) const {
+  assert(validFloatRegister(regNum));
+  return _vectorHalfRegisters[regNum - UNW_ARM_D0];
+}
+
+inline void Registers_arm::setFloatRegister(int regNum, double value) {
+  assert(validFloatRegister(regNum));
+  _vectorHalfRegisters[regNum - UNW_ARM_D0] = value;
+}
+
+inline bool Registers_arm::validVectorRegister(int) const {
+  return false;
+}
+
+inline v128 Registers_arm::getVectorRegister(int) const {
+  _LIBUNWIND_ABORT("ARM vector support not implemented");
+}
+
+inline void Registers_arm::setVectorRegister(int, v128) {
+  _LIBUNWIND_ABORT("ARM vector support not implemented");
+}
+
 } // namespace libunwind
 
 #endif // __REGISTERS_HPP__
Index: src/Unwind/UnwindRegistersRestore.S
===================================================================
--- src/Unwind/UnwindRegistersRestore.S	(revision 196555)
+++ src/Unwind/UnwindRegistersRestore.S	(working copy)
@@ -316,8 +316,27 @@
   ldp    x0, x1,  [x0, #0x000]  ; restore x0,x1
   ret    lr            ; jump to pc
 
+#elif __arm__
 
+  .text
+  .globl _ZN9libunwind13Registers_arm6jumptoEv
+  .hidden _ZN9libunwind13Registers_arm6jumptoEv
+@
+@ void libunwind::Registers_arm::jumpto()
+@
+@ On entry:
+@  thread_state pointer is in r0
+@
+_ZN9libunwind13Registers_arm6jumptoEv:
+  @ Use lr as base so that r0 can be restored.
+  mov lr, r0
+  @ 32bit thumb-2 restrictions for ldm:
+  @ . the sp (r13) cannot be in the list
+  @ . the pc (r15) and lr (r14) cannot both be in the list in an LDM instruction
+  ldm lr, {r0-r12}
+  ldr sp, [lr, #52]
+  ldr lr, [lr, #60]  @ restore pc into lr
+  mov pc, lr
 
-
 #endif
 
Index: src/Unwind/UnwindRegistersSave.S
===================================================================
--- src/Unwind/UnwindRegistersSave.S	(revision 196555)
+++ src/Unwind/UnwindRegistersSave.S	(working copy)
@@ -282,5 +282,26 @@
   ldr    x0, #0      ; return UNW_ESUCCESS
   ret
 
+#elif __arm__ && !__APPLE__
+
+@
+@ extern int unw_getcontext(unw_context_t* thread_state)
+@
+@ On entry:
+@  thread_state pointer is in r0
+@
+  .globl unw_getcontext
+unw_getcontext:
+  @ 32bit thumb-2 restrictions for stm:
+  @ . the sp (r13) cannot be in the list
+  @ . the pc (r15) cannot be in the list in an STM instruction
+  stm r0, {r0-r12}
+  str sp, [r0, #52]
+  str lr, [r0, #56]
+  str lr, [r0, #60]  @ store return address as pc
+  mov r0, #0      @ return UNW_ESUCCESS
+  mov pc, lr
+  @ FIXME: VFP registers
+
 #endif
 
