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