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