1/* libunwind - a platform-independent unwind library
2   Copyright 2011 Linaro Limited
3
4This file is part of libunwind.
5
6Permission is hereby granted, free of charge, to any person obtaining
7a copy of this software and associated documentation files (the
8"Software"), to deal in the Software without restriction, including
9without limitation the rights to use, copy, modify, merge, publish,
10distribute, sublicense, and/or sell copies of the Software, and to
11permit persons to whom the Software is furnished to do so, subject to
12the following conditions:
13
14The above copyright notice and this permission notice shall be
15included in all copies or substantial portions of the Software.
16
17THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.  */
24
25/* This file contains functionality for parsing and interpreting the ARM
26specific unwind information.  Documentation about the exception handling
27ABI for the ARM architecture can be found at:
28http://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
42enum arm_exbuf_cmd_flags {
43  ARM_EXIDX_VFP_SHIFT_16 = 1 << 16,
44  ARM_EXIDX_VFP_DOUBLE = 1 << 17,
45};
46
47struct 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
56static inline int
57prel31_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 */
74HIDDEN int
75arm_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 */
145HIDDEN int
146arm_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 */
283HIDDEN int
284arm_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
378PROTECTED int
379tdep_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 */
454static int
455arm_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
496HIDDEN int
497arm_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
540HIDDEN void
541arm_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