1diff --git a/Makefile.am b/Makefile.am
2index 42386be..e8f7402 100644
3--- a/Makefile.am
4+++ b/Makefile.am
5@@ -524,6 +524,8 @@ src_tools_linux_dump_syms_dump_syms_SOURCES = \
6 	src/common/dwarf/bytereader.cc \
7 	src/common/dwarf/dwarf2diehandler.cc \
8 	src/common/dwarf/dwarf2reader.cc \
9+	src/common/arm_ex_reader.cc \
10+	src/common/arm_ex_to_module.cc \
11 	src/common/linux/crc32.cc \
12 	src/common/linux/dump_symbols.cc \
13 	src/common/linux/elf_symbols_to_module.cc \
14@@ -573,6 +575,8 @@ src_tools_mac_dump_syms_dump_syms_CXXFLAGS= \
15 	-DHAVE_MACH_O_NLIST_H
16
17 src_common_dumper_unittest_SOURCES = \
18+	src/common/arm_ex_reader.cc \
19+	src/common/arm_ex_to_module.cc \
20 	src/common/byte_cursor_unittest.cc \
21 	src/common/dwarf_cfi_to_module.cc \
22 	src/common/dwarf_cfi_to_module_unittest.cc \
23@@ -1336,6 +1340,10 @@ EXTRA_DIST = \
24 	src/common/linux/crc32.cc \
25 	src/common/linux/dump_symbols.cc \
26 	src/common/linux/dump_symbols.h \
27+	src/common/arm_ex_reader.cc \
28+	src/common/arm_ex_reader.h \
29+	src/common/arm_ex_to_module.cc \
30+	src/common/arm_ex_to_module.h \
31 	src/common/linux/elf_symbols_to_module.cc \
32 	src/common/linux/elf_symbols_to_module.h \
33 	src/common/linux/elfutils.cc \
34diff --git a/src/common/arm_ex_reader.cc b/src/common/arm_ex_reader.cc
35new file mode 100644
36index 0000000..2d1ed98
37--- /dev/null
38+++ b/src/common/arm_ex_reader.cc
39@@ -0,0 +1,487 @@
40+
41+/* libunwind - a platform-independent unwind library
42+   Copyright 2011 Linaro Limited
43+
44+This file is part of libunwind.
45+
46+Permission is hereby granted, free of charge, to any person obtaining
47+a copy of this software and associated documentation files (the
48+"Software"), to deal in the Software without restriction, including
49+without limitation the rights to use, copy, modify, merge, publish,
50+distribute, sublicense, and/or sell copies of the Software, and to
51+permit persons to whom the Software is furnished to do so, subject to
52+the following conditions:
53+
54+The above copyright notice and this permission notice shall be
55+included in all copies or substantial portions of the Software.
56+
57+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
58+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
59+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
60+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
61+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
62+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
63+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.  */
64+
65+// Copyright (c) 2010 Google Inc.
66+// All rights reserved.
67+//
68+// Redistribution and use in source and binary forms, with or without
69+// modification, are permitted provided that the following conditions are
70+// met:
71+//
72+//     * Redistributions of source code must retain the above copyright
73+// notice, this list of conditions and the following disclaimer.
74+//     * Redistributions in binary form must reproduce the above
75+// copyright notice, this list of conditions and the following disclaimer
76+// in the documentation and/or other materials provided with the
77+// distribution.
78+//     * Neither the name of Google Inc. nor the names of its
79+// contributors may be used to endorse or promote products derived from
80+// this software without specific prior written permission.
81+//
82+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
83+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
84+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
85+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
86+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
87+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
88+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
89+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
90+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
91+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
92+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
93+
94+
95+// Derived from libunwind, with extensive modifications.
96+
97+
98+#include "common/arm_ex_reader.h"
99+
100+#include <assert.h>
101+#include <stdio.h>
102+
103+// This file, in conjunction with arm_ex_to_module.cc, translates
104+// EXIDX unwind information into the same format that Breakpad uses
105+// for CFI information.  Hence Breakpad's CFI unwinding abilities
106+// also become usable for EXIDX.
107+//
108+// See: "Exception Handling ABI for the ARM Architecture", ARM IHI 0038A
109+// http://infocenter.arm.com/help/topic/com.arm.doc.ihi0038a/IHI0038A_ehabi.pdf
110+
111+// EXIDX data is presented in two parts:
112+//
113+// * an index table.  This contains two words per routine,
114+//   the first of which identifies the routine, and the second
115+//   of which is a reference to the unwind bytecode.  If the
116+//   bytecode is very compact -- 3 bytes or less -- it can be
117+//   stored directly in the second word.
118+//
119+// * an area containing the unwind bytecodes.
120+
121+// General flow is: ExceptionTableInfo::Start iterates over all
122+// of the index table entries (pairs).  For each entry, it:
123+//
124+// * calls ExceptionTableInfo::ExtabEntryExtract to copy the bytecode
125+//   out into an intermediate buffer.
126+
127+// * uses ExceptionTableInfo::ExtabEntryDecode to parse the intermediate
128+//   buffer.  Each bytecode instruction is bundled into a
129+//   arm_ex_to_module::extab_data structure, and handed to ..
130+//
131+// * .. ARMExToModule::ImproveStackFrame, which in turn hands it to
132+//   ARMExToModule::TranslateCmd, and that generates the pseudo-CFI
133+//   records that Breakpad stores.
134+
135+#define ARM_EXIDX_CANT_UNWIND 0x00000001
136+#define ARM_EXIDX_COMPACT     0x80000000
137+#define ARM_EXTBL_OP_FINISH   0xb0
138+#define ARM_EXIDX_TABLE_LIMIT (255*4)
139+
140+namespace arm_ex_reader {
141+
142+using arm_ex_to_module::ARM_EXIDX_CMD_FINISH;
143+using arm_ex_to_module::ARM_EXIDX_CMD_SUB_FROM_VSP;
144+using arm_ex_to_module::ARM_EXIDX_CMD_ADD_TO_VSP;
145+using arm_ex_to_module::ARM_EXIDX_CMD_REG_POP;
146+using arm_ex_to_module::ARM_EXIDX_CMD_REG_TO_SP;
147+using arm_ex_to_module::ARM_EXIDX_CMD_VFP_POP;
148+using arm_ex_to_module::ARM_EXIDX_CMD_WREG_POP;
149+using arm_ex_to_module::ARM_EXIDX_CMD_WCGR_POP;
150+using arm_ex_to_module::ARM_EXIDX_CMD_RESERVED;
151+using arm_ex_to_module::ARM_EXIDX_CMD_REFUSED;
152+using arm_ex_to_module::exidx_entry;
153+using arm_ex_to_module::ARM_EXIDX_VFP_SHIFT_16;
154+using arm_ex_to_module::ARM_EXIDX_VFP_FSTMD;
155+using google_breakpad::MemoryRange;
156+
157+
158+static void* Prel31ToAddr(const void* addr) {
159+  uint32_t offset32 = *reinterpret_cast<const uint32_t*>(addr);
160+  // sign extend offset32[30:0] to 64 bits -- copy bit 30 to positions
161+  // 63:31 inclusive.
162+  uint64_t offset64 = offset32;
163+  if (offset64 & (1ULL << 30))
164+    offset64 |= 0xFFFFFFFF80000000ULL;
165+  else
166+    offset64 &= 0x000000007FFFFFFFULL;
167+  return ((char*)addr) + (uintptr_t)offset64;
168+}
169+
170+
171+// Extract unwind bytecode for the function denoted by |entry| into |buf|,
172+// and return the number of bytes of |buf| written, along with a code
173+// indicating the outcome.
174+
175+ExceptionTableInfo::ExExtractResult ExceptionTableInfo::ExtabEntryExtract(
176+    const struct exidx_entry* entry,
177+    uint8_t* buf, size_t buf_size,
178+    size_t* buf_used) {
179+  MemoryRange mr_out(buf, buf_size);
180+
181+  *buf_used = 0;
182+
183+# define PUT_BUF_U8(_byte) \
184+  do { if (!mr_out.Covers(*buf_used, 1)) return ExOutBufOverflow; \
185+       buf[(*buf_used)++] = (_byte); } while (0)
186+
187+# define GET_EX_U32(_lval, _addr, _sec_mr) \
188+  do { if (!(_sec_mr).Covers(reinterpret_cast<const uint8_t*>(_addr) \
189+                             - (_sec_mr).data(), 4)) \
190+         return ExInBufOverflow; \
191+       (_lval) = *(reinterpret_cast<const uint32_t*>(_addr)); } while (0)
192+
193+# define GET_EXIDX_U32(_lval, _addr) \
194+            GET_EX_U32(_lval, _addr, mr_exidx_)
195+# define GET_EXTAB_U32(_lval, _addr) \
196+            GET_EX_U32(_lval, _addr, mr_extab_)
197+
198+  uint32_t data;
199+  GET_EXIDX_U32(data, &entry->data);
200+
201+  // A function can be marked CANT_UNWIND if (eg) it is known to be
202+  // at the bottom of the stack.
203+  if (data == ARM_EXIDX_CANT_UNWIND)
204+    return ExCantUnwind;
205+
206+  uint32_t  pers;          // personality number
207+  uint32_t  extra;         // number of extra data words required
208+  uint32_t  extra_allowed; // number of extra data words allowed
209+  uint32_t* extbl_data;    // the handler entry, if not inlined
210+
211+  if (data & ARM_EXIDX_COMPACT) {
212+    // The handler table entry has been inlined into the index table entry.
213+    // In this case it can only be an ARM-defined compact model, since
214+    // bit 31 is 1.  Only personalities 0, 1 and 2 are defined for the
215+    // ARM compact model, but 1 and 2 are "Long format" and may require
216+    // extra data words.  Hence the allowable personalities here are:
217+    //   personality 0, in which case 'extra' has no meaning
218+    //   personality 1, with zero extra words
219+    //   personality 2, with zero extra words
220+    extbl_data = NULL;
221+    pers  = (data >> 24) & 0x0F;
222+    extra = (data >> 16) & 0xFF;
223+    extra_allowed = 0;
224+  }
225+  else {
226+    // The index table entry is a pointer to the handler entry.  Note
227+    // that Prel31ToAddr will read the given address, but we already
228+    // range-checked above.
229+    extbl_data = reinterpret_cast<uint32_t*>(Prel31ToAddr(&entry->data));
230+    GET_EXTAB_U32(data, extbl_data);
231+    if (!(data & ARM_EXIDX_COMPACT)) {
232+      // This denotes a "generic model" handler.  That will involve
233+      // executing arbitary machine code, which is something we
234+      // can't represent here; hence reject it.
235+      return ExCantRepresent;
236+    }
237+    // So we have a compact model representation.  Again, 3 possible
238+    // personalities, but this time up to 255 allowable extra words.
239+    pers  = (data >> 24) & 0x0F;
240+    extra = (data >> 16) & 0xFF;
241+    extra_allowed = 255;
242+    extbl_data++;
243+  }
244+
245+  // Now look at the the handler table entry.  The first word is
246+  // |data| and subsequent words start at |*extbl_data|.  The number
247+  // of extra words to use is |extra|, provided that the personality
248+  // allows extra words.  Even if it does, none may be available --
249+  // extra_allowed is the maximum number of extra words allowed. */
250+  if (pers == 0) {
251+    // "Su16" in the documentation -- 3 unwinding insn bytes
252+    // |extra| has no meaning here; instead that byte is an unwind-info byte
253+    PUT_BUF_U8(data >> 16);
254+    PUT_BUF_U8(data >> 8);
255+    PUT_BUF_U8(data);
256+  }
257+  else if ((pers == 1 || pers == 2) && extra <= extra_allowed) {
258+    // "Lu16" or "Lu32" respectively -- 2 unwinding insn bytes,
259+    // and up to 255 extra words.
260+    PUT_BUF_U8(data >> 8);
261+    PUT_BUF_U8(data);
262+    for (uint32_t j = 0; j < extra; j++) {
263+      GET_EXTAB_U32(data, extbl_data);
264+      extbl_data++;
265+      PUT_BUF_U8(data >> 24);
266+      PUT_BUF_U8(data >> 16);
267+      PUT_BUF_U8(data >> 8);
268+      PUT_BUF_U8(data >> 0);
269+    }
270+  }
271+  else {
272+    // The entry is invalid.
273+    return ExInvalid;
274+  }
275+
276+  // Make sure the entry is terminated with "FINISH"
277+  if (*buf_used > 0 && buf[(*buf_used) - 1] != ARM_EXTBL_OP_FINISH)
278+    PUT_BUF_U8(ARM_EXTBL_OP_FINISH);
279+
280+  return ExSuccess;
281+
282+# undef GET_EXTAB_U32
283+# undef GET_EXIDX_U32
284+# undef GET_U32
285+# undef PUT_BUF_U8
286+}
287+
288+
289+// Take the unwind information extracted by ExtabEntryExtract
290+// and parse it into frame-unwind instructions.  These are as
291+// specified in "Table 4, ARM-defined frame-unwinding instructions"
292+// in the specification document detailed in comments at the top
293+// of this file.
294+//
295+// This reads from |buf[0, +data_size)|.  It checks for overruns of
296+// the input buffer and returns a negative value if that happens, or
297+// for any other failure cases.  It returns zero in case of success.
298+int ExceptionTableInfo::ExtabEntryDecode(const uint8_t* buf, size_t buf_size) {
299+  if (buf == NULL || buf_size == 0)
300+    return -1;
301+
302+  MemoryRange mr_in(buf, buf_size);
303+  const uint8_t* buf_initially = buf;
304+
305+# define GET_BUF_U8(_lval) \
306+  do { if (!mr_in.Covers(buf - buf_initially, 1)) return -1; \
307+       (_lval) = *(buf++); } while (0)
308+
309+  const uint8_t* end = buf + buf_size;
310+
311+  while (buf < end) {
312+    struct arm_ex_to_module::extab_data edata;
313+    memset(&edata, 0, sizeof(edata));
314+
315+    uint8_t op;
316+    GET_BUF_U8(op);
317+    if ((op & 0xc0) == 0x00) {
318+      // vsp = vsp + (xxxxxx << 2) + 4
319+      edata.cmd = ARM_EXIDX_CMD_ADD_TO_VSP;
320+      edata.data = (((int)op & 0x3f) << 2) + 4;
321+    } else if ((op & 0xc0) == 0x40) {
322+      // vsp = vsp - (xxxxxx << 2) - 4
323+      edata.cmd = ARM_EXIDX_CMD_SUB_FROM_VSP;
324+      edata.data = (((int)op & 0x3f) << 2) + 4;
325+    } else if ((op & 0xf0) == 0x80) {
326+      uint8_t op2;
327+      GET_BUF_U8(op2);
328+      if (op == 0x80 && op2 == 0x00) {
329+        // Refuse to unwind
330+        edata.cmd = ARM_EXIDX_CMD_REFUSED;
331+      } else {
332+        // Pop up to 12 integer registers under masks {r15-r12},{r11-r4}
333+        edata.cmd = ARM_EXIDX_CMD_REG_POP;
334+        edata.data = ((op & 0xf) << 8) | op2;
335+        edata.data = edata.data << 4;
336+      }
337+    } else if ((op & 0xf0) == 0x90) {
338+      if (op == 0x9d || op == 0x9f) {
339+        // 9d: Reserved as prefix for ARM register to register moves
340+        // 9f: Reserved as perfix for Intel Wireless MMX reg to reg moves
341+        edata.cmd = ARM_EXIDX_CMD_RESERVED;
342+      } else {
343+        // Set vsp = r[nnnn]
344+        edata.cmd = ARM_EXIDX_CMD_REG_TO_SP;
345+        edata.data = op & 0x0f;
346+      }
347+    } else if ((op & 0xf0) == 0xa0) {
348+      // Pop r4 to r[4+nnn],          or
349+      // Pop r4 to r[4+nnn] and r14   or
350+      unsigned end = (op & 0x07);
351+      edata.data = (1 << (end + 1)) - 1;
352+      edata.data = edata.data << 4;
353+      if (op & 0x08) edata.data |= 1 << 14;
354+      edata.cmd = ARM_EXIDX_CMD_REG_POP;
355+    } else if (op == ARM_EXTBL_OP_FINISH) {
356+      // Finish
357+      edata.cmd = ARM_EXIDX_CMD_FINISH;
358+      buf = end;
359+    } else if (op == 0xb1) {
360+      uint8_t op2;
361+      GET_BUF_U8(op2);
362+      if (op2 == 0 || (op2 & 0xf0)) {
363+        // Spare
364+        edata.cmd = ARM_EXIDX_CMD_RESERVED;
365+      } else {
366+        // Pop integer registers under mask {r3,r2,r1,r0}
367+        edata.cmd = ARM_EXIDX_CMD_REG_POP;
368+        edata.data = op2 & 0x0f;
369+      }
370+    } else if (op == 0xb2) {
371+      // vsp = vsp + 0x204 + (uleb128 << 2)
372+      uint64_t offset = 0;
373+      uint8_t byte, shift = 0;
374+      do {
375+        GET_BUF_U8(byte);
376+        offset |= (byte & 0x7f) << shift;
377+        shift += 7;
378+      } while ((byte & 0x80) && buf < end);
379+      edata.data = offset * 4 + 0x204;
380+      edata.cmd = ARM_EXIDX_CMD_ADD_TO_VSP;
381+    } else if (op == 0xb3 || op == 0xc8 || op == 0xc9) {
382+      // b3: Pop VFP regs D[ssss]    to D[ssss+cccc],    FSTMFDX-ishly
383+      // c8: Pop VFP regs D[16+ssss] to D[16+ssss+cccc], FSTMFDD-ishly
384+      // c9: Pop VFP regs D[ssss]    to D[ssss+cccc],    FSTMFDD-ishly
385+      edata.cmd = ARM_EXIDX_CMD_VFP_POP;
386+      GET_BUF_U8(edata.data);
387+      if (op == 0xc8) edata.data |= ARM_EXIDX_VFP_SHIFT_16;
388+      if (op != 0xb3) edata.data |= ARM_EXIDX_VFP_FSTMD;
389+    } else if ((op & 0xf8) == 0xb8 || (op & 0xf8) == 0xd0) {
390+      // b8: Pop VFP regs D[8] to D[8+nnn], FSTMFDX-ishly
391+      // d0: Pop VFP regs D[8] to D[8+nnn], FSTMFDD-ishly
392+      edata.cmd = ARM_EXIDX_CMD_VFP_POP;
393+      edata.data = 0x80 | (op & 0x07);
394+      if ((op & 0xf8) == 0xd0) edata.data |= ARM_EXIDX_VFP_FSTMD;
395+    } else if (op >= 0xc0 && op <= 0xc5) {
396+      // Intel Wireless MMX pop wR[10]-wr[10+nnn], nnn != 6,7
397+      edata.cmd = ARM_EXIDX_CMD_WREG_POP;
398+      edata.data = 0xa0 | (op & 0x07);
399+    } else if (op == 0xc6) {
400+      // Intel Wireless MMX pop wR[ssss] to wR[ssss+cccc]
401+      edata.cmd = ARM_EXIDX_CMD_WREG_POP;
402+      GET_BUF_U8(edata.data);
403+    } else if (op == 0xc7) {
404+      uint8_t op2;
405+      GET_BUF_U8(op2);
406+      if (op2 == 0 || (op2 & 0xf0)) {
407+        // Spare
408+        edata.cmd = ARM_EXIDX_CMD_RESERVED;
409+      } else {
410+        // Intel Wireless MMX pop wCGR registers under mask {wCGR3,2,1,0}
411+        edata.cmd = ARM_EXIDX_CMD_WCGR_POP;
412+        edata.data = op2 & 0x0f;
413+      }
414+    } else {
415+      // Spare
416+      edata.cmd = ARM_EXIDX_CMD_RESERVED;
417+    }
418+
419+    int ret = handler_->ImproveStackFrame(&edata);
420+    if (ret < 0)
421+      return ret;
422+  }
423+  return 0;
424+
425+# undef GET_BUF_U8
426+}
427+
428+void ExceptionTableInfo::Start() {
429+  const struct exidx_entry* start
430+    = reinterpret_cast<const struct exidx_entry*>(mr_exidx_.data());
431+  const struct exidx_entry* end
432+    = reinterpret_cast<const struct exidx_entry*>(mr_exidx_.data()
433+                                                  + mr_exidx_.length());
434+
435+  // Iterate over each of the EXIDX entries (pairs of 32-bit words).
436+  // These occupy the entire .exidx section.
437+  for (const struct exidx_entry* entry = start; entry < end; ++entry) {
438+    // Figure out the code address range that this table entry is
439+    // associated with.
440+    uint32_t addr = (reinterpret_cast<char*>(Prel31ToAddr(&entry->addr))
441+                     - mapping_addr_ + loading_addr_) & 0x7fffffff;
442+    uint32_t next_addr;
443+    if (entry < end - 1) {
444+      next_addr = (reinterpret_cast<char*>(Prel31ToAddr(&((entry + 1)->addr)))
445+                   - mapping_addr_ + loading_addr_) & 0x7fffffff;
446+    } else {
447+      // This is the last EXIDX entry in the sequence, so we don't
448+      // have an address for the start of the next function, to limit
449+      // this one.  Instead use the address of the last byte of the
450+      // text section associated with this .exidx section, that we
451+      // have been given.  So as to avoid junking up the CFI unwind
452+      // tables with absurdly large address ranges in the case where
453+      // text_last_svma_ is wrong, only use the value if it is nonzero
454+      // and within one page of |addr|.  Otherwise assume a length of 1.
455+      //
456+      // In some cases, gcc has been observed to finish the exidx
457+      // section with an entry of length 1 marked CANT_UNWIND,
458+      // presumably exactly for the purpose of giving a definite
459+      // length for the last real entry, without having to look at
460+      // text segment boundaries.
461+      bool plausible = false;
462+      next_addr = addr + 1;
463+      if (text_last_svma_ != 0) {
464+        uint32_t maybe_next_addr = text_last_svma_ + 1;
465+        if (maybe_next_addr > addr && maybe_next_addr - addr <= 4096) {
466+          next_addr = maybe_next_addr;
467+          plausible = true;
468+        }
469+      }
470+      if (!plausible) {
471+        fprintf(stderr, "ExceptionTableInfo: implausible EXIDX last entry size "
472+                "%d, using 1 instead.", (int32_t)(text_last_svma_ - addr));
473+      }
474+    }
475+
476+    // Extract the unwind info into |buf|.  This might fail for
477+    // various reasons.  It involves reading both the .exidx and
478+    // .extab sections.  All accesses to those sections are
479+    // bounds-checked.
480+    uint8_t buf[ARM_EXIDX_TABLE_LIMIT];
481+    size_t buf_used = 0;
482+    ExExtractResult res = ExtabEntryExtract(entry, buf, sizeof(buf), &buf_used);
483+    if (res != ExSuccess) {
484+      // Couldn't extract the unwind info, for some reason.  Move on.
485+      switch (res) {
486+        case ExInBufOverflow:
487+          fprintf(stderr, "ExtabEntryExtract: .exidx/.extab section overrun");
488+          break;
489+        case ExOutBufOverflow:
490+          fprintf(stderr, "ExtabEntryExtract: bytecode buffer overflow");
491+          break;
492+        case ExCantUnwind:
493+          fprintf(stderr, "ExtabEntryExtract: function is marked CANT_UNWIND");
494+          break;
495+        case ExCantRepresent:
496+          fprintf(stderr, "ExtabEntryExtract: bytecode can't be represented");
497+          break;
498+        case ExInvalid:
499+          fprintf(stderr, "ExtabEntryExtract: index table entry is invalid");
500+          break;
501+        default:
502+          fprintf(stderr, "ExtabEntryExtract: unknown error: %d", (int)res);
503+          break;
504+      }
505+      continue;
506+    }
507+
508+    // Finally, work through the unwind instructions in |buf| and
509+    // create CFI entries that Breakpad can use.  This can also fail.
510+    // First, add a new stack frame entry, into which ExtabEntryDecode
511+    // will write the CFI entries.
512+    if (!handler_->HasStackFrame(addr, next_addr - addr)) {
513+      handler_->AddStackFrame(addr, next_addr - addr);
514+      int ret = ExtabEntryDecode(buf, buf_used);
515+      if (ret < 0) {
516+	handler_->DeleteStackFrame();
517+	fprintf(stderr, "ExtabEntryDecode: failed with error code: %d", ret);
518+	continue;
519+      }
520+      handler_->SubmitStackFrame();
521+    }
522+
523+  } /* iterating over .exidx */
524+}
525+
526+}  // namespace arm_ex_reader
527diff --git a/src/common/arm_ex_reader.h b/src/common/arm_ex_reader.h
528new file mode 100644
529index 0000000..9b54e8a
530--- /dev/null
531+++ b/src/common/arm_ex_reader.h
532@@ -0,0 +1,114 @@
533+/* libunwind - a platform-independent unwind library
534+   Copyright 2011 Linaro Limited
535+
536+This file is part of libunwind.
537+
538+Permission is hereby granted, free of charge, to any person obtaining
539+a copy of this software and associated documentation files (the
540+"Software"), to deal in the Software without restriction, including
541+without limitation the rights to use, copy, modify, merge, publish,
542+distribute, sublicense, and/or sell copies of the Software, and to
543+permit persons to whom the Software is furnished to do so, subject to
544+the following conditions:
545+
546+The above copyright notice and this permission notice shall be
547+included in all copies or substantial portions of the Software.
548+
549+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
550+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
551+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
552+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
553+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
554+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
555+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.  */
556+
557+// Copyright (c) 2010 Google Inc.
558+// All rights reserved.
559+//
560+// Redistribution and use in source and binary forms, with or without
561+// modification, are permitted provided that the following conditions are
562+// met:
563+//
564+//     * Redistributions of source code must retain the above copyright
565+// notice, this list of conditions and the following disclaimer.
566+//     * Redistributions in binary form must reproduce the above
567+// copyright notice, this list of conditions and the following disclaimer
568+// in the documentation and/or other materials provided with the
569+// distribution.
570+//     * Neither the name of Google Inc. nor the names of its
571+// contributors may be used to endorse or promote products derived from
572+// this software without specific prior written permission.
573+//
574+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
575+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
576+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
577+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
578+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
579+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
580+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
581+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
582+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
583+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
584+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
585+
586+
587+// Derived from libunwind, with extensive modifications.
588+
589+#ifndef COMMON_ARM_EX_READER_H__
590+#define COMMON_ARM_EX_READER_H__
591+
592+#include "common/arm_ex_to_module.h"
593+#include "common/memory_range.h"
594+
595+namespace arm_ex_reader {
596+
597+// This class is a reader for ARM unwind information
598+// from .ARM.exidx and .ARM.extab sections.
599+class ExceptionTableInfo {
600+ public:
601+  ExceptionTableInfo(const char* exidx, size_t exidx_size,
602+                     const char* extab, size_t extab_size,
603+                     uint32_t text_last_svma,
604+                     arm_ex_to_module::ARMExToModule* handler,
605+                     const char* mapping_addr,
606+                     uint32_t loading_addr)
607+      : mr_exidx_(google_breakpad::MemoryRange(exidx, exidx_size)),
608+        mr_extab_(google_breakpad::MemoryRange(extab, extab_size)),
609+        text_last_svma_(text_last_svma),
610+        handler_(handler), mapping_addr_(mapping_addr),
611+        loading_addr_(loading_addr) { }
612+
613+  ~ExceptionTableInfo() { }
614+
615+  // Parses the entries in .ARM.exidx and possibly
616+  // in .ARM.extab tables, reports what we find to
617+  // arm_ex_to_module::ARMExToModule.
618+  void Start();
619+
620+ private:
621+  google_breakpad::MemoryRange mr_exidx_;
622+  google_breakpad::MemoryRange mr_extab_;
623+  uint32_t text_last_svma_;
624+  arm_ex_to_module::ARMExToModule* handler_;
625+  const char* mapping_addr_;
626+  uint32_t loading_addr_;
627+
628+  enum ExExtractResult {
629+    ExSuccess,        // success
630+    ExInBufOverflow,  // out-of-range while reading .exidx
631+    ExOutBufOverflow, // output buffer is too small
632+    ExCantUnwind,     // this function is marked CANT_UNWIND
633+    ExCantRepresent,  // entry valid, but we can't represent it
634+    ExInvalid         // entry is invalid
635+  };
636+  ExExtractResult
637+    ExtabEntryExtract(const struct arm_ex_to_module::exidx_entry* entry,
638+                      uint8_t* buf, size_t buf_size,
639+                      size_t* buf_used);
640+
641+  int ExtabEntryDecode(const uint8_t* buf, size_t buf_size);
642+};
643+
644+}  // namespace arm_ex_reader
645+
646+#endif // COMMON_ARM_EX_READER_H__
647diff --git a/src/common/arm_ex_to_module.cc b/src/common/arm_ex_to_module.cc
648new file mode 100644
649index 0000000..c326744
650--- /dev/null
651+++ b/src/common/arm_ex_to_module.cc
652@@ -0,0 +1,209 @@
653+
654+/* libunwind - a platform-independent unwind library
655+   Copyright 2011 Linaro Limited
656+
657+This file is part of libunwind.
658+
659+Permission is hereby granted, free of charge, to any person obtaining
660+a copy of this software and associated documentation files (the
661+"Software"), to deal in the Software without restriction, including
662+without limitation the rights to use, copy, modify, merge, publish,
663+distribute, sublicense, and/or sell copies of the Software, and to
664+permit persons to whom the Software is furnished to do so, subject to
665+the following conditions:
666+
667+The above copyright notice and this permission notice shall be
668+included in all copies or substantial portions of the Software.
669+
670+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
671+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
672+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
673+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
674+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
675+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
676+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.  */
677+
678+// Copyright (c) 2010 Google Inc.
679+// All rights reserved.
680+//
681+// Redistribution and use in source and binary forms, with or without
682+// modification, are permitted provided that the following conditions are
683+// met:
684+//
685+//     * Redistributions of source code must retain the above copyright
686+// notice, this list of conditions and the following disclaimer.
687+//     * Redistributions in binary form must reproduce the above
688+// copyright notice, this list of conditions and the following disclaimer
689+// in the documentation and/or other materials provided with the
690+// distribution.
691+//     * Neither the name of Google Inc. nor the names of its
692+// contributors may be used to endorse or promote products derived from
693+// this software without specific prior written permission.
694+//
695+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
696+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
697+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
698+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
699+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
700+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
701+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
702+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
703+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
704+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
705+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
706+
707+
708+// Derived from libunwind, with extensive modifications.
709+
710+#include "common/arm_ex_to_module.h"
711+
712+#include <stdio.h>
713+#include <assert.h>
714+
715+// For big-picture comments on how the EXIDX reader works,
716+// see arm_ex_reader.cc.
717+
718+#define ARM_EXBUF_START(x) (((x) >> 4) & 0x0f)
719+#define ARM_EXBUF_COUNT(x) ((x) & 0x0f)
720+#define ARM_EXBUF_END(x)   (ARM_EXBUF_START(x) + ARM_EXBUF_COUNT(x))
721+
722+using google_breakpad::Module;
723+
724+namespace arm_ex_to_module {
725+
726+static const char* const regnames[] = {
727+ "r0", "r1", "r2",  "r3",  "r4",  "r5", "r6", "r7",
728+ "r8", "r9", "r10", "r11", "r12", "sp", "lr", "pc",
729+ "f0", "f1", "f2",  "f3",  "f4",  "f5", "f6", "f7",
730+ "fps", "cpsr"
731+};
732+
733+// Translate command from extab_data to command for Module.
734+int ARMExToModule::TranslateCmd(const struct extab_data* edata,
735+                                Module::StackFrameEntry* entry, string& vsp) {
736+  int ret = 0;
737+  switch (edata->cmd) {
738+    case ARM_EXIDX_CMD_FINISH:
739+      /* Copy LR to PC if there isn't currently a rule for PC in force. */
740+      if (entry->initial_rules.find("pc")
741+          == entry->initial_rules.end()) {
742+        if (entry->initial_rules.find("lr")
743+            == entry->initial_rules.end()) {
744+          entry->initial_rules["pc"] = "lr";
745+        } else {
746+          entry->initial_rules["pc"] = entry->initial_rules["lr"];
747+        }
748+      }
749+      break;
750+    case ARM_EXIDX_CMD_SUB_FROM_VSP:
751+      {
752+        char c[16];
753+        sprintf(c, " %d -", edata->data);
754+        vsp += c;
755+      }
756+      break;
757+    case ARM_EXIDX_CMD_ADD_TO_VSP:
758+      {
759+        char c[16];
760+        sprintf(c, " %d +", edata->data);
761+        vsp += c;
762+      }
763+      break;
764+    case ARM_EXIDX_CMD_REG_POP:
765+      for (unsigned int i = 0; i < 16; i++) {
766+        if (edata->data & (1 << i)) {
767+          entry->initial_rules[regnames[i]]
768+            = vsp + " ^";
769+          vsp += " 4 +";
770+        }
771+      }
772+      /* Set cfa in case the SP got popped. */
773+      if (edata->data & (1 << 13)) {
774+        vsp = entry->initial_rules["sp"];
775+      }
776+      break;
777+    case ARM_EXIDX_CMD_REG_TO_SP: {
778+      assert (edata->data < 16);
779+      const char* const regname = regnames[edata->data];
780+      if (entry->initial_rules.find(regname) == entry->initial_rules.end()) {
781+        entry->initial_rules["sp"] = regname;
782+      } else {
783+        entry->initial_rules["sp"] = entry->initial_rules[regname];
784+      }
785+      vsp = entry->initial_rules["sp"];
786+      break;
787+    }
788+    case ARM_EXIDX_CMD_VFP_POP:
789+      /* Don't recover VFP registers, but be sure to adjust the stack
790+         pointer. */
791+      for (unsigned int i = ARM_EXBUF_START(edata->data);
792+           i <= ARM_EXBUF_END(edata->data); i++) {
793+        vsp += " 8 +";
794+      }
795+      if (!(edata->data & ARM_EXIDX_VFP_FSTMD)) {
796+        vsp += " 4 +";
797+      }
798+      break;
799+    case ARM_EXIDX_CMD_WREG_POP:
800+      for (unsigned int i = ARM_EXBUF_START(edata->data);
801+           i <= ARM_EXBUF_END(edata->data); i++) {
802+        vsp += " 8 +";
803+      }
804+      break;
805+    case ARM_EXIDX_CMD_WCGR_POP:
806+      // Pop wCGR registers under mask {wCGR3,2,1,0}, hence "i < 4"
807+      for (unsigned int i = 0; i < 4; i++) {
808+        if (edata->data & (1 << i)) {
809+          vsp += " 4 +";
810+        }
811+      }
812+      break;
813+    case ARM_EXIDX_CMD_REFUSED:
814+    case ARM_EXIDX_CMD_RESERVED:
815+      ret = -1;
816+      break;
817+  }
818+  return ret;
819+}
820+
821+bool ARMExToModule::HasStackFrame(uintptr_t addr, size_t size) {
822+  // Invariant: the range [addr,covered) is covered by existing stack
823+  // frame entries.
824+  uintptr_t covered = addr;
825+  while (covered < addr + size) {
826+    const Module::StackFrameEntry *old_entry =
827+      module_->FindStackFrameEntryByAddress(covered);
828+    if (!old_entry) {
829+      return false;
830+    }
831+    covered = old_entry->address + old_entry->size;
832+  }
833+  return true;
834+}
835+
836+void ARMExToModule::AddStackFrame(uintptr_t addr, size_t size) {
837+  stack_frame_entry_ = new Module::StackFrameEntry;
838+  stack_frame_entry_->address = addr;
839+  stack_frame_entry_->size = size;
840+  stack_frame_entry_->initial_rules[".cfa"] = "sp";
841+  vsp_ = "sp";
842+}
843+
844+int ARMExToModule::ImproveStackFrame(const struct extab_data* edata) {
845+  return TranslateCmd(edata, stack_frame_entry_, vsp_) ;
846+}
847+
848+void ARMExToModule::DeleteStackFrame() {
849+  delete stack_frame_entry_;
850+}
851+
852+void ARMExToModule::SubmitStackFrame() {
853+  // return address always winds up in pc
854+  stack_frame_entry_->initial_rules[".ra"]
855+    = stack_frame_entry_->initial_rules["pc"];
856+  // the final value of vsp is the new value of sp
857+  stack_frame_entry_->initial_rules["sp"] = vsp_;
858+  module_->AddStackFrameEntry(stack_frame_entry_);
859+}
860+
861+}  // namespace arm_ex_to_module
862diff --git a/src/common/arm_ex_to_module.h b/src/common/arm_ex_to_module.h
863new file mode 100644
864index 0000000..f413a16
865--- /dev/null
866+++ b/src/common/arm_ex_to_module.h
867@@ -0,0 +1,119 @@
868+/* libunwind - a platform-independent unwind library
869+   Copyright 2011 Linaro Limited
870+
871+This file is part of libunwind.
872+
873+Permission is hereby granted, free of charge, to any person obtaining
874+a copy of this software and associated documentation files (the
875+"Software"), to deal in the Software without restriction, including
876+without limitation the rights to use, copy, modify, merge, publish,
877+distribute, sublicense, and/or sell copies of the Software, and to
878+permit persons to whom the Software is furnished to do so, subject to
879+the following conditions:
880+
881+The above copyright notice and this permission notice shall be
882+included in all copies or substantial portions of the Software.
883+
884+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
885+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
886+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
887+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
888+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
889+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
890+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.  */
891+
892+// Copyright (c) 2010 Google Inc.
893+// All rights reserved.
894+//
895+// Redistribution and use in source and binary forms, with or without
896+// modification, are permitted provided that the following conditions are
897+// met:
898+//
899+//     * Redistributions of source code must retain the above copyright
900+// notice, this list of conditions and the following disclaimer.
901+//     * Redistributions in binary form must reproduce the above
902+// copyright notice, this list of conditions and the following disclaimer
903+// in the documentation and/or other materials provided with the
904+// distribution.
905+//     * Neither the name of Google Inc. nor the names of its
906+// contributors may be used to endorse or promote products derived from
907+// this software without specific prior written permission.
908+//
909+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
910+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
911+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
912+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
913+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
914+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
915+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
916+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
917+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
918+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
919+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
920+
921+
922+// Derived from libunwind, with extensive modifications.
923+
924+#ifndef COMMON_ARM_EX_TO_MODULE__
925+#define COMMON_ARM_EX_TO_MODULE__
926+
927+#include "common/module.h"
928+
929+#include <string.h>
930+
931+namespace arm_ex_to_module {
932+
933+using google_breakpad::Module;
934+
935+typedef enum extab_cmd {
936+  ARM_EXIDX_CMD_FINISH,
937+  ARM_EXIDX_CMD_SUB_FROM_VSP,
938+  ARM_EXIDX_CMD_ADD_TO_VSP,
939+  ARM_EXIDX_CMD_REG_POP,
940+  ARM_EXIDX_CMD_REG_TO_SP,
941+  ARM_EXIDX_CMD_VFP_POP,
942+  ARM_EXIDX_CMD_WREG_POP,
943+  ARM_EXIDX_CMD_WCGR_POP,
944+  ARM_EXIDX_CMD_RESERVED,
945+  ARM_EXIDX_CMD_REFUSED,
946+} extab_cmd_t;
947+
948+struct exidx_entry {
949+  uint32_t addr;
950+  uint32_t data;
951+};
952+
953+struct extab_data {
954+  extab_cmd_t cmd;
955+  uint32_t data;
956+};
957+
958+enum extab_cmd_flags {
959+  ARM_EXIDX_VFP_SHIFT_16 = 1 << 16,
960+  ARM_EXIDX_VFP_FSTMD = 1 << 17, // distinguishes FSTMxxD from FSTMxxX
961+};
962+
963+// Receives information from arm_ex_reader::ExceptionTableInfo
964+// and adds it to the Module object
965+class ARMExToModule {
966+ public:
967+  ARMExToModule(Module* module)
968+      : module_(module) { }
969+  ~ARMExToModule() { }
970+  bool HasStackFrame(uintptr_t addr, size_t size);
971+  void AddStackFrame(uintptr_t addr, size_t size);
972+  int ImproveStackFrame(const struct extab_data* edata);
973+  void DeleteStackFrame();
974+  void SubmitStackFrame();
975+ private:
976+  Module* module_;
977+  Module::StackFrameEntry* stack_frame_entry_;
978+  string vsp_;
979+  int TranslateCmd(const struct extab_data* edata,
980+                   Module::StackFrameEntry* entry,
981+                   string& vsp);
982+};
983+
984+} // namespace arm_ex_to_module
985+
986+#endif // COMMON_ARM_EX_TO_MODULE__
987diff --git a/src/common/linux/dump_symbols.cc b/src/common/linux/dump_symbols.cc
988index 1e96ca6..4222ce3 100644
989--- a/src/common/linux/dump_symbols.cc
990+++ b/src/common/linux/dump_symbols.cc
991@@ -52,6 +52,7 @@
992 #include <utility>
993 #include <vector>
994
995+#include "common/arm_ex_reader.h"
996 #include "common/dwarf/bytereader-inl.h"
997 #include "common/dwarf/dwarf2diehandler.h"
998 #include "common/dwarf_cfi_to_module.h"
999@@ -71,6 +72,11 @@
1000 #endif
1001 #include "common/using_std_string.h"
1002
1003+#ifndef SHT_ARM_EXIDX
1004+// bionic and older glibc don't define this
1005+# define SHT_ARM_EXIDX (SHT_LOPROC + 1)
1006+#endif
1007+
1008 // This namespace contains helper functions.
1009 namespace {
1010
1011@@ -373,6 +379,52 @@ bool LoadDwarfCFI(const string& dwarf_filename,
1012   return true;
1013 }
1014
1015+template<typename ElfClass>
1016+bool LoadARMexidx(const typename ElfClass::Ehdr* elf_header,
1017+                  const typename ElfClass::Shdr* exidx_section,
1018+                  const typename ElfClass::Shdr* extab_section,
1019+                  uint32_t loading_addr,
1020+                  Module* module) {
1021+  // To do this properly we need to know:
1022+  // * the bounds of the .ARM.exidx section in the mapped image
1023+  // * the bounds of the .ARM.extab section in the mapped image
1024+  // * the vma of the last byte in the text section associated with the .exidx
1025+  // The first two are easy.  The third is a bit tricky.  If we can't
1026+  // figure out what it is, just pass in zero.
1027+  const char *exidx_img
1028+    = GetOffset<ElfClass, char>(elf_header, exidx_section->sh_offset);
1029+  size_t exidx_size = exidx_section->sh_size;
1030+  const char *extab_img
1031+    = GetOffset<ElfClass, char>(elf_header, extab_section->sh_offset);
1032+  size_t extab_size = extab_section->sh_size;
1033+
1034+  // The sh_link field of the exidx section gives the section number
1035+  // for the associated text section.
1036+  uint32_t exidx_text_last_svma = 0;
1037+  int exidx_text_sno = exidx_section->sh_link;
1038+  typedef typename ElfClass::Shdr Shdr;
1039+  // |sections| points to the section header table
1040+  const Shdr* sections
1041+    = GetOffset<ElfClass, Shdr>(elf_header, elf_header->e_shoff);
1042+  const int num_sections = elf_header->e_shnum;
1043+  if (exidx_text_sno >= 0 && exidx_text_sno < num_sections) {
1044+    const Shdr* exidx_text_shdr = &sections[exidx_text_sno];
1045+    if (exidx_text_shdr->sh_size > 0) {
1046+      exidx_text_last_svma
1047+        = exidx_text_shdr->sh_addr + exidx_text_shdr->sh_size - 1;
1048+    }
1049+  }
1050+
1051+  arm_ex_to_module::ARMExToModule handler(module);
1052+  arm_ex_reader::ExceptionTableInfo
1053+    parser(exidx_img, exidx_size, extab_img, extab_size, exidx_text_last_svma,
1054+           &handler,
1055+           reinterpret_cast<const char*>(elf_header),
1056+           loading_addr);
1057+  parser.Start();
1058+  return true;
1059+}
1060+
1061 bool LoadELF(const string& obj_file, MmapWrapper* map_wrapper,
1062              void** elf_header) {
1063   int obj_fd = open(obj_file.c_str(), O_RDONLY);
1064@@ -756,6 +808,29 @@ bool LoadSymbols(const string& obj_file,
1065     }
1066   }
1067
1068+  // ARM has special unwind tables that can be used.
1069+  const Shdr* arm_exidx_section =
1070+      FindElfSectionByName<ElfClass>(".ARM.exidx", SHT_ARM_EXIDX,
1071+                                     sections, names, names_end,
1072+                                     elf_header->e_shnum);
1073+  const Shdr* arm_extab_section =
1074+      FindElfSectionByName<ElfClass>(".ARM.extab", SHT_PROGBITS,
1075+                                     sections, names, names_end,
1076+                                     elf_header->e_shnum);
1077+  // Load information from these sections even if there is
1078+  // .debug_info, because some functions (e.g., hand-written or
1079+  // script-generated assembly) could have exidx entries but no DWARF.
1080+  // (For functions with both, the DWARF info that has already been
1081+  // parsed will take precedence.)
1082+  if (arm_exidx_section && arm_extab_section && options.symbol_data != NO_CFI) {
1083+    info->LoadedSection(".ARM.exidx");
1084+    info->LoadedSection(".ARM.extab");
1085+    bool result = LoadARMexidx<ElfClass>(elf_header,
1086+                                         arm_exidx_section, arm_extab_section,
1087+                                         loading_addr, module);
1088+    found_usable_info = found_usable_info || result;
1089+  }
1090+
1091   if (!found_debug_info_section) {
1092     fprintf(stderr, "%s: file contains no debugging information"
1093             " (no \".stab\" or \".debug_info\" sections)\n",
1094diff --git a/src/common/module.cc b/src/common/module.cc
1095index fa798f4..ca52f9f 100644
1096--- a/src/common/module.cc
1097+++ b/src/common/module.cc
1098@@ -63,7 +63,7 @@ Module::~Module() {
1099        it != functions_.end(); ++it) {
1100     delete *it;
1101   }
1102-  for (vector<StackFrameEntry *>::iterator it = stack_frame_entries_.begin();
1103+  for (StackFrameEntrySet::iterator it = stack_frame_entries_.begin();
1104        it != stack_frame_entries_.end(); ++it) {
1105     delete *it;
1106   }
1107@@ -119,8 +119,14 @@ void Module::AddFunctions(vector<Function *>::iterator begin,
1108     AddFunction(*it);
1109 }
1110
1111-void Module::AddStackFrameEntry(StackFrameEntry *stack_frame_entry) {
1112-  stack_frame_entries_.push_back(stack_frame_entry);
1113+void Module::AddStackFrameEntry(StackFrameEntry* stack_frame_entry) {
1114+  std::pair<StackFrameEntrySet::iterator,bool> ret =
1115+      stack_frame_entries_.insert(stack_frame_entry);
1116+  if (!ret.second) {
1117+    // Free the duplicate that was not inserted because this Module
1118+    // now owns it.
1119+    delete stack_frame_entry;
1120+  }
1121 }
1122
1123 void Module::AddExtern(Extern *ext) {
1124@@ -180,8 +186,25 @@ void Module::GetFiles(vector<File *> *vec) {
1125     vec->push_back(it->second);
1126 }
1127
1128-void Module::GetStackFrameEntries(vector<StackFrameEntry *> *vec) const {
1129-  *vec = stack_frame_entries_;
1130+void Module::GetStackFrameEntries(vector<StackFrameEntry*>* vec) const {
1131+  vec->clear();
1132+  vec->insert(vec->begin(), stack_frame_entries_.begin(),
1133+              stack_frame_entries_.end());
1134+}
1135+
1136+Module::StackFrameEntry* Module::FindStackFrameEntryByAddress(Address address) {
1137+  StackFrameEntry search;
1138+  search.address = address;
1139+  StackFrameEntrySet::iterator it = stack_frame_entries_.upper_bound(&search);
1140+
1141+  if (it == stack_frame_entries_.begin())
1142+    return NULL;
1143+
1144+  it--;
1145+  if ((*it)->address <= address && address < (*it)->address + (*it)->size)
1146+    return *it;
1147+
1148+  return NULL;
1149 }
1150
1151 void Module::AssignSourceIds() {
1152@@ -286,7 +309,7 @@ bool Module::Write(std::ostream &stream, SymbolData symbol_data) {
1153
1154   if (symbol_data != NO_CFI) {
1155     // Write out 'STACK CFI INIT' and 'STACK CFI' records.
1156-    vector<StackFrameEntry *>::const_iterator frame_it;
1157+    StackFrameEntrySet::const_iterator frame_it;
1158     for (frame_it = stack_frame_entries_.begin();
1159          frame_it != stack_frame_entries_.end(); ++frame_it) {
1160       StackFrameEntry *entry = *frame_it;
1161diff --git a/src/common/module.h b/src/common/module.h
1162index 65b5595..299bc38 100644
1163--- a/src/common/module.h
1164+++ b/src/common/module.h
1165@@ -176,6 +176,13 @@ class Module {
1166     }
1167   };
1168
1169+  struct StackFrameEntryCompare {
1170+    bool operator() (const StackFrameEntry* lhs,
1171+                     const StackFrameEntry* rhs) const {
1172+      return lhs->address < rhs->address;
1173+    }
1174+  };
1175+
1176   // Create a new module with the given name, operating system,
1177   // architecture, and ID string.
1178   Module(const string &name, const string &os, const string &architecture,
1179@@ -256,6 +263,10 @@ class Module {
1180   // a more appropriate interface.)
1181   void GetStackFrameEntries(vector<StackFrameEntry *> *vec) const;
1182
1183+  // If this module has a StackFrameEntry whose address range covers
1184+  // ADDRESS, return it. Otherwise return NULL.
1185+  StackFrameEntry* FindStackFrameEntryByAddress(Address address);
1186+
1187   // Find those files in this module that are actually referred to by
1188   // functions' line number data, and assign them source id numbers.
1189   // Set the source id numbers for all other files --- unused by the
1190@@ -316,6 +327,9 @@ class Module {
1191   // A set containing Extern structures, sorted by address.
1192   typedef set<Extern *, ExternCompare> ExternSet;
1193
1194+  // A set containing StackFrameEntry structures, sorted by address.
1195+  typedef set<StackFrameEntry*, StackFrameEntryCompare> StackFrameEntrySet;
1196+
1197   // The module owns all the files and functions that have been added
1198   // to it; destroying the module frees the Files and Functions these
1199   // point to.
1200@@ -324,7 +338,7 @@ class Module {
1201
1202   // The module owns all the call frame info entries that have been
1203   // added to it.
1204-  vector<StackFrameEntry *> stack_frame_entries_;
1205+  StackFrameEntrySet stack_frame_entries_;
1206
1207   // The module owns all the externs that have been added to it;
1208   // destroying the module frees the Externs these point to.
1209diff --git a/src/common/module_unittest.cc b/src/common/module_unittest.cc
1210index 0b64327..bf72736 100644
1211--- a/src/common/module_unittest.cc
1212+++ b/src/common/module_unittest.cc
1213@@ -326,11 +326,6 @@ TEST(Construct, AddFrames) {
1214   m.Write(s, ALL_SYMBOL_DATA);
1215   string contents = s.str();
1216   EXPECT_STREQ("MODULE os-name architecture id-string name with spaces\n"
1217-               "STACK CFI INIT ddb5f41285aa7757 1486493370dc5073 \n"
1218-               "STACK CFI INIT 8064f3af5e067e38 de2a5ee55509407"
1219-               " .cfa: I think that I shall never see"
1220-               " cannoli: a tree whose hungry mouth is prest"
1221-               " stromboli: a poem lovely as a tree\n"
1222                "STACK CFI INIT 5e8d0db0a7075c6c 1c7edb12a7aea229"
1223                " .cfa: Whose woods are these\n"
1224                "STACK CFI 36682fad3763ffff"
1225@@ -338,7 +333,12 @@ TEST(Construct, AddFrames) {
1226                " stromboli: his house is in\n"
1227                "STACK CFI 47ceb0f63c269d7f"
1228                " calzone: the village though"
1229-               " cannoli: he will not see me stopping here\n",
1230+               " cannoli: he will not see me stopping here\n"
1231+               "STACK CFI INIT 8064f3af5e067e38 de2a5ee55509407"
1232+               " .cfa: I think that I shall never see"
1233+               " cannoli: a tree whose hungry mouth is prest"
1234+               " stromboli: a poem lovely as a tree\n"
1235+               "STACK CFI INIT ddb5f41285aa7757 1486493370dc5073 \n",
1236                contents.c_str());
1237
1238   // Check that GetStackFrameEntries works.
1239@@ -346,10 +346,18 @@ TEST(Construct, AddFrames) {
1240   m.GetStackFrameEntries(&entries);
1241   ASSERT_EQ(3U, entries.size());
1242   // Check first entry.
1243-  EXPECT_EQ(0xddb5f41285aa7757ULL, entries[0]->address);
1244-  EXPECT_EQ(0x1486493370dc5073ULL, entries[0]->size);
1245-  ASSERT_EQ(0U, entries[0]->initial_rules.size());
1246-  ASSERT_EQ(0U, entries[0]->rule_changes.size());
1247+  EXPECT_EQ(0x5e8d0db0a7075c6cULL, entries[0]->address);
1248+  EXPECT_EQ(0x1c7edb12a7aea229ULL, entries[0]->size);
1249+  Module::RuleMap entry1_initial;
1250+  entry1_initial[".cfa"] = "Whose woods are these";
1251+  EXPECT_THAT(entries[0]->initial_rules, ContainerEq(entry1_initial));
1252+  Module::RuleChangeMap entry1_changes;
1253+  entry1_changes[0x36682fad3763ffffULL][".cfa"] = "I think I know";
1254+  entry1_changes[0x36682fad3763ffffULL]["stromboli"] = "his house is in";
1255+  entry1_changes[0x47ceb0f63c269d7fULL]["calzone"] = "the village though";
1256+  entry1_changes[0x47ceb0f63c269d7fULL]["cannoli"] =
1257+    "he will not see me stopping here";
1258+  EXPECT_THAT(entries[0]->rule_changes, ContainerEq(entry1_changes));
1259   // Check second entry.
1260   EXPECT_EQ(0x8064f3af5e067e38ULL, entries[1]->address);
1261   EXPECT_EQ(0x0de2a5ee55509407ULL, entries[1]->size);
1262@@ -361,18 +369,10 @@ TEST(Construct, AddFrames) {
1263   EXPECT_THAT(entries[1]->initial_rules, ContainerEq(entry2_initial));
1264   ASSERT_EQ(0U, entries[1]->rule_changes.size());
1265   // Check third entry.
1266-  EXPECT_EQ(0x5e8d0db0a7075c6cULL, entries[2]->address);
1267-  EXPECT_EQ(0x1c7edb12a7aea229ULL, entries[2]->size);
1268-  Module::RuleMap entry3_initial;
1269-  entry3_initial[".cfa"] = "Whose woods are these";
1270-  EXPECT_THAT(entries[2]->initial_rules, ContainerEq(entry3_initial));
1271-  Module::RuleChangeMap entry3_changes;
1272-  entry3_changes[0x36682fad3763ffffULL][".cfa"] = "I think I know";
1273-  entry3_changes[0x36682fad3763ffffULL]["stromboli"] = "his house is in";
1274-  entry3_changes[0x47ceb0f63c269d7fULL]["calzone"] = "the village though";
1275-  entry3_changes[0x47ceb0f63c269d7fULL]["cannoli"] =
1276-    "he will not see me stopping here";
1277-  EXPECT_THAT(entries[2]->rule_changes, ContainerEq(entry3_changes));
1278+  EXPECT_EQ(0xddb5f41285aa7757ULL, entries[2]->address);
1279+  EXPECT_EQ(0x1486493370dc5073ULL, entries[2]->size);
1280+  ASSERT_EQ(0U, entries[2]->initial_rules.size());
1281+  ASSERT_EQ(0U, entries[2]->rule_changes.size());
1282 }
1283
1284 TEST(Construct, UniqueFiles) {
1285@@ -544,3 +544,62 @@ TEST(Construct, FunctionsAndThumbExternsWithSameAddress) {
1286                "PUBLIC cc00 0 arm_func\n",
1287                contents.c_str());
1288 }
1289+
1290+TEST(Lookup, StackFrameEntries) {
1291+  Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID);
1292+
1293+  // First STACK CFI entry, with no initial rules or deltas.
1294+  Module::StackFrameEntry *entry1 = new Module::StackFrameEntry();
1295+  entry1->address = 0x2000;
1296+  entry1->size = 0x900;
1297+  m.AddStackFrameEntry(entry1);
1298+
1299+  // Second STACK CFI entry, with initial rules but no deltas.
1300+  Module::StackFrameEntry *entry2 = new Module::StackFrameEntry();
1301+  entry2->address = 0x3000;
1302+  entry2->size = 0x900;
1303+  entry2->initial_rules[".cfa"] = "I think that I shall never see";
1304+  entry2->initial_rules["stromboli"] = "a poem lovely as a tree";
1305+  entry2->initial_rules["cannoli"] = "a tree whose hungry mouth is prest";
1306+  m.AddStackFrameEntry(entry2);
1307+
1308+  // Third STACK CFI entry, with initial rules and deltas.
1309+  Module::StackFrameEntry *entry3 = new Module::StackFrameEntry();
1310+  entry3->address = 0x1000;
1311+  entry3->size = 0x900;
1312+  entry3->initial_rules[".cfa"] = "Whose woods are these";
1313+  entry3->rule_changes[0x47ceb0f63c269d7fULL]["calzone"] =
1314+    "the village though";
1315+  entry3->rule_changes[0x47ceb0f63c269d7fULL]["cannoli"] =
1316+    "he will not see me stopping here";
1317+  entry3->rule_changes[0x36682fad3763ffffULL]["stromboli"] =
1318+    "his house is in";
1319+  entry3->rule_changes[0x36682fad3763ffffULL][".cfa"] =
1320+    "I think I know";
1321+  m.AddStackFrameEntry(entry3);
1322+
1323+  Module::StackFrameEntry* s = m.FindStackFrameEntryByAddress(0x1000);
1324+  EXPECT_EQ(entry3, s);
1325+  s = m.FindStackFrameEntryByAddress(0x18FF);
1326+  EXPECT_EQ(entry3, s);
1327+
1328+  s = m.FindStackFrameEntryByAddress(0x1900);
1329+  EXPECT_EQ((Module::StackFrameEntry*)NULL, s);
1330+  s = m.FindStackFrameEntryByAddress(0x1A00);
1331+  EXPECT_EQ((Module::StackFrameEntry*)NULL, s);
1332+
1333+  s = m.FindStackFrameEntryByAddress(0x2000);
1334+  EXPECT_EQ(entry1, s);
1335+  s = m.FindStackFrameEntryByAddress(0x28FF);
1336+  EXPECT_EQ(entry1, s);
1337+
1338+  s = m.FindStackFrameEntryByAddress(0x3000);
1339+  EXPECT_EQ(entry2, s);
1340+  s = m.FindStackFrameEntryByAddress(0x38FF);
1341+  EXPECT_EQ(entry2, s);
1342+
1343+  s = m.FindStackFrameEntryByAddress(0x3900);
1344+  EXPECT_EQ((Module::StackFrameEntry*)NULL, s);
1345+  s = m.FindStackFrameEntryByAddress(0x3A00);
1346+  EXPECT_EQ((Module::StackFrameEntry*)NULL, s);
1347+}
1348