2077 lines · 50.8 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0#include <asm/bug.h>#include <linux/kernel.h>#include <linux/string.h>#include <linux/zalloc.h>#include <sys/time.h>#include <sys/resource.h>#include <sys/types.h>#include <sys/stat.h>#include <unistd.h>#include <errno.h>#include <fcntl.h>#include <stdlib.h>#ifdef HAVE_LIBBPF_SUPPORT#include <bpf/libbpf.h>#include "bpf-event.h"#include "bpf-utils.h"#endif#include "compress.h"#include "env.h"#include "namespaces.h"#include "path.h"#include "map.h"#include "symbol.h"#include "srcline.h"#include "dso.h"#include "dsos.h"#include "machine.h"#include "auxtrace.h"#include "util.h" /* O_CLOEXEC for older systems */#include "debug.h"#include "string2.h"#include "vdso.h"#include "annotate-data.h"#include "libdw.h" static const char * const debuglink_paths[] = {	"%.0s%s",	"%s/%s",	"%s/.debug/%s",	"/usr/lib/debug%s/%s"}; void dso__set_nsinfo(struct dso *dso, struct nsinfo *nsi){	nsinfo__put(RC_CHK_ACCESS(dso)->nsinfo);	RC_CHK_ACCESS(dso)->nsinfo = nsi;} char dso__symtab_origin(const struct dso *dso){	static const char origin[] = {		[DSO_BINARY_TYPE__KALLSYMS]			= 'k',		[DSO_BINARY_TYPE__VMLINUX]			= 'v',		[DSO_BINARY_TYPE__JAVA_JIT]			= 'j',		[DSO_BINARY_TYPE__DEBUGLINK]			= 'l',		[DSO_BINARY_TYPE__BUILD_ID_CACHE]		= 'B',		[DSO_BINARY_TYPE__BUILD_ID_CACHE_DEBUGINFO]	= 'D',		[DSO_BINARY_TYPE__FEDORA_DEBUGINFO]		= 'f',		[DSO_BINARY_TYPE__UBUNTU_DEBUGINFO]		= 'u',		[DSO_BINARY_TYPE__MIXEDUP_UBUNTU_DEBUGINFO]	= 'x',		[DSO_BINARY_TYPE__OPENEMBEDDED_DEBUGINFO]	= 'o',		[DSO_BINARY_TYPE__BUILDID_DEBUGINFO]		= 'b',		[DSO_BINARY_TYPE__SYSTEM_PATH_DSO]		= 'd',		[DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE]		= 'K',		[DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE_COMP]	= 'm',		[DSO_BINARY_TYPE__GUEST_KALLSYMS]		= 'g',		[DSO_BINARY_TYPE__GUEST_KMODULE]		= 'G',		[DSO_BINARY_TYPE__GUEST_KMODULE_COMP]		= 'M',		[DSO_BINARY_TYPE__GUEST_VMLINUX]		= 'V',		[DSO_BINARY_TYPE__GNU_DEBUGDATA]		= 'n',	}; 	if (dso == NULL || dso__symtab_type(dso) == DSO_BINARY_TYPE__NOT_FOUND)		return '!';	return origin[dso__symtab_type(dso)];} bool dso__is_object_file(const struct dso *dso){	switch (dso__binary_type(dso)) {	case DSO_BINARY_TYPE__KALLSYMS:	case DSO_BINARY_TYPE__GUEST_KALLSYMS:	case DSO_BINARY_TYPE__JAVA_JIT:	case DSO_BINARY_TYPE__BPF_PROG_INFO:	case DSO_BINARY_TYPE__BPF_IMAGE:	case DSO_BINARY_TYPE__OOL:		return false;	case DSO_BINARY_TYPE__VMLINUX:	case DSO_BINARY_TYPE__GUEST_VMLINUX:	case DSO_BINARY_TYPE__DEBUGLINK:	case DSO_BINARY_TYPE__BUILD_ID_CACHE:	case DSO_BINARY_TYPE__BUILD_ID_CACHE_DEBUGINFO:	case DSO_BINARY_TYPE__FEDORA_DEBUGINFO:	case DSO_BINARY_TYPE__UBUNTU_DEBUGINFO:	case DSO_BINARY_TYPE__MIXEDUP_UBUNTU_DEBUGINFO:	case DSO_BINARY_TYPE__BUILDID_DEBUGINFO:	case DSO_BINARY_TYPE__GNU_DEBUGDATA:	case DSO_BINARY_TYPE__SYSTEM_PATH_DSO:	case DSO_BINARY_TYPE__GUEST_KMODULE:	case DSO_BINARY_TYPE__GUEST_KMODULE_COMP:	case DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE:	case DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE_COMP:	case DSO_BINARY_TYPE__KCORE:	case DSO_BINARY_TYPE__GUEST_KCORE:	case DSO_BINARY_TYPE__OPENEMBEDDED_DEBUGINFO:	case DSO_BINARY_TYPE__NOT_FOUND:	default:		return true;	}} int dso__read_binary_type_filename(const struct dso *dso,				   enum dso_binary_type type,				   const char *root_dir, char *filename, size_t size){	char build_id_hex[SBUILD_ID_SIZE];	int ret = 0;	size_t len; 	switch (type) {	case DSO_BINARY_TYPE__DEBUGLINK:	{		const char *last_slash;		char dso_dir[PATH_MAX];		char symfile[PATH_MAX];		unsigned int i; 		len = __symbol__join_symfs(filename, size, dso__long_name(dso));		last_slash = filename + len;		while (last_slash != filename && *last_slash != '/')			last_slash--; 		strncpy(dso_dir, filename, last_slash - filename);		dso_dir[last_slash-filename] = '\0'; 		if (!is_regular_file(filename)) {			ret = -1;			break;		} 		ret = filename__read_debuglink(filename, symfile, PATH_MAX);		if (ret)			break; 		/* Check predefined locations where debug file might reside */		ret = -1;		for (i = 0; i < ARRAY_SIZE(debuglink_paths); i++) {			snprintf(filename, size,					debuglink_paths[i], dso_dir, symfile);			if (is_regular_file(filename)) {				ret = 0;				break;			}		} 		break;	}	case DSO_BINARY_TYPE__BUILD_ID_CACHE:		if (dso__build_id_filename(dso, filename, size, false) == NULL)			ret = -1;		break; 	case DSO_BINARY_TYPE__BUILD_ID_CACHE_DEBUGINFO:		if (dso__build_id_filename(dso, filename, size, true) == NULL)			ret = -1;		break; 	case DSO_BINARY_TYPE__FEDORA_DEBUGINFO:		len = __symbol__join_symfs(filename, size, "/usr/lib/debug");		snprintf(filename + len, size - len, "%s.debug", dso__long_name(dso));		break; 	case DSO_BINARY_TYPE__UBUNTU_DEBUGINFO:		len = __symbol__join_symfs(filename, size, "/usr/lib/debug");		snprintf(filename + len, size - len, "%s", dso__long_name(dso));		break; 	case DSO_BINARY_TYPE__MIXEDUP_UBUNTU_DEBUGINFO:		/*		 * Ubuntu can mixup /usr/lib with /lib, putting debuginfo in		 * /usr/lib/debug/lib when it is expected to be in		 * /usr/lib/debug/usr/lib		 */		if (strlen(dso__long_name(dso)) < 9 ||		    strncmp(dso__long_name(dso), "/usr/lib/", 9)) {			ret = -1;			break;		}		len = __symbol__join_symfs(filename, size, "/usr/lib/debug");		snprintf(filename + len, size - len, "%s", dso__long_name(dso) + 4);		break; 	case DSO_BINARY_TYPE__OPENEMBEDDED_DEBUGINFO:	{		const char *last_slash;		size_t dir_size; 		last_slash = dso__long_name(dso) + dso__long_name_len(dso);		while (last_slash != dso__long_name(dso) && *last_slash != '/')			last_slash--; 		len = __symbol__join_symfs(filename, size, "");		dir_size = last_slash - dso__long_name(dso) + 2;		if (dir_size > (size - len)) {			ret = -1;			break;		}		len += scnprintf(filename + len, dir_size, "%s",  dso__long_name(dso));		len += scnprintf(filename + len , size - len, ".debug%s",								last_slash);		break;	} 	case DSO_BINARY_TYPE__BUILDID_DEBUGINFO:		if (!dso__has_build_id(dso)) {			ret = -1;			break;		} 		build_id__snprintf(dso__bid(dso), build_id_hex, sizeof(build_id_hex));		len = __symbol__join_symfs(filename, size, "/usr/lib/debug/.build-id/");		snprintf(filename + len, size - len, "%.2s/%s.debug",			 build_id_hex, build_id_hex + 2);		break; 	case DSO_BINARY_TYPE__VMLINUX:	case DSO_BINARY_TYPE__GUEST_VMLINUX:	case DSO_BINARY_TYPE__SYSTEM_PATH_DSO:	case DSO_BINARY_TYPE__GNU_DEBUGDATA:		__symbol__join_symfs(filename, size, dso__long_name(dso));		break; 	case DSO_BINARY_TYPE__GUEST_KMODULE:	case DSO_BINARY_TYPE__GUEST_KMODULE_COMP:		path__join3(filename, size, symbol_conf.symfs,			    root_dir, dso__long_name(dso));		break; 	case DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE:	case DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE_COMP:		__symbol__join_symfs(filename, size, dso__long_name(dso));		break; 	case DSO_BINARY_TYPE__KCORE:	case DSO_BINARY_TYPE__GUEST_KCORE:		snprintf(filename, size, "%s", dso__long_name(dso));		break; 	default:	case DSO_BINARY_TYPE__KALLSYMS:	case DSO_BINARY_TYPE__GUEST_KALLSYMS:	case DSO_BINARY_TYPE__JAVA_JIT:	case DSO_BINARY_TYPE__BPF_PROG_INFO:	case DSO_BINARY_TYPE__BPF_IMAGE:	case DSO_BINARY_TYPE__OOL:	case DSO_BINARY_TYPE__NOT_FOUND:		ret = -1;		break;	} 	return ret;} enum {	COMP_ID__NONE = 0,}; static const struct {	const char *fmt;	int (*decompress)(const char *input, int output);	bool (*is_compressed)(const char *input);} compressions[] = {	[COMP_ID__NONE] = { .fmt = NULL, },#ifdef HAVE_ZLIB_SUPPORT	{ "gz", gzip_decompress_to_file, gzip_is_compressed },#endif#ifdef HAVE_LZMA_SUPPORT	{ "xz", lzma_decompress_to_file, lzma_is_compressed },#endif	{ NULL, NULL, NULL },}; static int is_supported_compression(const char *ext){	unsigned i; 	for (i = 1; compressions[i].fmt; i++) {		if (!strcmp(ext, compressions[i].fmt))			return i;	}	return COMP_ID__NONE;} bool is_kernel_module(const char *pathname, int cpumode){	struct kmod_path m;	int mode = cpumode & PERF_RECORD_MISC_CPUMODE_MASK; 	WARN_ONCE(mode != cpumode,		  "Internal error: passing unmasked cpumode (%x) to is_kernel_module",		  cpumode); 	switch (mode) {	case PERF_RECORD_MISC_USER:	case PERF_RECORD_MISC_HYPERVISOR:	case PERF_RECORD_MISC_GUEST_USER:		return false;	/* Treat PERF_RECORD_MISC_CPUMODE_UNKNOWN as kernel */	default:		if (kmod_path__parse(&m, pathname)) {			pr_err("Failed to check whether %s is a kernel module or not. Assume it is.",					pathname);			return true;		}	} 	return m.kmod;} bool dso__needs_decompress(struct dso *dso){	return dso__symtab_type(dso) == DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE_COMP ||		dso__symtab_type(dso) == DSO_BINARY_TYPE__GUEST_KMODULE_COMP;} int filename__decompress(const char *name, char *pathname,			 size_t len, int comp, int *err){	char tmpbuf[] = KMOD_DECOMP_NAME;	int fd = -1; 	/*	 * We have proper compression id for DSO and yet the file	 * behind the 'name' can still be plain uncompressed object.	 *	 * The reason is behind the logic we open the DSO object files,	 * when we try all possible 'debug' objects until we find the	 * data. So even if the DSO is represented by 'krava.xz' module,	 * we can end up here opening ~/.debug/....23432432/debug' file	 * which is not compressed.	 *	 * To keep this transparent, we detect this and return the file	 * descriptor to the uncompressed file.	 */	if (!compressions[comp].is_compressed(name)) {		fd = open(name, O_RDONLY | O_CLOEXEC);		if (fd < 0)			*err = errno;		if (pathname && len > 0)			pathname[0] = '\0';		return fd;	} 	fd = mkostemp(tmpbuf, O_CLOEXEC);	if (fd < 0) {		*err = errno;		return -1;	} 	if (compressions[comp].decompress(name, fd)) {		*err = DSO_LOAD_ERRNO__DECOMPRESSION_FAILURE;		close(fd);		fd = -1;	} 	if (!pathname || (fd < 0))		unlink(tmpbuf); 	if (pathname && (fd >= 0))		strlcpy(pathname, tmpbuf, len); 	return fd;} static int decompress_kmodule(struct dso *dso, const char *name,			      char *pathname, size_t len){	if (!dso__needs_decompress(dso))		return -1; 	if (dso__comp(dso) == COMP_ID__NONE)		return -1; 	return filename__decompress(name, pathname, len, dso__comp(dso), dso__load_errno(dso));} int dso__decompress_kmodule_fd(struct dso *dso, const char *name){	return decompress_kmodule(dso, name, NULL, 0);} int dso__decompress_kmodule_path(struct dso *dso, const char *name,				 char *pathname, size_t len){	int fd = decompress_kmodule(dso, name, pathname, len); 	/* decompress_kmodule() returns -1 on failure, don't close(-1) */	if (fd >= 0)		close(fd);	return fd >= 0 ? 0 : -1;} /* * Parses kernel module specified in @path and updates * @m argument like: * *    @comp - true if @path contains supported compression suffix, *            false otherwise *    @kmod - true if @path contains '.ko' suffix in right position, *            false otherwise *    @name - if (@alloc_name && @kmod) is true, it contains strdup-ed base name *            of the kernel module without suffixes, otherwise strudup-ed *            base name of @path *    @ext  - if (@alloc_ext && @comp) is true, it contains strdup-ed string *            the compression suffix * * Returns 0 if there's no strdup error, -ENOMEM otherwise. */int __kmod_path__parse(struct kmod_path *m, const char *path,		       bool alloc_name){	const char *name = strrchr(path, '/');	const char *ext  = strrchr(path, '.');	bool is_simple_name = false; 	memset(m, 0x0, sizeof(*m));	name = name ? name + 1 : path; 	/*	 * '.' is also a valid character for module name. For example:	 * [aaa.bbb] is a valid module name. '[' should have higher	 * priority than '.ko' suffix.	 *	 * The kernel names are from machine__mmap_name. Such	 * name should belong to kernel itself, not kernel module.	 */	if (name[0] == '[') {		is_simple_name = true;		if ((strncmp(name, "[kernel.kallsyms]", 17) == 0) ||		    (strncmp(name, "[guest.kernel.kallsyms", 22) == 0) ||		    (strncmp(name, "[vdso]", 6) == 0) ||		    (strncmp(name, "[vdso32]", 8) == 0) ||		    (strncmp(name, "[vdsox32]", 9) == 0) ||		    (strncmp(name, "[vsyscall]", 10) == 0)) {			m->kmod = false; 		} else			m->kmod = true;	} 	/* No extension, just return name. */	if ((ext == NULL) || is_simple_name) {		if (alloc_name) {			m->name = strdup(name);			return m->name ? 0 : -ENOMEM;		}		return 0;	} 	m->comp = is_supported_compression(ext + 1);	if (m->comp > COMP_ID__NONE)		ext -= 3; 	/* Check .ko extension only if there's enough name left. */	if (ext > name)		m->kmod = !strncmp(ext, ".ko", 3); 	if (alloc_name) {		if (m->kmod) {			if (asprintf(&m->name, "[%.*s]", (int) (ext - name), name) == -1)				return -ENOMEM;		} else {			if (asprintf(&m->name, "%s", name) == -1)				return -ENOMEM;		} 		strreplace(m->name, '-', '_');	} 	return 0;} void dso__set_module_info(struct dso *dso, struct kmod_path *m,			  struct machine *machine){	if (machine__is_host(machine))		dso__set_symtab_type(dso, DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE);	else		dso__set_symtab_type(dso, DSO_BINARY_TYPE__GUEST_KMODULE); 	/* _KMODULE_COMP should be next to _KMODULE */	if (m->kmod && m->comp) {		dso__set_symtab_type(dso, dso__symtab_type(dso) + 1);		dso__set_comp(dso, m->comp);	} 	dso__set_is_kmod(dso);	dso__set_short_name(dso, strdup(m->name), true);}

Lines 1–500 of 2077.