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 int
     57 prel31_to_addr (unw_addr_space_t as, void *arg, unw_word_t prel31,
     58 		unw_word_t *val)
     59 {
     60   unw_word_t offset;
     61 
     62   if ((*as->acc.access_mem)(as, prel31, &offset, 0, arg) < 0)
     63     return -UNW_EINVAL;
     64 
     65   offset = ((long)offset << 1) >> 1;
     66   *val = prel31 + offset;
     67 
     68   return 0;
     69 }
     70 
     71 /**
     72  * Applies the given command onto the new state to the given dwarf_cursor.
     73  */
     74 HIDDEN int
     75 arm_exidx_apply_cmd (struct arm_exbuf_data *edata, struct dwarf_cursor *c)
     76 {
     77   int ret = 0;
     78   unsigned i;
     79 
     80   switch (edata->cmd)
     81     {
     82     case ARM_EXIDX_CMD_FINISH:
     83       /* Set LR to PC if not set already.  */
     84       if (DWARF_IS_NULL_LOC (c->loc[UNW_ARM_R15]))
     85 	c->loc[UNW_ARM_R15] = c->loc[UNW_ARM_R14];
     86       /* Set IP.  */
     87       dwarf_get (c, c->loc[UNW_ARM_R15], &c->ip);
     88       break;
     89     case ARM_EXIDX_CMD_DATA_PUSH:
     90       Debug (2, "vsp = vsp - %d\n", edata->data);
     91       c->cfa -= edata->data;
     92       break;
     93     case ARM_EXIDX_CMD_DATA_POP:
     94       Debug (2, "vsp = vsp + %d\n", edata->data);
     95       c->cfa += edata->data;
     96       break;
     97     case ARM_EXIDX_CMD_REG_POP:
     98       for (i = 0; i < 16; i++)
     99 	if (edata->data & (1 << i))
    100 	  {
    101 	    Debug (2, "pop {r%d}\n", i);
    102 	    c->loc[UNW_ARM_R0 + i] = DWARF_LOC (c->cfa, 0);
    103 	    c->cfa += 4;
    104 	  }
    105       /* Set cfa in case the SP got popped. */
    106       if (edata->data & (1 << 13))
    107 	dwarf_get (c, c->loc[UNW_ARM_R13], &c->cfa);
    108       break;
    109     case ARM_EXIDX_CMD_REG_TO_SP:
    110       assert (edata->data < 16);
    111       Debug (2, "vsp = r%d\n", edata->data);
    112       c->loc[UNW_ARM_R13] = c->loc[UNW_ARM_R0 + edata->data];
    113       dwarf_get (c, c->loc[UNW_ARM_R13], &c->cfa);
    114       break;
    115     case ARM_EXIDX_CMD_VFP_POP:
    116       /* Skip VFP registers, but be sure to adjust stack */
    117       for (i = ARM_EXBUF_START (edata->data); i <= ARM_EXBUF_END (edata->data);
    118 	   i++)
    119 	c->cfa += 8;
    120       if (!(edata->data & ARM_EXIDX_VFP_DOUBLE))
    121 	c->cfa += 4;
    122       break;
    123     case ARM_EXIDX_CMD_WREG_POP:
    124       for (i = ARM_EXBUF_START (edata->data); i <= ARM_EXBUF_END (edata->data);
    125 	   i++)
    126 	c->cfa += 8;
    127       break;
    128     case ARM_EXIDX_CMD_WCGR_POP:
    129       for (i = 0; i < 4; i++)
    130 	if (edata->data & (1 << i))
    131 	  c->cfa += 4;
    132       break;
    133     case ARM_EXIDX_CMD_REFUSED:
    134     case ARM_EXIDX_CMD_RESERVED:
    135       ret = -1;
    136       break;
    137     }
    138   return ret;
    139 }
    140 
    141 /**
    142  * Decodes the given unwind instructions into arm_exbuf_data and calls
    143  * arm_exidx_apply_cmd that applies the command onto the dwarf_cursor.
    144  */
    145 HIDDEN int
    146 arm_exidx_decode (const uint8_t *buf, uint8_t len, struct dwarf_cursor *c)
    147 {
    148 #define READ_OP() *buf++
    149   const uint8_t *end = buf + len;
    150   int ret;
    151   struct arm_exbuf_data edata;
    152 
    153   assert(buf != NULL);
    154   assert(len > 0);
    155 
    156   while (buf < end)
    157     {
    158       uint8_t op = READ_OP ();
    159       if ((op & 0xc0) == 0x00)
    160 	{
    161 	  edata.cmd = ARM_EXIDX_CMD_DATA_POP;
    162 	  edata.data = (((int)op & 0x3f) << 2) + 4;
    163 	}
    164       else if ((op & 0xc0) == 0x40)
    165 	{
    166 	  edata.cmd = ARM_EXIDX_CMD_DATA_PUSH;
    167 	  edata.data = (((int)op & 0x3f) << 2) + 4;
    168 	}
    169       else if ((op & 0xf0) == 0x80)
    170 	{
    171 	  uint8_t op2 = READ_OP ();
    172 	  if (op == 0x80 && op2 == 0x00)
    173 	    edata.cmd = ARM_EXIDX_CMD_REFUSED;
    174 	  else
    175 	    {
    176 	      edata.cmd = ARM_EXIDX_CMD_REG_POP;
    177 	      edata.data = ((op & 0xf) << 8) | op2;
    178 	      edata.data = edata.data << 4;
    179 	    }
    180 	}
    181       else if ((op & 0xf0) == 0x90)
    182 	{
    183 	  if (op == 0x9d || op == 0x9f)
    184 	    edata.cmd = ARM_EXIDX_CMD_RESERVED;
    185 	  else
    186 	    {
    187 	      edata.cmd = ARM_EXIDX_CMD_REG_TO_SP;
    188 	      edata.data = op & 0x0f;
    189 	    }
    190 	}
    191       else if ((op & 0xf0) == 0xa0)
    192 	{
    193 	  unsigned end = (op & 0x07);
    194 	  edata.data = (1 << (end + 1)) - 1;
    195 	  edata.data = edata.data << 4;
    196 	  if (op & 0x08)
    197 	    edata.data |= 1 << 14;
    198 	  edata.cmd = ARM_EXIDX_CMD_REG_POP;
    199 	}
    200       else if (op == ARM_EXTBL_OP_FINISH)
    201 	{
    202 	  edata.cmd = ARM_EXIDX_CMD_FINISH;
    203 	  buf = end;
    204 	}
    205       else if (op == 0xb1)
    206 	{
    207 	  uint8_t op2 = READ_OP ();
    208 	  if (op2 == 0 || (op2 & 0xf0))
    209 	    edata.cmd = ARM_EXIDX_CMD_RESERVED;
    210 	  else
    211 	    {
    212 	      edata.cmd = ARM_EXIDX_CMD_REG_POP;
    213 	      edata.data = op2 & 0x0f;
    214 	    }
    215 	}
    216       else if (op == 0xb2)
    217 	{
    218 	  uint32_t offset = 0;
    219 	  uint8_t byte, shift = 0;
    220 	  do
    221 	    {
    222 	      byte = READ_OP ();
    223 	      offset |= (byte & 0x7f) << shift;
    224 	      shift += 7;
    225 	    }
    226 	  while (byte & 0x80);
    227 	  edata.data = offset * 4 + 0x204;
    228 	  edata.cmd = ARM_EXIDX_CMD_DATA_POP;
    229 	}
    230       else if (op == 0xb3 || op == 0xc8 || op == 0xc9)
    231 	{
    232 	  edata.cmd = ARM_EXIDX_CMD_VFP_POP;
    233 	  edata.data = READ_OP ();
    234 	  if (op == 0xc8)
    235 	    edata.data |= ARM_EXIDX_VFP_SHIFT_16;
    236 	  if (op != 0xb3)
    237 	    edata.data |= ARM_EXIDX_VFP_DOUBLE;
    238 	}
    239       else if ((op & 0xf8) == 0xb8 || (op & 0xf8) == 0xd0)
    240 	{
    241 	  edata.cmd = ARM_EXIDX_CMD_VFP_POP;
    242 	  edata.data = 0x80 | (op & 0x07);
    243 	  if ((op & 0xf8) == 0xd0)
    244 	    edata.data |= ARM_EXIDX_VFP_DOUBLE;
    245 	}
    246       else if (op >= 0xc0 && op <= 0xc5)
    247 	{
    248 	  edata.cmd = ARM_EXIDX_CMD_WREG_POP;
    249 	  edata.data = 0xa0 | (op & 0x07);
    250 	}
    251       else if (op == 0xc6)
    252 	{
    253 	  edata.cmd = ARM_EXIDX_CMD_WREG_POP;
    254 	  edata.data = READ_OP ();
    255 	}
    256       else if (op == 0xc7)
    257 	{
    258 	  uint8_t op2 = READ_OP ();
    259 	  if (op2 == 0 || (op2 & 0xf0))
    260 	    edata.cmd = ARM_EXIDX_CMD_RESERVED;
    261 	  else
    262 	    {
    263 	      edata.cmd = ARM_EXIDX_CMD_WCGR_POP;
    264 	      edata.data = op2 & 0x0f;
    265 	    }
    266 	}
    267       else
    268 	edata.cmd = ARM_EXIDX_CMD_RESERVED;
    269 
    270       ret = arm_exidx_apply_cmd (&edata, c);
    271       if (ret < 0)
    272 	return ret;
    273     }
    274   return 0;
    275 }
    276 
    277 /**
    278  * Reads the entry from the given cursor and extracts the unwind instructions
    279  * into buf.  Returns the number of the extracted unwind insns or
    280  * -UNW_ESTOPUNWIND if the special bit pattern ARM_EXIDX_CANT_UNWIND (0x1) was
    281  * found.
    282  */
    283 HIDDEN int
    284 arm_exidx_extract (struct dwarf_cursor *c, uint8_t *buf)
    285 {
    286   int nbuf = 0;
    287   unw_word_t entry = (unw_word_t) c->pi.unwind_info;
    288   unw_word_t addr;
    289   uint32_t data;
    290 
    291   /* An ARM unwind entry consists of a prel31 offset to the start of a
    292      function followed by 31bits of data:
    293        * if set to 0x1: the function cannot be unwound (EXIDX_CANTUNWIND)
    294        * if bit 31 is one: this is a table entry itself (ARM_EXIDX_COMPACT)
    295        * if bit 31 is zero: this is a prel31 offset of the start of the
    296 	 table entry for this function  */
    297   if (prel31_to_addr(c->as, c->as_arg, entry, &addr) < 0)
    298     return -UNW_EINVAL;
    299 
    300   if ((*c->as->acc.access_mem)(c->as, entry + 4, &data, 0, c->as_arg) < 0)
    301     return -UNW_EINVAL;
    302 
    303   if (data == ARM_EXIDX_CANT_UNWIND)
    304     {
    305       Debug (2, "0x1 [can't unwind]\n");
    306       nbuf = -UNW_ESTOPUNWIND;
    307     }
    308   else if (data & ARM_EXIDX_COMPACT)
    309     {
    310       Debug (2, "%p compact model %d [%8.8x]\n", (void *)addr,
    311 	     (data >> 24) & 0x7f, data);
    312       buf[nbuf++] = data >> 16;
    313       buf[nbuf++] = data >> 8;
    314       buf[nbuf++] = data;
    315     }
    316   else
    317     {
    318       unw_word_t extbl_data;
    319       unsigned int n_table_words = 0;
    320 
    321       if (prel31_to_addr(c->as, c->as_arg, entry + 4, &extbl_data) < 0)
    322         return -UNW_EINVAL;
    323 
    324       if ((*c->as->acc.access_mem)(c->as, extbl_data, &data, 0, c->as_arg) < 0)
    325 	return -UNW_EINVAL;
    326 
    327       if (data & ARM_EXIDX_COMPACT)
    328 	{
    329 	  int pers = (data >> 24) & 0x0f;
    330 	  Debug (2, "%p compact model %d [%8.8x]\n", (void *)addr, pers, data);
    331 	  if (pers == 1 || pers == 2)
    332 	    {
    333 	      n_table_words = (data >> 16) & 0xff;
    334 	      extbl_data += 4;
    335 	    }
    336 	  else
    337 	    buf[nbuf++] = data >> 16;
    338 	  buf[nbuf++] = data >> 8;
    339 	  buf[nbuf++] = data;
    340 	}
    341       else
    342 	{
    343 	  unw_word_t pers;
    344  	  if (prel31_to_addr (c->as, c->as_arg, extbl_data, &pers) < 0)
    345 	    return -UNW_EINVAL;
    346 	  Debug (2, "%p Personality routine: %8p\n", (void *)addr,
    347 		 (void *)pers);
    348 	  if ((*c->as->acc.access_mem)(c->as, extbl_data + 4, &data, 0,
    349 				       c->as_arg) < 0)
    350 	    return -UNW_EINVAL;
    351 	  n_table_words = data >> 24;
    352 	  buf[nbuf++] = data >> 16;
    353 	  buf[nbuf++] = data >> 8;
    354 	  buf[nbuf++] = data;
    355 	  extbl_data += 8;
    356 	}
    357       assert (n_table_words <= 5);
    358       unsigned j;
    359       for (j = 0; j < n_table_words; j++)
    360 	{
    361 	  if ((*c->as->acc.access_mem)(c->as, extbl_data, &data, 0,
    362 				       c->as_arg) < 0)
    363 	    return -UNW_EINVAL;
    364 	  extbl_data += 4;
    365 	  buf[nbuf++] = data >> 24;
    366 	  buf[nbuf++] = data >> 16;
    367 	  buf[nbuf++] = data >> 8;
    368 	  buf[nbuf++] = data >> 0;
    369 	}
    370     }
    371 
    372   if (nbuf > 0 && buf[nbuf - 1] != ARM_EXTBL_OP_FINISH)
    373     buf[nbuf++] = ARM_EXTBL_OP_FINISH;
    374 
    375   return nbuf;
    376 }
    377 
    378 PROTECTED int
    379 tdep_search_unwind_table (unw_addr_space_t as, unw_word_t ip,
    380 			     unw_dyn_info_t *di, unw_proc_info_t *pi,
    381 			     int need_unwind_info, void *arg)
    382 {
    383   if (UNW_TRY_METHOD (UNW_ARM_METHOD_EXIDX)
    384       && di->format == UNW_INFO_FORMAT_ARM_EXIDX)
    385     {
    386       /* The .ARM.exidx section contains a sorted list of key-value pairs -
    387 	 the unwind entries.  The 'key' is a prel31 offset to the start of a
    388 	 function.  We binary search this section in order to find the
    389 	 appropriate unwind entry.  */
    390       unw_word_t first = di->u.rti.table_data;
    391       unw_word_t last = di->u.rti.table_data + di->u.rti.table_len - 8;
    392       unw_word_t entry, val;
    393 
    394       if (prel31_to_addr (as, arg, first, &val) < 0 || ip < val)
    395 	return -UNW_ENOINFO;
    396 
    397       if (prel31_to_addr (as, arg, last, &val) < 0)
    398 	return -UNW_EINVAL;
    399 
    400       if (ip >= val)
    401 	{
    402 	  entry = last;
    403 
    404 	  if (prel31_to_addr (as, arg, last, &pi->start_ip) < 0)
    405 	    return -UNW_EINVAL;
    406 
    407 	  pi->end_ip = di->end_ip -1;
    408 	}
    409       else
    410 	{
    411 	  while (first < last - 8)
    412 	    {
    413 	      entry = first + (((last - first) / 8 + 1) >> 1) * 8;
    414 
    415 	      if (prel31_to_addr (as, arg, entry, &val) < 0)
    416 		return -UNW_EINVAL;
    417 
    418 	      if (ip < val)
    419 		last = entry;
    420 	      else
    421 		first = entry;
    422 	    }
    423 
    424 	  entry = first;
    425 
    426 	  if (prel31_to_addr (as, arg, entry, &pi->start_ip) < 0)
    427 	    return -UNW_EINVAL;
    428 
    429 	  if (prel31_to_addr (as, arg, entry + 8, &pi->end_ip) < 0)
    430 	    return -UNW_EINVAL;
    431 
    432 	  pi->end_ip--;
    433 	}
    434 
    435       if (need_unwind_info)
    436 	{
    437 	  pi->unwind_info_size = 8;
    438 	  pi->unwind_info = (void *) entry;
    439 	  pi->format = UNW_INFO_FORMAT_ARM_EXIDX;
    440 	}
    441       return 0;
    442     }
    443   else if (UNW_TRY_METHOD(UNW_ARM_METHOD_DWARF)
    444 	   && di->format != UNW_INFO_FORMAT_ARM_EXIDX)
    445     return dwarf_search_unwind_table (as, ip, di, pi, need_unwind_info, arg);
    446 
    447   return -UNW_ENOINFO;
    448 }
    449 
    450 #ifndef UNW_REMOTE_ONLY
    451 /**
    452  * Callback to dl_iterate_phdr to find infos about the ARM exidx segment.
    453  */
    454 static int
    455 arm_phdr_cb (struct dl_phdr_info *info, size_t size, void *data)
    456 {
    457   struct arm_cb_data *cb_data = data;
    458   const Elf_W(Phdr) *p_text = NULL;
    459   const Elf_W(Phdr) *p_arm_exidx = NULL;
    460   const Elf_W(Phdr) *phdr = info->dlpi_phdr;
    461   long n;
    462 
    463   for (n = info->dlpi_phnum; --n >= 0; phdr++)
    464     {
    465       switch (phdr->p_type)
    466 	{
    467 	case PT_LOAD:
    468 	  if (cb_data->ip >= phdr->p_vaddr + info->dlpi_addr &&
    469 	      cb_data->ip < phdr->p_vaddr + info->dlpi_addr + phdr->p_memsz)
    470 	    p_text = phdr;
    471 	  break;
    472 
    473 	case PT_ARM_EXIDX:
    474 	  p_arm_exidx = phdr;
    475 	  break;
    476 
    477 	default:
    478 	  break;
    479 	}
    480     }
    481 
    482   if (p_text && p_arm_exidx)
    483     {
    484       cb_data->di.format = UNW_INFO_FORMAT_ARM_EXIDX;
    485       cb_data->di.start_ip = p_text->p_vaddr + info->dlpi_addr;
    486       cb_data->di.end_ip = cb_data->di.start_ip + p_text->p_memsz;
    487       cb_data->di.u.rti.name_ptr = (unw_word_t) info->dlpi_name;
    488       cb_data->di.u.rti.table_data = p_arm_exidx->p_vaddr + info->dlpi_addr;
    489       cb_data->di.u.rti.table_len = p_arm_exidx->p_memsz;
    490       return 1;
    491     }
    492 
    493   return 0;
    494 }
    495 
    496 HIDDEN int
    497 arm_find_proc_info (unw_addr_space_t as, unw_word_t ip,
    498 		    unw_proc_info_t *pi, int need_unwind_info, void *arg)
    499 {
    500   int ret = -1;
    501   intrmask_t saved_mask;
    502 
    503   Debug (14, "looking for IP=0x%lx\n", (long) ip);
    504 
    505   if (UNW_TRY_METHOD(UNW_ARM_METHOD_DWARF))
    506     {
    507       ret = dwarf_find_proc_info (as, ip, pi, need_unwind_info, arg);
    508     }
    509 
    510   if (ret < 0 && UNW_TRY_METHOD (UNW_ARM_METHOD_EXIDX))
    511     {
    512       struct arm_cb_data cb_data;
    513 
    514       memset (&cb_data, 0, sizeof (cb_data));
    515       cb_data.ip = ip;
    516       cb_data.pi = pi;
    517       cb_data.di.format = -1;
    518 
    519       SIGPROCMASK (SIG_SETMASK, &unwi_full_mask, &saved_mask);
    520       ret = dl_iterate_phdr (arm_phdr_cb, &cb_data);
    521       SIGPROCMASK (SIG_SETMASK, &saved_mask, NULL);
    522 
    523       if (cb_data.di.format != -1)
    524 	ret = tdep_search_unwind_table (as, ip, &cb_data.di, pi,
    525 					need_unwind_info, arg);
    526       else
    527 	ret = -UNW_ENOINFO;
    528     }
    529 
    530   if (ret < 0)
    531     /* ANDROID support update. */
    532     {
    533       Debug (14, "IP=0x%lx not found\n", (long) ip);
    534     }
    535     /* End of ANDROID update. */
    536 
    537   return ret;
    538 }
    539 
    540 HIDDEN void
    541 arm_put_unwind_info (unw_addr_space_t as, unw_proc_info_t *proc_info, void *arg)
    542 {
    543   /* it's a no-op */
    544 }
    545 #endif /* !UNW_REMOTE_ONLY */
    546 
    547