summaryrefslogtreecommitdiff
path: root/libunwind
diff options
context:
space:
mode:
authorJohn Baldwin <jhb@FreeBSD.org>2017-12-12 21:43:36 +0000
committerJohn Baldwin <jhb@FreeBSD.org>2017-12-12 21:43:36 +0000
commite7eb254f8004388fecba7c26c4a071ee97897cf2 (patch)
tree70c44916c28c741103c1aede57de0fbe07aa637f /libunwind
parent708c3d970cffe7ae79da634714374f89549372a2 (diff)
[libunwind][MIPS]: Add support for unwinding in O32 and N64 processes.
This supports the soft-float ABI only and has been tested with both clang and gcc on FreeBSD. Reviewed By: sdardis, compnerd Differential Revision: https://reviews.llvm.org/D38110
Diffstat (limited to 'libunwind')
-rw-r--r--libunwind/include/__libunwind_config.h16
-rw-r--r--libunwind/include/libunwind.h38
-rw-r--r--libunwind/src/Registers.hpp412
-rw-r--r--libunwind/src/UnwindCursor.hpp36
-rw-r--r--libunwind/src/UnwindRegistersRestore.S112
-rw-r--r--libunwind/src/UnwindRegistersSave.S112
-rw-r--r--libunwind/src/config.h2
-rw-r--r--libunwind/src/libunwind.cpp6
8 files changed, 732 insertions, 2 deletions
diff --git a/libunwind/include/__libunwind_config.h b/libunwind/include/__libunwind_config.h
index 5e3cf274242..69a49961a6e 100644
--- a/libunwind/include/__libunwind_config.h
+++ b/libunwind/include/__libunwind_config.h
@@ -21,6 +21,7 @@
#define _LIBUNWIND_HIGHEST_DWARF_REGISTER_ARM64 95
#define _LIBUNWIND_HIGHEST_DWARF_REGISTER_ARM 287
#define _LIBUNWIND_HIGHEST_DWARF_REGISTER_OR1K 31
+#define _LIBUNWIND_HIGHEST_DWARF_REGISTER_MIPS 65
#if defined(_LIBUNWIND_IS_NATIVE_ONLY)
# if defined(__i386__)
@@ -63,6 +64,19 @@
# define _LIBUNWIND_CONTEXT_SIZE 16
# define _LIBUNWIND_CURSOR_SIZE 24
# define _LIBUNWIND_HIGHEST_DWARF_REGISTER _LIBUNWIND_HIGHEST_DWARF_REGISTER_OR1K
+# elif defined(__mips__)
+# if defined(_ABIO32) && defined(__mips_soft_float)
+# define _LIBUNWIND_TARGET_MIPS_O32 1
+# define _LIBUNWIND_CONTEXT_SIZE 18
+# define _LIBUNWIND_CURSOR_SIZE 24
+# elif defined(_ABI64) && defined(__mips_soft_float)
+# define _LIBUNWIND_TARGET_MIPS_N64 1
+# define _LIBUNWIND_CONTEXT_SIZE 35
+# define _LIBUNWIND_CURSOR_SIZE 47
+# else
+# error "Unsupported MIPS ABI and/or environment"
+# endif
+# define _LIBUNWIND_HIGHEST_DWARF_REGISTER _LIBUNWIND_HIGHEST_DWARF_REGISTER_MIPS
# else
# error "Unsupported architecture."
# endif
@@ -73,6 +87,8 @@
# define _LIBUNWIND_TARGET_AARCH64 1
# define _LIBUNWIND_TARGET_ARM 1
# define _LIBUNWIND_TARGET_OR1K 1
+# define _LIBUNWIND_TARGET_MIPS_O32 1
+# define _LIBUNWIND_TARGET_MIPS_N64 1
# define _LIBUNWIND_CONTEXT_SIZE 128
# define _LIBUNWIND_CURSOR_SIZE 140
# define _LIBUNWIND_HIGHEST_DWARF_REGISTER 287
diff --git a/libunwind/include/libunwind.h b/libunwind/include/libunwind.h
index ece30976aba..9011d55460c 100644
--- a/libunwind/include/libunwind.h
+++ b/libunwind/include/libunwind.h
@@ -563,4 +563,42 @@ enum {
UNW_OR1K_R31 = 31,
};
+// MIPS registers
+enum {
+ UNW_MIPS_R0 = 0,
+ UNW_MIPS_R1 = 1,
+ UNW_MIPS_R2 = 2,
+ UNW_MIPS_R3 = 3,
+ UNW_MIPS_R4 = 4,
+ UNW_MIPS_R5 = 5,
+ UNW_MIPS_R6 = 6,
+ UNW_MIPS_R7 = 7,
+ UNW_MIPS_R8 = 8,
+ UNW_MIPS_R9 = 9,
+ UNW_MIPS_R10 = 10,
+ UNW_MIPS_R11 = 11,
+ UNW_MIPS_R12 = 12,
+ UNW_MIPS_R13 = 13,
+ UNW_MIPS_R14 = 14,
+ UNW_MIPS_R15 = 15,
+ UNW_MIPS_R16 = 16,
+ UNW_MIPS_R17 = 17,
+ UNW_MIPS_R18 = 18,
+ UNW_MIPS_R19 = 19,
+ UNW_MIPS_R20 = 20,
+ UNW_MIPS_R21 = 21,
+ UNW_MIPS_R22 = 22,
+ UNW_MIPS_R23 = 23,
+ UNW_MIPS_R24 = 24,
+ UNW_MIPS_R25 = 25,
+ UNW_MIPS_R26 = 26,
+ UNW_MIPS_R27 = 27,
+ UNW_MIPS_R28 = 28,
+ UNW_MIPS_R29 = 29,
+ UNW_MIPS_R30 = 30,
+ UNW_MIPS_R31 = 31,
+ UNW_MIPS_HI = 64,
+ UNW_MIPS_LO = 65,
+};
+
#endif
diff --git a/libunwind/src/Registers.hpp b/libunwind/src/Registers.hpp
index bc25675e0d6..d881e200782 100644
--- a/libunwind/src/Registers.hpp
+++ b/libunwind/src/Registers.hpp
@@ -2041,6 +2041,418 @@ inline const char *Registers_or1k::getRegisterName(int regNum) {
}
#endif // _LIBUNWIND_TARGET_OR1K
+
+#if defined(_LIBUNWIND_TARGET_MIPS_O32)
+/// Registers_mips_o32 holds the register state of a thread in a 32-bit MIPS
+/// process.
+class _LIBUNWIND_HIDDEN Registers_mips_o32 {
+public:
+ Registers_mips_o32();
+ Registers_mips_o32(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();
+ static int lastDwarfRegNum() { return _LIBUNWIND_HIGHEST_DWARF_REGISTER_MIPS; }
+
+ uint32_t getSP() const { return _registers.__r[29]; }
+ void setSP(uint32_t value) { _registers.__r[29] = value; }
+ uint32_t getIP() const { return _registers.__pc; }
+ void setIP(uint32_t value) { _registers.__pc = value; }
+
+private:
+ struct mips_o32_thread_state_t {
+ uint32_t __r[32];
+ uint32_t __pc;
+ uint32_t __hi;
+ uint32_t __lo;
+ };
+
+ mips_o32_thread_state_t _registers;
+};
+
+inline Registers_mips_o32::Registers_mips_o32(const void *registers) {
+ static_assert((check_fit<Registers_mips_o32, unw_context_t>::does_fit),
+ "mips_o32 registers do not fit into unw_context_t");
+ memcpy(&_registers, static_cast<const uint8_t *>(registers),
+ sizeof(_registers));
+}
+
+inline Registers_mips_o32::Registers_mips_o32() {
+ memset(&_registers, 0, sizeof(_registers));
+}
+
+inline bool Registers_mips_o32::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_MIPS_R31)
+ return true;
+ if (regNum == UNW_MIPS_HI)
+ return true;
+ if (regNum == UNW_MIPS_LO)
+ return true;
+ // FIXME: Hard float, DSP accumulator registers, MSA registers
+ return false;
+}
+
+inline uint32_t Registers_mips_o32::getRegister(int regNum) const {
+ if (regNum >= UNW_MIPS_R0 && regNum <= UNW_MIPS_R31)
+ return _registers.__r[regNum - UNW_MIPS_R0];
+
+ switch (regNum) {
+ case UNW_REG_IP:
+ return _registers.__pc;
+ case UNW_REG_SP:
+ return _registers.__r[29];
+ case UNW_MIPS_HI:
+ return _registers.__hi;
+ case UNW_MIPS_LO:
+ return _registers.__lo;
+ }
+ _LIBUNWIND_ABORT("unsupported mips_o32 register");
+}
+
+inline void Registers_mips_o32::setRegister(int regNum, uint32_t value) {
+ if (regNum >= UNW_MIPS_R0 && regNum <= UNW_MIPS_R31) {
+ _registers.__r[regNum - UNW_MIPS_R0] = value;
+ return;
+ }
+
+ switch (regNum) {
+ case UNW_REG_IP:
+ _registers.__pc = value;
+ return;
+ case UNW_REG_SP:
+ _registers.__r[29] = value;
+ return;
+ case UNW_MIPS_HI:
+ _registers.__hi = value;
+ return;
+ case UNW_MIPS_LO:
+ _registers.__lo = value;
+ return;
+ }
+ _LIBUNWIND_ABORT("unsupported mips_o32 register");
+}
+
+inline bool Registers_mips_o32::validFloatRegister(int /* regNum */) const {
+ return false;
+}
+
+inline double Registers_mips_o32::getFloatRegister(int /* regNum */) const {
+ _LIBUNWIND_ABORT("mips_o32 float support not implemented");
+}
+
+inline void Registers_mips_o32::setFloatRegister(int /* regNum */,
+ double /* value */) {
+ _LIBUNWIND_ABORT("mips_o32 float support not implemented");
+}
+
+inline bool Registers_mips_o32::validVectorRegister(int /* regNum */) const {
+ return false;
+}
+
+inline v128 Registers_mips_o32::getVectorRegister(int /* regNum */) const {
+ _LIBUNWIND_ABORT("mips_o32 vector support not implemented");
+}
+
+inline void Registers_mips_o32::setVectorRegister(int /* regNum */, v128 /* value */) {
+ _LIBUNWIND_ABORT("mips_o32 vector support not implemented");
+}
+
+inline const char *Registers_mips_o32::getRegisterName(int regNum) {
+ switch (regNum) {
+ case UNW_MIPS_R0:
+ return "$0";
+ case UNW_MIPS_R1:
+ return "$1";
+ case UNW_MIPS_R2:
+ return "$2";
+ case UNW_MIPS_R3:
+ return "$3";
+ case UNW_MIPS_R4:
+ return "$4";
+ case UNW_MIPS_R5:
+ return "$5";
+ case UNW_MIPS_R6:
+ return "$6";
+ case UNW_MIPS_R7:
+ return "$7";
+ case UNW_MIPS_R8:
+ return "$8";
+ case UNW_MIPS_R9:
+ return "$9";
+ case UNW_MIPS_R10:
+ return "$10";
+ case UNW_MIPS_R11:
+ return "$11";
+ case UNW_MIPS_R12:
+ return "$12";
+ case UNW_MIPS_R13:
+ return "$13";
+ case UNW_MIPS_R14:
+ return "$14";
+ case UNW_MIPS_R15:
+ return "$15";
+ case UNW_MIPS_R16:
+ return "$16";
+ case UNW_MIPS_R17:
+ return "$17";
+ case UNW_MIPS_R18:
+ return "$18";
+ case UNW_MIPS_R19:
+ return "$19";
+ case UNW_MIPS_R20:
+ return "$20";
+ case UNW_MIPS_R21:
+ return "$21";
+ case UNW_MIPS_R22:
+ return "$22";
+ case UNW_MIPS_R23:
+ return "$23";
+ case UNW_MIPS_R24:
+ return "$24";
+ case UNW_MIPS_R25:
+ return "$25";
+ case UNW_MIPS_R26:
+ return "$26";
+ case UNW_MIPS_R27:
+ return "$27";
+ case UNW_MIPS_R28:
+ return "$28";
+ case UNW_MIPS_R29:
+ return "$29";
+ case UNW_MIPS_R30:
+ return "$30";
+ case UNW_MIPS_R31:
+ return "$31";
+ case UNW_MIPS_HI:
+ return "$hi";
+ case UNW_MIPS_LO:
+ return "$lo";
+ default:
+ return "unknown register";
+ }
+}
+#endif // _LIBUNWIND_TARGET_MIPS_O32
+
+#if defined(_LIBUNWIND_TARGET_MIPS_N64)
+/// Registers_mips_n64 holds the register state of a thread in a 64-bit MIPS
+/// process.
+class _LIBUNWIND_HIDDEN Registers_mips_n64 {
+public:
+ Registers_mips_n64();
+ Registers_mips_n64(const void *registers);
+
+ bool validRegister(int num) const;
+ uint64_t getRegister(int num) const;
+ void setRegister(int num, uint64_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();
+ static int lastDwarfRegNum() { return _LIBUNWIND_HIGHEST_DWARF_REGISTER_MIPS; }
+
+ uint64_t getSP() const { return _registers.__r[29]; }
+ void setSP(uint64_t value) { _registers.__r[29] = value; }
+ uint64_t getIP() const { return _registers.__pc; }
+ void setIP(uint64_t value) { _registers.__pc = value; }
+
+private:
+ struct mips_n64_thread_state_t {
+ uint64_t __r[32];
+ uint64_t __pc;
+ uint64_t __hi;
+ uint64_t __lo;
+ };
+
+ mips_n64_thread_state_t _registers;
+};
+
+inline Registers_mips_n64::Registers_mips_n64(const void *registers) {
+ static_assert((check_fit<Registers_mips_n64, unw_context_t>::does_fit),
+ "mips_n64 registers do not fit into unw_context_t");
+ memcpy(&_registers, static_cast<const uint8_t *>(registers),
+ sizeof(_registers));
+}
+
+inline Registers_mips_n64::Registers_mips_n64() {
+ memset(&_registers, 0, sizeof(_registers));
+}
+
+inline bool Registers_mips_n64::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_MIPS_R31)
+ return true;
+ if (regNum == UNW_MIPS_HI)
+ return true;
+ if (regNum == UNW_MIPS_LO)
+ return true;
+ // FIXME: Hard float, DSP accumulator registers, MSA registers
+ return false;
+}
+
+inline uint64_t Registers_mips_n64::getRegister(int regNum) const {
+ if (regNum >= UNW_MIPS_R0 && regNum <= UNW_MIPS_R31)
+ return _registers.__r[regNum - UNW_MIPS_R0];
+
+ switch (regNum) {
+ case UNW_REG_IP:
+ return _registers.__pc;
+ case UNW_REG_SP:
+ return _registers.__r[29];
+ case UNW_MIPS_HI:
+ return _registers.__hi;
+ case UNW_MIPS_LO:
+ return _registers.__lo;
+ }
+ _LIBUNWIND_ABORT("unsupported mips_n64 register");
+}
+
+inline void Registers_mips_n64::setRegister(int regNum, uint64_t value) {
+ if (regNum >= UNW_MIPS_R0 && regNum <= UNW_MIPS_R31) {
+ _registers.__r[regNum - UNW_MIPS_R0] = value;
+ return;
+ }
+
+ switch (regNum) {
+ case UNW_REG_IP:
+ _registers.__pc = value;
+ return;
+ case UNW_REG_SP:
+ _registers.__r[29] = value;
+ return;
+ case UNW_MIPS_HI:
+ _registers.__hi = value;
+ return;
+ case UNW_MIPS_LO:
+ _registers.__lo = value;
+ return;
+ }
+ _LIBUNWIND_ABORT("unsupported mips_n64 register");
+}
+
+inline bool Registers_mips_n64::validFloatRegister(int /* regNum */) const {
+ return false;
+}
+
+inline double Registers_mips_n64::getFloatRegister(int /* regNum */) const {
+ _LIBUNWIND_ABORT("mips_n64 float support not implemented");
+}
+
+inline void Registers_mips_n64::setFloatRegister(int /* regNum */,
+ double /* value */) {
+ _LIBUNWIND_ABORT("mips_n64 float support not implemented");
+}
+
+inline bool Registers_mips_n64::validVectorRegister(int /* regNum */) const {
+ return false;
+}
+
+inline v128 Registers_mips_n64::getVectorRegister(int /* regNum */) const {
+ _LIBUNWIND_ABORT("mips_n64 vector support not implemented");
+}
+
+inline void Registers_mips_n64::setVectorRegister(int /* regNum */, v128 /* value */) {
+ _LIBUNWIND_ABORT("mips_n64 vector support not implemented");
+}
+
+inline const char *Registers_mips_n64::getRegisterName(int regNum) {
+ switch (regNum) {
+ case UNW_MIPS_R0:
+ return "$0";
+ case UNW_MIPS_R1:
+ return "$1";
+ case UNW_MIPS_R2:
+ return "$2";
+ case UNW_MIPS_R3:
+ return "$3";
+ case UNW_MIPS_R4:
+ return "$4";
+ case UNW_MIPS_R5:
+ return "$5";
+ case UNW_MIPS_R6:
+ return "$6";
+ case UNW_MIPS_R7:
+ return "$7";
+ case UNW_MIPS_R8:
+ return "$8";
+ case UNW_MIPS_R9:
+ return "$9";
+ case UNW_MIPS_R10:
+ return "$10";
+ case UNW_MIPS_R11:
+ return "$11";
+ case UNW_MIPS_R12:
+ return "$12";
+ case UNW_MIPS_R13:
+ return "$13";
+ case UNW_MIPS_R14:
+ return "$14";
+ case UNW_MIPS_R15:
+ return "$15";
+ case UNW_MIPS_R16:
+ return "$16";
+ case UNW_MIPS_R17:
+ return "$17";
+ case UNW_MIPS_R18:
+ return "$18";
+ case UNW_MIPS_R19:
+ return "$19";
+ case UNW_MIPS_R20:
+ return "$20";
+ case UNW_MIPS_R21:
+ return "$21";
+ case UNW_MIPS_R22:
+ return "$22";
+ case UNW_MIPS_R23:
+ return "$23";
+ case UNW_MIPS_R24:
+ return "$24";
+ case UNW_MIPS_R25:
+ return "$25";
+ case UNW_MIPS_R26:
+ return "$26";
+ case UNW_MIPS_R27:
+ return "$27";
+ case UNW_MIPS_R28:
+ return "$28";
+ case UNW_MIPS_R29:
+ return "$29";
+ case UNW_MIPS_R30:
+ return "$30";
+ case UNW_MIPS_R31:
+ return "$31";
+ case UNW_MIPS_HI:
+ return "$hi";
+ case UNW_MIPS_LO:
+ return "$lo";
+ default:
+ return "unknown register";
+ }
+}
+#endif // _LIBUNWIND_TARGET_MIPS_N64
} // namespace libunwind
#endif // __REGISTERS_HPP__
diff --git a/libunwind/src/UnwindCursor.hpp b/libunwind/src/UnwindCursor.hpp
index b2793da27ac..f09bd2347af 100644
--- a/libunwind/src/UnwindCursor.hpp
+++ b/libunwind/src/UnwindCursor.hpp
@@ -508,6 +508,18 @@ private:
}
#endif
+#if defined(_LIBUNWIND_TARGET_MIPS_O32)
+ int stepWithCompactEncoding(Registers_mips_o32 &) {
+ return UNW_EINVAL;
+ }
+#endif
+
+#if defined(_LIBUNWIND_TARGET_MIPS_N64)
+ int stepWithCompactEncoding(Registers_mips_n64 &) {
+ return UNW_EINVAL;
+ }
+#endif
+
bool compactSaysUseDwarf(uint32_t *offset=NULL) const {
R dummy;
return compactSaysUseDwarf(dummy, offset);
@@ -551,6 +563,18 @@ private:
return false;
}
#endif
+
+#if defined(_LIBUNWIND_TARGET_MIPS_O32)
+ bool compactSaysUseDwarf(Registers_mips_o32 &, uint32_t *) const {
+ return true;
+ }
+#endif
+
+#if defined(_LIBUNWIND_TARGET_MIPS_N64)
+ bool compactSaysUseDwarf(Registers_mips_n64 &, uint32_t *) const {
+ return true;
+ }
+#endif
#endif // defined(_LIBUNWIND_SUPPORT_COMPACT_UNWIND)
#if defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND)
@@ -594,6 +618,18 @@ private:
return 0;
}
#endif
+
+#if defined (_LIBUNWIND_TARGET_MIPS_O32)
+ compact_unwind_encoding_t dwarfEncoding(Registers_mips_o32 &) const {
+ return 0;
+ }
+#endif
+
+#if defined (_LIBUNWIND_TARGET_MIPS_N64)
+ compact_unwind_encoding_t dwarfEncoding(Registers_mips_n64 &) const {
+ return 0;
+ }
+#endif
#endif // defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND)
diff --git a/libunwind/src/UnwindRegistersRestore.S b/libunwind/src/UnwindRegistersRestore.S
index bb451f39ce1..bd797f62914 100644
--- a/libunwind/src/UnwindRegistersRestore.S
+++ b/libunwind/src/UnwindRegistersRestore.S
@@ -534,6 +534,118 @@ DEFINE_LIBUNWIND_PRIVATE_FUNCTION(_ZN9libunwind14Registers_or1k6jumptoEv)
l.jr r9
l.nop
+#elif defined(__mips__) && defined(_ABIO32) && defined(__mips_soft_float)
+
+//
+// void libunwind::Registers_mips_o32::jumpto()
+//
+// On entry:
+// thread state pointer is in a0 ($4)
+//
+DEFINE_LIBUNWIND_PRIVATE_FUNCTION(_ZN9libunwind18Registers_mips_o326jumptoEv)
+ .set push
+ .set noat
+ .set noreorder
+ .set nomacro
+ // restore hi and lo
+ lw $8, (4 * 33)($4)
+ mthi $8
+ lw $8, (4 * 34)($4)
+ mtlo $8
+ // r0 is zero
+ lw $1, (4 * 1)($4)
+ lw $2, (4 * 2)($4)
+ lw $3, (4 * 3)($4)
+ // skip a0 for now
+ lw $5, (4 * 5)($4)
+ lw $6, (4 * 6)($4)
+ lw $7, (4 * 7)($4)
+ lw $8, (4 * 8)($4)
+ lw $9, (4 * 9)($4)
+ lw $10, (4 * 10)($4)
+ lw $11, (4 * 11)($4)
+ lw $12, (4 * 12)($4)
+ lw $13, (4 * 13)($4)
+ lw $14, (4 * 14)($4)
+ lw $15, (4 * 15)($4)
+ lw $16, (4 * 16)($4)
+ lw $17, (4 * 17)($4)
+ lw $18, (4 * 18)($4)
+ lw $19, (4 * 19)($4)
+ lw $20, (4 * 20)($4)
+ lw $21, (4 * 21)($4)
+ lw $22, (4 * 22)($4)
+ lw $23, (4 * 23)($4)
+ lw $24, (4 * 24)($4)
+ lw $25, (4 * 25)($4)
+ lw $26, (4 * 26)($4)
+ lw $27, (4 * 27)($4)
+ lw $28, (4 * 28)($4)
+ lw $29, (4 * 29)($4)
+ lw $30, (4 * 30)($4)
+ // load new pc into ra
+ lw $31, (4 * 32)($4)
+ // jump to ra, load a0 in the delay slot
+ jr $31
+ lw $4, (4 * 4)($4)
+ .set pop
+
+#elif defined(__mips__) && defined(_ABI64) && defined(__mips_soft_float)
+
+//
+// void libunwind::Registers_mips_n64::jumpto()
+//
+// On entry:
+// thread state pointer is in a0 ($4)
+//
+DEFINE_LIBUNWIND_PRIVATE_FUNCTION(_ZN9libunwind18Registers_mips_n646jumptoEv)
+ .set push
+ .set noat
+ .set noreorder
+ .set nomacro
+ // restore hi and lo
+ ld $8, (8 * 33)($4)
+ mthi $8
+ ld $8, (8 * 34)($4)
+ mtlo $8
+ // r0 is zero
+ ld $1, (8 * 1)($4)
+ ld $2, (8 * 2)($4)
+ ld $3, (8 * 3)($4)
+ // skip a0 for now
+ ld $5, (8 * 5)($4)
+ ld $6, (8 * 6)($4)
+ ld $7, (8 * 7)($4)
+ ld $8, (8 * 8)($4)
+ ld $9, (8 * 9)($4)
+ ld $10, (8 * 10)($4)
+ ld $11, (8 * 11)($4)
+ ld $12, (8 * 12)($4)
+ ld $13, (8 * 13)($4)
+ ld $14, (8 * 14)($4)
+ ld $15, (8 * 15)($4)
+ ld $16, (8 * 16)($4)
+ ld $17, (8 * 17)($4)
+ ld $18, (8 * 18)($4)
+ ld $19, (8 * 19)($4)
+ ld $20, (8 * 20)($4)
+ ld $21, (8 * 21)($4)
+ ld $22, (8 * 22)($4)
+ ld $23, (8 * 23)($4)
+ ld $24, (8 * 24)($4)
+ ld $25, (8 * 25)($4)
+ ld $26, (8 * 26)($4)
+ ld $27, (8 * 27)($4)
+ ld $28, (8 * 28)($4)
+ ld $29, (8 * 29)($4)
+ ld $30, (8 * 30)($4)
+ // load new pc into ra
+ ld $31, (8 * 32)($4)
+ // jump to ra, load a0 in the delay slot
+ jr $31
+ ld $4, (8 * 4)($4)
+ .set pop
+
#endif
#endif /* !defined(__USING_SJLJ_EXCEPTIONS__) */
diff --git a/libunwind/src/UnwindRegistersSave.S b/libunwind/src/UnwindRegistersSave.S
index 069f6c039c0..4583f505b29 100644
--- a/libunwind/src/UnwindRegistersSave.S
+++ b/libunwind/src/UnwindRegistersSave.S
@@ -116,6 +116,118 @@ DEFINE_LIBUNWIND_FUNCTION(unw_getcontext)
xorl %eax, %eax # return UNW_ESUCCESS
ret
+#elif defined(__mips__) && defined(_ABIO32) && defined(__mips_soft_float)
+
+#
+# extern int unw_getcontext(unw_context_t* thread_state)
+#
+# On entry:
+# thread_state pointer is in a0 ($4)
+#
+DEFINE_LIBUNWIND_FUNCTION(unw_getcontext)
+ .set push
+ .set noat
+ .set noreorder
+ .set nomacro
+ sw $1, (4 * 1)($4)
+ sw $2, (4 * 2)($4)
+ sw $3, (4 * 3)($4)
+ sw $4, (4 * 4)($4)
+ sw $5, (4 * 5)($4)
+ sw $6, (4 * 6)($4)
+ sw $7, (4 * 7)($4)
+ sw $8, (4 * 8)($4)
+ sw $9, (4 * 9)($4)
+ sw $10, (4 * 10)($4)
+ sw $11, (4 * 11)($4)
+ sw $12, (4 * 12)($4)
+ sw $13, (4 * 13)($4)
+ sw $14, (4 * 14)($4)
+ sw $15, (4 * 15)($4)
+ sw $16, (4 * 16)($4)
+ sw $17, (4 * 17)($4)
+ sw $18, (4 * 18)($4)
+ sw $19, (4 * 19)($4)
+ sw $20, (4 * 20)($4)
+ sw $21, (4 * 21)($4)
+ sw $22, (4 * 22)($4)
+ sw $23, (4 * 23)($4)
+ sw $24, (4 * 24)($4)
+ sw $25, (4 * 25)($4)
+ sw $26, (4 * 26)($4)
+ sw $27, (4 * 27)($4)
+ sw $28, (4 * 28)($4)
+ sw $29, (4 * 29)($4)
+ sw $30, (4 * 30)($4)
+ sw $31, (4 * 31)($4)
+ # Store return address to pc
+ sw $31, (4 * 32)($4)
+ # hi and lo
+ mfhi $8
+ sw $8, (4 * 33)($4)
+ mflo $8
+ sw $8, (4 * 34)($4)
+ jr $31
+ # return UNW_ESUCCESS
+ or $2, $0, $0
+ .set pop
+
+#elif defined(__mips__) && defined(_ABI64) && defined(__mips_soft_float)
+
+#
+# extern int unw_getcontext(unw_context_t* thread_state)
+#
+# On entry:
+# thread_state pointer is in a0 ($4)
+#
+DEFINE_LIBUNWIND_FUNCTION(unw_getcontext)
+ .set push
+ .set noat
+ .set noreorder
+ .set nomacro
+ sd $1, (8 * 1)($4)
+ sd $2, (8 * 2)($4)
+ sd $3, (8 * 3)($4)
+ sd $4, (8 * 4)($4)
+ sd $5, (8 * 5)($4)
+ sd $6, (8 * 6)($4)
+ sd $7, (8 * 7)($4)
+ sd $8, (8 * 8)($4)
+ sd $9, (8 * 9)($4)
+ sd $10, (8 * 10)($4)
+ sd $11, (8 * 11)($4)
+ sd $12, (8 * 12)($4)
+ sd $13, (8 * 13)($4)
+ sd $14, (8 * 14)($4)
+ sd $15, (8 * 15)($4)
+ sd $16, (8 * 16)($4)
+ sd $17, (8 * 17)($4)
+ sd $18, (8 * 18)($4)
+ sd $19, (8 * 19)($4)
+ sd $20, (8 * 20)($4)
+ sd $21, (8 * 21)($4)
+ sd $22, (8 * 22)($4)
+ sd $23, (8 * 23)($4)
+ sd $24, (8 * 24)($4)
+ sd $25, (8 * 25)($4)
+ sd $26, (8 * 26)($4)
+ sd $27, (8 * 27)($4)
+ sd $28, (8 * 28)($4)
+ sd $29, (8 * 29)($4)
+ sd $30, (8 * 30)($4)
+ sd $31, (8 * 31)($4)
+ # Store return address to pc
+ sd $31, (8 * 32)($4)
+ # hi and lo
+ mfhi $8
+ sd $8, (8 * 33)($4)
+ mflo $8
+ sd $8, (8 * 34)($4)
+ jr $31
+ # return UNW_ESUCCESS
+ or $2, $0, $0
+ .set pop
+
# elif defined(__mips__)
#
diff --git a/libunwind/src/config.h b/libunwind/src/config.h
index 07ca26bf171..0f29b770178 100644
--- a/libunwind/src/config.h
+++ b/libunwind/src/config.h
@@ -71,7 +71,7 @@
defined(__ppc__) || defined(__ppc64__) || \
(!defined(__APPLE__) && defined(__arm__)) || \
(defined(__arm64__) || defined(__aarch64__)) || \
- (defined(__APPLE__) && defined(__mips__))
+ defined(__mips__)
#define _LIBUNWIND_BUILD_ZERO_COST_APIS
#endif
diff --git a/libunwind/src/libunwind.cpp b/libunwind/src/libunwind.cpp
index 1039a3b0bae..0b7fb4019e1 100644
--- a/libunwind/src/libunwind.cpp
+++ b/libunwind/src/libunwind.cpp
@@ -59,8 +59,12 @@ _LIBUNWIND_EXPORT int unw_init_local(unw_cursor_t *cursor,
# define REGISTER_KIND Registers_arm
#elif defined(__or1k__)
# define REGISTER_KIND Registers_or1k
+#elif defined(__mips__) && defined(_ABIO32) && defined(__mips_soft_float)
+# define REGISTER_KIND Registers_mips_o32
+#elif defined(__mips__) && defined(_ABI64) && defined(__mips_soft_float)
+# define REGISTER_KIND Registers_mips_n64
#elif defined(__mips__)
-# warning The MIPS architecture is not supported.
+# warning The MIPS architecture is not supported with this ABI and environment!
#else
# error Architecture not supported
#endif