// SPDX-License-Identifier: GPL-2.0
/*
* build-id.c
*
* build-id support
*
* Copyright (C) 2009, 2010 Red Hat Inc.
* Copyright (C) 2009, 2010 Arnaldo Carvalho de Melo <acme@redhat.com>
*/
#include "util.h" // lsdir(), mkdir_p(), rm_rf()
#include <dirent.h>
#include <errno.h>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include "util/copyfile.h"
#include "dso.h"
#include "build-id.h"
#include "event.h"
#include "namespaces.h"
#include "map.h"
#include "symbol.h"
#include "thread.h"
#include <linux/kernel.h>
#include "debug.h"
#include "session.h"
#include "tool.h"
#include "header.h"
#include "vdso.h"
#include "path.h"
#include "probe-file.h"
#include "strlist.h"
#ifdef HAVE_DEBUGINFOD_SUPPORT
#include <elfutils/debuginfod.h>
#endif
#include <linux/ctype.h>
#include <linux/zalloc.h>
#include <linux/string.h>
#include <asm/bug.h>
static bool no_buildid_cache;
int build_id__mark_dso_hit(const struct perf_tool *tool __maybe_unused,
union perf_event *event,
struct perf_sample *sample,
struct evsel *evsel __maybe_unused,
struct machine *machine)
{
struct addr_location al;
struct thread *thread = machine__findnew_thread(machine, sample->pid,
sample->tid);
if (thread == NULL) {
pr_err("problem processing %d event, skipping it.\n",
event->header.type);
return -1;
}
addr_location__init(&al);
if (thread__find_map(thread, sample->cpumode, sample->ip, &al))
dso__set_hit(map__dso(al.map));
addr_location__exit(&al);
thread__put(thread);
return 0;
}
int build_id__sprintf(const struct build_id *build_id, char *bf)
{
char *bid = bf;
const u8 *raw = build_id->data;
size_t i;
bf[0] = 0x0;
for (i = 0; i < build_id->size; ++i) {
sprintf(bid, "%02x", *raw);
++raw;
bid += 2;
}
return (bid - bf) + 1;
}
int sysfs__sprintf_build_id(const char *root_dir, char *sbuild_id)
{
char notes[PATH_MAX];
struct build_id bid;
int ret;
if (!root_dir)
root_dir = "";
scnprintf(notes, sizeof(notes), "%s/sys/kernel/notes", root_dir);
ret = sysfs__read_build_id(notes, &bid);
if (ret < 0)
return ret;
return build_id__sprintf(&bid, sbuild_id);
}
int filename__sprintf_build_id(const char *pathname, char *sbuild_id)
{
struct build_id bid;
int ret;
ret = filename__read_build_id(pathname, &bid);
if (ret < 0)
return ret;
return build_id__sprintf(&bid, sbuild_id);
}
/* asnprintf consolidates asprintf and snprintf */
static int asnprintf(char **strp, size_t size, const char *fmt, ...)
{
va_list ap;
int ret;
if (!strp)
return -EINVAL;
va_start(ap, fmt);
if (*strp)
ret = vsnprintf(*strp, size, fmt, ap);
else
ret = vasprintf(strp, fmt, ap);
va_end(ap);
return ret;
}
char *build_id_cache__kallsyms_path(const char *sbuild_id, char *bf,
size_t size)
{
bool retry_old = true;
snprintf(bf, size, "%s/%s/%s/kallsyms",
buildid_dir, DSO__NAME_KALLSYMS, sbuild_id);
retry:
if (!access(bf, F_OK))
return bf;
if (retry_old) {
/* Try old style kallsyms cache */
snprintf(bf, size, "%s/%s/%s",
buildid_dir, DSO__NAME_KALLSYMS, sbuild_id);
retry_old = false;
goto retry;
}
return NULL;
}
char *build_id_cache__linkname(const char *sbuild_id, char *bf, size_t size)
{
char *tmp = bf;
int ret = asnprintf(&bf, size, "%s/.build-id/%.2s/%s", buildid_dir,
sbuild_id, sbuild_id + 2);
if (ret < 0 || (tmp && size < (unsigned int)ret))
return NULL;
return bf;
}
/* The caller is responsible to free the returned buffer. */
char *build_id_cache__origname(const char *sbuild_id)
{
char *linkname;
char buf[PATH_MAX];
char *ret = NULL, *p;
size_t offs = 5; /* == strlen("../..") */
ssize_t len;
linkname = build_id_cache__linkname(sbuild_id, NULL, 0);
if (!linkname)
return NULL;
len = readlink(linkname, buf, sizeof(buf) - 1);
if (len <= 0)
goto out;
buf[len] = '\0';
/* The link should be "../..<origpath>/<sbuild_id>" */
p = strrchr(buf, '/'); /* Cut off the "/<sbuild_id>" */
if (p && (p > buf + offs)) {
*p = '\0';
if (buf[offs + 1] == '[')
offs++; /*
* This is a DSO name, like [kernel.kallsyms].
* Skip the first '/', since this is not the
* cache of a regular file.
*/
ret = strdup(buf + offs); /* Skip "../..[/]" */
}
out:
free(linkname);
return ret;
}
/* Check if the given build_id cache is valid on current running system */
static bool build_id_cache__valid_id(char *sbuild_id)
{
char real_sbuild_id[SBUILD_ID_SIZE] = "";
char *pathname;
int ret = 0;
bool result = false;
pathname = build_id_cache__origname(sbuild_id);
if (!