18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Copyright (C) 2015 Naveen N. Rao, IBM Corporation
58c2ecf20Sopenharmony_ci */
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci#include "dso.h"
88c2ecf20Sopenharmony_ci#include "symbol.h"
98c2ecf20Sopenharmony_ci#include "map.h"
108c2ecf20Sopenharmony_ci#include "probe-event.h"
118c2ecf20Sopenharmony_ci#include "probe-file.h"
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ciint arch__choose_best_symbol(struct symbol *syma,
148c2ecf20Sopenharmony_ci			     struct symbol *symb __maybe_unused)
158c2ecf20Sopenharmony_ci{
168c2ecf20Sopenharmony_ci	char *sym = syma->name;
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci#if !defined(_CALL_ELF) || _CALL_ELF != 2
198c2ecf20Sopenharmony_ci	/* Skip over any initial dot */
208c2ecf20Sopenharmony_ci	if (*sym == '.')
218c2ecf20Sopenharmony_ci		sym++;
228c2ecf20Sopenharmony_ci#endif
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci	/* Avoid "SyS" kernel syscall aliases */
258c2ecf20Sopenharmony_ci	if (strlen(sym) >= 3 && !strncmp(sym, "SyS", 3))
268c2ecf20Sopenharmony_ci		return SYMBOL_B;
278c2ecf20Sopenharmony_ci	if (strlen(sym) >= 10 && !strncmp(sym, "compat_SyS", 10))
288c2ecf20Sopenharmony_ci		return SYMBOL_B;
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci	return SYMBOL_A;
318c2ecf20Sopenharmony_ci}
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci#if !defined(_CALL_ELF) || _CALL_ELF != 2
348c2ecf20Sopenharmony_ci/* Allow matching against dot variants */
358c2ecf20Sopenharmony_ciint arch__compare_symbol_names(const char *namea, const char *nameb)
368c2ecf20Sopenharmony_ci{
378c2ecf20Sopenharmony_ci	/* Skip over initial dot */
388c2ecf20Sopenharmony_ci	if (*namea == '.')
398c2ecf20Sopenharmony_ci		namea++;
408c2ecf20Sopenharmony_ci	if (*nameb == '.')
418c2ecf20Sopenharmony_ci		nameb++;
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci	return strcmp(namea, nameb);
448c2ecf20Sopenharmony_ci}
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ciint arch__compare_symbol_names_n(const char *namea, const char *nameb,
478c2ecf20Sopenharmony_ci				 unsigned int n)
488c2ecf20Sopenharmony_ci{
498c2ecf20Sopenharmony_ci	/* Skip over initial dot */
508c2ecf20Sopenharmony_ci	if (*namea == '.')
518c2ecf20Sopenharmony_ci		namea++;
528c2ecf20Sopenharmony_ci	if (*nameb == '.')
538c2ecf20Sopenharmony_ci		nameb++;
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci	return strncmp(namea, nameb, n);
568c2ecf20Sopenharmony_ci}
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ciconst char *arch__normalize_symbol_name(const char *name)
598c2ecf20Sopenharmony_ci{
608c2ecf20Sopenharmony_ci	/* Skip over initial dot */
618c2ecf20Sopenharmony_ci	if (name && *name == '.')
628c2ecf20Sopenharmony_ci		name++;
638c2ecf20Sopenharmony_ci	return name;
648c2ecf20Sopenharmony_ci}
658c2ecf20Sopenharmony_ci#endif
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci#if defined(_CALL_ELF) && _CALL_ELF == 2
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci#ifdef HAVE_LIBELF_SUPPORT
708c2ecf20Sopenharmony_civoid arch__sym_update(struct symbol *s, GElf_Sym *sym)
718c2ecf20Sopenharmony_ci{
728c2ecf20Sopenharmony_ci	s->arch_sym = sym->st_other;
738c2ecf20Sopenharmony_ci}
748c2ecf20Sopenharmony_ci#endif
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci#define PPC64LE_LEP_OFFSET	8
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_civoid arch__fix_tev_from_maps(struct perf_probe_event *pev,
798c2ecf20Sopenharmony_ci			     struct probe_trace_event *tev, struct map *map,
808c2ecf20Sopenharmony_ci			     struct symbol *sym)
818c2ecf20Sopenharmony_ci{
828c2ecf20Sopenharmony_ci	int lep_offset;
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	/*
858c2ecf20Sopenharmony_ci	 * When probing at a function entry point, we normally always want the
868c2ecf20Sopenharmony_ci	 * LEP since that catches calls to the function through both the GEP and
878c2ecf20Sopenharmony_ci	 * the LEP. Hence, we would like to probe at an offset of 8 bytes if
888c2ecf20Sopenharmony_ci	 * the user only specified the function entry.
898c2ecf20Sopenharmony_ci	 *
908c2ecf20Sopenharmony_ci	 * However, if the user specifies an offset, we fall back to using the
918c2ecf20Sopenharmony_ci	 * GEP since all userspace applications (objdump/readelf) show function
928c2ecf20Sopenharmony_ci	 * disassembly with offsets from the GEP.
938c2ecf20Sopenharmony_ci	 */
948c2ecf20Sopenharmony_ci	if (pev->point.offset || !map || !sym)
958c2ecf20Sopenharmony_ci		return;
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci	/* For kretprobes, add an offset only if the kernel supports it */
988c2ecf20Sopenharmony_ci	if (!pev->uprobes && pev->point.retprobe) {
998c2ecf20Sopenharmony_ci#ifdef HAVE_LIBELF_SUPPORT
1008c2ecf20Sopenharmony_ci		if (!kretprobe_offset_is_supported())
1018c2ecf20Sopenharmony_ci#endif
1028c2ecf20Sopenharmony_ci			return;
1038c2ecf20Sopenharmony_ci	}
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	lep_offset = PPC64_LOCAL_ENTRY_OFFSET(sym->arch_sym);
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	if (map->dso->symtab_type == DSO_BINARY_TYPE__KALLSYMS)
1088c2ecf20Sopenharmony_ci		tev->point.offset += PPC64LE_LEP_OFFSET;
1098c2ecf20Sopenharmony_ci	else if (lep_offset) {
1108c2ecf20Sopenharmony_ci		if (pev->uprobes)
1118c2ecf20Sopenharmony_ci			tev->point.address += lep_offset;
1128c2ecf20Sopenharmony_ci		else
1138c2ecf20Sopenharmony_ci			tev->point.offset += lep_offset;
1148c2ecf20Sopenharmony_ci	}
1158c2ecf20Sopenharmony_ci}
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci#ifdef HAVE_LIBELF_SUPPORT
1188c2ecf20Sopenharmony_civoid arch__post_process_probe_trace_events(struct perf_probe_event *pev,
1198c2ecf20Sopenharmony_ci					   int ntevs)
1208c2ecf20Sopenharmony_ci{
1218c2ecf20Sopenharmony_ci	struct probe_trace_event *tev;
1228c2ecf20Sopenharmony_ci	struct map *map;
1238c2ecf20Sopenharmony_ci	struct symbol *sym = NULL;
1248c2ecf20Sopenharmony_ci	struct rb_node *tmp;
1258c2ecf20Sopenharmony_ci	int i = 0;
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	map = get_target_map(pev->target, pev->nsi, pev->uprobes);
1288c2ecf20Sopenharmony_ci	if (!map || map__load(map) < 0)
1298c2ecf20Sopenharmony_ci		return;
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci	for (i = 0; i < ntevs; i++) {
1328c2ecf20Sopenharmony_ci		tev = &pev->tevs[i];
1338c2ecf20Sopenharmony_ci		map__for_each_symbol(map, sym, tmp) {
1348c2ecf20Sopenharmony_ci			if (map->unmap_ip(map, sym->start) == tev->point.address) {
1358c2ecf20Sopenharmony_ci				arch__fix_tev_from_maps(pev, tev, map, sym);
1368c2ecf20Sopenharmony_ci				break;
1378c2ecf20Sopenharmony_ci			}
1388c2ecf20Sopenharmony_ci		}
1398c2ecf20Sopenharmony_ci	}
1408c2ecf20Sopenharmony_ci}
1418c2ecf20Sopenharmony_ci#endif /* HAVE_LIBELF_SUPPORT */
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci#endif
144