commit 9e4ab3e599dd97249e4202bd8e4748b5e0dd5614 Author: Mahesh Salgaonkar Date: Fri Aug 19 17:03:34 2011 +0900 [PATCH v2 2/8] Apply relocation while loading module debuginfo. So far makedumpfile implementation does not have to deal with module debuginfo files. In order to support filtering of kernel module data, it needs to read dwarf information from module debuginfo files. The linux kernel module debuginfo are of ET_REL type and requires relocation to be applied in order to get the dwarf information. This patch uses dwfl_* API's to make the code relocation-aware while loading module debuginfo files. It also uses dwfl_* API to search module debuginfo files installed under default debuginfo path. Signed-off-by: Mahesh Salgaonkar Signed-off-by: Prerna Saxena --- diff -Nrup kexec-tools-2.0.0.orig/makedumpfile-1.3.5/makedumpfile.c kexec-tools-2.0.0/makedumpfile-1.3.5/makedumpfile.c --- kexec-tools-2.0.0.orig/makedumpfile-1.3.5/makedumpfile.c 2012-01-02 16:50:41.000000000 +0800 +++ kexec-tools-2.0.0/makedumpfile-1.3.5/makedumpfile.c 2012-01-02 16:51:37.000000000 +0800 @@ -1345,6 +1345,146 @@ get_elf_info(void) return TRUE; } +static int +process_module (Dwfl_Module *dwflmod, + void **userdata __attribute__ ((unused)), + const char *name __attribute__ ((unused)), + Dwarf_Addr base __attribute__ ((unused)), + void *arg) +{ + const char *fname, *mod_name, *debugfile; + Dwarf_Addr dwbias; + + /* get a debug context descriptor.*/ + dwarf_info.dwarfd = dwfl_module_getdwarf (dwflmod, &dwbias); + dwarf_info.elfd = dwarf_getelf(dwarf_info.dwarfd); + + mod_name = dwfl_module_info(dwflmod, NULL, NULL, NULL, NULL, NULL, + &fname, &debugfile); + + if (!strcmp(dwarf_info.module_name, mod_name) && + !dwarf_info.name_debuginfo && debugfile) { + /* + * Store the debuginfo filename. Next time we will + * open debuginfo file direclty instead of searching + * for it again. + */ + dwarf_info.name_debuginfo = strdup(debugfile); + } + + return DWARF_CB_OK; +} + +static int +dwfl_report_module_p(const char *modname, const char *filename) +{ + if (filename && !strcmp(modname, dwarf_info.module_name)) + return 1; + return 0; +} + +static void +clean_dwfl_info(void) +{ + if (dwarf_info.dwfl) + dwfl_end(dwarf_info.dwfl); + + dwarf_info.dwfl = NULL; + dwarf_info.dwarfd = NULL; + dwarf_info.elfd = NULL; +} + +/* + * Intitialize the dwarf info. + * Linux kernel module debuginfo are of ET_REL (relocatable) type. The old + * implementation of get_debug_info() function that reads the debuginfo was + * not relocation-aware and hence could not read the dwarf info properly + * from module debuginfo. + * + * This function uses dwfl API's to apply relocation before reading the + * dwarf information from module debuginfo. + * On success, this function sets the dwarf_info.elfd and dwarf_info.dwarfd + * after applying relocation to module debuginfo. + */ +static int +init_dwarf_info(void) +{ + Dwfl *dwfl = NULL; + int dwfl_fd = -1; + static char *debuginfo_path = DEFAULT_DEBUGINFO_PATH; + static const Dwfl_Callbacks callbacks = { + .section_address = dwfl_offline_section_address, + .find_debuginfo = dwfl_standard_find_debuginfo, + .debuginfo_path = &debuginfo_path, + }; + + dwarf_info.elfd = NULL; + dwarf_info.dwarfd = NULL; + + if ((dwfl = dwfl_begin(&callbacks)) == NULL) { + ERRMSG("Can't create a handle for a new dwfl session.\n"); + return FALSE; + } + + if (dwarf_info.name_debuginfo) { + /* We have absolute path for debuginfo file, use it directly + * instead of searching it through + * dwfl_linux_kernel_report_offline() call. + * + * Open the debuginfo file if it is not already open. + */ + if (dwarf_info.fd_debuginfo < 0) + dwarf_info.fd_debuginfo = + open(dwarf_info.name_debuginfo, O_RDONLY); + + dwfl_fd = dup(dwarf_info.fd_debuginfo); + if (dwfl_fd < 0) { + ERRMSG("Failed to get a duplicate handle for" + " debuginfo.\n"); + goto err_out; + } + } + if (dwarf_info.fd_debuginfo > 0) { + if (dwfl_report_offline(dwfl, dwarf_info.module_name, + dwarf_info.name_debuginfo, dwfl_fd) == NULL) { + ERRMSG("Failed reading %s: %s\n", + dwarf_info.name_debuginfo, dwfl_errmsg (-1)); + /* dwfl_fd is consumed on success, not on failure */ + close(dwfl_fd); + goto err_out; + } + } + else if (dwfl_linux_kernel_report_offline(dwfl, + info->system_utsname.release, + &dwfl_report_module_p)) { + ERRMSG("Can't get Module debuginfo for module '%s'\n", + dwarf_info.module_name); + goto err_out; + } + dwfl_report_end(dwfl, NULL, NULL); + + dwfl_getmodules(dwfl, &process_module, NULL, 0); + + if (dwarf_info.elfd == NULL) { + ERRMSG("Can't get first elf header of %s.\n", + dwarf_info.name_debuginfo); + goto err_out; + } + + if (dwarf_info.dwarfd == NULL) { + ERRMSG("Can't get debug context descriptor for %s.\n", + dwarf_info.name_debuginfo); + goto err_out; + } + dwarf_info.dwfl = dwfl; + return TRUE; +err_out: + if (dwfl) + dwfl_end(dwfl); + + return FALSE; +} + unsigned long long get_symbol_addr(char *symname) { @@ -1356,18 +1496,12 @@ get_symbol_addr(char *symname) Elf_Data *data = NULL; Elf_Scn *scn = NULL; char *sym_name = NULL; - const off_t failed = (off_t)-1; - if (lseek(dwarf_info.fd_debuginfo, 0, SEEK_SET) == failed) { - ERRMSG("Can't seek the kernel file(%s). %s\n", - dwarf_info.name_debuginfo, strerror(errno)); - return NOT_FOUND_SYMBOL; - } - if (!(elfd = elf_begin(dwarf_info.fd_debuginfo, ELF_C_READ, NULL))) { - ERRMSG("Can't get first elf header of %s.\n", - dwarf_info.name_debuginfo); - return NOT_FOUND_SYMBOL; - } + if (!init_dwarf_info()) + return 0; + + elfd = dwarf_info.elfd; + while ((scn = elf_nextscn(elfd, scn)) != NULL) { if (gelf_getshdr(scn, &shdr) == NULL) { ERRMSG("Can't get section header.\n"); @@ -1404,8 +1538,7 @@ get_symbol_addr(char *symname) } } out: - if (elfd != NULL) - elf_end(elfd); + clean_dwfl_info(); return symbol; } @@ -1422,18 +1555,12 @@ get_next_symbol_addr(char *symname) Elf_Data *data = NULL; Elf_Scn *scn = NULL; char *sym_name = NULL; - const off_t failed = (off_t)-1; - if (lseek(dwarf_info.fd_debuginfo, 0, SEEK_SET) == failed) { - ERRMSG("Can't seek the kernel file(%s). %s\n", - dwarf_info.name_debuginfo, strerror(errno)); - return NOT_FOUND_SYMBOL; - } - if (!(elfd = elf_begin(dwarf_info.fd_debuginfo, ELF_C_READ, NULL))) { - ERRMSG("Can't get first elf header of %s.\n", - dwarf_info.name_debuginfo); - return NOT_FOUND_SYMBOL; - } + if (!init_dwarf_info()) + return 0; + + elfd = dwarf_info.elfd; + while ((scn = elf_nextscn(elfd, scn)) != NULL) { if (gelf_getshdr(scn, &shdr) == NULL) { ERRMSG("Can't get section header.\n"); @@ -1495,8 +1622,7 @@ get_next_symbol_addr(char *symname) } } out: - if (elfd != NULL) - elf_end(elfd); + clean_dwfl_info(); return next_symbol; } @@ -2007,24 +2133,15 @@ get_debug_info(void) Elf_Scn *scn = NULL; GElf_Shdr scnhdr_mem, *scnhdr = NULL; Dwarf_Die cu_die; - const off_t failed = (off_t)-1; int ret = FALSE; - if (lseek(dwarf_info.fd_debuginfo, 0, SEEK_SET) == failed) { - ERRMSG("Can't seek the kernel file(%s). %s\n", - dwarf_info.name_debuginfo, strerror(errno)); - return FALSE; - } - if (!(elfd = elf_begin(dwarf_info.fd_debuginfo, ELF_C_READ_MMAP, NULL))) { - ERRMSG("Can't get first elf header of %s.\n", - dwarf_info.name_debuginfo); + if (!init_dwarf_info()) return FALSE; - } - if (!(dwarfd = dwarf_begin_elf(elfd, DWARF_C_READ, NULL))) { - ERRMSG("Can't create a handle for a new debug session.\n"); - goto out; - } + + elfd = dwarf_info.elfd; + dwarfd = dwarf_info.dwarfd; + if (elf_getshstrndx(elfd, &shstrndx) < 0) { ERRMSG("Can't get the section index of the string table.\n"); goto out; @@ -2061,10 +2178,7 @@ get_debug_info(void) } ret = TRUE; out: - if (dwarfd != NULL) - dwarf_end(dwarfd); - if (elfd != NULL) - elf_end(elfd); + clean_dwfl_info(); return ret; } @@ -2421,14 +2535,58 @@ get_mem_type(void) return ret; } +/* + * Set the dwarf_info with kernel/module debuginfo file information. + */ +int +set_dwarf_debuginfo(char *mod_name, char *name_debuginfo, int fd_debuginfo) +{ + if (!mod_name) + return FALSE; + if (dwarf_info.module_name && !strcmp(dwarf_info.module_name, mod_name)) + return TRUE; + + /* Switching to different module. + * + * Close the file descriptor if previous module is != kernel and + * xen-syms. The reason is, vmlinux file will always be supplied + * by user and code to open/close kernel debuginfo file already + * in place. The module debuginfo files are opened only if '--config' + * option is used. This helps not to break the existing functionlity + * if called without '--config' option. + */ + + if (dwarf_info.module_name + && strcmp(dwarf_info.module_name, "vmlinux") + && strcmp(dwarf_info.module_name, "xen-syms")) { + if (dwarf_info.fd_debuginfo > 0) + close(dwarf_info.fd_debuginfo); + if (dwarf_info.name_debuginfo) + free(dwarf_info.name_debuginfo); + } + dwarf_info.fd_debuginfo = fd_debuginfo; + dwarf_info.name_debuginfo = name_debuginfo; + dwarf_info.module_name = mod_name; + + if (!strcmp(dwarf_info.module_name, "vmlinux") || + !strcmp(dwarf_info.module_name, "xen-syms")) + return TRUE; + + /* check to see whether module debuginfo is available */ + if (!init_dwarf_info()) + return FALSE; + else + clean_dwfl_info(); + return TRUE; +} + int generate_vmcoreinfo(void) { if (!set_page_size(sysconf(_SC_PAGE_SIZE))) return FALSE; - dwarf_info.fd_debuginfo = info->fd_vmlinux; - dwarf_info.name_debuginfo = info->name_vmlinux; + set_dwarf_debuginfo("vmlinux", info->name_vmlinux, info->fd_vmlinux); if (!get_symbol_info()) return FALSE; @@ -3861,8 +4019,8 @@ initial(void) * Get the debug information for analysis from the kernel file */ } else if (info->name_vmlinux) { - dwarf_info.fd_debuginfo = info->fd_vmlinux; - dwarf_info.name_debuginfo = info->name_vmlinux; + set_dwarf_debuginfo("vmlinux", info->name_vmlinux, + info->fd_vmlinux); if (!get_symbol_info()) return FALSE; @@ -6501,8 +6659,7 @@ generate_vmcoreinfo_xen(void) ERRMSG("Can't get the size of page.\n"); return FALSE; } - dwarf_info.fd_debuginfo = info->fd_xen_syms; - dwarf_info.name_debuginfo = info->name_xen_syms; + set_dwarf_debuginfo("xen-syms", info->name_xen_syms, info->fd_xen_syms); if (!get_symbol_info_xen()) return FALSE; @@ -6765,8 +6922,8 @@ initial_xen(void) * Get the debug information for analysis from the xen-syms file */ } else if (info->name_xen_syms) { - dwarf_info.fd_debuginfo = info->fd_xen_syms; - dwarf_info.name_debuginfo = info->name_xen_syms; + set_dwarf_debuginfo("xen-syms", info->name_xen_syms, + info->fd_xen_syms); if (!get_symbol_info_xen()) return FALSE; diff -Nrup kexec-tools-2.0.0.orig/makedumpfile-1.3.5/makedumpfile.h kexec-tools-2.0.0/makedumpfile-1.3.5/makedumpfile.h --- kexec-tools-2.0.0.orig/makedumpfile-1.3.5/makedumpfile.h 2012-01-02 16:50:41.000000000 +0800 +++ kexec-tools-2.0.0/makedumpfile-1.3.5/makedumpfile.h 2012-01-02 16:51:37.000000000 +0800 @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -1145,6 +1146,8 @@ extern struct srcfile_table srcfile_tabl /* * Debugging information */ +#define DEFAULT_DEBUGINFO_PATH "/usr/lib/debug" + enum { DWARF_INFO_GET_STRUCT_SIZE, DWARF_INFO_GET_MEMBER_OFFSET, @@ -1162,10 +1165,14 @@ struct dwarf_info { unsigned int cmd; /* IN */ int fd_debuginfo; /* IN */ char *name_debuginfo; /* IN */ + char *module_name; /* IN */ char *struct_name; /* IN */ char *symbol_name; /* IN */ char *member_name; /* IN */ char *enum_name; /* IN */ + Elf *elfd; /* OUT */ + Dwarf *dwarfd; /* OUT */ + Dwfl *dwfl; /* OUT */ long struct_size; /* OUT */ long member_offset; /* OUT */ long array_length; /* OUT */