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