mail archive of the barebox mailing list
 help / color / mirror / Atom feed
* [PATCH] ARM: fix unwinding for XIP kernels
@ 2022-11-07 11:14 Sascha Hauer
  0 siblings, 0 replies; only message in thread
From: Sascha Hauer @ 2022-11-07 11:14 UTC (permalink / raw)
  To: Barebox List

Newer gcc versions generate a warning about accessing an array out of
bounds:

In function 'search_index',
    inlined from 'unwind_find_idx' at arch/arm/lib32/unwind.c:109: ,
    inlined from 'unwind_frame' at arch/arm/lib32/unwind.c:248:8:
arch/arm/lib32/unwind.c:86:32: warning: array subscript -1 is outside array bounds of 'struct unwind_idx[268435455]' [-Warray-bounds]
   86 |         } else if (addr >= last->addr)
      |                            ~~~~^~~~~~
arch/arm/lib32/unwind.c: In function 'unwind_frame':
arch/arm/lib32/unwind.c:46:26: note: at offset -8 into object '__stop_unwind_idx' of size [0, 2147483647]
   46 | extern struct unwind_idx __stop_unwind_idx[];
      |                          ^~~~~~~~~~~~~~~~~

This was fixed by accident in the Kernel back in 2011 in
de66a979012d ("ARM: 7187/1: fix unwinding for XIP kernels") . Adopt that
commit for barebox.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 arch/arm/include/asm/unwind.h |   7 +-
 arch/arm/lib32/unwind.c       | 116 +++++++++++++++++++++++-----------
 2 files changed, 84 insertions(+), 39 deletions(-)

diff --git a/arch/arm/include/asm/unwind.h b/arch/arm/include/asm/unwind.h
index 319527ec9b..37aba2aa4c 100644
--- a/arch/arm/include/asm/unwind.h
+++ b/arch/arm/include/asm/unwind.h
@@ -16,14 +16,15 @@ enum unwind_reason_code {
 };
 
 struct unwind_idx {
-	unsigned long addr;
+	unsigned long addr_offset;
 	unsigned long insn;
 };
 
 struct unwind_table {
 	struct list_head list;
-	struct unwind_idx *start;
-	struct unwind_idx *stop;
+	const struct unwind_idx *start;
+	const struct unwind_idx *origin;
+	const struct unwind_idx *stop;
 	unsigned long begin_addr;
 	unsigned long end_addr;
 };
diff --git a/arch/arm/lib32/unwind.c b/arch/arm/lib32/unwind.c
index 79bf7420d7..6f73cb1b73 100644
--- a/arch/arm/lib32/unwind.c
+++ b/arch/arm/lib32/unwind.c
@@ -28,7 +28,7 @@ EXPORT_SYMBOL(__aeabi_unwind_cpp_pr2);
 
 struct unwind_ctrl_block {
 	unsigned long vrs[16];		/* virtual register set */
-	unsigned long *insn;		/* pointer to the current instructions word */
+	const unsigned long *insn;	/* pointer to the current instructions word */
 	int entries;			/* number of entries left to interpret */
 	int byte;			/* current byte number in the instructions word */
 };
