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