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