xref: /linux/tools/perf/util/libunwind-arch/libunwind-x86_64.c (revision 3d5e48944e824bddc20d7b874e784f7b279636fe)
1fdf08e4bSIan Rogers // SPDX-License-Identifier: GPL-2.0
2fdf08e4bSIan Rogers #include "libunwind-arch.h"
3fdf08e4bSIan Rogers #include "../debug.h"
42723e9f8SIan Rogers #include "../maps.h"
5*88b4275fSIan Rogers #include "../thread.h"
6fdf08e4bSIan Rogers #include "../../../arch/x86/include/uapi/asm/perf_regs.h"
7fdf08e4bSIan Rogers #include <linux/compiler.h>
8fdf08e4bSIan Rogers #include <linux/kernel.h>
9*88b4275fSIan Rogers #include <linux/zalloc.h>
10*88b4275fSIan Rogers #include <elf.h>
11fdf08e4bSIan Rogers #include <errno.h>
12fdf08e4bSIan Rogers 
13fdf08e4bSIan Rogers #ifdef HAVE_LIBUNWIND_X86_64_SUPPORT
14fdf08e4bSIan Rogers #include <libunwind-x86_64.h>
15fdf08e4bSIan Rogers #endif
16fdf08e4bSIan Rogers 
17fdf08e4bSIan Rogers int __get_perf_regnum_for_unw_regnum_x86_64(int unw_regnum __maybe_unused)
18fdf08e4bSIan Rogers {
19fdf08e4bSIan Rogers #ifndef HAVE_LIBUNWIND_X86_64_SUPPORT
20fdf08e4bSIan Rogers 	return -EINVAL;
21fdf08e4bSIan Rogers #else
22fdf08e4bSIan Rogers 	static const int perf_x86_64_regnums[] = {
23fdf08e4bSIan Rogers #define REGNUM(reg) [UNW_X86_64_R ## reg] = PERF_REG_X86_ ## reg
24fdf08e4bSIan Rogers 		REGNUM(AX),
25fdf08e4bSIan Rogers 		REGNUM(DX),
26fdf08e4bSIan Rogers 		REGNUM(CX),
27fdf08e4bSIan Rogers 		REGNUM(BX),
28fdf08e4bSIan Rogers 		REGNUM(SI),
29fdf08e4bSIan Rogers 		REGNUM(DI),
30fdf08e4bSIan Rogers 		REGNUM(BP),
31fdf08e4bSIan Rogers 		REGNUM(SP),
32fdf08e4bSIan Rogers 		REGNUM(IP),
33fdf08e4bSIan Rogers #undef REGNUM
34fdf08e4bSIan Rogers #define REGNUM(reg) [UNW_X86_64_ ## reg] = PERF_REG_X86_ ## reg
35fdf08e4bSIan Rogers 		REGNUM(R8),
36fdf08e4bSIan Rogers 		REGNUM(R9),
37fdf08e4bSIan Rogers 		REGNUM(R10),
38fdf08e4bSIan Rogers 		REGNUM(R11),
39fdf08e4bSIan Rogers 		REGNUM(R12),
40fdf08e4bSIan Rogers 		REGNUM(R13),
41fdf08e4bSIan Rogers 		REGNUM(R14),
42fdf08e4bSIan Rogers 		REGNUM(R15),
43fdf08e4bSIan Rogers #undef REGNUM
44fdf08e4bSIan Rogers 	};
45fdf08e4bSIan Rogers 
46fdf08e4bSIan Rogers 	if (unw_regnum == UNW_X86_64_RAX)
47fdf08e4bSIan Rogers 		return PERF_REG_X86_AX;
48fdf08e4bSIan Rogers 
49fdf08e4bSIan Rogers 	if (unw_regnum <  0 || unw_regnum >= (int)ARRAY_SIZE(perf_x86_64_regnums) ||
50fdf08e4bSIan Rogers             perf_x86_64_regnums[unw_regnum] == 0) {
51fdf08e4bSIan Rogers 		pr_err("unwind: invalid reg id %d\n", unw_regnum);
52fdf08e4bSIan Rogers 		return -EINVAL;
53fdf08e4bSIan Rogers 	}
54fdf08e4bSIan Rogers 	return perf_x86_64_regnums[unw_regnum];
55fdf08e4bSIan Rogers #endif // HAVE_LIBUNWIND_X86_64_SUPPORT
56fdf08e4bSIan Rogers }
572723e9f8SIan Rogers 
582723e9f8SIan Rogers void __libunwind_arch__flush_access_x86_64(struct maps *maps __maybe_unused)
592723e9f8SIan Rogers {
602723e9f8SIan Rogers #ifdef HAVE_LIBUNWIND_X86_64_SUPPORT
612723e9f8SIan Rogers 	unw_flush_cache(maps__addr_space(maps), 0, 0);
622723e9f8SIan Rogers #endif
632723e9f8SIan Rogers }
642723e9f8SIan Rogers 
652723e9f8SIan Rogers void __libunwind_arch__finish_access_x86_64(struct maps *maps __maybe_unused)
662723e9f8SIan Rogers {
672723e9f8SIan Rogers #ifdef HAVE_LIBUNWIND_X86_64_SUPPORT
682723e9f8SIan Rogers 	unw_destroy_addr_space(maps__addr_space(maps));
692723e9f8SIan Rogers #endif
702723e9f8SIan Rogers }
71*88b4275fSIan Rogers 
72*88b4275fSIan Rogers #ifdef HAVE_LIBUNWIND_X86_64_SUPPORT
73*88b4275fSIan Rogers static int find_proc_info(unw_addr_space_t as, unw_word_t ip, unw_proc_info_t *pi,
74*88b4275fSIan Rogers 			  int need_unwind_info, void *arg)
75*88b4275fSIan Rogers {
76*88b4275fSIan Rogers 	return __libunwind__find_proc_info(as, ip, pi, need_unwind_info, arg);
77*88b4275fSIan Rogers }
78*88b4275fSIan Rogers 
79*88b4275fSIan Rogers static void put_unwind_info(unw_addr_space_t __maybe_unused as,
80*88b4275fSIan Rogers 			    unw_proc_info_t *pi __maybe_unused,
81*88b4275fSIan Rogers 			    void *arg __maybe_unused)
82*88b4275fSIan Rogers {
83*88b4275fSIan Rogers 	pr_debug("unwind: put_unwind_info called\n");
84*88b4275fSIan Rogers }
85*88b4275fSIan Rogers 
86*88b4275fSIan Rogers static int get_dyn_info_list_addr(unw_addr_space_t __maybe_unused as,
87*88b4275fSIan Rogers 				  unw_word_t __maybe_unused *dil_addr,
88*88b4275fSIan Rogers 				  void __maybe_unused *arg)
89*88b4275fSIan Rogers {
90*88b4275fSIan Rogers 	return -UNW_ENOINFO;
91*88b4275fSIan Rogers }
92*88b4275fSIan Rogers 
93*88b4275fSIan Rogers static int access_mem(unw_addr_space_t as, unw_word_t addr, unw_word_t *valp,
94*88b4275fSIan Rogers 		      int __write, void *arg)
95*88b4275fSIan Rogers {
96*88b4275fSIan Rogers 	return __libunwind__access_mem(as, addr, valp, __write, arg);
97*88b4275fSIan Rogers }
98*88b4275fSIan Rogers 
99*88b4275fSIan Rogers static int access_reg(unw_addr_space_t as, unw_regnum_t regnum, unw_word_t *valp,
100*88b4275fSIan Rogers 		      int __write, void *arg)
101*88b4275fSIan Rogers {
102*88b4275fSIan Rogers 	return __libunwind__access_reg(as, regnum, valp, __write, arg);
103*88b4275fSIan Rogers }
104*88b4275fSIan Rogers 
105*88b4275fSIan Rogers static int access_fpreg(unw_addr_space_t __maybe_unused as,
106*88b4275fSIan Rogers 			unw_regnum_t __maybe_unused num,
107*88b4275fSIan Rogers 			unw_fpreg_t __maybe_unused *val,
108*88b4275fSIan Rogers 			int __maybe_unused __write,
109*88b4275fSIan Rogers 			void __maybe_unused *arg)
110*88b4275fSIan Rogers {
111*88b4275fSIan Rogers 	pr_err("unwind: access_fpreg unsupported\n");
112*88b4275fSIan Rogers 	return -UNW_EINVAL;
113*88b4275fSIan Rogers }
114*88b4275fSIan Rogers 
115*88b4275fSIan Rogers static int resume(unw_addr_space_t __maybe_unused as,
116*88b4275fSIan Rogers 		  unw_cursor_t __maybe_unused *cu,
117*88b4275fSIan Rogers 		  void __maybe_unused *arg)
118*88b4275fSIan Rogers {
119*88b4275fSIan Rogers 	pr_err("unwind: resume unsupported\n");
120*88b4275fSIan Rogers 	return -UNW_EINVAL;
121*88b4275fSIan Rogers }
122*88b4275fSIan Rogers 
123*88b4275fSIan Rogers static int get_proc_name(unw_addr_space_t __maybe_unused as,
124*88b4275fSIan Rogers 			 unw_word_t __maybe_unused addr,
125*88b4275fSIan Rogers 			 char __maybe_unused *bufp, size_t __maybe_unused buf_len,
126*88b4275fSIan Rogers 			 unw_word_t __maybe_unused *offp, void __maybe_unused *arg)
127*88b4275fSIan Rogers {
128*88b4275fSIan Rogers 	pr_err("unwind: get_proc_name unsupported\n");
129*88b4275fSIan Rogers 	return -UNW_EINVAL;
130*88b4275fSIan Rogers }
131*88b4275fSIan Rogers #endif
132*88b4275fSIan Rogers 
133*88b4275fSIan Rogers void *__libunwind_arch__create_addr_space_x86_64(void)
134*88b4275fSIan Rogers {
135*88b4275fSIan Rogers #ifdef HAVE_LIBUNWIND_X86_64_SUPPORT
136*88b4275fSIan Rogers 	static unw_accessors_t accessors = {
137*88b4275fSIan Rogers 		.find_proc_info		= find_proc_info,
138*88b4275fSIan Rogers 		.put_unwind_info	= put_unwind_info,
139*88b4275fSIan Rogers 		.get_dyn_info_list_addr	= get_dyn_info_list_addr,
140*88b4275fSIan Rogers 		.access_mem		= access_mem,
141*88b4275fSIan Rogers 		.access_reg		= access_reg,
142*88b4275fSIan Rogers 		.access_fpreg		= access_fpreg,
143*88b4275fSIan Rogers 		.resume			= resume,
144*88b4275fSIan Rogers 		.get_proc_name		= get_proc_name,
145*88b4275fSIan Rogers 	};
146*88b4275fSIan Rogers 	unw_addr_space_t addr_space;
147*88b4275fSIan Rogers 
148*88b4275fSIan Rogers 	addr_space = unw_create_addr_space(&accessors, /*byte_order=*/0);
149*88b4275fSIan Rogers 	unw_set_caching_policy(addr_space, UNW_CACHE_GLOBAL);
150*88b4275fSIan Rogers 	return addr_space;
151*88b4275fSIan Rogers #else
152*88b4275fSIan Rogers 	return NULL;
153*88b4275fSIan Rogers #endif
154*88b4275fSIan Rogers }
155*88b4275fSIan Rogers 
156*88b4275fSIan Rogers #ifdef HAVE_LIBUNWIND_X86_64_SUPPORT
157*88b4275fSIan Rogers extern int UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as,
158*88b4275fSIan Rogers 					       unw_word_t ip,
159*88b4275fSIan Rogers 					       unw_dyn_info_t *di,
160*88b4275fSIan Rogers 					       unw_proc_info_t *pi,
161*88b4275fSIan Rogers 					       int need_unwind_info, void *arg);
162*88b4275fSIan Rogers #define dwarf_search_unwind_table UNW_OBJ(dwarf_search_unwind_table)
163*88b4275fSIan Rogers #endif
164*88b4275fSIan Rogers 
165*88b4275fSIan Rogers int __libunwind_arch__dwarf_search_unwind_table_x86_64(void *as __maybe_unused,
166*88b4275fSIan Rogers 						       uint64_t ip __maybe_unused,
167*88b4275fSIan Rogers 						       struct libarch_unwind__dyn_info *_di __maybe_unused,
168*88b4275fSIan Rogers 						       void *pi __maybe_unused,
169*88b4275fSIan Rogers 						       int need_unwind_info __maybe_unused,
170*88b4275fSIan Rogers 						       void *arg __maybe_unused)
171*88b4275fSIan Rogers {
172*88b4275fSIan Rogers #ifdef HAVE_LIBUNWIND_X86_64_SUPPORT
173*88b4275fSIan Rogers 	unw_dyn_info_t di = {
174*88b4275fSIan Rogers 		.format     = UNW_INFO_FORMAT_REMOTE_TABLE,
175*88b4275fSIan Rogers 		.start_ip   = _di->start_ip,
176*88b4275fSIan Rogers 		.end_ip     = _di->end_ip,
177*88b4275fSIan Rogers 		.u = {
178*88b4275fSIan Rogers 			.rti = {
179*88b4275fSIan Rogers 				.segbase    = _di->segbase,
180*88b4275fSIan Rogers 				.table_data = _di->table_data,
181*88b4275fSIan Rogers 				.table_len  = _di->table_len,
182*88b4275fSIan Rogers 			},
183*88b4275fSIan Rogers 		},
184*88b4275fSIan Rogers 	};
185*88b4275fSIan Rogers 	int ret = dwarf_search_unwind_table(as, ip, &di, pi, need_unwind_info, arg);
186*88b4275fSIan Rogers 
187*88b4275fSIan Rogers 	_di->start_ip = di.start_ip;
188*88b4275fSIan Rogers 	_di->end_ip = di.end_ip;
189*88b4275fSIan Rogers 	_di->segbase = di.u.rti.segbase;
190*88b4275fSIan Rogers 	_di->table_data = di.u.rti.table_data;
191*88b4275fSIan Rogers 	_di->table_len = di.u.rti.table_len;
192*88b4275fSIan Rogers 	return ret;
193*88b4275fSIan Rogers #else
194*88b4275fSIan Rogers 	return -EINVAL;
195*88b4275fSIan Rogers #endif
196*88b4275fSIan Rogers }
197*88b4275fSIan Rogers 
198*88b4275fSIan Rogers #if defined(HAVE_LIBUNWIND_X86_64_SUPPORT) && !defined(NO_LIBUNWIND_DEBUG_FRAME_X86_64)
199*88b4275fSIan Rogers extern int UNW_OBJ(dwarf_find_debug_frame) (int found, unw_dyn_info_t *di_debug,
200*88b4275fSIan Rogers 					    unw_word_t ip,
201*88b4275fSIan Rogers 					    unw_word_t segbase,
202*88b4275fSIan Rogers 					    const char *obj_name, unw_word_t start,
203*88b4275fSIan Rogers 					    unw_word_t end);
204*88b4275fSIan Rogers #define dwarf_find_debug_frame UNW_OBJ(dwarf_find_debug_frame)
205*88b4275fSIan Rogers #endif
206*88b4275fSIan Rogers 
207*88b4275fSIan Rogers int __libunwind_arch__dwarf_find_debug_frame_x86_64(int found __maybe_unused,
208*88b4275fSIan Rogers 						 struct libarch_unwind__dyn_info *_di __maybe_unused,
209*88b4275fSIan Rogers 						 uint64_t ip __maybe_unused,
210*88b4275fSIan Rogers 						 uint64_t segbase __maybe_unused,
211*88b4275fSIan Rogers 						 const char *obj_name __maybe_unused,
212*88b4275fSIan Rogers 						 uint64_t start __maybe_unused,
213*88b4275fSIan Rogers 						 uint64_t end __maybe_unused)
214*88b4275fSIan Rogers {
215*88b4275fSIan Rogers #if defined(HAVE_LIBUNWIND_X86_64_SUPPORT) && !defined(NO_LIBUNWIND_DEBUG_FRAME_X86_64)
216*88b4275fSIan Rogers 	unw_dyn_info_t di = {
217*88b4275fSIan Rogers 		.format     = UNW_INFO_FORMAT_REMOTE_TABLE,
218*88b4275fSIan Rogers 		.start_ip   = _di->start_ip,
219*88b4275fSIan Rogers 		.end_ip     = _di->end_ip,
220*88b4275fSIan Rogers 		.u = {
221*88b4275fSIan Rogers 			.rti = {
222*88b4275fSIan Rogers 				.segbase    = _di->segbase,
223*88b4275fSIan Rogers 				.table_data = _di->table_data,
224*88b4275fSIan Rogers 				.table_len  = _di->table_len,
225*88b4275fSIan Rogers 			},
226*88b4275fSIan Rogers 		},
227*88b4275fSIan Rogers 	};
228*88b4275fSIan Rogers 	int ret = dwarf_find_debug_frame(found, &di, ip, segbase, obj_name, start, end);
229*88b4275fSIan Rogers 
230*88b4275fSIan Rogers 	_di->start_ip = di.start_ip;
231*88b4275fSIan Rogers 	_di->end_ip = di.end_ip;
232*88b4275fSIan Rogers 	_di->segbase = di.u.ti.segbase;
233*88b4275fSIan Rogers 	_di->table_data = di.u.ti.table_data;
234*88b4275fSIan Rogers 	_di->table_len = di.u.ti.table_len;
235*88b4275fSIan Rogers 	return ret;
236*88b4275fSIan Rogers #else
237*88b4275fSIan Rogers 	return -EINVAL;
238*88b4275fSIan Rogers #endif
239*88b4275fSIan Rogers }
240*88b4275fSIan Rogers 
241*88b4275fSIan Rogers struct unwind_info *__libunwind_arch_unwind_info__new_x86_64(struct thread *thread __maybe_unused,
242*88b4275fSIan Rogers 							     struct perf_sample *sample  __maybe_unused,
243*88b4275fSIan Rogers 							     int max_stack __maybe_unused,
244*88b4275fSIan Rogers 							     bool best_effort  __maybe_unused,
245*88b4275fSIan Rogers 							     uint64_t first_ip  __maybe_unused)
246*88b4275fSIan Rogers {
247*88b4275fSIan Rogers #ifdef HAVE_LIBUNWIND_X86_64_SUPPORT
248*88b4275fSIan Rogers 	struct arch_unwind_info {
249*88b4275fSIan Rogers 		struct unwind_info ui;
250*88b4275fSIan Rogers 		unw_cursor_t _cursor;
251*88b4275fSIan Rogers 		uint64_t _ips[];
252*88b4275fSIan Rogers 	};
253*88b4275fSIan Rogers 
254*88b4275fSIan Rogers 	struct maps *maps = thread__maps(thread);
255*88b4275fSIan Rogers 	void *addr_space = maps__addr_space(maps);
256*88b4275fSIan Rogers 	struct arch_unwind_info *ui;
257*88b4275fSIan Rogers 	int ret;
258*88b4275fSIan Rogers 
259*88b4275fSIan Rogers 	if (addr_space == NULL)
260*88b4275fSIan Rogers 		return NULL;
261*88b4275fSIan Rogers 
262*88b4275fSIan Rogers 	ui = zalloc(sizeof(*ui) + sizeof(ui->_ips[0]) * max_stack);
263*88b4275fSIan Rogers 	if (!ui)
264*88b4275fSIan Rogers 		return NULL;
265*88b4275fSIan Rogers 
266*88b4275fSIan Rogers 	ui->ui.machine = maps__machine(maps);
267*88b4275fSIan Rogers 	ui->ui.thread = thread;
268*88b4275fSIan Rogers 	ui->ui.sample = sample;
269*88b4275fSIan Rogers 	ui->ui.cursor = &ui->_cursor;
270*88b4275fSIan Rogers 	ui->ui.ips = &ui->_ips[0];
271*88b4275fSIan Rogers 	ui->ui.ips[0] = first_ip;
272*88b4275fSIan Rogers 	ui->ui.cur_ip = 1;
273*88b4275fSIan Rogers 	ui->ui.max_ips = max_stack;
274*88b4275fSIan Rogers 	ui->ui.unw_word_t_size = sizeof(unw_word_t);
275*88b4275fSIan Rogers 	ui->ui.e_machine = EM_X86_64;
276*88b4275fSIan Rogers 	ui->ui.best_effort = best_effort;
277*88b4275fSIan Rogers 
278*88b4275fSIan Rogers 	ret = unw_init_remote(&ui->_cursor, addr_space, &ui->ui);
279*88b4275fSIan Rogers 	if (ret) {
280*88b4275fSIan Rogers 		if (!best_effort)
281*88b4275fSIan Rogers 			pr_err("libunwind: %s\n", unw_strerror(ret));
282*88b4275fSIan Rogers 		free(ui);
283*88b4275fSIan Rogers 		return NULL;
284*88b4275fSIan Rogers 	}
285*88b4275fSIan Rogers 	return &ui->ui;
286*88b4275fSIan Rogers #else
287*88b4275fSIan Rogers 	return NULL;
288*88b4275fSIan Rogers #endif
289*88b4275fSIan Rogers }
290*88b4275fSIan Rogers 
291*88b4275fSIan Rogers int __libunwind_arch__unwind_step_x86_64(struct unwind_info *ui __maybe_unused)
292*88b4275fSIan Rogers {
293*88b4275fSIan Rogers #ifdef HAVE_LIBUNWIND_X86_64_SUPPORT
294*88b4275fSIan Rogers 	int ret;
295*88b4275fSIan Rogers 
296*88b4275fSIan Rogers 	if (ui->cur_ip >= ui->max_ips)
297*88b4275fSIan Rogers 		return 0;
298*88b4275fSIan Rogers 
299*88b4275fSIan Rogers 	ret = unw_step(ui->cursor);
300*88b4275fSIan Rogers 	if (ret > 0) {
301*88b4275fSIan Rogers 		uint64_t ip;
302*88b4275fSIan Rogers 
303*88b4275fSIan Rogers 		unw_get_reg(ui->cursor, UNW_REG_IP, &ip);
304*88b4275fSIan Rogers 
305*88b4275fSIan Rogers 		if (unw_is_signal_frame(ui->cursor) <= 0) {
306*88b4275fSIan Rogers 			/*
307*88b4275fSIan Rogers 			 * Decrement the IP for any non-activation frames. This
308*88b4275fSIan Rogers 			 * is required to properly find the srcline for caller
309*88b4275fSIan Rogers 			 * frames.  See also the documentation for
310*88b4275fSIan Rogers 			 * dwfl_frame_pc(), which this code tries to replicate.
311*88b4275fSIan Rogers 			 */
312*88b4275fSIan Rogers 			--ip;
313*88b4275fSIan Rogers 		}
314*88b4275fSIan Rogers 		ui->ips[ui->cur_ip++] = ip;
315*88b4275fSIan Rogers 	}
316*88b4275fSIan Rogers 	return ret;
317*88b4275fSIan Rogers #else
318*88b4275fSIan Rogers 	return -EINVAL;
319*88b4275fSIan Rogers #endif
320*88b4275fSIan Rogers }
321