// 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. Load more
// 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); }