All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] ARM: fix unwinding for XIP kernels
@ 2011-11-17 13:40 Uwe Kleine-König
  2011-11-17 14:17 ` Catalin Marinas
  0 siblings, 1 reply; 20+ messages in thread
From: Uwe Kleine-König @ 2011-11-17 13:40 UTC (permalink / raw)
  To: linux-arm-kernel

The linker places the unwind tables in readonly sections. So when using
an XIP kernel these are located in ROM and cannot be modified.

For that reason don't convert the symbol addresses during boot (or
module loading) but only when interpreting them in search_index().
Moreover several consts are added to catch future writes and rename the
member "addr" of struct unwind_idx to "addr_offset" to better match the
new semantic.

This fixes unwinding on XIP which compared prel31 offsets to absolute
addresses because the initial conversion from prel31 to absolute failed.

Cc: Catalin Marinas <catalin.marinas@arm.com>
Signed-off-by: Uwe Kleine-K?nig <u.kleine-koenig@pengutronix.de>
---
Hello,

this is broken since unwinding was introduce in v2.6.30-rc1.

Best regards
Uwe

 arch/arm/include/asm/unwind.h |   15 ++----------
 arch/arm/kernel/setup.c       |    2 -
 arch/arm/kernel/unwind.c      |   48 ++++++++++++----------------------------
 3 files changed, 18 insertions(+), 47 deletions(-)

diff --git a/arch/arm/include/asm/unwind.h b/arch/arm/include/asm/unwind.h
index a5edf42..2ae0e0a 100644
--- a/arch/arm/include/asm/unwind.h
+++ b/arch/arm/include/asm/unwind.h
@@ -30,14 +30,14 @@ 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 *stop;
 	unsigned long begin_addr;
 	unsigned long end_addr;
 };
@@ -49,15 +49,6 @@ extern struct unwind_table *unwind_table_add(unsigned long start,
 extern void unwind_table_del(struct unwind_table *tab);
 extern void unwind_backtrace(struct pt_regs *regs, struct task_struct *tsk);
 
-#ifdef CONFIG_ARM_UNWIND
-extern int __init unwind_init(void);
-#else
-static inline int __init unwind_init(void)
-{
-	return 0;
-}
-#endif
-
 #endif	/* !__ASSEMBLY__ */
 
 #ifdef CONFIG_ARM_UNWIND
diff --git a/arch/arm/kernel/setup.c b/arch/arm/kernel/setup.c
index 7e7977a..cf9d71a 100644
--- a/arch/arm/kernel/setup.c
+++ b/arch/arm/kernel/setup.c
@@ -893,8 +893,6 @@ void __init setup_arch(char **cmdline_p)
 {
 	struct machine_desc *mdesc;
 
-	unwind_init();
-
 	setup_processor();
 	mdesc = setup_machine_fdt(__atags_pointer);
 	if (!mdesc)
diff --git a/arch/arm/kernel/unwind.c b/arch/arm/kernel/unwind.c
index e7e8365..ad3f06f 100644
--- a/arch/arm/kernel/unwind.c
+++ b/arch/arm/kernel/unwind.c
@@ -67,7 +67,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 */
 };
@@ -83,8 +83,8 @@ enum regs {
 	PC = 15
 };
 
-extern struct unwind_idx __start_unwind_idx[];
-extern struct unwind_idx __stop_unwind_idx[];
+extern const struct unwind_idx __start_unwind_idx[];
+extern const struct unwind_idx __stop_unwind_idx[];
 
 static DEFINE_SPINLOCK(unwind_lock);
 static LIST_HEAD(unwind_tables);
@@ -101,22 +101,22 @@ static LIST_HEAD(unwind_tables);
  * Binary search in the unwind index. The entries entries are
  * guaranteed to be sorted in ascending order by the linker.
  */
-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 *first,
+				       const struct unwind_idx *last)
 {
 	pr_debug("%s(%08lx, %p, %p)\n", __func__, addr, first, last);
 
-	if (addr < first->addr) {
+	if (addr < prel31_to_addr(&first->addr_offset)) {
 		pr_warning("unwind: Unknown symbol address %08lx\n", addr);
 		return NULL;
-	} else if (addr >= last->addr)
+	} else if (addr >= prel31_to_addr(&last->addr_offset))
 		return last;
 
 	while (first < last - 1) {
-		struct unwind_idx *mid = first + ((last - first + 1) >> 1);
+		const struct unwind_idx *mid = first + ((last - first + 1) >> 1);
 
-		if (addr < mid->addr)
+		if (addr < prel31_to_addr(&mid->addr_offset))
 			last = mid;
 		else
 			first = mid;
@@ -125,9 +125,9 @@ static struct unwind_idx *search_index(unsigned long addr,
 	return first;
 }
 
-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;
 	unsigned long flags;
 
 	pr_debug("%s(%08lx)\n", __func__, addr);
@@ -274,7 +274,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 */
@@ -399,7 +399,6 @@ struct unwind_table *unwind_table_add(unsigned long start, unsigned long size,
 				      unsigned long text_size)
 {
 	unsigned long flags;
-	struct unwind_idx *idx;
 	struct unwind_table *tab = kmalloc(sizeof(*tab), GFP_KERNEL);
 
 	pr_debug("%s(%08lx, %08lx, %08lx, %08lx)\n", __func__, start, size,
@@ -408,15 +407,11 @@ struct unwind_table *unwind_table_add(unsigned long start, unsigned long size,
 	if (!tab)
 		return tab;
 
-	tab->start = (struct unwind_idx *)start;
-	tab->stop = (struct unwind_idx *)(start + size);
+	tab->start = (const struct unwind_idx *)start;
+	tab->stop = (const struct unwind_idx *)(start + size);
 	tab->begin_addr = text_addr;
 	tab->end_addr = text_addr + text_size;
 
-	/* Convert the symbol addresses to absolute values */
-	for (idx = tab->start; idx < tab->stop; idx++)
-		idx->addr = prel31_to_addr(&idx->addr);
-
 	spin_lock_irqsave(&unwind_lock, flags);
 	list_add_tail(&tab->list, &unwind_tables);
 	spin_unlock_irqrestore(&unwind_lock, flags);
@@ -437,16 +432,3 @@ void unwind_table_del(struct unwind_table *tab)
 
 	kfree(tab);
 }
-
-int __init 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);
-
-	pr_debug("unwind: ARM stack unwinding initialised\n");
-
-	return 0;
-}
-- 
1.7.7.1

^ permalink raw reply related	[flat|nested] 20+ messages in thread
* [PATCH] ARM: fix unwinding for XIP kernels
@ 2022-11-07 11:14 Sascha Hauer
  0 siblings, 0 replies; 20+ messages 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 related	[flat|nested] 20+ messages in thread

end of thread, other threads:[~2022-11-07 11:16 UTC | newest]

Thread overview: 20+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2011-11-17 13:40 [PATCH] ARM: fix unwinding for XIP kernels Uwe Kleine-König
2011-11-17 14:17 ` Catalin Marinas
2011-11-17 18:59   ` Uwe Kleine-König
2011-11-18 18:28   ` Nicolas Pitre
2011-11-18 21:36     ` Catalin Marinas
2011-11-20 11:28   ` Uwe Kleine-König
2011-11-20 22:52     ` Uwe Kleine-König
2011-11-20 23:12       ` [PATCH RFC] ARM: unwind: optimize to not convert each table value but the address Uwe Kleine-König
2011-11-21 16:34         ` Catalin Marinas
2011-11-21 18:16           ` Uwe Kleine-König
2011-11-30 17:58         ` Catalin Marinas
2011-11-30 19:07           ` [PATCH] ARM: fix unwinding for XIP kernels Uwe Kleine-König
2011-11-30 19:37             ` Nicolas Pitre
2011-11-30 19:52             ` Catalin Marinas
2011-11-21 18:35     ` Catalin Marinas
2011-11-28  9:22       ` Uwe Kleine-König
2011-11-28  9:45         ` Catalin Marinas
2011-11-28 10:02           ` Uwe Kleine-König
2011-11-28 10:07             ` Catalin Marinas
  -- strict thread matches above, loose matches on Subject: below --
2022-11-07 11:14 Sascha Hauer

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.