162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Post mortem Dwarf CFI based unwinding on top of regs and stack dumps.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Lots of this code have been borrowed or heavily inspired from parts of
662306a36Sopenharmony_ci * the libunwind 0.99 code which are (amongst other contributors I may have
762306a36Sopenharmony_ci * forgotten):
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci * Copyright (C) 2002-2007 Hewlett-Packard Co
1062306a36Sopenharmony_ci *	Contributed by David Mosberger-Tang <davidm@hpl.hp.com>
1162306a36Sopenharmony_ci *
1262306a36Sopenharmony_ci * And the bugs have been added by:
1362306a36Sopenharmony_ci *
1462306a36Sopenharmony_ci * Copyright (C) 2010, Frederic Weisbecker <fweisbec@gmail.com>
1562306a36Sopenharmony_ci * Copyright (C) 2012, Jiri Olsa <jolsa@redhat.com>
1662306a36Sopenharmony_ci *
1762306a36Sopenharmony_ci */
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#include <elf.h>
2062306a36Sopenharmony_ci#include <errno.h>
2162306a36Sopenharmony_ci#include <gelf.h>
2262306a36Sopenharmony_ci#include <fcntl.h>
2362306a36Sopenharmony_ci#include <inttypes.h>
2462306a36Sopenharmony_ci#include <string.h>
2562306a36Sopenharmony_ci#include <unistd.h>
2662306a36Sopenharmony_ci#include <sys/mman.h>
2762306a36Sopenharmony_ci#include <linux/list.h>
2862306a36Sopenharmony_ci#include <linux/zalloc.h>
2962306a36Sopenharmony_ci#ifndef REMOTE_UNWIND_LIBUNWIND
3062306a36Sopenharmony_ci#include <libunwind.h>
3162306a36Sopenharmony_ci#include <libunwind-ptrace.h>
3262306a36Sopenharmony_ci#endif
3362306a36Sopenharmony_ci#include "callchain.h"
3462306a36Sopenharmony_ci#include "thread.h"
3562306a36Sopenharmony_ci#include "session.h"
3662306a36Sopenharmony_ci#include "perf_regs.h"
3762306a36Sopenharmony_ci#include "unwind.h"
3862306a36Sopenharmony_ci#include "map.h"
3962306a36Sopenharmony_ci#include "symbol.h"
4062306a36Sopenharmony_ci#include "debug.h"
4162306a36Sopenharmony_ci#include "asm/bug.h"
4262306a36Sopenharmony_ci#include "dso.h"
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ciextern int
4562306a36Sopenharmony_ciUNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as,
4662306a36Sopenharmony_ci				    unw_word_t ip,
4762306a36Sopenharmony_ci				    unw_dyn_info_t *di,
4862306a36Sopenharmony_ci				    unw_proc_info_t *pi,
4962306a36Sopenharmony_ci				    int need_unwind_info, void *arg);
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci#define dwarf_search_unwind_table UNW_OBJ(dwarf_search_unwind_table)
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ciextern int
5462306a36Sopenharmony_ciUNW_OBJ(dwarf_find_debug_frame) (int found, unw_dyn_info_t *di_debug,
5562306a36Sopenharmony_ci				 unw_word_t ip,
5662306a36Sopenharmony_ci				 unw_word_t segbase,
5762306a36Sopenharmony_ci				 const char *obj_name, unw_word_t start,
5862306a36Sopenharmony_ci				 unw_word_t end);
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci#define dwarf_find_debug_frame UNW_OBJ(dwarf_find_debug_frame)
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci#define DW_EH_PE_FORMAT_MASK	0x0f	/* format of the encoded value */
6362306a36Sopenharmony_ci#define DW_EH_PE_APPL_MASK	0x70	/* how the value is to be applied */
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci/* Pointer-encoding formats: */
6662306a36Sopenharmony_ci#define DW_EH_PE_omit		0xff
6762306a36Sopenharmony_ci#define DW_EH_PE_ptr		0x00	/* pointer-sized unsigned value */
6862306a36Sopenharmony_ci#define DW_EH_PE_udata4		0x03	/* unsigned 32-bit value */
6962306a36Sopenharmony_ci#define DW_EH_PE_udata8		0x04	/* unsigned 64-bit value */
7062306a36Sopenharmony_ci#define DW_EH_PE_sdata4		0x0b	/* signed 32-bit value */
7162306a36Sopenharmony_ci#define DW_EH_PE_sdata8		0x0c	/* signed 64-bit value */
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci/* Pointer-encoding application: */
7462306a36Sopenharmony_ci#define DW_EH_PE_absptr		0x00	/* absolute value */
7562306a36Sopenharmony_ci#define DW_EH_PE_pcrel		0x10	/* rel. to addr. of encoded value */
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci/*
7862306a36Sopenharmony_ci * The following are not documented by LSB v1.3, yet they are used by
7962306a36Sopenharmony_ci * GCC, presumably they aren't documented by LSB since they aren't
8062306a36Sopenharmony_ci * used on Linux:
8162306a36Sopenharmony_ci */
8262306a36Sopenharmony_ci#define DW_EH_PE_funcrel	0x40	/* start-of-procedure-relative */
8362306a36Sopenharmony_ci#define DW_EH_PE_aligned	0x50	/* aligned pointer */
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci/* Flags intentionally not handled, since they're not needed:
8662306a36Sopenharmony_ci * #define DW_EH_PE_indirect      0x80
8762306a36Sopenharmony_ci * #define DW_EH_PE_uleb128       0x01
8862306a36Sopenharmony_ci * #define DW_EH_PE_udata2        0x02
8962306a36Sopenharmony_ci * #define DW_EH_PE_sleb128       0x09
9062306a36Sopenharmony_ci * #define DW_EH_PE_sdata2        0x0a
9162306a36Sopenharmony_ci * #define DW_EH_PE_textrel       0x20
9262306a36Sopenharmony_ci * #define DW_EH_PE_datarel       0x30
9362306a36Sopenharmony_ci */
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_cistruct unwind_info {
9662306a36Sopenharmony_ci	struct perf_sample	*sample;
9762306a36Sopenharmony_ci	struct machine		*machine;
9862306a36Sopenharmony_ci	struct thread		*thread;
9962306a36Sopenharmony_ci	bool			 best_effort;
10062306a36Sopenharmony_ci};
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci#define dw_read(ptr, type, end) ({	\
10362306a36Sopenharmony_ci	type *__p = (type *) ptr;	\
10462306a36Sopenharmony_ci	type  __v;			\
10562306a36Sopenharmony_ci	if ((__p + 1) > (type *) end)	\
10662306a36Sopenharmony_ci		return -EINVAL;		\
10762306a36Sopenharmony_ci	__v = *__p++;			\
10862306a36Sopenharmony_ci	ptr = (typeof(ptr)) __p;	\
10962306a36Sopenharmony_ci	__v;				\
11062306a36Sopenharmony_ci	})
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_cistatic int __dw_read_encoded_value(u8 **p, u8 *end, u64 *val,
11362306a36Sopenharmony_ci				   u8 encoding)
11462306a36Sopenharmony_ci{
11562306a36Sopenharmony_ci	u8 *cur = *p;
11662306a36Sopenharmony_ci	*val = 0;
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	switch (encoding) {
11962306a36Sopenharmony_ci	case DW_EH_PE_omit:
12062306a36Sopenharmony_ci		*val = 0;
12162306a36Sopenharmony_ci		goto out;
12262306a36Sopenharmony_ci	case DW_EH_PE_ptr:
12362306a36Sopenharmony_ci		*val = dw_read(cur, unsigned long, end);
12462306a36Sopenharmony_ci		goto out;
12562306a36Sopenharmony_ci	default:
12662306a36Sopenharmony_ci		break;
12762306a36Sopenharmony_ci	}
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	switch (encoding & DW_EH_PE_APPL_MASK) {
13062306a36Sopenharmony_ci	case DW_EH_PE_absptr:
13162306a36Sopenharmony_ci		break;
13262306a36Sopenharmony_ci	case DW_EH_PE_pcrel:
13362306a36Sopenharmony_ci		*val = (unsigned long) cur;
13462306a36Sopenharmony_ci		break;
13562306a36Sopenharmony_ci	default:
13662306a36Sopenharmony_ci		return -EINVAL;
13762306a36Sopenharmony_ci	}
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	if ((encoding & 0x07) == 0x00)
14062306a36Sopenharmony_ci		encoding |= DW_EH_PE_udata4;
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	switch (encoding & DW_EH_PE_FORMAT_MASK) {
14362306a36Sopenharmony_ci	case DW_EH_PE_sdata4:
14462306a36Sopenharmony_ci		*val += dw_read(cur, s32, end);
14562306a36Sopenharmony_ci		break;
14662306a36Sopenharmony_ci	case DW_EH_PE_udata4:
14762306a36Sopenharmony_ci		*val += dw_read(cur, u32, end);
14862306a36Sopenharmony_ci		break;
14962306a36Sopenharmony_ci	case DW_EH_PE_sdata8:
15062306a36Sopenharmony_ci		*val += dw_read(cur, s64, end);
15162306a36Sopenharmony_ci		break;
15262306a36Sopenharmony_ci	case DW_EH_PE_udata8:
15362306a36Sopenharmony_ci		*val += dw_read(cur, u64, end);
15462306a36Sopenharmony_ci		break;
15562306a36Sopenharmony_ci	default:
15662306a36Sopenharmony_ci		return -EINVAL;
15762306a36Sopenharmony_ci	}
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci out:
16062306a36Sopenharmony_ci	*p = cur;
16162306a36Sopenharmony_ci	return 0;
16262306a36Sopenharmony_ci}
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci#define dw_read_encoded_value(ptr, end, enc) ({			\
16562306a36Sopenharmony_ci	u64 __v;						\
16662306a36Sopenharmony_ci	if (__dw_read_encoded_value(&ptr, end, &__v, enc)) {	\
16762306a36Sopenharmony_ci		return -EINVAL;                                 \
16862306a36Sopenharmony_ci	}                                                       \
16962306a36Sopenharmony_ci	__v;                                                    \
17062306a36Sopenharmony_ci	})
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_cistatic int elf_section_address_and_offset(int fd, const char *name, u64 *address, u64 *offset)
17362306a36Sopenharmony_ci{
17462306a36Sopenharmony_ci	Elf *elf;
17562306a36Sopenharmony_ci	GElf_Ehdr ehdr;
17662306a36Sopenharmony_ci	GElf_Shdr shdr;
17762306a36Sopenharmony_ci	int ret = -1;
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL);
18062306a36Sopenharmony_ci	if (elf == NULL)
18162306a36Sopenharmony_ci		return -1;
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	if (gelf_getehdr(elf, &ehdr) == NULL)
18462306a36Sopenharmony_ci		goto out_err;
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	if (!elf_section_by_name(elf, &ehdr, &shdr, name, NULL))
18762306a36Sopenharmony_ci		goto out_err;
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	*address = shdr.sh_addr;
19062306a36Sopenharmony_ci	*offset = shdr.sh_offset;
19162306a36Sopenharmony_ci	ret = 0;
19262306a36Sopenharmony_ciout_err:
19362306a36Sopenharmony_ci	elf_end(elf);
19462306a36Sopenharmony_ci	return ret;
19562306a36Sopenharmony_ci}
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci#ifndef NO_LIBUNWIND_DEBUG_FRAME
19862306a36Sopenharmony_cistatic u64 elf_section_offset(int fd, const char *name)
19962306a36Sopenharmony_ci{
20062306a36Sopenharmony_ci	u64 address, offset = 0;
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	if (elf_section_address_and_offset(fd, name, &address, &offset))
20362306a36Sopenharmony_ci		return 0;
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	return offset;
20662306a36Sopenharmony_ci}
20762306a36Sopenharmony_ci#endif
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_cistatic u64 elf_base_address(int fd)
21062306a36Sopenharmony_ci{
21162306a36Sopenharmony_ci	Elf *elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL);
21262306a36Sopenharmony_ci	GElf_Phdr phdr;
21362306a36Sopenharmony_ci	u64 retval = 0;
21462306a36Sopenharmony_ci	size_t i, phdrnum = 0;
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	if (elf == NULL)
21762306a36Sopenharmony_ci		return 0;
21862306a36Sopenharmony_ci	(void)elf_getphdrnum(elf, &phdrnum);
21962306a36Sopenharmony_ci	/* PT_LOAD segments are sorted by p_vaddr, so the first has the minimum p_vaddr. */
22062306a36Sopenharmony_ci	for (i = 0; i < phdrnum; i++) {
22162306a36Sopenharmony_ci		if (gelf_getphdr(elf, i, &phdr) && phdr.p_type == PT_LOAD) {
22262306a36Sopenharmony_ci			retval = phdr.p_vaddr & -getpagesize();
22362306a36Sopenharmony_ci			break;
22462306a36Sopenharmony_ci		}
22562306a36Sopenharmony_ci	}
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci	elf_end(elf);
22862306a36Sopenharmony_ci	return retval;
22962306a36Sopenharmony_ci}
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci#ifndef NO_LIBUNWIND_DEBUG_FRAME
23262306a36Sopenharmony_cistatic int elf_is_exec(int fd, const char *name)
23362306a36Sopenharmony_ci{
23462306a36Sopenharmony_ci	Elf *elf;
23562306a36Sopenharmony_ci	GElf_Ehdr ehdr;
23662306a36Sopenharmony_ci	int retval = 0;
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL);
23962306a36Sopenharmony_ci	if (elf == NULL)
24062306a36Sopenharmony_ci		return 0;
24162306a36Sopenharmony_ci	if (gelf_getehdr(elf, &ehdr) == NULL)
24262306a36Sopenharmony_ci		goto out;
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	retval = (ehdr.e_type == ET_EXEC);
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ciout:
24762306a36Sopenharmony_ci	elf_end(elf);
24862306a36Sopenharmony_ci	pr_debug("unwind: elf_is_exec(%s): %d\n", name, retval);
24962306a36Sopenharmony_ci	return retval;
25062306a36Sopenharmony_ci}
25162306a36Sopenharmony_ci#endif
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_cistruct table_entry {
25462306a36Sopenharmony_ci	u32 start_ip_offset;
25562306a36Sopenharmony_ci	u32 fde_offset;
25662306a36Sopenharmony_ci};
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_cistruct eh_frame_hdr {
25962306a36Sopenharmony_ci	unsigned char version;
26062306a36Sopenharmony_ci	unsigned char eh_frame_ptr_enc;
26162306a36Sopenharmony_ci	unsigned char fde_count_enc;
26262306a36Sopenharmony_ci	unsigned char table_enc;
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci	/*
26562306a36Sopenharmony_ci	 * The rest of the header is variable-length and consists of the
26662306a36Sopenharmony_ci	 * following members:
26762306a36Sopenharmony_ci	 *
26862306a36Sopenharmony_ci	 *	encoded_t eh_frame_ptr;
26962306a36Sopenharmony_ci	 *	encoded_t fde_count;
27062306a36Sopenharmony_ci	 */
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci	/* A single encoded pointer should not be more than 8 bytes. */
27362306a36Sopenharmony_ci	u64 enc[2];
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci	/*
27662306a36Sopenharmony_ci	 * struct {
27762306a36Sopenharmony_ci	 *    encoded_t start_ip;
27862306a36Sopenharmony_ci	 *    encoded_t fde_addr;
27962306a36Sopenharmony_ci	 * } binary_search_table[fde_count];
28062306a36Sopenharmony_ci	 */
28162306a36Sopenharmony_ci	char data[];
28262306a36Sopenharmony_ci} __packed;
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_cistatic int unwind_spec_ehframe(struct dso *dso, struct machine *machine,
28562306a36Sopenharmony_ci			       u64 offset, u64 *table_data_offset, u64 *fde_count)
28662306a36Sopenharmony_ci{
28762306a36Sopenharmony_ci	struct eh_frame_hdr hdr;
28862306a36Sopenharmony_ci	u8 *enc = (u8 *) &hdr.enc;
28962306a36Sopenharmony_ci	u8 *end = (u8 *) &hdr.data;
29062306a36Sopenharmony_ci	ssize_t r;
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	r = dso__data_read_offset(dso, machine, offset,
29362306a36Sopenharmony_ci				  (u8 *) &hdr, sizeof(hdr));
29462306a36Sopenharmony_ci	if (r != sizeof(hdr))
29562306a36Sopenharmony_ci		return -EINVAL;
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci	/* We dont need eh_frame_ptr, just skip it. */
29862306a36Sopenharmony_ci	dw_read_encoded_value(enc, end, hdr.eh_frame_ptr_enc);
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci	*fde_count  = dw_read_encoded_value(enc, end, hdr.fde_count_enc);
30162306a36Sopenharmony_ci	*table_data_offset = enc - (u8 *) &hdr;
30262306a36Sopenharmony_ci	return 0;
30362306a36Sopenharmony_ci}
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_cistatic int read_unwind_spec_eh_frame(struct dso *dso, struct unwind_info *ui,
30662306a36Sopenharmony_ci				     u64 *table_data, u64 *segbase,
30762306a36Sopenharmony_ci				     u64 *fde_count)
30862306a36Sopenharmony_ci{
30962306a36Sopenharmony_ci	struct map_rb_node *map_node;
31062306a36Sopenharmony_ci	u64 base_addr = UINT64_MAX;
31162306a36Sopenharmony_ci	int ret, fd;
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci	if (dso->data.eh_frame_hdr_offset == 0) {
31462306a36Sopenharmony_ci		fd = dso__data_get_fd(dso, ui->machine);
31562306a36Sopenharmony_ci		if (fd < 0)
31662306a36Sopenharmony_ci			return -EINVAL;
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci		/* Check the .eh_frame section for unwinding info */
31962306a36Sopenharmony_ci		ret = elf_section_address_and_offset(fd, ".eh_frame_hdr",
32062306a36Sopenharmony_ci						     &dso->data.eh_frame_hdr_addr,
32162306a36Sopenharmony_ci						     &dso->data.eh_frame_hdr_offset);
32262306a36Sopenharmony_ci		dso->data.elf_base_addr = elf_base_address(fd);
32362306a36Sopenharmony_ci		dso__data_put_fd(dso);
32462306a36Sopenharmony_ci		if (ret || dso->data.eh_frame_hdr_offset == 0)
32562306a36Sopenharmony_ci			return -EINVAL;
32662306a36Sopenharmony_ci	}
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci	maps__for_each_entry(thread__maps(ui->thread), map_node) {
32962306a36Sopenharmony_ci		struct map *map = map_node->map;
33062306a36Sopenharmony_ci		u64 start = map__start(map) - map__pgoff(map);
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci		if (map__dso(map) == dso && start < base_addr)
33362306a36Sopenharmony_ci			base_addr = start;
33462306a36Sopenharmony_ci	}
33562306a36Sopenharmony_ci	base_addr -= dso->data.elf_base_addr;
33662306a36Sopenharmony_ci	/* Address of .eh_frame_hdr */
33762306a36Sopenharmony_ci	*segbase = base_addr + dso->data.eh_frame_hdr_addr;
33862306a36Sopenharmony_ci	ret = unwind_spec_ehframe(dso, ui->machine, dso->data.eh_frame_hdr_offset,
33962306a36Sopenharmony_ci				   table_data, fde_count);
34062306a36Sopenharmony_ci	if (ret)
34162306a36Sopenharmony_ci		return ret;
34262306a36Sopenharmony_ci	/* binary_search_table offset plus .eh_frame_hdr address */
34362306a36Sopenharmony_ci	*table_data += *segbase;
34462306a36Sopenharmony_ci	return 0;
34562306a36Sopenharmony_ci}
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci#ifndef NO_LIBUNWIND_DEBUG_FRAME
34862306a36Sopenharmony_cistatic int read_unwind_spec_debug_frame(struct dso *dso,
34962306a36Sopenharmony_ci					struct machine *machine, u64 *offset)
35062306a36Sopenharmony_ci{
35162306a36Sopenharmony_ci	int fd;
35262306a36Sopenharmony_ci	u64 ofs = dso->data.debug_frame_offset;
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci	/* debug_frame can reside in:
35562306a36Sopenharmony_ci	 *  - dso
35662306a36Sopenharmony_ci	 *  - debug pointed by symsrc_filename
35762306a36Sopenharmony_ci	 *  - gnu_debuglink, which doesn't necessary
35862306a36Sopenharmony_ci	 *    has to be pointed by symsrc_filename
35962306a36Sopenharmony_ci	 */
36062306a36Sopenharmony_ci	if (ofs == 0) {
36162306a36Sopenharmony_ci		fd = dso__data_get_fd(dso, machine);
36262306a36Sopenharmony_ci		if (fd >= 0) {
36362306a36Sopenharmony_ci			ofs = elf_section_offset(fd, ".debug_frame");
36462306a36Sopenharmony_ci			dso__data_put_fd(dso);
36562306a36Sopenharmony_ci		}
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci		if (ofs <= 0) {
36862306a36Sopenharmony_ci			fd = open(dso->symsrc_filename, O_RDONLY);
36962306a36Sopenharmony_ci			if (fd >= 0) {
37062306a36Sopenharmony_ci				ofs = elf_section_offset(fd, ".debug_frame");
37162306a36Sopenharmony_ci				close(fd);
37262306a36Sopenharmony_ci			}
37362306a36Sopenharmony_ci		}
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci		if (ofs <= 0) {
37662306a36Sopenharmony_ci			char *debuglink = malloc(PATH_MAX);
37762306a36Sopenharmony_ci			int ret = 0;
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci			ret = dso__read_binary_type_filename(
38062306a36Sopenharmony_ci				dso, DSO_BINARY_TYPE__DEBUGLINK,
38162306a36Sopenharmony_ci				machine->root_dir, debuglink, PATH_MAX);
38262306a36Sopenharmony_ci			if (!ret) {
38362306a36Sopenharmony_ci				fd = open(debuglink, O_RDONLY);
38462306a36Sopenharmony_ci				if (fd >= 0) {
38562306a36Sopenharmony_ci					ofs = elf_section_offset(fd,
38662306a36Sopenharmony_ci							".debug_frame");
38762306a36Sopenharmony_ci					close(fd);
38862306a36Sopenharmony_ci				}
38962306a36Sopenharmony_ci			}
39062306a36Sopenharmony_ci			if (ofs > 0) {
39162306a36Sopenharmony_ci				if (dso->symsrc_filename != NULL) {
39262306a36Sopenharmony_ci					pr_warning(
39362306a36Sopenharmony_ci						"%s: overwrite symsrc(%s,%s)\n",
39462306a36Sopenharmony_ci							__func__,
39562306a36Sopenharmony_ci							dso->symsrc_filename,
39662306a36Sopenharmony_ci							debuglink);
39762306a36Sopenharmony_ci					zfree(&dso->symsrc_filename);
39862306a36Sopenharmony_ci				}
39962306a36Sopenharmony_ci				dso->symsrc_filename = debuglink;
40062306a36Sopenharmony_ci			} else {
40162306a36Sopenharmony_ci				free(debuglink);
40262306a36Sopenharmony_ci			}
40362306a36Sopenharmony_ci		}
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci		dso->data.debug_frame_offset = ofs;
40662306a36Sopenharmony_ci	}
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci	*offset = ofs;
40962306a36Sopenharmony_ci	if (*offset)
41062306a36Sopenharmony_ci		return 0;
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_ci	return -EINVAL;
41362306a36Sopenharmony_ci}
41462306a36Sopenharmony_ci#endif
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_cistatic struct map *find_map(unw_word_t ip, struct unwind_info *ui)
41762306a36Sopenharmony_ci{
41862306a36Sopenharmony_ci	struct addr_location al;
41962306a36Sopenharmony_ci	struct map *ret;
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_ci	addr_location__init(&al);
42262306a36Sopenharmony_ci	thread__find_map(ui->thread, PERF_RECORD_MISC_USER, ip, &al);
42362306a36Sopenharmony_ci	ret = map__get(al.map);
42462306a36Sopenharmony_ci	addr_location__exit(&al);
42562306a36Sopenharmony_ci	return ret;
42662306a36Sopenharmony_ci}
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_cistatic int
42962306a36Sopenharmony_cifind_proc_info(unw_addr_space_t as, unw_word_t ip, unw_proc_info_t *pi,
43062306a36Sopenharmony_ci	       int need_unwind_info, void *arg)
43162306a36Sopenharmony_ci{
43262306a36Sopenharmony_ci	struct unwind_info *ui = arg;
43362306a36Sopenharmony_ci	struct map *map;
43462306a36Sopenharmony_ci	struct dso *dso;
43562306a36Sopenharmony_ci	unw_dyn_info_t di;
43662306a36Sopenharmony_ci	u64 table_data, segbase, fde_count;
43762306a36Sopenharmony_ci	int ret = -EINVAL;
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci	map = find_map(ip, ui);
44062306a36Sopenharmony_ci	if (!map)
44162306a36Sopenharmony_ci		return -EINVAL;
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ci	dso = map__dso(map);
44462306a36Sopenharmony_ci	if (!dso) {
44562306a36Sopenharmony_ci		map__put(map);
44662306a36Sopenharmony_ci		return -EINVAL;
44762306a36Sopenharmony_ci	}
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci	pr_debug("unwind: find_proc_info dso %s\n", dso->name);
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_ci	/* Check the .eh_frame section for unwinding info */
45262306a36Sopenharmony_ci	if (!read_unwind_spec_eh_frame(dso, ui, &table_data, &segbase, &fde_count)) {
45362306a36Sopenharmony_ci		memset(&di, 0, sizeof(di));
45462306a36Sopenharmony_ci		di.format   = UNW_INFO_FORMAT_REMOTE_TABLE;
45562306a36Sopenharmony_ci		di.start_ip = map__start(map);
45662306a36Sopenharmony_ci		di.end_ip   = map__end(map);
45762306a36Sopenharmony_ci		di.u.rti.segbase    = segbase;
45862306a36Sopenharmony_ci		di.u.rti.table_data = table_data;
45962306a36Sopenharmony_ci		di.u.rti.table_len  = fde_count * sizeof(struct table_entry)
46062306a36Sopenharmony_ci				      / sizeof(unw_word_t);
46162306a36Sopenharmony_ci		ret = dwarf_search_unwind_table(as, ip, &di, pi,
46262306a36Sopenharmony_ci						need_unwind_info, arg);
46362306a36Sopenharmony_ci	}
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_ci#ifndef NO_LIBUNWIND_DEBUG_FRAME
46662306a36Sopenharmony_ci	/* Check the .debug_frame section for unwinding info */
46762306a36Sopenharmony_ci	if (ret < 0 &&
46862306a36Sopenharmony_ci	    !read_unwind_spec_debug_frame(dso, ui->machine, &segbase)) {
46962306a36Sopenharmony_ci		int fd = dso__data_get_fd(dso, ui->machine);
47062306a36Sopenharmony_ci		int is_exec = elf_is_exec(fd, dso->name);
47162306a36Sopenharmony_ci		u64 start = map__start(map);
47262306a36Sopenharmony_ci		unw_word_t base = is_exec ? 0 : start;
47362306a36Sopenharmony_ci		const char *symfile;
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci		if (fd >= 0)
47662306a36Sopenharmony_ci			dso__data_put_fd(dso);
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_ci		symfile = dso->symsrc_filename ?: dso->name;
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci		memset(&di, 0, sizeof(di));
48162306a36Sopenharmony_ci		if (dwarf_find_debug_frame(0, &di, ip, base, symfile, start, map__end(map)))
48262306a36Sopenharmony_ci			ret = dwarf_search_unwind_table(as, ip, &di, pi,
48362306a36Sopenharmony_ci							need_unwind_info, arg);
48462306a36Sopenharmony_ci	}
48562306a36Sopenharmony_ci#endif
48662306a36Sopenharmony_ci	map__put(map);
48762306a36Sopenharmony_ci	return ret;
48862306a36Sopenharmony_ci}
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_cistatic int access_fpreg(unw_addr_space_t __maybe_unused as,
49162306a36Sopenharmony_ci			unw_regnum_t __maybe_unused num,
49262306a36Sopenharmony_ci			unw_fpreg_t __maybe_unused *val,
49362306a36Sopenharmony_ci			int __maybe_unused __write,
49462306a36Sopenharmony_ci			void __maybe_unused *arg)
49562306a36Sopenharmony_ci{
49662306a36Sopenharmony_ci	pr_err("unwind: access_fpreg unsupported\n");
49762306a36Sopenharmony_ci	return -UNW_EINVAL;
49862306a36Sopenharmony_ci}
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_cistatic int get_dyn_info_list_addr(unw_addr_space_t __maybe_unused as,
50162306a36Sopenharmony_ci				  unw_word_t __maybe_unused *dil_addr,
50262306a36Sopenharmony_ci				  void __maybe_unused *arg)
50362306a36Sopenharmony_ci{
50462306a36Sopenharmony_ci	return -UNW_ENOINFO;
50562306a36Sopenharmony_ci}
50662306a36Sopenharmony_ci
50762306a36Sopenharmony_cistatic int resume(unw_addr_space_t __maybe_unused as,
50862306a36Sopenharmony_ci		  unw_cursor_t __maybe_unused *cu,
50962306a36Sopenharmony_ci		  void __maybe_unused *arg)
51062306a36Sopenharmony_ci{
51162306a36Sopenharmony_ci	pr_err("unwind: resume unsupported\n");
51262306a36Sopenharmony_ci	return -UNW_EINVAL;
51362306a36Sopenharmony_ci}
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_cistatic int
51662306a36Sopenharmony_ciget_proc_name(unw_addr_space_t __maybe_unused as,
51762306a36Sopenharmony_ci	      unw_word_t __maybe_unused addr,
51862306a36Sopenharmony_ci		char __maybe_unused *bufp, size_t __maybe_unused buf_len,
51962306a36Sopenharmony_ci		unw_word_t __maybe_unused *offp, void __maybe_unused *arg)
52062306a36Sopenharmony_ci{
52162306a36Sopenharmony_ci	pr_err("unwind: get_proc_name unsupported\n");
52262306a36Sopenharmony_ci	return -UNW_EINVAL;
52362306a36Sopenharmony_ci}
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_cistatic int access_dso_mem(struct unwind_info *ui, unw_word_t addr,
52662306a36Sopenharmony_ci			  unw_word_t *data)
52762306a36Sopenharmony_ci{
52862306a36Sopenharmony_ci	struct map *map;
52962306a36Sopenharmony_ci	struct dso *dso;
53062306a36Sopenharmony_ci	ssize_t size;
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_ci	map = find_map(addr, ui);
53362306a36Sopenharmony_ci	if (!map) {
53462306a36Sopenharmony_ci		pr_debug("unwind: no map for %lx\n", (unsigned long)addr);
53562306a36Sopenharmony_ci		return -1;
53662306a36Sopenharmony_ci	}
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_ci	dso = map__dso(map);
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ci	if (!dso) {
54162306a36Sopenharmony_ci		map__put(map);
54262306a36Sopenharmony_ci		return -1;
54362306a36Sopenharmony_ci	}
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_ci	size = dso__data_read_addr(dso, map, ui->machine,
54662306a36Sopenharmony_ci				   addr, (u8 *) data, sizeof(*data));
54762306a36Sopenharmony_ci	map__put(map);
54862306a36Sopenharmony_ci	return !(size == sizeof(*data));
54962306a36Sopenharmony_ci}
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_cistatic int access_mem(unw_addr_space_t __maybe_unused as,
55262306a36Sopenharmony_ci		      unw_word_t addr, unw_word_t *valp,
55362306a36Sopenharmony_ci		      int __write, void *arg)
55462306a36Sopenharmony_ci{
55562306a36Sopenharmony_ci	struct unwind_info *ui = arg;
55662306a36Sopenharmony_ci	const char *arch = perf_env__arch(ui->machine->env);
55762306a36Sopenharmony_ci	struct stack_dump *stack = &ui->sample->user_stack;
55862306a36Sopenharmony_ci	u64 start, end;
55962306a36Sopenharmony_ci	int offset;
56062306a36Sopenharmony_ci	int ret;
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_ci	/* Don't support write, probably not needed. */
56362306a36Sopenharmony_ci	if (__write || !stack || !ui->sample->user_regs.regs) {
56462306a36Sopenharmony_ci		*valp = 0;
56562306a36Sopenharmony_ci		return 0;
56662306a36Sopenharmony_ci	}
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci	ret = perf_reg_value(&start, &ui->sample->user_regs,
56962306a36Sopenharmony_ci			     perf_arch_reg_sp(arch));
57062306a36Sopenharmony_ci	if (ret)
57162306a36Sopenharmony_ci		return ret;
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_ci	end = start + stack->size;
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_ci	/* Check overflow. */
57662306a36Sopenharmony_ci	if (addr + sizeof(unw_word_t) < addr)
57762306a36Sopenharmony_ci		return -EINVAL;
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_ci	if (addr < start || addr + sizeof(unw_word_t) >= end) {
58062306a36Sopenharmony_ci		ret = access_dso_mem(ui, addr, valp);
58162306a36Sopenharmony_ci		if (ret) {
58262306a36Sopenharmony_ci			pr_debug("unwind: access_mem %p not inside range"
58362306a36Sopenharmony_ci				 " 0x%" PRIx64 "-0x%" PRIx64 "\n",
58462306a36Sopenharmony_ci				 (void *) (uintptr_t) addr, start, end);
58562306a36Sopenharmony_ci			*valp = 0;
58662306a36Sopenharmony_ci			return ret;
58762306a36Sopenharmony_ci		}
58862306a36Sopenharmony_ci		return 0;
58962306a36Sopenharmony_ci	}
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_ci	offset = addr - start;
59262306a36Sopenharmony_ci	*valp  = *(unw_word_t *)&stack->data[offset];
59362306a36Sopenharmony_ci	pr_debug("unwind: access_mem addr %p val %lx, offset %d\n",
59462306a36Sopenharmony_ci		 (void *) (uintptr_t) addr, (unsigned long)*valp, offset);
59562306a36Sopenharmony_ci	return 0;
59662306a36Sopenharmony_ci}
59762306a36Sopenharmony_ci
59862306a36Sopenharmony_cistatic int access_reg(unw_addr_space_t __maybe_unused as,
59962306a36Sopenharmony_ci		      unw_regnum_t regnum, unw_word_t *valp,
60062306a36Sopenharmony_ci		      int __write, void *arg)
60162306a36Sopenharmony_ci{
60262306a36Sopenharmony_ci	struct unwind_info *ui = arg;
60362306a36Sopenharmony_ci	int id, ret;
60462306a36Sopenharmony_ci	u64 val;
60562306a36Sopenharmony_ci
60662306a36Sopenharmony_ci	/* Don't support write, I suspect we don't need it. */
60762306a36Sopenharmony_ci	if (__write) {
60862306a36Sopenharmony_ci		pr_err("unwind: access_reg w %d\n", regnum);
60962306a36Sopenharmony_ci		return 0;
61062306a36Sopenharmony_ci	}
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_ci	if (!ui->sample->user_regs.regs) {
61362306a36Sopenharmony_ci		*valp = 0;
61462306a36Sopenharmony_ci		return 0;
61562306a36Sopenharmony_ci	}
61662306a36Sopenharmony_ci
61762306a36Sopenharmony_ci	id = LIBUNWIND__ARCH_REG_ID(regnum);
61862306a36Sopenharmony_ci	if (id < 0)
61962306a36Sopenharmony_ci		return -EINVAL;
62062306a36Sopenharmony_ci
62162306a36Sopenharmony_ci	ret = perf_reg_value(&val, &ui->sample->user_regs, id);
62262306a36Sopenharmony_ci	if (ret) {
62362306a36Sopenharmony_ci		if (!ui->best_effort)
62462306a36Sopenharmony_ci			pr_err("unwind: can't read reg %d\n", regnum);
62562306a36Sopenharmony_ci		return ret;
62662306a36Sopenharmony_ci	}
62762306a36Sopenharmony_ci
62862306a36Sopenharmony_ci	*valp = (unw_word_t) val;
62962306a36Sopenharmony_ci	pr_debug("unwind: reg %d, val %lx\n", regnum, (unsigned long)*valp);
63062306a36Sopenharmony_ci	return 0;
63162306a36Sopenharmony_ci}
63262306a36Sopenharmony_ci
63362306a36Sopenharmony_cistatic void put_unwind_info(unw_addr_space_t __maybe_unused as,
63462306a36Sopenharmony_ci			    unw_proc_info_t *pi __maybe_unused,
63562306a36Sopenharmony_ci			    void *arg __maybe_unused)
63662306a36Sopenharmony_ci{
63762306a36Sopenharmony_ci	pr_debug("unwind: put_unwind_info called\n");
63862306a36Sopenharmony_ci}
63962306a36Sopenharmony_ci
64062306a36Sopenharmony_cistatic int entry(u64 ip, struct thread *thread,
64162306a36Sopenharmony_ci		 unwind_entry_cb_t cb, void *arg)
64262306a36Sopenharmony_ci{
64362306a36Sopenharmony_ci	struct unwind_entry e;
64462306a36Sopenharmony_ci	struct addr_location al;
64562306a36Sopenharmony_ci	int ret;
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_ci	addr_location__init(&al);
64862306a36Sopenharmony_ci	e.ms.sym = thread__find_symbol(thread, PERF_RECORD_MISC_USER, ip, &al);
64962306a36Sopenharmony_ci	e.ip     = ip;
65062306a36Sopenharmony_ci	e.ms.map = al.map;
65162306a36Sopenharmony_ci	e.ms.maps = al.maps;
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_ci	pr_debug("unwind: %s:ip = 0x%" PRIx64 " (0x%" PRIx64 ")\n",
65462306a36Sopenharmony_ci		 al.sym ? al.sym->name : "''",
65562306a36Sopenharmony_ci		 ip,
65662306a36Sopenharmony_ci		 al.map ? map__map_ip(al.map, ip) : (u64) 0);
65762306a36Sopenharmony_ci
65862306a36Sopenharmony_ci	ret = cb(&e, arg);
65962306a36Sopenharmony_ci	addr_location__exit(&al);
66062306a36Sopenharmony_ci	return ret;
66162306a36Sopenharmony_ci}
66262306a36Sopenharmony_ci
66362306a36Sopenharmony_cistatic void display_error(int err)
66462306a36Sopenharmony_ci{
66562306a36Sopenharmony_ci	switch (err) {
66662306a36Sopenharmony_ci	case UNW_EINVAL:
66762306a36Sopenharmony_ci		pr_err("unwind: Only supports local.\n");
66862306a36Sopenharmony_ci		break;
66962306a36Sopenharmony_ci	case UNW_EUNSPEC:
67062306a36Sopenharmony_ci		pr_err("unwind: Unspecified error.\n");
67162306a36Sopenharmony_ci		break;
67262306a36Sopenharmony_ci	case UNW_EBADREG:
67362306a36Sopenharmony_ci		pr_err("unwind: Register unavailable.\n");
67462306a36Sopenharmony_ci		break;
67562306a36Sopenharmony_ci	default:
67662306a36Sopenharmony_ci		break;
67762306a36Sopenharmony_ci	}
67862306a36Sopenharmony_ci}
67962306a36Sopenharmony_ci
68062306a36Sopenharmony_cistatic unw_accessors_t accessors = {
68162306a36Sopenharmony_ci	.find_proc_info		= find_proc_info,
68262306a36Sopenharmony_ci	.put_unwind_info	= put_unwind_info,
68362306a36Sopenharmony_ci	.get_dyn_info_list_addr	= get_dyn_info_list_addr,
68462306a36Sopenharmony_ci	.access_mem		= access_mem,
68562306a36Sopenharmony_ci	.access_reg		= access_reg,
68662306a36Sopenharmony_ci	.access_fpreg		= access_fpreg,
68762306a36Sopenharmony_ci	.resume			= resume,
68862306a36Sopenharmony_ci	.get_proc_name		= get_proc_name,
68962306a36Sopenharmony_ci};
69062306a36Sopenharmony_ci
69162306a36Sopenharmony_cistatic int _unwind__prepare_access(struct maps *maps)
69262306a36Sopenharmony_ci{
69362306a36Sopenharmony_ci	void *addr_space = unw_create_addr_space(&accessors, 0);
69462306a36Sopenharmony_ci
69562306a36Sopenharmony_ci	RC_CHK_ACCESS(maps)->addr_space = addr_space;
69662306a36Sopenharmony_ci	if (!addr_space) {
69762306a36Sopenharmony_ci		pr_err("unwind: Can't create unwind address space.\n");
69862306a36Sopenharmony_ci		return -ENOMEM;
69962306a36Sopenharmony_ci	}
70062306a36Sopenharmony_ci
70162306a36Sopenharmony_ci	unw_set_caching_policy(addr_space, UNW_CACHE_GLOBAL);
70262306a36Sopenharmony_ci	return 0;
70362306a36Sopenharmony_ci}
70462306a36Sopenharmony_ci
70562306a36Sopenharmony_cistatic void _unwind__flush_access(struct maps *maps)
70662306a36Sopenharmony_ci{
70762306a36Sopenharmony_ci	unw_flush_cache(maps__addr_space(maps), 0, 0);
70862306a36Sopenharmony_ci}
70962306a36Sopenharmony_ci
71062306a36Sopenharmony_cistatic void _unwind__finish_access(struct maps *maps)
71162306a36Sopenharmony_ci{
71262306a36Sopenharmony_ci	unw_destroy_addr_space(maps__addr_space(maps));
71362306a36Sopenharmony_ci}
71462306a36Sopenharmony_ci
71562306a36Sopenharmony_cistatic int get_entries(struct unwind_info *ui, unwind_entry_cb_t cb,
71662306a36Sopenharmony_ci		       void *arg, int max_stack)
71762306a36Sopenharmony_ci{
71862306a36Sopenharmony_ci	const char *arch = perf_env__arch(ui->machine->env);
71962306a36Sopenharmony_ci	u64 val;
72062306a36Sopenharmony_ci	unw_word_t ips[max_stack];
72162306a36Sopenharmony_ci	unw_addr_space_t addr_space;
72262306a36Sopenharmony_ci	unw_cursor_t c;
72362306a36Sopenharmony_ci	int ret, i = 0;
72462306a36Sopenharmony_ci
72562306a36Sopenharmony_ci	ret = perf_reg_value(&val, &ui->sample->user_regs,
72662306a36Sopenharmony_ci			     perf_arch_reg_ip(arch));
72762306a36Sopenharmony_ci	if (ret)
72862306a36Sopenharmony_ci		return ret;
72962306a36Sopenharmony_ci
73062306a36Sopenharmony_ci	ips[i++] = (unw_word_t) val;
73162306a36Sopenharmony_ci
73262306a36Sopenharmony_ci	/*
73362306a36Sopenharmony_ci	 * If we need more than one entry, do the DWARF
73462306a36Sopenharmony_ci	 * unwind itself.
73562306a36Sopenharmony_ci	 */
73662306a36Sopenharmony_ci	if (max_stack - 1 > 0) {
73762306a36Sopenharmony_ci		WARN_ONCE(!ui->thread, "WARNING: ui->thread is NULL");
73862306a36Sopenharmony_ci		addr_space = maps__addr_space(thread__maps(ui->thread));
73962306a36Sopenharmony_ci
74062306a36Sopenharmony_ci		if (addr_space == NULL)
74162306a36Sopenharmony_ci			return -1;
74262306a36Sopenharmony_ci
74362306a36Sopenharmony_ci		ret = unw_init_remote(&c, addr_space, ui);
74462306a36Sopenharmony_ci		if (ret && !ui->best_effort)
74562306a36Sopenharmony_ci			display_error(ret);
74662306a36Sopenharmony_ci
74762306a36Sopenharmony_ci		while (!ret && (unw_step(&c) > 0) && i < max_stack) {
74862306a36Sopenharmony_ci			unw_get_reg(&c, UNW_REG_IP, &ips[i]);
74962306a36Sopenharmony_ci
75062306a36Sopenharmony_ci			/*
75162306a36Sopenharmony_ci			 * Decrement the IP for any non-activation frames.
75262306a36Sopenharmony_ci			 * this is required to properly find the srcline
75362306a36Sopenharmony_ci			 * for caller frames.
75462306a36Sopenharmony_ci			 * See also the documentation for dwfl_frame_pc(),
75562306a36Sopenharmony_ci			 * which this code tries to replicate.
75662306a36Sopenharmony_ci			 */
75762306a36Sopenharmony_ci			if (unw_is_signal_frame(&c) <= 0)
75862306a36Sopenharmony_ci				--ips[i];
75962306a36Sopenharmony_ci
76062306a36Sopenharmony_ci			++i;
76162306a36Sopenharmony_ci		}
76262306a36Sopenharmony_ci
76362306a36Sopenharmony_ci		max_stack = i;
76462306a36Sopenharmony_ci	}
76562306a36Sopenharmony_ci
76662306a36Sopenharmony_ci	/*
76762306a36Sopenharmony_ci	 * Display what we got based on the order setup.
76862306a36Sopenharmony_ci	 */
76962306a36Sopenharmony_ci	for (i = 0; i < max_stack && !ret; i++) {
77062306a36Sopenharmony_ci		int j = i;
77162306a36Sopenharmony_ci
77262306a36Sopenharmony_ci		if (callchain_param.order == ORDER_CALLER)
77362306a36Sopenharmony_ci			j = max_stack - i - 1;
77462306a36Sopenharmony_ci		ret = ips[j] ? entry(ips[j], ui->thread, cb, arg) : 0;
77562306a36Sopenharmony_ci	}
77662306a36Sopenharmony_ci
77762306a36Sopenharmony_ci	return ret;
77862306a36Sopenharmony_ci}
77962306a36Sopenharmony_ci
78062306a36Sopenharmony_cistatic int _unwind__get_entries(unwind_entry_cb_t cb, void *arg,
78162306a36Sopenharmony_ci			struct thread *thread,
78262306a36Sopenharmony_ci			struct perf_sample *data, int max_stack,
78362306a36Sopenharmony_ci			bool best_effort)
78462306a36Sopenharmony_ci{
78562306a36Sopenharmony_ci	struct unwind_info ui = {
78662306a36Sopenharmony_ci		.sample       = data,
78762306a36Sopenharmony_ci		.thread       = thread,
78862306a36Sopenharmony_ci		.machine      = maps__machine(thread__maps(thread)),
78962306a36Sopenharmony_ci		.best_effort  = best_effort
79062306a36Sopenharmony_ci	};
79162306a36Sopenharmony_ci
79262306a36Sopenharmony_ci	if (!data->user_regs.regs)
79362306a36Sopenharmony_ci		return -EINVAL;
79462306a36Sopenharmony_ci
79562306a36Sopenharmony_ci	if (max_stack <= 0)
79662306a36Sopenharmony_ci		return -EINVAL;
79762306a36Sopenharmony_ci
79862306a36Sopenharmony_ci	return get_entries(&ui, cb, arg, max_stack);
79962306a36Sopenharmony_ci}
80062306a36Sopenharmony_ci
80162306a36Sopenharmony_cistatic struct unwind_libunwind_ops
80262306a36Sopenharmony_ci_unwind_libunwind_ops = {
80362306a36Sopenharmony_ci	.prepare_access = _unwind__prepare_access,
80462306a36Sopenharmony_ci	.flush_access   = _unwind__flush_access,
80562306a36Sopenharmony_ci	.finish_access  = _unwind__finish_access,
80662306a36Sopenharmony_ci	.get_entries    = _unwind__get_entries,
80762306a36Sopenharmony_ci};
80862306a36Sopenharmony_ci
80962306a36Sopenharmony_ci#ifndef REMOTE_UNWIND_LIBUNWIND
81062306a36Sopenharmony_cistruct unwind_libunwind_ops *
81162306a36Sopenharmony_cilocal_unwind_libunwind_ops = &_unwind_libunwind_ops;
81262306a36Sopenharmony_ci#endif
813