cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

skip-callchain-idx.c (6950B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 * Use DWARF Debug information to skip unnecessary callchain entries.
      4 *
      5 * Copyright (C) 2014 Sukadev Bhattiprolu, IBM Corporation.
      6 * Copyright (C) 2014 Ulrich Weigand, IBM Corporation.
      7 */
      8#include <inttypes.h>
      9#include <dwarf.h>
     10#include <elfutils/libdwfl.h>
     11
     12#include "util/thread.h"
     13#include "util/callchain.h"
     14#include "util/debug.h"
     15#include "util/dso.h"
     16#include "util/event.h" // struct ip_callchain
     17#include "util/map.h"
     18#include "util/symbol.h"
     19
     20/*
     21 * When saving the callchain on Power, the kernel conservatively saves
     22 * excess entries in the callchain. A few of these entries are needed
     23 * in some cases but not others. If the unnecessary entries are not
     24 * ignored, we end up with duplicate arcs in the call-graphs. Use
     25 * DWARF debug information to skip over any unnecessary callchain
     26 * entries.
     27 *
     28 * See function header for arch_adjust_callchain() below for more details.
     29 *
     30 * The libdwfl code in this file is based on code from elfutils
     31 * (libdwfl/argp-std.c, libdwfl/tests/addrcfi.c, etc).
     32 */
     33static char *debuginfo_path;
     34
     35static const Dwfl_Callbacks offline_callbacks = {
     36	.debuginfo_path = &debuginfo_path,
     37	.find_debuginfo = dwfl_standard_find_debuginfo,
     38	.section_address = dwfl_offline_section_address,
     39};
     40
     41
     42/*
     43 * Use the DWARF expression for the Call-frame-address and determine
     44 * if return address is in LR and if a new frame was allocated.
     45 */
     46static int check_return_reg(int ra_regno, Dwarf_Frame *frame)
     47{
     48	Dwarf_Op ops_mem[3];
     49	Dwarf_Op dummy;
     50	Dwarf_Op *ops = &dummy;
     51	size_t nops;
     52	int result;
     53
     54	result = dwarf_frame_register(frame, ra_regno, ops_mem, &ops, &nops);
     55	if (result < 0) {
     56		pr_debug("dwarf_frame_register() %s\n", dwarf_errmsg(-1));
     57		return -1;
     58	}
     59
     60	/*
     61	 * Check if return address is on the stack. If return address
     62	 * is in a register (typically R0), it is yet to be saved on
     63	 * the stack.
     64	 */
     65	if ((nops != 0 || ops != NULL) &&
     66		!(nops == 1 && ops[0].atom == DW_OP_regx &&
     67			ops[0].number2 == 0 && ops[0].offset == 0))
     68		return 0;
     69
     70	/*
     71	 * Return address is in LR. Check if a frame was allocated
     72	 * but not-yet used.
     73	 */
     74	result = dwarf_frame_cfa(frame, &ops, &nops);
     75	if (result < 0) {
     76		pr_debug("dwarf_frame_cfa() returns %d, %s\n", result,
     77					dwarf_errmsg(-1));
     78		return -1;
     79	}
     80
     81	/*
     82	 * If call frame address is in r1, no new frame was allocated.
     83	 */
     84	if (nops == 1 && ops[0].atom == DW_OP_bregx && ops[0].number == 1 &&
     85				ops[0].number2 == 0)
     86		return 1;
     87
     88	/*
     89	 * A new frame was allocated but has not yet been used.
     90	 */
     91	return 2;
     92}
     93
     94/*
     95 * Get the DWARF frame from the .eh_frame section.
     96 */
     97static Dwarf_Frame *get_eh_frame(Dwfl_Module *mod, Dwarf_Addr pc)
     98{
     99	int		result;
    100	Dwarf_Addr	bias;
    101	Dwarf_CFI	*cfi;
    102	Dwarf_Frame	*frame;
    103
    104	cfi = dwfl_module_eh_cfi(mod, &bias);
    105	if (!cfi) {
    106		pr_debug("%s(): no CFI - %s\n", __func__, dwfl_errmsg(-1));
    107		return NULL;
    108	}
    109
    110	result = dwarf_cfi_addrframe(cfi, pc-bias, &frame);
    111	if (result) {
    112		pr_debug("%s(): %s\n", __func__, dwfl_errmsg(-1));
    113		return NULL;
    114	}
    115
    116	return frame;
    117}
    118
    119/*
    120 * Get the DWARF frame from the .debug_frame section.
    121 */
    122static Dwarf_Frame *get_dwarf_frame(Dwfl_Module *mod, Dwarf_Addr pc)
    123{
    124	Dwarf_CFI       *cfi;
    125	Dwarf_Addr      bias;
    126	Dwarf_Frame     *frame;
    127	int             result;
    128
    129	cfi = dwfl_module_dwarf_cfi(mod, &bias);
    130	if (!cfi) {
    131		pr_debug("%s(): no CFI - %s\n", __func__, dwfl_errmsg(-1));
    132		return NULL;
    133	}
    134
    135	result = dwarf_cfi_addrframe(cfi, pc-bias, &frame);
    136	if (result) {
    137		pr_debug("%s(): %s\n", __func__, dwfl_errmsg(-1));
    138		return NULL;
    139	}
    140
    141	return frame;
    142}
    143
    144/*
    145 * Return:
    146 *	0 if return address for the program counter @pc is on stack
    147 *	1 if return address is in LR and no new stack frame was allocated
    148 *	2 if return address is in LR and a new frame was allocated (but not
    149 *		yet used)
    150 *	-1 in case of errors
    151 */
    152static int check_return_addr(struct dso *dso, u64 map_start, Dwarf_Addr pc)
    153{
    154	int		rc = -1;
    155	Dwfl		*dwfl;
    156	Dwfl_Module	*mod;
    157	Dwarf_Frame	*frame;
    158	int		ra_regno;
    159	Dwarf_Addr	start = pc;
    160	Dwarf_Addr	end = pc;
    161	bool		signalp;
    162	const char	*exec_file = dso->long_name;
    163
    164	dwfl = dso->dwfl;
    165
    166	if (!dwfl) {
    167		dwfl = dwfl_begin(&offline_callbacks);
    168		if (!dwfl) {
    169			pr_debug("dwfl_begin() failed: %s\n", dwarf_errmsg(-1));
    170			return -1;
    171		}
    172
    173		mod = dwfl_report_elf(dwfl, exec_file, exec_file, -1,
    174						map_start, false);
    175		if (!mod) {
    176			pr_debug("dwfl_report_elf() failed %s\n",
    177						dwarf_errmsg(-1));
    178			/*
    179			 * We normally cache the DWARF debug info and never
    180			 * call dwfl_end(). But to prevent fd leak, free in
    181			 * case of error.
    182			 */
    183			dwfl_end(dwfl);
    184			goto out;
    185		}
    186		dso->dwfl = dwfl;
    187	}
    188
    189	mod = dwfl_addrmodule(dwfl, pc);
    190	if (!mod) {
    191		pr_debug("dwfl_addrmodule() failed, %s\n", dwarf_errmsg(-1));
    192		goto out;
    193	}
    194
    195	/*
    196	 * To work with split debug info files (eg: glibc), check both
    197	 * .eh_frame and .debug_frame sections of the ELF header.
    198	 */
    199	frame = get_eh_frame(mod, pc);
    200	if (!frame) {
    201		frame = get_dwarf_frame(mod, pc);
    202		if (!frame)
    203			goto out;
    204	}
    205
    206	ra_regno = dwarf_frame_info(frame, &start, &end, &signalp);
    207	if (ra_regno < 0) {
    208		pr_debug("Return address register unavailable: %s\n",
    209				dwarf_errmsg(-1));
    210		goto out;
    211	}
    212
    213	rc = check_return_reg(ra_regno, frame);
    214
    215out:
    216	return rc;
    217}
    218
    219/*
    220 * The callchain saved by the kernel always includes the link register (LR).
    221 *
    222 *	0:	PERF_CONTEXT_USER
    223 *	1:	Program counter (Next instruction pointer)
    224 *	2:	LR value
    225 *	3:	Caller's caller
    226 *	4:	...
    227 *
    228 * The value in LR is only needed when it holds a return address. If the
    229 * return address is on the stack, we should ignore the LR value.
    230 *
    231 * Further, when the return address is in the LR, if a new frame was just
    232 * allocated but the LR was not saved into it, then the LR contains the
    233 * caller, slot 4: contains the caller's caller and the contents of slot 3:
    234 * (chain->ips[3]) is undefined and must be ignored.
    235 *
    236 * Use DWARF debug information to determine if any entries need to be skipped.
    237 *
    238 * Return:
    239 *	index:	of callchain entry that needs to be ignored (if any)
    240 *	-1	if no entry needs to be ignored or in case of errors
    241 */
    242int arch_skip_callchain_idx(struct thread *thread, struct ip_callchain *chain)
    243{
    244	struct addr_location al;
    245	struct dso *dso = NULL;
    246	int rc;
    247	u64 ip;
    248	u64 skip_slot = -1;
    249
    250	if (!chain || chain->nr < 3)
    251		return skip_slot;
    252
    253	ip = chain->ips[1];
    254
    255	thread__find_symbol(thread, PERF_RECORD_MISC_USER, ip, &al);
    256
    257	if (al.map)
    258		dso = al.map->dso;
    259
    260	if (!dso) {
    261		pr_debug("%" PRIx64 " dso is NULL\n", ip);
    262		return skip_slot;
    263	}
    264
    265	rc = check_return_addr(dso, al.map->start, ip);
    266
    267	pr_debug("[DSO %s, sym %s, ip 0x%" PRIx64 "] rc %d\n",
    268				dso->long_name, al.sym->name, ip, rc);
    269
    270	if (rc == 0) {
    271		/*
    272		 * Return address on stack. Ignore LR value in callchain
    273		 */
    274		skip_slot = 2;
    275	} else if (rc == 2) {
    276		/*
    277		 * New frame allocated but return address still in LR.
    278		 * Ignore the caller's caller entry in callchain.
    279		 */
    280		skip_slot = 3;
    281	}
    282	return skip_slot;
    283}