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