summaryrefslogtreecommitdiffstats
path: root/backends
diff options
context:
space:
mode:
Diffstat (limited to 'backends')
-rw-r--r--backends/ChangeLog6
-rw-r--r--backends/Makefile.am3
-rw-r--r--backends/arm_init.c1
-rw-r--r--backends/arm_unwind.c93
4 files changed, 102 insertions, 1 deletions
diff --git a/backends/ChangeLog b/backends/ChangeLog
index e2f05295..930854ba 100644
--- a/backends/ChangeLog
+++ b/backends/ChangeLog
@@ -1,3 +1,9 @@
+2017-04-26 Ulf Hermann <[email protected]>
+
+ * arm_unwind.c: New file
+ * Makefile.am (arm_SRCS): Add arm_unwind.c
+ * arm_init.c (arm_init): Hook arm_unwind
+
2017-04-28 Ulf Hermann <[email protected]>
* Makefile.am: Have the backend file names follow the platform's
diff --git a/backends/Makefile.am b/backends/Makefile.am
index 55ff871f..526fcb99 100644
--- a/backends/Makefile.am
+++ b/backends/Makefile.am
@@ -80,7 +80,8 @@ libebl_alpha_pic_a_SOURCES = $(alpha_SRCS)
am_libebl_alpha_pic_a_OBJECTS = $(alpha_SRCS:.c=.os)
arm_SRCS = arm_init.c arm_symbol.c arm_regs.c arm_corenote.c \
- arm_auxv.c arm_attrs.c arm_retval.c arm_cfi.c arm_initreg.c
+ arm_auxv.c arm_attrs.c arm_retval.c arm_cfi.c arm_initreg.c \
+ arm_unwind.c
libebl_arm_pic_a_SOURCES = $(arm_SRCS)
am_libebl_arm_pic_a_OBJECTS = $(arm_SRCS:.c=.os)
diff --git a/backends/arm_init.c b/backends/arm_init.c
index caadac65..4fa0601a 100644
--- a/backends/arm_init.c
+++ b/backends/arm_init.c
@@ -68,6 +68,7 @@ arm_init (Elf *elf __attribute__ ((unused)),
/* We only unwind the core integer registers. */
eh->frame_nregs = 16;
HOOK (eh, set_initial_registers_tid);
+ HOOK (eh, unwind);
/* Bit zero encodes whether an function address is THUMB or ARM. */
eh->func_addr_mask = ~(GElf_Addr)1;
diff --git a/backends/arm_unwind.c b/backends/arm_unwind.c
new file mode 100644
index 00000000..3d1e1f25
--- /dev/null
+++ b/backends/arm_unwind.c
@@ -0,0 +1,93 @@
+/* Get previous frame state for an existing frame state.
+ Copyright (C) 2016 The Qt Company Ltd.
+ This file is part of elfutils.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of either
+
+ * the GNU Lesser General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at
+ your option) any later version
+
+ or
+
+ * the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at
+ your option) any later version
+
+ or both in parallel, as here.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received copies of the GNU General Public License and
+ the GNU Lesser General Public License along with this program. If
+ not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <assert.h>
+
+#ifndef BACKEND
+#define BACKEND arm_
+
+// Actually, in THUMB mode it's r7. Let's ignore this.
+#define FP_REG 11
+#define LR_REG 14
+#define SP_REG 13
+
+// The offsets are a mess. gcc-generated code with -mapcs-frame has FP and LR the other way around.
+// We make it work with QV4.
+#define FP_OFFSET 0
+#define LR_OFFSET 4
+#define SP_OFFSET 8
+#endif
+
+#include "libebl_CPU.h"
+
+/* There was no CFI. Maybe we happen to have a frame pointer and can unwind from that? */
+
+bool
+EBLHOOK(unwind) (Ebl *ebl __attribute__ ((unused)), Dwarf_Addr pc __attribute__ ((unused)),
+ ebl_tid_registers_t *setfunc, ebl_tid_registers_get_t *getfunc,
+ ebl_pid_memory_read_t *readfunc, void *arg,
+ bool *signal_framep __attribute__ ((unused)))
+{
+ Dwarf_Word fp = 0, sp = 0; // have to be initialized because registers are 32bit only
+
+ if (!getfunc(FP_REG, 1, &fp, arg))
+ fp = 0;
+
+ if (!getfunc(SP_REG, 1, &sp, arg))
+ sp = 0;
+
+ Dwarf_Word newLr, newFp, newSp;
+
+ if (!readfunc(fp + LR_OFFSET, &newLr, arg))
+ newLr = 0;
+
+ if (!readfunc(fp + FP_OFFSET, &newFp, arg))
+ newFp = 0;
+
+ newSp = fp + SP_OFFSET;
+
+ if (!setfunc(-1, 1, &newLr, arg))
+ return false;
+
+ // unset the "thumb" bit. We get LR without thumb bit, so let's also pass it on that way.
+ newLr &= 0xfffffffe;
+
+ // These are not fatal if they don't work. They will just prevent unwinding at the next frame.
+ setfunc(LR_REG, 1, &newLr, arg);
+ setfunc(FP_REG, 1, &newFp, arg);
+ setfunc(SP_REG, 1, &newSp, arg);
+
+ // If the fp is invalid, we might still have a valid lr.
+ // But if the fp is valid, then the stack should be moving in the right direction.
+ return newLr != 0 && (fp == 0 || newSp > sp);
+}