@@ -42,8 +42,9 @@ enum regs {
 
 #define THREAD_SIZE               8192
 
-extern struct unwind_idx __start_unwind_idx[];
-extern struct unwind_idx __stop_unwind_idx[];
+extern const struct unwind_idx __start_unwind_idx[];
+static const struct unwind_idx *__origin_unwind_idx;
+extern const struct unwind_idx __stop_unwind_idx[];
 
 /* Convert a prel31 symbol to an absolute address */
 #define prel31_to_addr(ptr)				\
@@ -71,44 +72,99 @@ static void dump_backtrace_entry(unsigned long where, unsigned long from,
 }
 
 /*
- * Binary search in the unwind index. The entries entries are
+ * Binary search in the unwind index. The entries are
  * guaranteed to be sorted in ascending order by the linker.
+ *
+ * start = first entry
+ * origin = first entry with positive offset (or stop if there is no such entry)
+ * stop - 1 = last entry
  */
-static struct unwind_idx *search_index(unsigned long addr,
-				       struct unwind_idx *first,
-				       struct unwind_idx *last)
+static const struct unwind_idx *search_index(unsigned long addr,
+					const struct unwind_idx *start,
+					const struct unwind_idx *origin,
+					const struct unwind_idx *stop)
 {
-	pr_debug("%s(%08lx, %p, %p)\n", __func__, addr, first, last);
+	unsigned long addr_prel31;
+
+	pr_debug("%s(%08lx, %p, %p, %p)\n",
+			__func__, addr, start, origin, stop);
+
+	/*
+	 * only search in the section with the matching sign. This way the
+	 * prel31 numbers can be compared as unsigned longs.
+	 */
+	if (addr < (unsigned long)start)
+		/* negative offsets: [start; origin) */
+		stop = origin;
+	else
+		/* positive offsets: [origin; stop) */
+		start = origin;
+
+	/* prel31 for address relavive to start */
+	addr_prel31 = (addr - (unsigned long)start) & 0x7fffffff;
+
+	while (start < stop - 1) {
+		const struct unwind_idx *mid = start + ((stop - start) >> 1);
+
+		/*
+		 * As addr_prel31 is relative to start an offset is needed to
+		 * make it relative to mid.
+		 */
+		if (addr_prel31 - ((unsigned long)mid - (unsigned long)start) <
+				mid->addr_offset)
+			stop = mid;
+		else {
+			/* keep addr_prel31 relative to start */
+			addr_prel31 -= ((unsigned long)mid -
+					(unsigned long)start);
+			start = mid;
+		}
+	}
 
-	if (addr < first->addr) {
+	if (likely(start->addr_offset <= addr_prel31))
+		return start;
+	else {
 		pr_warning("unwind: Unknown symbol address %08lx\n", addr);
 		return NULL;
-	} else if (addr >= last->addr)
-		return last;
+	}
+}
 
-	while (first < last - 1) {
-		struct unwind_idx *mid = first + ((last - first + 1) >> 1);
+static const struct unwind_idx *unwind_find_origin(
+		const struct unwind_idx *start, const struct unwind_idx *stop)
+{
+	pr_debug("%s(%p, %p)\n", __func__, start, stop);
+	while (start < stop - 1) {
+		const struct unwind_idx *mid = start + ((stop - start) >> 1);
 
-		if (addr < mid->addr)
-			last = mid;
+		if (mid->addr_offset >= 0x40000000)
+			/* negative offset */
+			start = mid;
 		else
-			first = mid;
+			/* positive offset */
+			stop = mid;
 	}
 
-	return first;
-}
+	pr_debug("%s -> %p\n", __func__, stop);
+	return stop;
+ }
 
-static struct unwind_idx *unwind_find_idx(unsigned long addr)
+static const struct unwind_idx *unwind_find_idx(unsigned long addr)
 {
-	struct unwind_idx *idx = NULL;
+	const struct unwind_idx *idx = NULL;
 
 	pr_debug("%s(%08lx)\n", __func__, addr);
 
-	if (is_kernel_text(addr))
+	if (is_kernel_text(addr)) {
+		if (unlikely(!__origin_unwind_idx))
+			__origin_unwind_idx =
+				unwind_find_origin(__start_unwind_idx,
+						__stop_unwind_idx);
+
 		/* main unwind table */
 		idx = search_index(addr, __start_unwind_idx,
-				   __stop_unwind_idx - 1);
-	else {
+					__origin_unwind_idx,
+					__stop_unwind_idx);
+	} else {
 		/* module unwinding not supported */
 	}
 
@@ -232,7 +288,7 @@ static int unwind_exec_insn(struct unwind_ctrl_block *ctrl)
 int unwind_frame(struct stackframe *frame)
 {
 	unsigned long high, low;
-	struct unwind_idx *idx;
+	const struct unwind_idx *idx;
 	struct unwind_ctrl_block ctrl;
 
 	/* only go to a higher address on the stack */
@@ -342,15 +398,3 @@ void dump_stack(void)
 {
 	unwind_backtrace(NULL);
 }
-
-static int unwind_init(void)
-{
-	struct unwind_idx *idx;
-
-	/* Convert the symbol addresses to absolute values */
-	for (idx = __start_unwind_idx; idx < __stop_unwind_idx; idx++)
-		idx->addr = prel31_to_addr(&idx->addr);
-
-	return 0;
-}
-core_initcall(unwind_init);
-- 
2.30.2




^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2022-11-07 11:16 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-11-07 11:14 [PATCH] ARM: fix unwinding for XIP kernels Sascha Hauer

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox