The .sframe in kernel modules is built without SFRAME_F_FDE_SORTED set.
In order to allow sframe PC lookup in modules, add a code path to handle
unsorted FDE tables by doing a simple linear search.

Reviewed-by: Jens Remus <[email protected]>
Signed-off-by: Dylan Hatch <[email protected]>
---
 include/linux/sframe.h |  1 +
 kernel/unwind/sframe.c | 45 +++++++++++++++++++++++++++++++++++++-----
 2 files changed, 41 insertions(+), 5 deletions(-)

diff --git a/include/linux/sframe.h b/include/linux/sframe.h
index 5b7341b61a7c..8ae31ed36226 100644
--- a/include/linux/sframe.h
+++ b/include/linux/sframe.h
@@ -28,6 +28,7 @@ struct sframe_section {
        unsigned long           fres_start;
        unsigned long           fres_end;
        unsigned int            num_fdes;
+       bool                    fdes_sorted;
 
        signed char             ra_off;
        signed char             fp_off;
diff --git a/kernel/unwind/sframe.c b/kernel/unwind/sframe.c
index 430bff9533ee..dcf4deb378dc 100644
--- a/kernel/unwind/sframe.c
+++ b/kernel/unwind/sframe.c
@@ -176,9 +176,35 @@ static __always_inline int __read_fde(struct 
sframe_section *sec,
        return -EFAULT;
 }
 
-static __always_inline int __find_fde(struct sframe_section *sec,
-                                     unsigned long ip,
-                                     struct sframe_fde_internal *fde)
+static __always_inline int __find_fde_unsorted(struct sframe_section *sec,
+                                              unsigned long ip,
+                                              struct sframe_fde_internal *fde)
+{
+       struct sframe_fde_v3 *cur, *start, *end;
+
+       start = (struct sframe_fde_v3 *)sec->fdes_start;
+       end = start + sec->num_fdes;
+
+       for (cur = start; cur < end; cur++) {
+               s64 func_off;
+               u32 func_size;
+               unsigned long func_addr;
+
+               DATA_GET(sec, func_off, &cur->func_start_off, s64, Efault);
+               DATA_GET(sec, func_size, &cur->func_size, u32, Efault);
+               func_addr = (unsigned long)cur + func_off;
+
+               if (ip >= func_addr && ip < func_addr + func_size)
+                       return __read_fde(sec, cur - start, fde);
+       }
+       return -EINVAL;
+Efault:
+       return -EFAULT;
+}
+
+static __always_inline int __find_fde_sorted(struct sframe_section *sec,
+                                            unsigned long ip,
+                                            struct sframe_fde_internal *fde)
 {
        unsigned long func_addr_low = 0, func_addr_high = ULONG_MAX;
        struct sframe_fde_v3 *first, *low, *high, *found = NULL;
@@ -233,6 +259,15 @@ static __always_inline int __find_fde(struct 
sframe_section *sec,
        return -EFAULT;
 }
 
+static __always_inline int __find_fde(struct sframe_section *sec,
+                                            unsigned long ip,
+                                            struct sframe_fde_internal *fde)
+{
+       if (sec->fdes_sorted)
+               return __find_fde_sorted(sec, ip, fde);
+       return __find_fde_unsorted(sec, ip, fde);
+}
+
 #define ____GET_INC(sec, to, from, type, label)                                
\
 ({                                                                     \
        type __to;                                                      \
@@ -657,7 +692,7 @@ static int sframe_validate_section(struct sframe_section 
*sec)
                        return ret;
 
                ip = fde.func_addr;
-               if (ip <= prev_ip) {
+               if (sec->fdes_sorted && ip <= prev_ip) {
                        dbg_sec("fde %u not sorted\n", i);
                        return -EFAULT;
                }
@@ -736,7 +771,6 @@ static int sframe_read_header(struct sframe_section *sec)
 
        if (shdr.preamble.magic != SFRAME_MAGIC ||
            shdr.preamble.version != SFRAME_VERSION_3 ||
-           !(shdr.preamble.flags & SFRAME_F_FDE_SORTED) ||
            !(shdr.preamble.flags & SFRAME_F_FDE_FUNC_START_PCREL) ||
            shdr.auxhdr_len) {
                dbg_sec("bad/unsupported sframe header\n");
@@ -766,6 +800,7 @@ static int sframe_read_header(struct sframe_section *sec)
                return -EINVAL;
        }
 
+       sec->fdes_sorted        = shdr.preamble.flags & SFRAME_F_FDE_SORTED;
        sec->num_fdes           = num_fdes;
        sec->fdes_start         = fdes_start;
        sec->fres_start         = fres_start;
-- 
2.54.0.545.g6539524ca2-goog


Reply via email to