Home | History | Annotate | Download | only in arm
      1 /* libunwind - a platform-independent unwind library
      2    Copyright 2011 Linaro Limited
      3 
      4 This file is part of libunwind.
      5 
      6 Permission is hereby granted, free of charge, to any person obtaining
      7 a copy of this software and associated documentation files (the
      8 "Software"), to deal in the Software without restriction, including
      9 without limitation the rights to use, copy, modify, merge, publish,
     10 distribute, sublicense, and/or sell copies of the Software, and to
     11 permit persons to whom the Software is furnished to do so, subject to
     12 the following conditions:
     13 
     14 The above copyright notice and this permission notice shall be
     15 included in all copies or substantial portions of the Software.
     16 
     17 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
     18 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
     19 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
     20 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
     21 LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
     22 OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
     23 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.  */
     24 
     25 /* This file contains functionality for parsing and interpreting the ARM
     26 specific unwind information.  Documentation about the exception handling
     27 ABI for the ARM architecture can be found at:
     28 http://infocenter.arm.com/help/topic/com.arm.doc.ihi0038a/IHI0038A_ehabi.pdf
     29 */
     30 
     31 #include "libunwind_i.h"
     32 
     33 #define ARM_EXBUF_START(x)	(((x) >> 4) & 0x0f)
     34 #define ARM_EXBUF_COUNT(x)	((x) & 0x0f)
     35 #define ARM_EXBUF_END(x)	(ARM_EXBUF_START(x) + ARM_EXBUF_COUNT(x))
     36 
     37 #define ARM_EXIDX_CANT_UNWIND	0x00000001
     38 #define ARM_EXIDX_COMPACT	0x80000000
     39 
     40 #define ARM_EXTBL_OP_FINISH	0xb0
     41 
     42 enum arm_exbuf_cmd_flags {
     43   ARM_EXIDX_VFP_SHIFT_16 = 1 << 16,
     44   ARM_EXIDX_VFP_DOUBLE = 1 << 17,
     45 };
     46 
     47 struct arm_cb_data
     48   {
     49     /* in: */
     50     unw_word_t ip;             /* instruction-pointer we're looking for */
     51     unw_proc_info_t *pi;       /* proc-info pointer */
     52     /* out: */
     53     unw_dyn_info_t di;         /* info about the ARM exidx segment */
     54   };
     55 
     56 static inline uint32_t CONST_ATTR
     57 prel31_read (uint32_t prel31)
     58 {
     59   return ((int32_t)prel31 << 1) >> 1;
     60 }
     61 
     62 static inline int
     63 prel31_to_addr (unw_addr_space_t as, void *arg, unw_word_t prel31,
     64 		unw_word_t *val)
     65 {
     66   unw_word_t offset;
     67 
     68   if ((*as->acc.access_mem)(as, prel31, &offset, 0, arg) < 0)
     69     return -UNW_EINVAL;
     70 
     71   offset = ((long)offset << 1) >> 1;
     72   *val = prel31 + offset;
     73 
     74   return 0;
     75 }
     76 
     77 /**
     78  * Applies the given command onto the new state to the given dwarf_cursor.
     79  */
     80 HIDDEN int
     81 arm_exidx_apply_cmd (struct arm_exbuf_data *edata, struct dwarf_cursor *c)
     82 {
     83   int ret = 0;
     84   unsigned i;
     85 
     86   switch (edata->cmd)
     87     {
     88     case ARM_EXIDX_CMD_FINISH:
     89       /* Set LR to PC if not set already.  */
     90       if (DWARF_IS_NULL_LOC (c->loc[UNW_ARM_R15]))
     91 	c->loc[UNW_ARM_R15] = c->loc[UNW_ARM_R14];
     92       /* Set IP.  */
     93       dwarf_get (c, c->loc[UNW_ARM_R15], &c->ip);
     94       break;
     95     case ARM_EXIDX_CMD_DATA_PUSH:
     96       Debug (2, "vsp = vsp - %d\n", edata->data);
     97       c->cfa -= edata->data;
     98       break;
     99     case ARM_EXIDX_CMD_DATA_POP:
    100       Debug (2, "vsp = vsp + %d\n", edata->data);
    101       c->cfa += edata->data;
    102       break;
    103     case ARM_EXIDX_CMD_REG_POP:
    104       for (i = 0; i < 16; i++)
    105 	if (edata->data & (1 << i))
    106 	  {
    107 	    Debug (2, "pop {r%d}\n", i);
    108 	    c->loc[UNW_ARM_R0 + i] = DWARF_LOC (c->cfa, 0);
    109 	    c->cfa += 4;
    110 	  }
    111       /* Set cfa in case the SP got popped. */
    112       if (edata->data & (1 << 13))
    113 	dwarf_get (c, c->loc[UNW_ARM_R13], &c->cfa);
    114       break;
    115     case ARM_EXIDX_CMD_REG_TO_SP:
    116       assert (edata->data < 16);
    117       Debug (2, "vsp = r%d\n", edata->data);
    118       c->loc[UNW_ARM_R13] = c->loc[UNW_ARM_R0 + edata->data];
    119       dwarf_get (c, c->loc[UNW_ARM_R13], &c->cfa);
    120       break;
    121     case ARM_EXIDX_CMD_VFP_POP:
    122       /* Skip VFP registers, but be sure to adjust stack */
    123       for (i = ARM_EXBUF_START (edata->data); i <= ARM_EXBUF_END (edata->data);
    124 	   i++)
    125 	c->cfa += 8;
    126       if (!(edata->data & ARM_EXIDX_VFP_DOUBLE))
    127 	c->cfa += 4;
    128       break;
    129     case ARM_EXIDX_CMD_WREG_POP:
    130       for (i = ARM_EXBUF_START (edata->data); i <= ARM_EXBUF_END (edata->data);
    131 	   i++)
    132 	c->cfa += 8;
    133       break;
    134     case ARM_EXIDX_CMD_WCGR_POP:
    135       for (i = 0; i < 4; i++)
    136 	if (edata->data & (1 << i))
    137 	  c->cfa += 4;
    138       break;
    139     case ARM_EXIDX_CMD_REFUSED:
    140     case ARM_EXIDX_CMD_RESERVED:
    141       ret = -1;
    142       break;
    143     }
    144   return ret;
    145 }
    146 
    147 /**
    148  * Decodes the given unwind instructions into arm_exbuf_data and calls
    149  * arm_exidx_apply_cmd that applies the command onto the dwarf_cursor.
    150  */
    151 HIDDEN int
    152 arm_exidx_decode (const uint8_t *buf, uint8_t len, struct dwarf_cursor *c)
    153 {
    154 #define READ_OP() *buf++
    155   const uint8_t *end = buf + len;
    156   int ret;
    157   struct arm_exbuf_data edata;
    158 
    159   assert(buf != NULL);
    160   assert(len > 0);
    161 
    162   while (buf < end)
    163     {
    164       uint8_t op = READ_OP ();
    165       if ((op & 0xc0) == 0x00)
    166 	{
    167 	  edata.cmd = ARM_EXIDX_CMD_DATA_POP;
    168 	  edata.data = (((int)op & 0x3f) << 2) + 4;
    169 	}
    170       else if ((op & 0xc0) == 0x40)
    171 	{
    172 	  edata.cmd = ARM_EXIDX_CMD_DATA_PUSH;
    173 	  edata.data = (((int)op & 0x3f) << 2) + 4;
    174 	}
    175       else if ((op & 0xf0) == 0x80)
    176 	{
    177 	  uint8_t op2 = READ_OP ();
    178 	  if (op == 0x80 && op2 == 0x00)
    179 	    edata.cmd = ARM_EXIDX_CMD_REFUSED;
    180 	  else
    181 	    {
    182 	      edata.cmd = ARM_EXIDX_CMD_REG_POP;
    183 	      edata.data = ((op & 0xf) << 8) | op2;
    184 	      edata.data = edata.data << 4;
    185 	    }
    186 	}
    187       else if ((op & 0xf0) == 0x90)
    188 	{
    189 	  if (op == 0x9d || op == 0x9f)
    190 	    edata.cmd = ARM_EXIDX_CMD_RESERVED;
    191 	  else
    192 	    {
    193 	      edata.cmd = ARM_EXIDX_CMD_REG_TO_SP;
    194 	      edata.data = op & 0x0f;
    195 	    }
    196 	}
    197       else if ((op & 0xf0) == 0xa0)
    198 	{
    199 	  unsigned end = (op & 0x07);
    200 	  edata.data = (1 << (end + 1)) - 1;
    201 	  edata.data = edata.data << 4;
    202 	  if (op & 0x08)
    203 	    edata.data |= 1 << 14;
    204 	  edata.cmd = ARM_EXIDX_CMD_REG_POP;
    205 	}
    206       else if (op == ARM_EXTBL_OP_FINISH)
    207 	{
    208 	  edata.cmd = ARM_EXIDX_CMD_FINISH;
    209 	  buf = end;
    210 	}
    211       else if (op == 0xb1)
    212 	{
    213 	  uint8_t op2 = READ_OP ();
    214 	  if (op2 == 0 || (op2 & 0xf0))
    215 	    edata.cmd = ARM_EXIDX_CMD_RESERVED;
    216 	  else
    217 	    {
    218 	      edata.cmd = ARM_EXIDX_CMD_REG_POP;
    219 	      edata.data = op2 & 0x0f;
    220 	    }
    221 	}
    222       else if (op == 0xb2)
    223 	{
    224 	  uint32_t offset = 0;
    225 	  uint8_t byte, shift = 0;
    226 	  do
    227 	    {
    228 	      byte = READ_OP ();
    229 	      offset |= (byte & 0x7f) << shift;
    230 	      shift += 7;
    231 	    }
    232 	  while (byte & 0x80);
    233 	  edata.data = offset * 4 + 0x204;
    234 	  edata.cmd = ARM_EXIDX_CMD_DATA_POP;
    235 	}
    236       else if (op == 0xb3 || op == 0xc8 || op == 0xc9)
    237 	{
    238 	  edata.cmd = ARM_EXIDX_CMD_VFP_POP;
    239 	  edata.data = READ_OP ();
    240 	  if (op == 0xc8)
    241 	    edata.data |= ARM_EXIDX_VFP_SHIFT_16;
    242 	  if (op != 0xb3)
    243 	    edata.data |= ARM_EXIDX_VFP_DOUBLE;
    244 	}
    245       else if ((op & 0xf8) == 0xb8 || (op & 0xf8) == 0xd0)
    246 	{
    247 	  edata.cmd = ARM_EXIDX_CMD_VFP_POP;
    248 	  edata.data = 0x80 | (op & 0x07);
    249 	  if ((op & 0xf8) == 0xd0)
    250 	    edata.data |= ARM_EXIDX_VFP_DOUBLE;
    251 	}
    252       else if (op >= 0xc0 && op <= 0xc5)
    253 	{
    254 	  edata.cmd = ARM_EXIDX_CMD_WREG_POP;
    255 	  edata.data = 0xa0 | (op & 0x07);
    256 	}
    257       else if (op == 0xc6)
    258 	{
    259 	  edata.cmd = ARM_EXIDX_CMD_WREG_POP;
    260 	  edata.data = READ_OP ();
    261 	}
    262       else if (op == 0xc7)
    263 	{
    264 	  uint8_t op2 = READ_OP ();
    265 	  if (op2 == 0 || (op2 & 0xf0))
    266 	    edata.cmd = ARM_EXIDX_CMD_RESERVED;
    267 	  else
    268 	    {
    269 	      edata.cmd = ARM_EXIDX_CMD_WCGR_POP;
    270 	      edata.data = op2 & 0x0f;
    271 	    }
    272 	}
    273       else
    274 	edata.cmd = ARM_EXIDX_CMD_RESERVED;
    275 
    276       ret = arm_exidx_apply_cmd (&edata, c);
    277       if (ret < 0)
    278 	return ret;
    279     }
    280   return 0;
    281 }
    282 
    283 /**
    284  * Reads the entry from the given cursor and extracts the unwind instructions
    285  * into buf.  Returns the number of the extracted unwind insns or
    286  * -UNW_ESTOPUNWIND if the special bit pattern ARM_EXIDX_CANT_UNWIND (0x1) was
    287  * found.
    288  */
    289 HIDDEN int
    290 arm_exidx_extract (struct dwarf_cursor *c, uint8_t *buf)
    291 {
    292   int nbuf = 0;
    293   unw_word_t entry = (unw_word_t) c->pi.unwind_info;
    294   unw_word_t addr;
    295   uint32_t data;
    296 
    297   /* An ARM unwind entry consists of a prel31 offset to the start of a
    298      function followed by 31bits of data:
    299        * if set to 0x1: the function cannot be unwound (EXIDX_CANTUNWIND)
    300        * if bit 31 is one: this is a table entry itself (ARM_EXIDX_COMPACT)
    301        * if bit 31 is zero: this is a prel31 offset of the start of the
    302 	 table entry for this function  */
    303   if (prel31_to_addr(c->as, c->as_arg, entry, &addr) < 0)
    304     return -UNW_EINVAL;
    305 
    306   if ((*c->as->acc.access_mem)(c->as, entry + 4, &data, 0, c->as_arg) < 0)
    307     return -UNW_EINVAL;
    308 
    309   if (data == ARM_EXIDX_CANT_UNWIND)
    310     {
    311       Debug (2, "0x1 [can't unwind]\n");
    312       nbuf = -UNW_ESTOPUNWIND;
    313     }
    314   else if (data & ARM_EXIDX_COMPACT)
    315     {
    316       Debug (2, "%p compact model %d [%8.8x]\n", (void *)addr,
    317 	     (data >> 24) & 0x7f, data);
    318       buf[nbuf++] = data >> 16;
    319       buf[nbuf++] = data >> 8;
    320       buf[nbuf++] = data;
    321     }
    322   else
    323     {
    324       unw_word_t extbl_data;
    325       unsigned int n_table_words = 0;
    326 
    327       if (prel31_to_addr(c->as, c->as_arg, entry + 4, &extbl_data) < 0)
    328         return -UNW_EINVAL;
    329 
    330       if ((*c->as->acc.access_mem)(c->as, extbl_data, &data, 0, c->as_arg) < 0)
    331 	return -UNW_EINVAL;
    332 
    333       if (data & ARM_EXIDX_COMPACT)
    334 	{
    335 	  int pers = (data >> 24) & 0x0f;
    336 	  Debug (2, "%p compact model %d [%8.8x]\n", (void *)addr, pers, data);
    337 	  if (pers == 1 || pers == 2)
    338 	    {
    339 	      n_table_words = (data >> 16) & 0xff;
    340 	      extbl_data += 4;
    341 	    }
    342 	  else
    343 	    buf[nbuf++] = data >> 16;
    344 	  buf[nbuf++] = data >> 8;
    345 	  buf[nbuf++] = data;
    346 	}
    347       else
    348 	{
    349 	  unw_word_t pers;
    350  	  if (prel31_to_addr (c->as, c->as_arg, extbl_data, &pers) < 0)
    351 	    return -UNW_EINVAL;
    352 	  Debug (2, "%p Personality routine: %8p\n", (void *)addr,
    353 		 (void *)pers);
    354 	  if ((*c->as->acc.access_mem)(c->as, extbl_data + 4, &data, 0,
    355 				       c->as_arg) < 0)
    356 	    return -UNW_EINVAL;
    357 	  n_table_words = data >> 24;
    358 	  buf[nbuf++] = data >> 16;
    359 	  buf[nbuf++] = data >> 8;
    360 	  buf[nbuf++] = data;
    361 	  extbl_data += 8;
    362 	}
    363       assert (n_table_words <= 5);
    364       unsigned j;
    365       for (j = 0; j < n_table_words; j++)
    366 	{
    367 	  if ((*c->as->acc.access_mem)(c->as, extbl_data, &data, 0,
    368 				       c->as_arg) < 0)
    369 	    return -UNW_EINVAL;
    370 	  extbl_data += 4;
    371 	  buf[nbuf++] = data >> 24;
    372 	  buf[nbuf++] = data >> 16;
    373 	  buf[nbuf++] = data >> 8;
    374 	  buf[nbuf++] = data >> 0;
    375 	}
    376     }
    377 
    378   if (nbuf > 0 && buf[nbuf - 1] != ARM_EXTBL_OP_FINISH)
    379     buf[nbuf++] = ARM_EXTBL_OP_FINISH;
    380 
    381   return nbuf;
    382 }
    383 
    384 PROTECTED int
    385 tdep_search_unwind_table (unw_addr_space_t as, unw_word_t ip,
    386 			     unw_dyn_info_t *di, unw_proc_info_t *pi,
    387 			     int need_unwind_info, void *arg)
    388 {
    389   if (UNW_TRY_METHOD (UNW_ARM_METHOD_EXIDX)
    390       && di->format == UNW_INFO_FORMAT_ARM_EXIDX)
    391     {
    392       /* The .ARM.exidx section contains a sorted list of key-value pairs -
    393 	 the unwind entries.  The 'key' is a prel31 offset to the start of a
    394 	 function.  We binary search this section in order to find the
    395 	 appropriate unwind entry.  */
    396       unw_word_t first = di->u.rti.table_data;
    397       unw_word_t last = di->u.rti.table_data + di->u.rti.table_len - 8;
    398       unw_word_t entry, val;
    399 
    400       if (prel31_to_addr (as, arg, first, &val) < 0 || ip < val)
    401 	return -UNW_ENOINFO;
    402 
    403       if (prel31_to_addr (as, arg, last, &val) < 0)
    404 	return -UNW_EINVAL;
    405 
    406       if (ip >= val)
    407 	{
    408 	  entry = last;
    409 
    410 	  if (prel31_to_addr (as, arg, last, &pi->start_ip) < 0)
    411 	    return -UNW_EINVAL;
    412 
    413 	  pi->end_ip = di->end_ip -1;
    414 	}
    415       else
    416 	{
    417 	  while (first < last - 8)
    418 	    {
    419 	      entry = first + (((last - first) / 8 + 1) >> 1) * 8;
    420 
    421 	      if (prel31_to_addr (as, arg, entry, &val) < 0)
    422 		return -UNW_EINVAL;
    423 
    424 	      if (ip < val)
    425 		last = entry;
    426 	      else
    427 		first = entry;
    428 	    }
    429 
    430 	  entry = first;
    431 
    432 	  if (prel31_to_addr (as, arg, entry, &pi->start_ip) < 0)
    433 	    return -UNW_EINVAL;
    434 
    435 	  if (prel31_to_addr (as, arg, entry + 8, &pi->end_ip) < 0)
    436 	    return -UNW_EINVAL;
    437 
    438 	  pi->end_ip--;
    439 	}
    440 
    441       if (need_unwind_info)
    442 	{
    443 	  pi->unwind_info_size = 8;
    444 	  pi->unwind_info = (void *) entry;
    445 	  pi->format = UNW_INFO_FORMAT_ARM_EXIDX;
    446 	}
    447       return 0;
    448     }
    449   else if (UNW_TRY_METHOD(UNW_ARM_METHOD_DWARF)
    450 	   && di->format != UNW_INFO_FORMAT_ARM_EXIDX)
    451     return dwarf_search_unwind_table (as, ip, di, pi, need_unwind_info, arg);
    452 
    453   return -UNW_ENOINFO;
    454 }
    455 
    456 #ifndef UNW_REMOTE_ONLY
    457 /**
    458  * Callback to dl_iterate_phdr to find infos about the ARM exidx segment.
    459  */
    460 static int
    461 arm_phdr_cb (struct dl_phdr_info *info, size_t size, void *data)
    462 {
    463   struct arm_cb_data *cb_data = data;
    464   const Elf_W(Phdr) *p_text = NULL;
    465   const Elf_W(Phdr) *p_arm_exidx = NULL;
    466   const Elf_W(Phdr) *phdr = info->dlpi_phdr;
    467   long n;
    468 
    469   for (n = info->dlpi_phnum; --n >= 0; phdr++)
    470     {
    471       switch (phdr->p_type)
    472 	{
    473 	case PT_LOAD:
    474 	  if (cb_data->ip >= phdr->p_vaddr + info->dlpi_addr &&
    475 	      cb_data->ip < phdr->p_vaddr + info->dlpi_addr + phdr->p_memsz)
    476 	    p_text = phdr;
    477 	  break;
    478 
    479 	case PT_ARM_EXIDX:
    480 	  p_arm_exidx = phdr;
    481 	  break;
    482 
    483 	default:
    484 	  break;
    485 	}
    486     }
    487 
    488   if (p_text && p_arm_exidx)
    489     {
    490       cb_data->di.format = UNW_INFO_FORMAT_ARM_EXIDX;
    491       cb_data->di.start_ip = p_text->p_vaddr + info->dlpi_addr;
    492       cb_data->di.end_ip = cb_data->di.start_ip + p_text->p_memsz;
    493       cb_data->di.u.rti.name_ptr = (unw_word_t) info->dlpi_name;
    494       cb_data->di.u.rti.table_data = p_arm_exidx->p_vaddr + info->dlpi_addr;
    495       cb_data->di.u.rti.table_len = p_arm_exidx->p_memsz;
    496       return 1;
    497     }
    498 
    499   return 0;
    500 }
    501 
    502 HIDDEN int
    503 arm_find_proc_info (unw_addr_space_t as, unw_word_t ip,
    504 		    unw_proc_info_t *pi, int need_unwind_info, void *arg)
    505 {
    506   int ret = -1;
    507   intrmask_t saved_mask;
    508 
    509   Debug (14, "looking for IP=0x%lx\n", (long) ip);
    510 
    511   if (UNW_TRY_METHOD(UNW_ARM_METHOD_DWARF))
    512     {
    513       ret = dwarf_find_proc_info (as, ip, pi, need_unwind_info, arg);
    514     }
    515 
    516   if (ret < 0 && UNW_TRY_METHOD (UNW_ARM_METHOD_EXIDX))
    517     {
    518       struct arm_cb_data cb_data;
    519 
    520       memset (&cb_data, 0, sizeof (cb_data));
    521       cb_data.ip = ip;
    522       cb_data.pi = pi;
    523       cb_data.di.format = -1;
    524 
    525       SIGPROCMASK (SIG_SETMASK, &unwi_full_mask, &saved_mask);
    526       ret = dl_iterate_phdr (arm_phdr_cb, &cb_data);
    527       SIGPROCMASK (SIG_SETMASK, &saved_mask, NULL);
    528 
    529       if (cb_data.di.format != -1)
    530 	ret = tdep_search_unwind_table (as, ip, &cb_data.di, pi,
    531 					need_unwind_info, arg);
    532       else
    533 	ret = -UNW_ENOINFO;
    534     }
    535 
    536   if (ret < 0)
    537     /* ANDROID support update. */
    538     {
    539       Debug (14, "IP=0x%lx not found\n", (long) ip);
    540     }
    541     /* End of ANDROID update. */
    542 
    543   return ret;
    544 }
    545 
    546 HIDDEN void
    547 arm_put_unwind_info (unw_addr_space_t as, unw_proc_info_t *proc_info, void *arg)
    548 {
    549   /* it's a no-op */
    550 }
    551 #endif /* !UNW_REMOTE_ONLY */
    552 
    553