When loading an ELF executable, automatically detect .eh_frame_hdr
sections and associate them with the mm_struct.

Based on Josh Poimboeuf's, Steven Rostedt's, and my unwind user sframe
implementation.

Signed-off-by: Jens Remus <[email protected]>
---
 fs/binfmt_elf.c | 49 +++++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 47 insertions(+), 2 deletions(-)

diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c
index 16a56b6b3f6c..8c1cdd8318c5 100644
--- a/fs/binfmt_elf.c
+++ b/fs/binfmt_elf.c
@@ -48,6 +48,7 @@
 #include <linux/uaccess.h>
 #include <uapi/linux/rseq.h>
 #include <linux/rseq.h>
+#include <linux/eh_frame.h>
 #include <asm/param.h>
 #include <asm/page.h>
 
@@ -637,6 +638,23 @@ static inline int make_prot(u32 p_flags, struct 
arch_elf_state *arch_state,
        return arch_elf_adjust_prot(prot, arch_state, has_interp, is_interp);
 }
 
+static void elf_add_eh_frame(const struct elf_phdr *text,
+                            const struct elf_phdr *eh_frame,
+                            unsigned long base_addr)
+{
+       unsigned long eh_frame_start, eh_frame_end,
+                     text_start, text_end;
+
+       eh_frame_start = base_addr + eh_frame->p_vaddr;
+       eh_frame_end   = eh_frame_start + eh_frame->p_memsz;
+
+       text_start   = base_addr + text->p_vaddr;
+       text_end     = text_start + text->p_memsz;
+
+       /* Ignore return value, eh_frame[_hdr] section isn't critical */
+       eh_frame_add_section(eh_frame_start, eh_frame_end, text_start, 
text_end);
+}
+
 /* This is much more generalized than the library routine read function,
    so we keep this separate.  Technically the library read function
    is only provided so that we can read a.out libraries that have
@@ -647,7 +665,7 @@ static unsigned long load_elf_interp(struct elfhdr 
*interp_elf_ex,
                unsigned long no_base, struct elf_phdr *interp_elf_phdata,
                struct arch_elf_state *arch_state)
 {
-       struct elf_phdr *eppnt;
+       struct elf_phdr *eppnt, *eh_frame_phdr = NULL;
        unsigned long load_addr = 0;
        int load_addr_set = 0;
        unsigned long error = ~0UL;
@@ -673,7 +691,8 @@ static unsigned long load_elf_interp(struct elfhdr 
*interp_elf_ex,
 
        eppnt = interp_elf_phdata;
        for (i = 0; i < interp_elf_ex->e_phnum; i++, eppnt++) {
-               if (eppnt->p_type == PT_LOAD) {
+               switch (eppnt->p_type) {
+               case PT_LOAD: {
                        int elf_type = MAP_PRIVATE;
                        int elf_prot = make_prot(eppnt->p_flags, arch_state,
                                                 true, true);
@@ -712,6 +731,19 @@ static unsigned long load_elf_interp(struct elfhdr 
*interp_elf_ex,
                                error = -ENOMEM;
                                goto out;
                        }
+                       break;
+               }
+               case PT_GNU_EH_FRAME:
+                       eh_frame_phdr = eppnt;
+                       break;
+               }
+       }
+
+       if (eh_frame_phdr) {
+               eppnt = interp_elf_phdata;
+               for (i = 0; i < interp_elf_ex->e_phnum; i++, eppnt++) {
+                       if (eppnt->p_flags & PF_X && eppnt->p_type == PT_LOAD)
+                               elf_add_eh_frame(eppnt, eh_frame_phdr, 
load_addr);
                }
        }
 
@@ -837,6 +869,7 @@ static int load_elf_binary(struct linux_binprm *bprm)
        unsigned long error;
        struct elf_phdr *elf_ppnt, *elf_phdata, *interp_elf_phdata = NULL;
        struct elf_phdr *elf_property_phdata = NULL;
+       struct elf_phdr *eh_frame_phdr = NULL;
        unsigned long elf_brk;
        bool brk_moved = false;
        int retval, i;
@@ -945,6 +978,10 @@ static int load_elf_binary(struct linux_binprm *bprm)
                                executable_stack = EXSTACK_DISABLE_X;
                        break;
 
+               case PT_GNU_EH_FRAME:
+                       eh_frame_phdr = elf_ppnt;
+                       break;
+
                case PT_LOPROC ... PT_HIPROC:
                        retval = arch_elf_pt_proc(elf_ex, elf_ppnt,
                                                  bprm->file, false,
@@ -1242,6 +1279,14 @@ static int load_elf_binary(struct linux_binprm *bprm)
                        elf_brk = k;
        }
 
+       if (eh_frame_phdr) {
+               for (i = 0, elf_ppnt = elf_phdata;
+                    i < elf_ex->e_phnum; i++, elf_ppnt++) {
+                       if (elf_ppnt->p_flags & PF_X && elf_ppnt->p_type == 
PT_LOAD)
+                               elf_add_eh_frame(elf_ppnt, eh_frame_phdr, 
load_bias);
+               }
+       }
+
        e_entry = elf_ex->e_entry + load_bias;
        phdr_addr += load_bias;
        elf_brk += load_bias;
-- 
2.53.0


Reply via email to