@@ -60,4 +60,16 @@
SYM_FUNC_END(x); \
SYM_FUNC_END_ALIAS(__pi_##x)
+/*
+ * Record the address range of each SYM_CODE function in a struct code_range
+ * in a special section.
+ */
+#define SYM_CODE_END(name) \
+ SYM_END(name, SYM_T_NONE) ;\
+ 99: ;\
+ .pushsection "sym_code_functions", "aw" ;\
+ .quad name ;\
+ .quad 99b ;\
+ .popsection
+
#endif
@@ -20,5 +20,6 @@ extern char __exittext_begin[], __exittext_end[];
extern char __irqentry_text_start[], __irqentry_text_end[];
extern char __mmuoff_data_start[], __mmuoff_data_end[];
extern char __entry_tramp_text_start[], __entry_tramp_text_end[];
+extern char __sym_code_functions_start[], __sym_code_functions_end[];
#endif /* __ASM_SECTIONS_H */
@@ -18,6 +18,52 @@
#include <asm/stack_pointer.h>
#include <asm/stacktrace.h>
+struct code_range {
+ unsigned long start;
+ unsigned long end;
+};
+
+static struct code_range *sym_code_functions;
+static int num_sym_code_functions;
+
+int __init init_sym_code_functions(void)
+{
+ size_t size;
+
+ size = (unsigned long)__sym_code_functions_end -
+ (unsigned long)__sym_code_functions_start;
+
+ sym_code_functions = kmalloc(size, GFP_KERNEL);
+ if (!sym_code_functions)
+ return -ENOMEM;
+
+ memcpy(sym_code_functions, __sym_code_functions_start, size);
+ /* Update num_sym_code_functions after copying sym_code_functions. */
+ smp_mb();
+ num_sym_code_functions = size / sizeof(struct code_range);
+
+ return 0;
+}
+early_initcall(init_sym_code_functions);
+
+static bool unwinder_blacklisted(unsigned long pc)
+{
+ const struct code_range *range;
+ int i;
+
+ /*
+ * If sym_code_functions[] were sorted, a binary search could be
+ * done to make this more performant.
+ */
+ for (i = 0; i < num_sym_code_functions; i++) {
+ range = &sym_code_functions[i];
+ if (pc >= range->start && pc < range->end)
+ return true;
+ }
+
+ return false;
+}
+
/*
* AArch64 PCS assigns the frame pointer to x29.
*
@@ -130,7 +176,20 @@ int notrace unwind_frame(struct task_struct *tsk, struct stackframe *frame)
* A NULL or invalid return address probably means there's some
* generated code which __kernel_text_address() doesn't know about.
*/
- if (!__kernel_text_address(frame->pc))
+ if (!__kernel_text_address(frame->pc)) {
+ frame->reliable = false;
+ return 0;
+ }
+
+ /*
+ * If the final frame has been reached, there is no more unwinding
+ * to do. There is no need to check if the return PC is blacklisted
+ * by the unwinder.
+ */
+ if (!frame->fp)
+ return 0;
+
+ if (unwinder_blacklisted(frame->pc))
frame->reliable = false;
return 0;
@@ -103,6 +103,12 @@ jiffies = jiffies_64;
#define TRAMP_TEXT
#endif
+#define SYM_CODE_FUNCTIONS \
+ . = ALIGN(16); \
+ __sym_code_functions_start = .; \
+ KEEP(*(sym_code_functions)) \
+ __sym_code_functions_end = .;
+
/*
* The size of the PE/COFF section that covers the kernel image, which
* runs from _stext to _edata, must be a round multiple of the PE/COFF
@@ -218,6 +224,7 @@ SECTIONS
CON_INITCALL
INIT_RAM_FS
*(.init.altinstructions .init.bss) /* from the EFI stub */
+ SYM_CODE_FUNCTIONS
}
.exit.data : {
EXIT_DATA