206 lines
		
	
	
		
			4.5 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			206 lines
		
	
	
		
			4.5 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| // SPDX-License-Identifier: GPL-2.0-or-later
 | |
| /*
 | |
|  * DWARF debug information handling code.  Copied from probe-finder.c.
 | |
|  *
 | |
|  * Written by Masami Hiramatsu <mhiramat@redhat.com>
 | |
|  */
 | |
| 
 | |
| #include <errno.h>
 | |
| #include <fcntl.h>
 | |
| #include <stdio.h>
 | |
| #include <stdlib.h>
 | |
| #include <string.h>
 | |
| #include <unistd.h>
 | |
| #include <linux/zalloc.h>
 | |
| 
 | |
| #include "build-id.h"
 | |
| #include "dso.h"
 | |
| #include "debug.h"
 | |
| #include "debuginfo.h"
 | |
| #include "symbol.h"
 | |
| 
 | |
| #ifdef HAVE_DEBUGINFOD_SUPPORT
 | |
| #include <elfutils/debuginfod.h>
 | |
| #endif
 | |
| 
 | |
| /* Dwarf FL wrappers */
 | |
| static char *debuginfo_path;	/* Currently dummy */
 | |
| 
 | |
| static const Dwfl_Callbacks offline_callbacks = {
 | |
| 	.find_debuginfo = dwfl_standard_find_debuginfo,
 | |
| 	.debuginfo_path = &debuginfo_path,
 | |
| 
 | |
| 	.section_address = dwfl_offline_section_address,
 | |
| 
 | |
| 	/* We use this table for core files too.  */
 | |
| 	.find_elf = dwfl_build_id_find_elf,
 | |
| };
 | |
| 
 | |
| /* Get a Dwarf from offline image */
 | |
| static int debuginfo__init_offline_dwarf(struct debuginfo *dbg,
 | |
| 					 const char *path)
 | |
| {
 | |
| 	GElf_Addr dummy;
 | |
| 	int fd;
 | |
| 
 | |
| 	fd = open(path, O_RDONLY);
 | |
| 	if (fd < 0)
 | |
| 		return fd;
 | |
| 
 | |
| 	dbg->dwfl = dwfl_begin(&offline_callbacks);
 | |
| 	if (!dbg->dwfl)
 | |
| 		goto error;
 | |
| 
 | |
| 	dwfl_report_begin(dbg->dwfl);
 | |
| 	dbg->mod = dwfl_report_offline(dbg->dwfl, "", "", fd);
 | |
| 	if (!dbg->mod)
 | |
| 		goto error;
 | |
| 
 | |
| 	dbg->dbg = dwfl_module_getdwarf(dbg->mod, &dbg->bias);
 | |
| 	if (!dbg->dbg)
 | |
| 		goto error;
 | |
| 
 | |
| 	dwfl_module_build_id(dbg->mod, &dbg->build_id, &dummy);
 | |
| 
 | |
| 	dwfl_report_end(dbg->dwfl, NULL, NULL);
 | |
| 
 | |
| 	return 0;
 | |
| error:
 | |
| 	if (dbg->dwfl)
 | |
| 		dwfl_end(dbg->dwfl);
 | |
| 	else
 | |
| 		close(fd);
 | |
| 	memset(dbg, 0, sizeof(*dbg));
 | |
| 
 | |
| 	return -ENOENT;
 | |
| }
 | |
| 
 | |
| static struct debuginfo *__debuginfo__new(const char *path)
 | |
| {
 | |
| 	struct debuginfo *dbg = zalloc(sizeof(*dbg));
 | |
| 	if (!dbg)
 | |
| 		return NULL;
 | |
| 
 | |
| 	if (debuginfo__init_offline_dwarf(dbg, path) < 0)
 | |
| 		zfree(&dbg);
 | |
| 	if (dbg)
 | |
| 		pr_debug("Open Debuginfo file: %s\n", path);
 | |
| 	return dbg;
 | |
| }
 | |
| 
 | |
| enum dso_binary_type distro_dwarf_types[] = {
 | |
| 	DSO_BINARY_TYPE__FEDORA_DEBUGINFO,
 | |
| 	DSO_BINARY_TYPE__UBUNTU_DEBUGINFO,
 | |
| 	DSO_BINARY_TYPE__OPENEMBEDDED_DEBUGINFO,
 | |
| 	DSO_BINARY_TYPE__BUILDID_DEBUGINFO,
 | |
| 	DSO_BINARY_TYPE__MIXEDUP_UBUNTU_DEBUGINFO,
 | |
| 	DSO_BINARY_TYPE__NOT_FOUND,
 | |
| };
 | |
| 
 | |
