From: "Madhavan T. Venkataraman" <madvenka@xxxxxxxxxxxxxxxxxxx> When a module is loaded, allocate and initialize its struct dwarf_info. When a module is unloaded, free the same. Add code in dwarf_lookup() to look up a given address in modules, if vmlinux does not contain the address. Signed-off-by: Madhavan T. Venkataraman <madvenka@xxxxxxxxxxxxxxxxxxx> --- include/linux/dwarf.h | 18 ++++++++++ include/linux/module.h | 3 ++ kernel/dwarf_fp.c | 71 ++++++++++++++++++++++++++++++++++--- kernel/module.c | 31 ++++++++++++++++ tools/include/linux/dwarf.h | 18 ++++++++++ 5 files changed, 136 insertions(+), 5 deletions(-) diff --git a/include/linux/dwarf.h b/include/linux/dwarf.h index 3df15e79003c..aa44a414b0b6 100644 --- a/include/linux/dwarf.h +++ b/include/linux/dwarf.h @@ -11,6 +11,7 @@ #define _LINUX_DWARF_H #include <linux/types.h> +#include <linux/module.h> /* * objtool generates two special sections that contain DWARF information that @@ -54,11 +55,28 @@ struct dwarf_block { #ifdef CONFIG_DWARF_FP extern struct dwarf_rule *dwarf_lookup(unsigned long pc); +#ifdef CONFIG_MODULES +extern void dwarf_module_alloc(struct module *mod, + struct dwarf_rule *rules, size_t rules_size, + unsigned long *pcs, size_t pcs_size); +extern void dwarf_module_free(struct module *mod); +#endif #else static inline struct dwarf_rule *dwarf_lookup(unsigned long pc) { return NULL; } +#ifdef CONFIG_MODULES +static inline void dwarf_module_alloc(struct module *mod, + struct dwarf_rule *rules, + size_t rules_size, + unsigned long *pcs, size_t pcs_size) +{ +} +static inline void dwarf_module_free(struct module *mod) +{ +} +#endif #endif #endif /* _LINUX_DWARF_H */ diff --git a/include/linux/module.h b/include/linux/module.h index c9f1200b2312..bd7c69b82808 100644 --- a/include/linux/module.h +++ b/include/linux/module.h @@ -538,6 +538,9 @@ struct module { struct error_injection_entry *ei_funcs; unsigned int num_ei_funcs; #endif +#ifdef CONFIG_DWARF_FP + void *dwarf_info; +#endif } ____cacheline_aligned __randomize_layout; #ifndef MODULE_ARCH_INIT #define MODULE_ARCH_INIT {} diff --git a/kernel/dwarf_fp.c b/kernel/dwarf_fp.c index bb14fbe3f3e1..07d647e828cd 100644 --- a/kernel/dwarf_fp.c +++ b/kernel/dwarf_fp.c @@ -164,6 +164,44 @@ static struct dwarf_info *dwarf_alloc(struct dwarf_rule *rules, int nrules, return NULL; } +#ifdef CONFIG_MODULES + +/* + * Errors encountered in this function should not be fatal. All it will mean + * is that stack traces through the module would be considered unreliable. + */ +void dwarf_module_alloc(struct module *mod, + struct dwarf_rule *rules, size_t rules_size, + unsigned long *pcs, size_t pcs_size) +{ + int nrules, npcs; + + mod->dwarf_info = NULL; + + nrules = rules_size / sizeof(*rules); + npcs = pcs_size / sizeof(*pcs); + if (!nrules || npcs != nrules) + return; + + mod->dwarf_info = dwarf_alloc(rules, nrules, pcs); +} + +void dwarf_module_free(struct module *mod) +{ + struct dwarf_info *info; + + info = mod->dwarf_info; + mod->dwarf_info = NULL; + + if (info) { + kfree(info->blocks); + kfree(info->offsets); + kfree(info); + } +} + +#endif + static struct dwarf_rule *dwarf_lookup_rule(struct dwarf_info *info, unsigned long pc) { @@ -212,13 +250,36 @@ static struct dwarf_rule *dwarf_lookup_rule(struct dwarf_info *info, return NULL; } +#ifdef CONFIG_MODULES + +static struct dwarf_rule *dwarf_module_lookup_rule(unsigned long pc) +{ + struct module *mod; + + mod = __module_address(pc); + if (!mod || !mod->dwarf_info) + return NULL; + + return dwarf_lookup_rule(mod->dwarf_info, pc); +} + +#else + +static struct dwarf_rule *dwarf_module_lookup_rule(unsigned long pc) +{ + return NULL; +} + +#endif + struct dwarf_rule *dwarf_lookup(unsigned long pc) { - /* - * Currently, only looks up vmlinux. Support for modules will be - * added later. - */ - return dwarf_lookup_rule(vmlinux_dwarf_info, pc); + struct dwarf_rule *rule; + + rule = dwarf_lookup_rule(vmlinux_dwarf_info, pc); + if (!rule) + rule = dwarf_module_lookup_rule(pc); + return rule; } static int __init dwarf_init_feature(void) diff --git a/kernel/module.c b/kernel/module.c index 84a9141a5e15..d9b73995b70a 100644 --- a/kernel/module.c +++ b/kernel/module.c @@ -59,6 +59,7 @@ #include <linux/audit.h> #include <uapi/linux/module.h> #include "module-internal.h" +#include <linux/dwarf.h> #define CREATE_TRACE_POINTS #include <trace/events/module.h> @@ -2153,6 +2154,7 @@ void __weak module_arch_freeing_init(struct module *mod) } static void cfi_cleanup(struct module *mod); +static void module_dwarf_free(struct module *mod); /* Free a module, remove from lists, etc. */ static void free_module(struct module *mod) @@ -2175,6 +2177,9 @@ static void free_module(struct module *mod) /* Arch-specific cleanup. */ module_arch_cleanup(mod); + /* Dwarf cleanup. */ + module_dwarf_free(mod); + /* Module unload stuff */ module_unload_free(mod); @@ -3946,6 +3951,7 @@ static int unknown_module_param_cb(char *param, char *val, const char *modname, } static void cfi_init(struct module *mod); +static void module_dwarf_init(struct module *mod, struct load_info *info); /* * Allocate and load the module: note that size of section 0 is always @@ -4074,6 +4080,8 @@ static int load_module(struct load_info *info, const char __user *uargs, if (err < 0) goto free_modinfo; + module_dwarf_init(mod, info); + flush_module_icache(mod); /* Setup CFI for the module. */ @@ -4154,6 +4162,7 @@ static int load_module(struct load_info *info, const char __user *uargs, kfree(mod->args); free_arch_cleanup: cfi_cleanup(mod); + module_dwarf_free(mod); module_arch_cleanup(mod); free_modinfo: free_modinfo(mod); @@ -4542,6 +4551,28 @@ static void cfi_cleanup(struct module *mod) #endif } +static void module_dwarf_init(struct module *mod, struct load_info *info) +{ + Elf_Shdr *dwarf_rules, *dwarf_pcs; + + dwarf_rules = &info->sechdrs[find_sec(info, ".dwarf_rules")]; + dwarf_pcs = &info->sechdrs[find_sec(info, ".dwarf_pcs")]; + + if (!dwarf_rules || !dwarf_pcs) + return; + + dwarf_module_alloc(mod, + (void *) dwarf_rules->sh_addr, + dwarf_rules->sh_size, + (void *) dwarf_pcs->sh_addr, + dwarf_pcs->sh_size); +} + +static void module_dwarf_free(struct module *mod) +{ + dwarf_module_free(mod); +} + /* Maximum number of characters written by module_flags() */ #define MODULE_FLAGS_BUF_SIZE (TAINT_FLAGS_COUNT + 4) diff --git a/tools/include/linux/dwarf.h b/tools/include/linux/dwarf.h index 3df15e79003c..aa44a414b0b6 100644 --- a/tools/include/linux/dwarf.h +++ b/tools/include/linux/dwarf.h @@ -11,6 +11,7 @@ #define _LINUX_DWARF_H #include <linux/types.h> +#include <linux/module.h> /* * objtool generates two special sections that contain DWARF information that @@ -54,11 +55,28 @@ struct dwarf_block { #ifdef CONFIG_DWARF_FP extern struct dwarf_rule *dwarf_lookup(unsigned long pc); +#ifdef CONFIG_MODULES +extern void dwarf_module_alloc(struct module *mod, + struct dwarf_rule *rules, size_t rules_size, + unsigned long *pcs, size_t pcs_size); +extern void dwarf_module_free(struct module *mod); +#endif #else static inline struct dwarf_rule *dwarf_lookup(unsigned long pc) { return NULL; } +#ifdef CONFIG_MODULES +static inline void dwarf_module_alloc(struct module *mod, + struct dwarf_rule *rules, + size_t rules_size, + unsigned long *pcs, size_t pcs_size) +{ +} +static inline void dwarf_module_free(struct module *mod) +{ +} +#endif #endif #endif /* _LINUX_DWARF_H */ -- 2.25.1