1/* libunwind - a platform-independent unwind library 2 Copyright (C) 2008 CodeSourcery 3 Copyright 2011 Linaro Limited 4 Copyright (C) 2012 Tommi Rantala <tt.rantala@gmail.com> 5 6This file is part of libunwind. 7 8Permission is hereby granted, free of charge, to any person obtaining 9a copy of this software and associated documentation files (the 10"Software"), to deal in the Software without restriction, including 11without limitation the rights to use, copy, modify, merge, publish, 12distribute, sublicense, and/or sell copies of the Software, and to 13permit persons to whom the Software is furnished to do so, subject to 14the following conditions: 15 16The above copyright notice and this permission notice shall be 17included in all copies or substantial portions of the Software. 18 19THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 20EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 21MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 22NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 23LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 24OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 25WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ 26 27#include "unwind_i.h" 28#include "offsets.h" 29#include "ex_tables.h" 30 31#include <signal.h> 32 33#define arm_exidx_step UNW_OBJ(arm_exidx_step) 34 35static inline int 36arm_exidx_step (struct cursor *c) 37{ 38 unw_word_t old_ip, old_cfa; 39 uint8_t buf[32]; 40 int ret; 41 42 old_ip = c->dwarf.ip; 43 old_cfa = c->dwarf.cfa; 44 45 /* mark PC unsaved */ 46 c->dwarf.loc[UNW_ARM_R15] = DWARF_NULL_LOC; 47 unw_word_t ip = c->dwarf.ip; 48 if (c->dwarf.use_prev_instr) 49 /* The least bit denotes thumb/arm mode, clear it. */ 50 ip = (ip & ~(unw_word_t)0x1) - 1; 51 52 /* check dynamic info first --- it overrides everything else */ 53 ret = unwi_find_dynamic_proc_info (c->dwarf.as, ip, &c->dwarf.pi, 1, 54 c->dwarf.as_arg); 55 if (ret == -UNW_ENOINFO) 56 { 57 if ((ret = tdep_find_proc_info (&c->dwarf, ip, 1)) < 0) 58 return ret; 59 } 60 61 if (c->dwarf.pi.format != UNW_INFO_FORMAT_ARM_EXIDX) 62 return -UNW_ENOINFO; 63 64 ret = arm_exidx_extract (&c->dwarf, buf); 65 if (ret == -UNW_ESTOPUNWIND) 66 return 0; 67 else if (ret < 0) 68 return ret; 69 70 ret = arm_exidx_decode (buf, ret, &c->dwarf); 71 if (ret < 0) 72 return ret; 73 74 if (c->dwarf.ip == old_ip && c->dwarf.cfa == old_cfa) 75 { 76 Dprintf ("%s: ip and cfa unchanged; stopping here (ip=0x%lx)\n", 77 __FUNCTION__, (long) c->dwarf.ip); 78 return -UNW_EBADFRAME; 79 } 80 81 c->dwarf.pi_valid = 0; 82 83 return (c->dwarf.ip == 0) ? 0 : 1; 84} 85 86int 87unw_step (unw_cursor_t *cursor) 88{ 89 struct cursor *c = (struct cursor *) cursor; 90 int ret = -UNW_EUNSPEC; 91 92 Debug (1, "(cursor=%p)\n", c); 93 94 /* Check if this is a signal frame. */ 95 if (unw_is_signal_frame (cursor) > 0) 96 return arm_handle_signal_frame (cursor); 97 98#ifdef CONFIG_DEBUG_FRAME 99 /* First, try DWARF-based unwinding. */ 100 if (UNW_TRY_METHOD(UNW_ARM_METHOD_DWARF)) 101 { 102 ret = dwarf_step (&c->dwarf); 103 Debug(1, "dwarf_step()=%d\n", ret); 104 105 if (likely (ret > 0)) 106 return 1; 107 else if (unlikely (ret == -UNW_ESTOPUNWIND)) 108 return ret; 109 110 if (ret < 0 && ret != -UNW_ENOINFO) 111 { 112 Debug (2, "returning %d\n", ret); 113 return ret; 114 } 115 } 116#endif /* CONFIG_DEBUG_FRAME */ 117 118 /* Next, try extbl-based unwinding. */ 119 if (UNW_TRY_METHOD (UNW_ARM_METHOD_EXIDX)) 120 { 121 Debug (13, "%s(ret=%d), trying extbl\n", 122 UNW_TRY_METHOD(UNW_ARM_METHOD_DWARF) ? "dwarf_step() failed " : "", 123 ret); 124 ret = arm_exidx_step (c); 125 if (ret > 0) 126 return 1; 127 if (ret == -UNW_ESTOPUNWIND || ret == 0) 128 return ret; 129 } 130 131 /* Fall back on APCS frame parsing. 132 Note: This won't work in case the ARM EABI is used. */ 133#ifdef __FreeBSD__ 134 if (0) 135#else 136 if (unlikely (ret < 0)) 137#endif 138 { 139 if (UNW_TRY_METHOD(UNW_ARM_METHOD_FRAME)) 140 { 141 Debug (13, "%s%s%s%s(ret=%d), trying frame-chain\n", 142 UNW_TRY_METHOD(UNW_ARM_METHOD_DWARF) ? "dwarf_step() " : "", 143 (UNW_TRY_METHOD(UNW_ARM_METHOD_DWARF) && UNW_TRY_METHOD(UNW_ARM_METHOD_EXIDX)) ? "and " : "", 144 UNW_TRY_METHOD(UNW_ARM_METHOD_EXIDX) ? "arm_exidx_step() " : "", 145 (UNW_TRY_METHOD(UNW_ARM_METHOD_DWARF) || UNW_TRY_METHOD(UNW_ARM_METHOD_EXIDX)) ? "failed " : "", 146 ret); 147 ret = UNW_ESUCCESS; 148 /* DWARF unwinding failed, try to follow APCS/optimized APCS frame chain */ 149 unw_word_t instr, i; 150 dwarf_loc_t ip_loc, fp_loc; 151 unw_word_t frame; 152 /* Mark all registers unsaved, since we don't know where 153 they are saved (if at all), except for the EBP and 154 EIP. */ 155 if (dwarf_get(&c->dwarf, c->dwarf.loc[UNW_ARM_R11], &frame) < 0) 156 { 157 return 0; 158 } 159 for (i = 0; i < DWARF_NUM_PRESERVED_REGS; ++i) { 160 c->dwarf.loc[i] = DWARF_NULL_LOC; 161 } 162 if (frame) 163 { 164 if (dwarf_get(&c->dwarf, DWARF_LOC(frame, 0), &instr) < 0) 165 { 166 return 0; 167 } 168 instr -= 8; 169 if (dwarf_get(&c->dwarf, DWARF_LOC(instr, 0), &instr) < 0) 170 { 171 return 0; 172 } 173 if ((instr & 0xFFFFD800) == 0xE92DD800) 174 { 175 /* Standard APCS frame. */ 176 ip_loc = DWARF_LOC(frame - 4, 0); 177 fp_loc = DWARF_LOC(frame - 12, 0); 178 } 179 else 180 { 181 /* Codesourcery optimized normal frame. */ 182 ip_loc = DWARF_LOC(frame, 0); 183 fp_loc = DWARF_LOC(frame - 4, 0); 184 } 185 if (dwarf_get(&c->dwarf, ip_loc, &c->dwarf.ip) < 0) 186 { 187 return 0; 188 } 189 c->dwarf.loc[UNW_ARM_R12] = ip_loc; 190 c->dwarf.loc[UNW_ARM_R11] = fp_loc; 191 c->dwarf.pi_valid = 0; 192 Debug(15, "ip=%x\n", c->dwarf.ip); 193 } 194 else 195 { 196 ret = -UNW_ENOINFO; 197 } 198 } 199 } 200 return ret == -UNW_ENOINFO ? 0 : ret; 201} 202