Message ID | cover.1737511963.git.jpoimboe@kernel.org (mailing list archive) |
---|---|
Headers | show |
Series | unwind, perf: sframe user space unwinding | expand |
On Tue, Jan 21, 2025 at 06:30:52PM -0800, Josh Poimboeuf wrote: > For testing with user space, here are the latest binutils fixes: > > 1785837a2570 ("ld: fix PR/32297") > 938fb512184d ("ld: fix wrong SFrame info for lazy IBT PLT") > 47c88752f9ad ("ld: generate SFrame stack trace info for .plt.got") > > An out-of-tree glibc patch is also needed -- will attach in a reply. Latest out-of-tree glibc patch below: diff --git a/elf/dl-load.c b/elf/dl-load.c index e986d7faab..5a593c2126 100644 --- a/elf/dl-load.c +++ b/elf/dl-load.c @@ -29,6 +29,7 @@ #include <bits/wordsize.h> #include <sys/mman.h> #include <sys/param.h> +#include <sys/prctl.h> #include <sys/stat.h> #include <sys/types.h> #include <gnu/lib-names.h> @@ -87,6 +88,9 @@ struct filebuf #define STRING(x) __STRING (x) +#ifndef PT_GNU_SFRAME +#define PT_GNU_SFRAME 0x6474e554 +#endif /* This is the decomposed LD_LIBRARY_PATH search path. */ struct r_search_path_struct __rtld_env_path_list attribute_relro; @@ -1186,6 +1190,11 @@ _dl_map_object_from_fd (const char *name, const char *origname, int fd, l->l_relro_addr = ph->p_vaddr; l->l_relro_size = ph->p_memsz; break; + + case PT_GNU_SFRAME: + l->l_sframe_start = ph->p_vaddr; + l->l_sframe_end = ph->p_vaddr + ph->p_memsz; + break; } if (__glibc_unlikely (nloadcmds == 0)) @@ -1236,6 +1245,26 @@ _dl_map_object_from_fd (const char *name, const char *origname, int fd, l->l_map_start = l->l_map_end = 0; goto lose; } + +#define PR_ADD_SFRAME 77 + if (l->l_sframe_start != 0) + { + l->l_sframe_start += l->l_addr; + l->l_sframe_end += l->l_addr; + + for (size_t i = 0; i < nloadcmds; i++) + { + struct loadcmd *c = &loadcmds[i]; + + if (c->prot & PROT_EXEC) + { + ElfW(Addr) text_start = l->l_addr + c->mapstart; + ElfW(Addr) text_end = l->l_addr + c->mapend; + + __prctl(PR_ADD_SFRAME, l->l_sframe_start, l->l_sframe_end, text_start, text_end); + } + } + } } if (l->l_ld != NULL) diff --git a/elf/dl-unmap-segments.h b/elf/dl-unmap-segments.h index f16f4d7ded..dd14162e00 100644 --- a/elf/dl-unmap-segments.h +++ b/elf/dl-unmap-segments.h @@ -21,14 +21,20 @@ #include <link.h> #include <sys/mman.h> +#include <sys/prctl.h> /* _dl_map_segments ensures that any whole pages in gaps between segments are filled in with PROT_NONE mappings. So we can just unmap the whole range in one fell swoop. */ +#define PR_REMOVE_SFRAME 78 + static __always_inline void _dl_unmap_segments (struct link_map *l) { + if (l->l_sframe_start != 0) + __prctl(PR_REMOVE_SFRAME, l->l_sframe_start, NULL, NULL, NULL); + __munmap ((void *) l->l_map_start, l->l_map_end - l->l_map_start); } diff --git a/elf/setup-vdso.h b/elf/setup-vdso.h index 888e1e4897..2a6bb9b944 100644 --- a/elf/setup-vdso.h +++ b/elf/setup-vdso.h @@ -16,6 +16,11 @@ License along with the GNU C Library; if not, see <https://www.gnu.org/licenses/>. */ +#include <sys/prctl.h> +#ifndef PT_GNU_SFRAME +#define PT_GNU_SFRAME 0x6474e554 +#endif + static inline void __attribute__ ((always_inline)) setup_vdso (struct link_map *main_map __attribute__ ((unused)), struct link_map ***first_preload __attribute__ ((unused))) @@ -52,6 +57,14 @@ setup_vdso (struct link_map *main_map __attribute__ ((unused)), if (ph->p_vaddr + ph->p_memsz >= l->l_map_end) l->l_map_end = ph->p_vaddr + ph->p_memsz; } + else if (ph->p_type == PT_GNU_SFRAME) + { + if (! l->l_sframe_start) + { + l->l_sframe_start = ph->p_vaddr; + l->l_sframe_end = ph->p_vaddr + ph->p_memsz; + } + } else /* There must be no TLS segment. */ assert (ph->p_type != PT_TLS); @@ -74,6 +87,15 @@ setup_vdso (struct link_map *main_map __attribute__ ((unused)), l->l_local_scope[0]->r_nlist = 1; l->l_local_scope[0]->r_list = &l->l_real; +#define PR_ADD_SFRAME 77 + if (l->l_sframe_start != 0) + { + l->l_sframe_start += l->l_addr; + l->l_sframe_end += l->l_addr; + + __prctl(PR_ADD_SFRAME, l->l_sframe_start, l->l_sframe_end, l->l_addr, l->l_map_end); + } + /* Now that we have the info handy, use the DSO image's soname so this object can be looked up by name. */ if (l->l_info[DT_SONAME] != NULL) diff --git a/include/link.h b/include/link.h index 5ed445d5a6..e94390b29e 100644 --- a/include/link.h +++ b/include/link.h @@ -345,6 +345,9 @@ struct link_map ElfW(Addr) l_relro_addr; size_t l_relro_size; + ElfW(Addr) l_sframe_start; + ElfW(Addr) l_sframe_end; + unsigned long long int l_serial; };
On Tue, 21 Jan 2025 18:30:52 -0800 Josh Poimboeuf <jpoimboe@kernel.org> wrote: > The interface is similar to {task,irq}_work. The caller owns an > unwind_work struct: > > struct unwind_work { > struct callback_head work; > unwind_callback_t func; > int pending; > }; > > For perf, struct unwind_work is embedded in struct perf_event. For > ftrace maybe it would live in task_struct? Hmm, this is going to be difficult, as I don't want to add more to a task struct as it's already too bloated as is. I'll have to think about this a bit. -- Steve