| struct debuginfo *debuginfo__new(const char *path)
 | |
| {
 | |
| 	enum dso_binary_type *type;
 | |
| 	char buf[PATH_MAX], nil = '\0';
 | |
| 	struct dso *dso;
 | |
| 	struct debuginfo *dinfo = NULL;
 | |
| 	struct build_id bid;
 | |
| 
 | |
| 	/* Try to open distro debuginfo files */
 | |
| 	dso = dso__new(path);
 | |
| 	if (!dso)
 | |
| 		goto out;
 | |
| 
 | |
| 	/* Set the build id for DSO_BINARY_TYPE__BUILDID_DEBUGINFO */
 | |
| 	if (is_regular_file(path) && filename__read_build_id(path, &bid) > 0)
 | |
| 		dso__set_build_id(dso, &bid);
 | |
| 
 | |
| 	for (type = distro_dwarf_types;
 | |
| 	     !dinfo && *type != DSO_BINARY_TYPE__NOT_FOUND;
 | |
| 	     type++) {
 | |
| 		if (dso__read_binary_type_filename(dso, *type, &nil,
 | |
| 						   buf, PATH_MAX) < 0)
 | |
| 			continue;
 | |
| 		dinfo = __debuginfo__new(buf);
 | |
| 	}
 | |
| 	dso__put(dso);
 | |
| 
 | |
| out:
 | |
| 	/* if failed to open all distro debuginfo, open given binary */
 | |
| 	return dinfo ? : __debuginfo__new(path);
 | |
| }
 | |
| 
 | |
| void debuginfo__delete(struct debuginfo *dbg)
 | |
| {
 | |
| 	if (dbg) {
 | |
| 		if (dbg->dwfl)
 | |
| 			dwfl_end(dbg->dwfl);
 | |
| 		free(dbg);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| /* For the kernel module, we need a special code to get a DIE */
 | |
| int debuginfo__get_text_offset(struct debuginfo *dbg, Dwarf_Addr *offs,
 | |
| 				bool adjust_offset)
 | |
| {
 | |
| 	int n, i;
 | |
| 	Elf32_Word shndx;
 | |
| 	Elf_Scn *scn;
 | |
| 	Elf *elf;
 | |
| 	GElf_Shdr mem, *shdr;
 | |
| 	const char *p;
 | |
| 
 | |
| 	elf = dwfl_module_getelf(dbg->mod, &dbg->bias);
 | |
| 	if (!elf)
 | |
| 		return -EINVAL;
 | |
| 
 | |
| 	/* Get the number of relocations */
 | |
| 	n = dwfl_module_relocations(dbg->mod);
 | |
| 	if (n < 0)
 | |
| 		return -ENOENT;
 | |
| 	/* Search the relocation related .text section */
 | |
| 	for (i = 0; i < n; i++) {
 | |
| 		p = dwfl_module_relocation_info(dbg->mod, i, &shndx);
 | |
| 		if (strcmp(p, ".text") == 0) {
 | |
| 			/* OK, get the section header */
 | |
| 			scn = elf_getscn(elf, shndx);
 | |
| 			if (!scn)
 | |
| 				return -ENOENT;
 | |
| 			shdr = gelf_getshdr(scn, &mem);
 | |
| 			if (!shdr)
 | |
| 				return -ENOENT;
 | |
| 			*offs = shdr->sh_addr;
 | |
| 			if (adjust_offset)
 | |
| 				*offs -= shdr->sh_offset;
 | |
| 		}
 | |
| 	}
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| #ifdef HAVE_DEBUGINFOD_SUPPORT
 | |
| int get_source_from_debuginfod(const char *raw_path,
 | |
| 			       const char *sbuild_id, char **new_path)
 | |
| {
 | |
| 	debuginfod_client *c = debuginfod_begin();
 | |
| 	const char *p = raw_path;
 | |
| 	int fd;
 | |
| 
 | |
| 	if (!c)
 | |
| 		return -ENOMEM;
 | |
| 
 | |
| 	fd = debuginfod_find_source(c, (const unsigned char *)sbuild_id,
 | |
| 				0, p, new_path);
 | |
| 	pr_debug("Search %s from debuginfod -> %d\n", p, fd);
 | |
| 	if (fd >= 0)
 | |
| 		close(fd);
 | |
| 	debuginfod_end(c);
 | |
| 	if (fd < 0) {
 | |
| 		pr_debug("Failed to find %s in debuginfod (%s)\n",
 | |
| 			raw_path, sbuild_id);
 | |
| 		return -ENOENT;
 | |
| 	}
 | |
| 	pr_debug("Got a source %s\n", *new_path);
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| #endif /* HAVE_DEBUGINFOD_SUPPORT */
 |