summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTommi Rantala <tt.rantala@gmail.com>2012-09-07 12:12:50 +0300
committerTommi Rantala <tt.rantala@gmail.com>2012-09-28 14:50:03 +0300
commit79c2c254a74ca850aefe2c49b973dea1fa2934c2 (patch)
tree5f5a5f3014a9571dd10741914b505c9f5782dea3
parentded94b98ff3534a3c394591f4b254b1af6f0711c (diff)
downloadlibunwind-79c2c254a74ca850aefe2c49b973dea1fa2934c2.tar.gz
MIPS coredump support
Add MIPS support to the coredump library. Explicit support for the MIPS program counter register is added so that we can start backtracing from the program counter value we read from a core dump. The PC register support was not strictly required for local backtracing, and we will in fact just plug the return address to the PC register in getcontext(). I have only tested the 32bit "OABI" paths.
-rw-r--r--configure.ac2
-rw-r--r--include/libunwind-mips.h2
-rw-r--r--src/coredump/_UCD_access_reg_linux.c37
-rw-r--r--src/mips/Gget_save_loc.c1
-rw-r--r--src/mips/Ginit.c2
-rw-r--r--src/mips/Gregs.c4
-rw-r--r--src/mips/getcontext.S8
-rw-r--r--src/mips/init.h4
-rw-r--r--src/mips/offsets.h3
-rw-r--r--src/mips/regname.c2
10 files changed, 62 insertions, 3 deletions
diff --git a/configure.ac b/configure.ac
index a5e0537d..0597f516 100644
--- a/configure.ac
+++ b/configure.ac
@@ -103,7 +103,7 @@ SET_ARCH([$target_cpu],[target_arch])
AC_ARG_ENABLE(coredump,
AS_HELP_STRING([--enable-coredump],[building libunwind-coredump library]),,
- [AS_CASE([$host_arch], [x86*], [enable_coredump=yes], [enable_coredump=no])]
+ [AS_CASE([$host_arch], [mips*|x86*], [enable_coredump=yes], [enable_coredump=no])]
)
AC_MSG_CHECKING([if we should build libunwind-coredump])
diff --git a/include/libunwind-mips.h b/include/libunwind-mips.h
index 91f70015..cf22fc90 100644
--- a/include/libunwind-mips.h
+++ b/include/libunwind-mips.h
@@ -95,6 +95,8 @@ typedef enum
UNW_MIPS_R30,
UNW_MIPS_R31,
+ UNW_MIPS_PC = 34,
+
/* FIXME: Other registers! */
/* For MIPS, the CFA is the value of SP (r29) at the call site in the
diff --git a/src/coredump/_UCD_access_reg_linux.c b/src/coredump/_UCD_access_reg_linux.c
index c48f5c9b..9fd7f7d4 100644
--- a/src/coredump/_UCD_access_reg_linux.c
+++ b/src/coredump/_UCD_access_reg_linux.c
@@ -43,7 +43,42 @@ _UCD_access_reg (unw_addr_space_t as,
if (regnum < 0 || regnum >= 16)
goto badreg;
#else
-#if defined(UNW_TARGET_X86)
+#if defined(UNW_TARGET_MIPS)
+ static const uint8_t remap_regs[] =
+ {
+ [UNW_MIPS_R0] = EF_REG0,
+ [UNW_MIPS_R1] = EF_REG1,
+ [UNW_MIPS_R2] = EF_REG2,
+ [UNW_MIPS_R3] = EF_REG3,
+ [UNW_MIPS_R4] = EF_REG4,
+ [UNW_MIPS_R5] = EF_REG5,
+ [UNW_MIPS_R6] = EF_REG6,
+ [UNW_MIPS_R7] = EF_REG7,
+ [UNW_MIPS_R8] = EF_REG8,
+ [UNW_MIPS_R9] = EF_REG9,
+ [UNW_MIPS_R10] = EF_REG10,
+ [UNW_MIPS_R11] = EF_REG11,
+ [UNW_MIPS_R12] = EF_REG12,
+ [UNW_MIPS_R13] = EF_REG13,
+ [UNW_MIPS_R14] = EF_REG14,
+ [UNW_MIPS_R15] = EF_REG15,
+ [UNW_MIPS_R16] = EF_REG16,
+ [UNW_MIPS_R17] = EF_REG17,
+ [UNW_MIPS_R18] = EF_REG18,
+ [UNW_MIPS_R19] = EF_REG19,
+ [UNW_MIPS_R20] = EF_REG20,
+ [UNW_MIPS_R21] = EF_REG21,
+ [UNW_MIPS_R22] = EF_REG22,
+ [UNW_MIPS_R23] = EF_REG23,
+ [UNW_MIPS_R24] = EF_REG24,
+ [UNW_MIPS_R25] = EF_REG25,
+ [UNW_MIPS_R28] = EF_REG28,
+ [UNW_MIPS_R29] = EF_REG29,
+ [UNW_MIPS_R30] = EF_REG30,
+ [UNW_MIPS_R31] = EF_REG31,
+ [UNW_MIPS_PC] = EF_CP0_EPC,
+ };
+#elif defined(UNW_TARGET_X86)
static const uint8_t remap_regs[] =
{
/* names from libunwind-x86.h */
diff --git a/src/mips/Gget_save_loc.c b/src/mips/Gget_save_loc.c
index 262e23ec..0f90847a 100644
--- a/src/mips/Gget_save_loc.c
+++ b/src/mips/Gget_save_loc.c
@@ -68,6 +68,7 @@ unw_get_save_loc (unw_cursor_t *cursor, int reg, unw_save_loc_t *sloc)
case UNW_MIPS_R29:
case UNW_MIPS_R30:
case UNW_MIPS_R31:
+ case UNW_MIPS_PC:
loc = c->dwarf.loc[reg - UNW_MIPS_R0];
break;
diff --git a/src/mips/Ginit.c b/src/mips/Ginit.c
index 6ffeabfa..31000b3c 100644
--- a/src/mips/Ginit.c
+++ b/src/mips/Ginit.c
@@ -46,6 +46,8 @@ uc_addr (ucontext_t *uc, int reg)
{
if (reg >= UNW_MIPS_R0 && reg < UNW_MIPS_R0 + 32)
return &uc->uc_mcontext.gregs[reg - UNW_MIPS_R0];
+ else if (reg == UNW_MIPS_PC)
+ return &uc->uc_mcontext.pc;
else
return NULL;
}
diff --git a/src/mips/Gregs.c b/src/mips/Gregs.c
index 9f37299c..0dfc4a93 100644
--- a/src/mips/Gregs.c
+++ b/src/mips/Gregs.c
@@ -69,6 +69,10 @@ tdep_access_reg (struct cursor *c, unw_regnum_t reg, unw_word_t *valp,
loc = c->dwarf.loc[reg - UNW_MIPS_R0];
break;
+ case UNW_MIPS_PC:
+ loc = c->dwarf.loc[reg];
+ break;
+
case UNW_MIPS_CFA:
if (write)
return -UNW_EREADONLYREG;
diff --git a/src/mips/getcontext.S b/src/mips/getcontext.S
index ad9e1f00..d1dbd579 100644
--- a/src/mips/getcontext.S
+++ b/src/mips/getcontext.S
@@ -1,5 +1,6 @@
/* libunwind - a platform-independent unwind library
Copyright (C) 2008 CodeSourcery
+ Copyright (C) 2012 Tommi Rantala <tt.rantala@gmail.com>
This file is part of libunwind.
@@ -37,8 +38,14 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
sw $X, (LINUX_UC_MCONTEXT_GREGS + 8 * X + OFFSET) ($4); \
sra $1, $X, 31; \
sw $1, (LINUX_UC_MCONTEXT_GREGS + 8 * X + 4 - OFFSET) ($4)
+/* Yes, we save the return address to PC. */
+# define SPC \
+ sw $31, (LINUX_UC_MCONTEXT_PC + OFFSET) ($4); \
+ sra $1, $31, 31; \
+ sw $1, (LINUX_UC_MCONTEXT_PC + 4 - OFFSET) ($4)
#else
# define SREG(X) sd $X, (LINUX_UC_MCONTEXT_GREGS + 8 * X) ($4)
+# define SPC sd $31, (LINUX_UC_MCONTEXT_PC) ($4)
#endif
.global _Umips_getcontext
@@ -79,6 +86,7 @@ _Umips_getcontext:
SREG (29)
SREG (30)
SREG (31)
+ SPC
li $2, 0
j $31
diff --git a/src/mips/init.h b/src/mips/init.h
index 3a4bb008..95322c6b 100644
--- a/src/mips/init.h
+++ b/src/mips/init.h
@@ -34,7 +34,9 @@ common_init (struct cursor *c, unsigned use_prev_instr)
for (i = 32; i < DWARF_NUM_PRESERVED_REGS; ++i)
c->dwarf.loc[i] = DWARF_NULL_LOC;
- ret = dwarf_get (&c->dwarf, c->dwarf.loc[31], &c->dwarf.ip);
+ c->dwarf.loc[UNW_MIPS_PC] = DWARF_REG_LOC (&c->dwarf, UNW_MIPS_PC);
+
+ ret = dwarf_get (&c->dwarf, c->dwarf.loc[UNW_MIPS_PC], &c->dwarf.ip);
if (ret < 0)
return ret;
diff --git a/src/mips/offsets.h b/src/mips/offsets.h
index 8040f6a5..49af9704 100644
--- a/src/mips/offsets.h
+++ b/src/mips/offsets.h
@@ -16,6 +16,7 @@
# define LINUX_UC_STACK_OFF 0x8
# define LINUX_UC_MCONTEXT_OFF 0x18
# define LINUX_UC_SIGMASK_OFF 0x268
+# define LINUX_UC_MCONTEXT_PC 0x20
# define LINUX_UC_MCONTEXT_GREGS 0x28
#elif _MIPS_SIM == _ABIN32
@@ -25,6 +26,7 @@
# define LINUX_UC_STACK_OFF 0x8
# define LINUX_UC_MCONTEXT_OFF 0x18
# define LINUX_UC_SIGMASK_OFF 0x270
+# define LINUX_UC_MCONTEXT_PC 0x258
# define LINUX_UC_MCONTEXT_GREGS 0x18
#elif _MIPS_SIM == _ABI64
@@ -34,6 +36,7 @@
# define LINUX_UC_STACK_OFF 0x10
# define LINUX_UC_MCONTEXT_OFF 0x28
# define LINUX_UC_SIGMASK_OFF 0x280
+# define LINUX_UC_MCONTEXT_PC 0x268
# define LINUX_UC_MCONTEXT_GREGS 0x28
#endif
diff --git a/src/mips/regname.c b/src/mips/regname.c
index 94000d49..a4a63340 100644
--- a/src/mips/regname.c
+++ b/src/mips/regname.c
@@ -41,6 +41,8 @@ unw_regname (unw_regnum_t reg)
{
if (reg < (unw_regnum_t) ARRAY_SIZE (regname))
return regname[reg];
+ else if (reg == UNW_MIPS_PC)
+ return "pc";
else
return "???";
}