[PATCH v1 2/6] makedumpfile: Apply relocation while loading module debuginfo.

Mahesh J Salgaonkar mahesh at linux.vnet.ibm.com
Fri Mar 11 03:04:53 EST 2011


From: Mahesh Salgaonkar <mahesh at linux.vnet.ibm.com>

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 <mahesh at linux.vnet.ibm.com>
Signed-off-by: Prerna Saxena <prerna at linux.vnet.ibm.com>
---

 makedumpfile.c |  261 +++++++++++++++++++++++++++++++++++++++++++++-----------
 makedumpfile.h |    7 ++
 2 files changed, 216 insertions(+), 52 deletions(-)

diff --git a/makedumpfile.c b/makedumpfile.c
index f136eba..2274b18 100644
--- a/makedumpfile.c
+++ b/makedumpfile.c
@@ -1372,6 +1372,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)
 {
@@ -1383,18 +1523,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");
@@ -1431,8 +1565,7 @@ get_symbol_addr(char *symname)
 		}
 	}
 out:
-	if (elfd != NULL)
-		elf_end(elfd);
+	clean_dwfl_info();
 
 	return symbol;
 }
@@ -1449,18 +1582,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");
@@ -1522,8 +1649,7 @@ get_next_symbol_addr(char *symname)
 		}
 	}
 out:
-	if (elfd != NULL)
-		elf_end(elfd);
+	clean_dwfl_info();
 
 	return next_symbol;
 }
@@ -2034,24 +2160,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;
@@ -2088,10 +2205,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;
 }
@@ -2448,14 +2562,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;
@@ -3888,8 +4046,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;
@@ -6552,8 +6710,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;
@@ -6820,8 +6977,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 --git a/makedumpfile.h b/makedumpfile.h
index 342e419..92e3e41 100644
--- a/makedumpfile.h
+++ b/makedumpfile.h
@@ -27,6 +27,7 @@
 #include <sys/wait.h>
 #include <zlib.h>
 #include <elfutils/libdw.h>
+#include <elfutils/libdwfl.h>
 #include <libelf.h>
 #include <dwarf.h>
 #include <byteswap.h>
@@ -1177,6 +1178,8 @@ extern struct srcfile_table	srcfile_table;
 /*
  * Debugging information
  */
+#define DEFAULT_DEBUGINFO_PATH	"/usr/lib/debug"
+
 enum {
 	DWARF_INFO_GET_STRUCT_SIZE,
 	DWARF_INFO_GET_MEMBER_OFFSET,
@@ -1194,10 +1197,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 */




More information about the kexec mailing list