7eec00a747
After copying Arm64's perf archive with object files and perf.data file to x86 laptop, the x86's perf kernel symbol resolution fails. It outputs 'unknown' for all symbols parsing. This issue is root caused by the function elf__needs_adjust_symbols(), x86 perf tool uses one weak version, Arm64 (and powerpc) has rewritten their own version. elf__needs_adjust_symbols() decides if need to parse symbols with the relative offset address; but x86 building uses the weak function which misses to check for the elf type 'ET_DYN', so that it cannot parse symbols in Arm DSOs due to the wrong result from elf__needs_adjust_symbols(). The DSO parsing should not depend on any specific architecture perf building; e.g. x86 perf tool can parse Arm and Arm64 DSOs, vice versa. And confirmed by Naveen N. Rao that powerpc64 kernels are not being built as ET_DYN anymore and change to ET_EXEC. This patch removes the arch specific functions for Arm64 and powerpc and changes elf__needs_adjust_symbols() as a common function. In the common elf__needs_adjust_symbols(), it checks an extra condition 'ET_DYN' for elf header type. With this fixing, the Arm64 DSO can be parsed properly with x86's perf tool. Before: # perf script main 3258 1 branches: 0 [unknown] ([unknown]) => ffff800010c4665c [unknown] ([kernel.kallsyms]) main 3258 1 branches: ffff800010c46670 [unknown] ([kernel.kallsyms]) => ffff800010c4eaec [unknown] ([kernel.kallsyms]) main 3258 1 branches: ffff800010c4eaec [unknown] ([kernel.kallsyms]) => ffff800010c4eb00 [unknown] ([kernel.kallsyms]) main 3258 1 branches: ffff800010c4eb08 [unknown] ([kernel.kallsyms]) => ffff800010c4e780 [unknown] ([kernel.kallsyms]) main 3258 1 branches: ffff800010c4e7a0 [unknown] ([kernel.kallsyms]) => ffff800010c4eeac [unknown] ([kernel.kallsyms]) main 3258 1 branches: ffff800010c4eebc [unknown] ([kernel.kallsyms]) => ffff800010c4ed80 [unknown] ([kernel.kallsyms]) After: # perf script main 3258 1 branches: 0 [unknown] ([unknown]) => ffff800010c4665c coresight_timeout+0x54 ([kernel.kallsyms]) main 3258 1 branches: ffff800010c46670 coresight_timeout+0x68 ([kernel.kallsyms]) => ffff800010c4eaec etm4_enable_hw+0x3cc ([kernel.kallsyms]) main 3258 1 branches: ffff800010c4eaec etm4_enable_hw+0x3cc ([kernel.kallsyms]) => ffff800010c4eb00 etm4_enable_hw+0x3e0 ([kernel.kallsyms]) main 3258 1 branches: ffff800010c4eb08 etm4_enable_hw+0x3e8 ([kernel.kallsyms]) => ffff800010c4e780 etm4_enable_hw+0x60 ([kernel.kallsyms]) main 3258 1 branches: ffff800010c4e7a0 etm4_enable_hw+0x80 ([kernel.kallsyms]) => ffff800010c4eeac etm4_enable+0x2d4 ([kernel.kallsyms]) main 3258 1 branches: ffff800010c4eebc etm4_enable+0x2e4 ([kernel.kallsyms]) => ffff800010c4ed80 etm4_enable+0x1a8 ([kernel.kallsyms]) v3: Changed to check for ET_DYN across all architectures. v2: Fixed Arm64 and powerpc native building. Reported-by: Mike Leach <mike.leach@linaro.org> Signed-off-by: Leo Yan <leo.yan@linaro.org> Reviewed-by: Naveen N. Rao <naveen.n.rao@linux.vnet.ibm.com> Acked-by: Jiri Olsa <jolsa@redhat.com> Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com> Cc: Allison Randal <allison@lohutok.net> Cc: Enrico Weigelt <info@metux.net> Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Cc: Hendrik Brueckner <brueckner@linux.vnet.ibm.com> Cc: John Garry <john.garry@huawei.com> Cc: Kate Stewart <kstewart@linuxfoundation.org> Cc: Mark Rutland <mark.rutland@arm.com> Cc: Mathieu Poirier <mathieu.poirier@linaro.org> Cc: Namhyung Kim <namhyung@kernel.org> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: Thomas Richter <tmricht@linux.vnet.ibm.com> Link: http://lore.kernel.org/lkml/20200306015759.10084-1-leo.yan@linaro.org Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
144 lines
3.2 KiB
C
144 lines
3.2 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
/*
|
|
*
|
|
* Copyright (C) 2015 Naveen N. Rao, IBM Corporation
|
|
*/
|
|
|
|
#include "dso.h"
|
|
#include "symbol.h"
|
|
#include "map.h"
|
|
#include "probe-event.h"
|
|
#include "probe-file.h"
|
|
|
|
int arch__choose_best_symbol(struct symbol *syma,
|
|
struct symbol *symb __maybe_unused)
|
|
{
|
|
char *sym = syma->name;
|
|
|
|
#if !defined(_CALL_ELF) || _CALL_ELF != 2
|
|
/* Skip over any initial dot */
|
|
if (*sym == '.')
|
|
sym++;
|
|
#endif
|
|
|
|
/* Avoid "SyS" kernel syscall aliases */
|
|
if (strlen(sym) >= 3 && !strncmp(sym, "SyS", 3))
|
|
return SYMBOL_B;
|
|
if (strlen(sym) >= 10 && !strncmp(sym, "compat_SyS", 10))
|
|
return SYMBOL_B;
|
|
|
|
return SYMBOL_A;
|
|
}
|
|
|
|
#if !defined(_CALL_ELF) || _CALL_ELF != 2
|
|
/* Allow matching against dot variants */
|
|
int arch__compare_symbol_names(const char *namea, const char *nameb)
|
|
{
|
|
/* Skip over initial dot */
|
|
if (*namea == '.')
|
|
namea++;
|
|
if (*nameb == '.')
|
|
nameb++;
|
|
|
|
return strcmp(namea, nameb);
|
|
}
|
|
|
|
int arch__compare_symbol_names_n(const char *namea, const char *nameb,
|
|
unsigned int n)
|
|
{
|
|
/* Skip over initial dot */
|
|
if (*namea == '.')
|
|
namea++;
|
|
if (*nameb == '.')
|
|
nameb++;
|
|
|
|
return strncmp(namea, nameb, n);
|
|
}
|
|
|
|
const char *arch__normalize_symbol_name(const char *name)
|
|
{
|
|
/* Skip over initial dot */
|
|
if (name && *name == '.')
|
|
name++;
|
|
return name;
|
|
}
|
|
#endif
|
|
|
|
#if defined(_CALL_ELF) && _CALL_ELF == 2
|
|
|
|
#ifdef HAVE_LIBELF_SUPPORT
|
|
void arch__sym_update(struct symbol *s, GElf_Sym *sym)
|
|
{
|
|
s->arch_sym = sym->st_other;
|
|
}
|
|
#endif
|
|
|
|
#define PPC64LE_LEP_OFFSET 8
|
|
|
|
void arch__fix_tev_from_maps(struct perf_probe_event *pev,
|
|
struct probe_trace_event *tev, struct map *map,
|
|
struct symbol *sym)
|
|
{
|
|
int lep_offset;
|
|
|
|
/*
|
|
* When probing at a function entry point, we normally always want the
|
|
* LEP since that catches calls to the function through both the GEP and
|
|
* the LEP. Hence, we would like to probe at an offset of 8 bytes if
|
|
* the user only specified the function entry.
|
|
*
|
|
* However, if the user specifies an offset, we fall back to using the
|
|
* GEP since all userspace applications (objdump/readelf) show function
|
|
* disassembly with offsets from the GEP.
|
|
*/
|
|
if (pev->point.offset || !map || !sym)
|
|
return;
|
|
|
|
/* For kretprobes, add an offset only if the kernel supports it */
|
|
if (!pev->uprobes && pev->point.retprobe) {
|
|
#ifdef HAVE_LIBELF_SUPPORT
|
|
if (!kretprobe_offset_is_supported())
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
lep_offset = PPC64_LOCAL_ENTRY_OFFSET(sym->arch_sym);
|
|
|
|
if (map->dso->symtab_type == DSO_BINARY_TYPE__KALLSYMS)
|
|
tev->point.offset += PPC64LE_LEP_OFFSET;
|
|
else if (lep_offset) {
|
|
if (pev->uprobes)
|
|
tev->point.address += lep_offset;
|
|
else
|
|
tev->point.offset += lep_offset;
|
|
}
|
|
}
|
|
|
|
#ifdef HAVE_LIBELF_SUPPORT
|
|
void arch__post_process_probe_trace_events(struct perf_probe_event *pev,
|
|
int ntevs)
|
|
{
|
|
struct probe_trace_event *tev;
|
|
struct map *map;
|
|
struct symbol *sym = NULL;
|
|
struct rb_node *tmp;
|
|
int i = 0;
|
|
|
|
map = get_target_map(pev->target, pev->nsi, pev->uprobes);
|
|
if (!map || map__load(map) < 0)
|
|
return;
|
|
|
|
for (i = 0; i < ntevs; i++) {
|
|
tev = &pev->tevs[i];
|
|
map__for_each_symbol(map, sym, tmp) {
|
|
if (map->unmap_ip(map, sym->start) == tev->point.address) {
|
|
arch__fix_tev_from_maps(pev, tev, map, sym);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif /* HAVE_LIBELF_SUPPORT */
|
|
|
|
#endif
|