Add support for stack unwinding using the ARM32 specific EXIDX format.
git-svn-id: svn://svn.valgrind.org/valgrind/trunk@14217 a5019735-40e9-0310-863c-91ae7b9d1cf9
diff --git a/NEWS b/NEWS
index 1231937..53f33fc 100644
--- a/NEWS
+++ b/NEWS
@@ -90,6 +90,10 @@
* Reduction of memory used by Valgrind to read and store the debug information.
+* Valgrind can now read EXIDX unwind information on 32-bit ARM targets.
+ If an object contains both CFI and EXIDX unwind information, Valgrind
+ will prefer the CFI over the EXIDX.
+
* ==================== FIXED BUGS ====================
The following bugs have been fixed or resolved. Note that "n-i-bz"
diff --git a/coregrind/Makefile.am b/coregrind/Makefile.am
index 655a462..aabadcb 100644
--- a/coregrind/Makefile.am
+++ b/coregrind/Makefile.am
@@ -222,6 +222,7 @@
m_debuginfo/priv_readdwarf.h \
m_debuginfo/priv_readdwarf3.h \
m_debuginfo/priv_readelf.h \
+ m_debuginfo/priv_readexidx.h \
m_debuginfo/priv_readmacho.h \
m_debuginfo/priv_image.h \
m_debuginfo/lzoconf.h \
@@ -319,6 +320,7 @@
m_debuginfo/readdwarf.c \
m_debuginfo/readdwarf3.c \
m_debuginfo/readelf.c \
+ m_debuginfo/readexidx.c \
m_debuginfo/readmacho.c \
m_debuginfo/readpdb.c \
m_debuginfo/readstabs.c \
diff --git a/coregrind/m_debuginfo/debuginfo.c b/coregrind/m_debuginfo/debuginfo.c
index 02a2139..7db4608 100644
--- a/coregrind/m_debuginfo/debuginfo.c
+++ b/coregrind/m_debuginfo/debuginfo.c
@@ -2378,6 +2378,7 @@
case Creg_ARM_R14: return eec->uregs->r14;
case Creg_ARM_R13: return eec->uregs->r13;
case Creg_ARM_R12: return eec->uregs->r12;
+ case Creg_ARM_R7: return eec->uregs->r7;
# elif defined(VGA_s390x)
case Creg_IA_IP: return eec->uregs->ia;
case Creg_IA_SP: return eec->uregs->sp;
diff --git a/coregrind/m_debuginfo/priv_readexidx.h b/coregrind/m_debuginfo/priv_readexidx.h
new file mode 100644
index 0000000..8b21ffb
--- /dev/null
+++ b/coregrind/m_debuginfo/priv_readexidx.h
@@ -0,0 +1,50 @@
+/* -*- mode: C; c-basic-offset: 3; -*- */
+
+/*--------------------------------------------------------------------*/
+/*--- Reading of ARM(32) EXIDX unwind information ---*/
+/*-- priv_readexidx.h ---*/
+/*--------------------------------------------------------------------*/
+
+/*
+ This file is part of Valgrind, a dynamic binary instrumentation
+ framework.
+
+ Copyright (C) 2014-2014 Mozilla Foundation
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307, USA.
+
+ The GNU General Public License is contained in the file COPYING.
+*/
+
+/* Contributed by Julian Seward <jseward@acm.org> */
+
+#ifndef __PRIV_READEXIDX_H
+#define __PRIV_READEXIDX_H
+
+#include "pub_core_debuginfo.h" // DebugInfo
+
+extern
+void ML_(read_exidx) ( /*MOD*/DebugInfo* di,
+ UChar* exidx_img, SizeT exidx_size,
+ UChar* extab_img, SizeT extab_size,
+ Addr text_last_svma,
+ PtrdiffT text_bias );
+
+#endif /* ndef __PRIV_READEXIDX_H */
+
+/*--------------------------------------------------------------------*/
+/*--- end priv_readexidx.h ---*/
+/*--------------------------------------------------------------------*/
diff --git a/coregrind/m_debuginfo/priv_storage.h b/coregrind/m_debuginfo/priv_storage.h
index 817efae..ccf6742 100644
--- a/coregrind/m_debuginfo/priv_storage.h
+++ b/coregrind/m_debuginfo/priv_storage.h
@@ -274,6 +274,8 @@
Int r12_off;
Int r11_off;
Int r7_off;
+ // If you add additional fields, don't forget to update the
+ // initialisation of this in readexidx.c accordingly.
}
DiCfSI_m;
#elif defined(VGA_arm64)
@@ -369,13 +371,15 @@
typedef
enum {
- Creg_IA_SP=0x213,
+ Creg_INVALID=0x213,
+ Creg_IA_SP,
Creg_IA_BP,
Creg_IA_IP,
Creg_ARM_R13,
Creg_ARM_R12,
Creg_ARM_R15,
Creg_ARM_R14,
+ Creg_ARM_R7,
Creg_ARM64_X30,
Creg_S390_R14,
Creg_MIPS_RA
@@ -788,6 +792,18 @@
PtrdiffT sbss_bias;
Addr sbss_debug_svma;
PtrdiffT sbss_debug_bias;
+ /* .ARM.exidx -- sometimes present on arm32, containing unwind info. */
+ Bool exidx_present;
+ Addr exidx_avma;
+ Addr exidx_svma;
+ SizeT exidx_size;
+ PtrdiffT exidx_bias;
+ /* .ARM.extab -- sometimes present on arm32, containing unwind info. */
+ Bool extab_present;
+ Addr extab_avma;
+ Addr extab_svma;
+ SizeT extab_size;
+ PtrdiffT extab_bias;
/* .plt */
Bool plt_present;
Addr plt_avma;
diff --git a/coregrind/m_debuginfo/readelf.c b/coregrind/m_debuginfo/readelf.c
index 9c69750..839f56c 100644
--- a/coregrind/m_debuginfo/readelf.c
+++ b/coregrind/m_debuginfo/readelf.c
@@ -51,6 +51,7 @@
#include "priv_readdwarf.h" /* 'cos ELF contains DWARF */
#include "priv_readdwarf3.h"
#include "priv_readstabs.h" /* and stabs, if we're unlucky */
+#include "priv_readexidx.h"
/* --- !!! --- EXTERNAL HEADERS start --- !!! --- */
#include <elf.h>
@@ -2176,6 +2177,50 @@
}
}
+ /* Accept .ARM.exidx where mapped as rx (code). */
+ /* FIXME: make sure the entire section is mapped in, not just
+ the first address. */
+ if (0 == VG_(strcmp)(name, ".ARM.exidx")) {
+ if (inrx && !di->exidx_present) {
+ di->exidx_present = True;
+ di->exidx_svma = svma;
+ di->exidx_avma = svma + inrx->bias;
+ di->exidx_size = size;
+ di->exidx_bias = inrx->bias;
+ TRACE_SYMTAB("acquiring .exidx svma = %#lx .. %#lx\n",
+ di->exidx_svma,
+ di->exidx_svma + di->exidx_size - 1);
+ TRACE_SYMTAB("acquiring .exidx avma = %#lx .. %#lx\n",
+ di->exidx_avma,
+ di->exidx_avma + di->exidx_size - 1);
+ TRACE_SYMTAB("acquiring .exidx bias = %#lx\n", di->exidx_bias);
+ } else {
+ BAD(".ARM.exidx");
+ }
+ }
+
+ /* Accept .ARM.extab where mapped as rx (code). */
+ /* FIXME: make sure the entire section is mapped in, not just
+ the first address. */
+ if (0 == VG_(strcmp)(name, ".ARM.extab")) {
+ if (inrx && !di->extab_present) {
+ di->extab_present = True;
+ di->extab_svma = svma;
+ di->extab_avma = svma + inrx->bias;
+ di->extab_size = size;
+ di->extab_bias = inrx->bias;
+ TRACE_SYMTAB("acquiring .extab svma = %#lx .. %#lx\n",
+ di->extab_svma,
+ di->extab_svma + di->extab_size - 1);
+ TRACE_SYMTAB("acquiring .extab avma = %#lx .. %#lx\n",
+ di->extab_avma,
+ di->extab_avma + di->extab_size - 1);
+ TRACE_SYMTAB("acquiring .extab bias = %#lx\n", di->extab_bias);
+ } else {
+ BAD(".ARM.extab");
+ }
+ }
+
ML_(dinfo_free)(name);
# undef BAD
@@ -2729,6 +2774,7 @@
vg_assert((dynsym_escn.szB % sizeof(ElfXX_Sym)) == 0);
vg_assert((symtab_escn.szB % sizeof(ElfXX_Sym)) == 0);
+ /* TOPLEVEL */
/* Read symbols */
{
void (*read_elf_symtab)(struct _DebugInfo*, const HChar*,
@@ -2746,7 +2792,7 @@
read_elf_symtab(di, "dynamic symbol table",
&dynsym_escn, &dynstr_escn, &opd_escn,
False);
- } /* Read symbols */
+ }
/* TOPLEVEL */
/* Read .eh_frame and .debug_frame (call-frame-info) if any. Do
@@ -2781,13 +2827,15 @@
&& !defined(VGPV_arm_linux_android) \
&& !defined(VGPV_x86_linux_android) \
&& !defined(VGP_mips64_linux)
-#if 0
- if (stab_img && stabstr_img) {
- ML_(read_debuginfo_stabs) ( di, stab_img, stab_sz,
- stabstr_img, stabstr_sz );
- }
-#endif
+ // JRS 31 July 2014: stabs reading is currently broken and
+ // therefore deactivated.
+ //if (stab_img && stabstr_img) {
+ // ML_(read_debuginfo_stabs) ( di, stab_img, stab_sz,
+ // stabstr_img, stabstr_sz );
+ //}
# endif
+
+ /* TOPLEVEL */
/* jrs 2006-01-01: icc-8.1 has been observed to generate
binaries without debug_str sections. Don't preclude
debuginfo reading for that reason, but, in
@@ -2820,13 +2868,50 @@
);
}
}
-#if 0
- if (dwarf1d_img && dwarf1l_img) {
- ML_(read_debuginfo_dwarf1) ( di, dwarf1d_img, dwarf1d_sz,
- dwarf1l_img, dwarf1l_sz );
- }
-#endif
+
/* TOPLEVEL */
+ // JRS 31 July 2014: dwarf-1 reading is currently broken and
+ // therefore deactivated.
+ //if (dwarf1d_img && dwarf1l_img) {
+ // ML_(read_debuginfo_dwarf1) ( di, dwarf1d_img, dwarf1d_sz,
+ // dwarf1l_img, dwarf1l_sz );
+ //}
+
+# if defined(VGA_arm)
+ /* TOPLEVEL */
+ /* ARM32 only: read .exidx/.extab if present. Note we are
+ reading these directly out of the mapped in (running) image.
+ Also, read these only if no CFI based unwind info was
+ acquired for this file.
+
+ An .exidx section is always required, but the .extab section
+ can be optionally omitted, provided that .exidx does not
+ refer to it. If the .exidx is erroneous and does refer to
+ .extab even though .extab is missing, the range checks done
+ by GET_EX_U32 in ExtabEntryExtract in readexidx.c should
+ prevent any invalid memory accesses, and cause the .extab to
+ be rejected as invalid.
+
+ FIXME:
+ * check with m_aspacemgr that the entire [exidx_avma, +exidx_size)
+ and [extab_avma, +extab_size) areas are readable, since we're
+ reading this stuff out of the running image (not from a file/socket)
+ and we don't want to segfault.
+ * DebugInfo::exidx_bias and use text_bias instead.
+ I think it's always the same.
+ * remove DebugInfo::{extab_bias, exidx_svma, extab_svma} since
+ they are never used.
+ */
+ if (di->exidx_present
+ && di->cfsi_used == 0
+ && di->text_present && di->text_size > 0) {
+ Addr text_last_svma = di->text_svma + di->text_size - 1;
+ ML_(read_exidx)( di, (UChar*)di->exidx_avma, di->exidx_size,
+ (UChar*)di->extab_avma, di->extab_size,
+ text_last_svma,
+ di->exidx_bias );
+ }
+# endif /* defined(VGA_arm) */
} /* "Find interesting sections, read the symbol table(s), read any debug
information" (a local scope) */
diff --git a/coregrind/m_debuginfo/readexidx.c b/coregrind/m_debuginfo/readexidx.c
new file mode 100644
index 0000000..ab34367
--- /dev/null
+++ b/coregrind/m_debuginfo/readexidx.c
@@ -0,0 +1,1097 @@
+/* -*- mode: C; c-basic-offset: 3; -*- */
+
+/*--------------------------------------------------------------------*/
+/*--- Reading of ARM(32) EXIDX unwind information readexidx.c ---*/
+/*--------------------------------------------------------------------*/
+
+/*
+ This file is part of Valgrind, a dynamic binary instrumentation
+ framework.
+
+ Copyright (C) 2014-2014 Mozilla Foundation
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307, USA.
+
+ The GNU General Public License is contained in the file COPYING.
+*/
+
+/* libunwind - a platform-independent unwind library
+ Copyright 2011 Linaro Limited
+
+This file is part of libunwind.
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+
+
+// Copyright (c) 2010 Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+// Derived originally from libunwind, with very extensive modifications.
+/* Contributed by Julian Seward <jseward@acm.org> */
+
+
+// This file translates EXIDX unwind information into the same format
+// that Valgrind uses for CFI information. Hence Valgrind's CFI
+// unwinding abilities also become usable for EXIDX.
+//
+// See: "Exception Handling ABI for the ARM Architecture", ARM IHI 0038A
+// http://infocenter.arm.com/help/topic/com.arm.doc.ihi0038a/IHI0038A_ehabi.pdf
+
+// EXIDX data is presented in two parts:
+//
+// * an index table. This contains two words per routine,
+// the first of which identifies the routine, and the second
+// of which is a reference to the unwind bytecode. If the
+// bytecode is very compact -- 3 bytes or less -- it can be
+// stored directly in the second word.
+//
+// * an area containing the unwind bytecodes.
+//
+// General flow is: ML_(read_exidx) iterates over all
+// of the index table entries (pairs). For each entry, it:
+//
+// * calls ExtabEntryExtract to copy the bytecode out into
+// an intermediate buffer.
+
+// * uses ExtabEntryDecode to parse the intermediate
+// buffer. Each bytecode instruction is bundled into a
+// arm_ex_to_module::extab_data structure, and handed to ..
+//
+// * .. TranslateCmd, which generates the pseudo-CFI
+// records that Valgrind stores.
+
+// This file is derived from the following files in the Mozilla tree
+// toolkit/crashreporter/google-breakpad:
+// src/common/arm_ex_to_module.cc
+// src/common/arm_ex_reader.cc
+
+
+#if defined(VGA_arm)
+
+#include "pub_core_basics.h"
+#include "pub_core_libcbase.h"
+#include "pub_core_libcprint.h"
+#include "pub_core_libcassert.h"
+#include "pub_core_options.h"
+
+#include "priv_storage.h"
+#include "priv_readexidx.h"
+
+
+static void complain ( const HChar* str )
+{
+ if (!VG_(clo_xml) && VG_(clo_verbosity) > 1)
+ VG_(message)(Vg_UserMsg,
+ " Warning: whilst reading EXIDX: %s\n", str);
+}
+
+
+/*------------------------------------------------------------*/
+/*--- MemoryRange ---*/
+/*------------------------------------------------------------*/
+
+typedef struct { Addr start; SizeT len; } MemoryRange;
+
+/* Initialise |mr| for [start .. start+len). Zero ranges are allowed,
+ but wraparounds are not. Returns True on success. */
+static Bool MemoryRange__init ( /*OUT*/MemoryRange* mr,
+ const void* startV, SizeT len )
+{
+ VG_(memset)(mr, 0, sizeof(*mr));
+ /* This relies on Addr being unsigned. */
+ Addr start = (Addr)startV;
+ if (len > 0 && start + len - 1 < start) {
+ return False;
+ }
+ mr->start = start;
+ mr->len = len;
+ return True;
+}
+
+static Bool MemoryRange__covers ( MemoryRange* mr,
+ const void* startV, SizeT len )
+{
+ vg_assert(len > 0);
+ if (mr->len == 0) {
+ return False;
+ }
+ Addr start = (Addr)startV;
+ return start >= mr->start && start + len - 1 <= mr->start + mr->len - 1;
+}
+
+
+/*------------------------------------------------------------*/
+/*--- (Pass 1 of 3) The EXIDX extractor ---*/
+/*------------------------------------------------------------*/
+
+#define ARM_EXIDX_CANT_UNWIND 0x00000001
+#define ARM_EXIDX_COMPACT 0x80000000
+#define ARM_EXTBL_OP_FINISH 0xb0
+#define ARM_EXIDX_TABLE_LIMIT (255*4)
+
+/* These are in the ARM-defined format, so their layout is important. */
+typedef
+ struct { UInt addr; UInt data; }
+ ExidxEntry;
+
+
+typedef
+ enum {
+ ExSuccess=1, // success
+ ExInBufOverflow, // out-of-range while reading .exidx
+ ExOutBufOverflow, // output buffer is too small
+ ExCantUnwind, // this function is marked CANT_UNWIND
+ ExCantRepresent, // entry valid, but we can't represent it
+ ExInvalid // entry is invalid
+ }
+ ExExtractResult;
+
+
+/* Helper function for fishing bits out of the EXIDX representation. */
+static void* Prel31ToAddr(const void* addr)
+{
+ UInt offset32 = *(const UInt*)addr;
+ // sign extend offset32[30:0] to 64 bits -- copy bit 30 to positions
+ // 63:31 inclusive.
+ ULong offset64 = offset32;
+ if (offset64 & (1ULL << 30))
+ offset64 |= 0xFFFFFFFF80000000ULL;
+ else
+ offset64 &= 0x000000007FFFFFFFULL;
+ return ((UChar*)addr) + (UWord)offset64;
+}
+
+
+// Extract unwind bytecode for the function denoted by |entry| into |buf|,
+// and return the number of bytes of |buf| written, along with a code
+// indicating the outcome.
+static
+ExExtractResult ExtabEntryExtract ( MemoryRange* mr_exidx,
+ MemoryRange* mr_extab,
+ const ExidxEntry* entry,
+ UChar* buf, SizeT buf_size,
+ /*OUT*/SizeT* buf_used)
+{
+ Bool ok;
+ MemoryRange mr_out;
+ ok = MemoryRange__init(&mr_out, buf, buf_size);
+ if (!ok) return ExOutBufOverflow;
+
+ *buf_used = 0;
+
+# define PUT_BUF_U8(_byte) \
+ do { if (!MemoryRange__covers(&mr_out, &buf[*buf_used], 1)) \
+ return ExOutBufOverflow; \
+ buf[(*buf_used)++] = (_byte); } while (0)
+
+# define GET_EX_U32(_lval, _addr, _mr) \
+ do { if (!MemoryRange__covers((_mr), (void*)(_addr), 4)) \
+ return ExInBufOverflow; \
+ (_lval) = *(UInt*)(_addr); } while (0)
+
+# define GET_EXIDX_U32(_lval, _addr) \
+ GET_EX_U32(_lval, _addr, mr_exidx)
+
+# define GET_EXTAB_U32(_lval, _addr) \
+ GET_EX_U32(_lval, _addr, mr_extab)
+
+ UInt data;
+ GET_EXIDX_U32(data, &entry->data);
+
+ // A function can be marked CANT_UNWIND if (eg) it is known to be
+ // at the bottom of the stack.
+ if (data == ARM_EXIDX_CANT_UNWIND)
+ return ExCantUnwind;
+
+ UInt pers; // personality number
+ UInt extra; // number of extra data words required
+ UInt extra_allowed; // number of extra data words allowed
+ UInt* extbl_data; // the handler entry, if not inlined
+
+ if (data & ARM_EXIDX_COMPACT) {
+ // The handler table entry has been inlined into the index table entry.
+ // In this case it can only be an ARM-defined compact model, since
+ // bit 31 is 1. Only personalities 0, 1 and 2 are defined for the
+ // ARM compact model, but 1 and 2 are "Long format" and may require
+ // extra data words. Hence the allowable personalities here are:
+ // personality 0, in which case 'extra' has no meaning
+ // personality 1, with zero extra words
+ // personality 2, with zero extra words
+ extbl_data = NULL;
+ pers = (data >> 24) & 0x0F;
+ extra = (data >> 16) & 0xFF;
+ extra_allowed = 0;
+ }
+ else {
+ // The index table entry is a pointer to the handler entry. Note
+ // that Prel31ToAddr will read the given address, but we already
+ // range-checked above.
+ extbl_data = (UInt*)(Prel31ToAddr(&entry->data));
+ GET_EXTAB_U32(data, extbl_data);
+ if (!(data & ARM_EXIDX_COMPACT)) {
+ // This denotes a "generic model" handler. That will involve
+ // executing arbitary machine code, which is something we
+ // can't represent here; hence reject it.
+ return ExCantRepresent;
+ }
+ // So we have a compact model representation. Again, 3 possible
+ // personalities, but this time up to 255 allowable extra words.
+ pers = (data >> 24) & 0x0F;
+ extra = (data >> 16) & 0xFF;
+ extra_allowed = 255;
+ extbl_data++;
+ }
+
+ // Now look at the the handler table entry. The first word is
+ // |data| and subsequent words start at |*extbl_data|. The number
+ // of extra words to use is |extra|, provided that the personality
+ // allows extra words. Even if it does, none may be available --
+ // extra_allowed is the maximum number of extra words allowed. */
+ if (pers == 0) {
+ // "Su16" in the documentation -- 3 unwinding insn bytes
+ // |extra| has no meaning here; instead that byte is an unwind-info byte
+ PUT_BUF_U8(data >> 16);
+ PUT_BUF_U8(data >> 8);
+ PUT_BUF_U8(data);
+ }
+ else if ((pers == 1 || pers == 2) && extra <= extra_allowed) {
+ // "Lu16" or "Lu32" respectively -- 2 unwinding insn bytes,
+ // and up to 255 extra words.
+ PUT_BUF_U8(data >> 8);
+ PUT_BUF_U8(data);
+ UInt j;
+ for (j = 0; j < extra; j++) {
+ GET_EXTAB_U32(data, extbl_data);
+ extbl_data++;
+ PUT_BUF_U8(data >> 24);
+ PUT_BUF_U8(data >> 16);
+ PUT_BUF_U8(data >> 8);
+ PUT_BUF_U8(data >> 0);
+ }
+ }
+ else {
+ // The entry is invalid.
+ return ExInvalid;
+ }
+
+ // Make sure the entry is terminated with "FINISH"
+ if (*buf_used > 0 && buf[(*buf_used) - 1] != ARM_EXTBL_OP_FINISH)
+ PUT_BUF_U8(ARM_EXTBL_OP_FINISH);
+
+ return ExSuccess;
+
+# undef GET_EXTAB_U32
+# undef GET_EXIDX_U32
+# undef GET_U32
+# undef PUT_BUF_U8
+}
+
+
+/*------------------------------------------------------------*/
+/*--- (Pass 2 of 3) The EXIDX decoder ---*/
+/*------------------------------------------------------------*/
+
+/* This (ExtabData) is an intermediate structure, used to carry
+ information from the decoder (pass 2) to the summariser (pass 3).
+ I don't think its layout is important. */
+typedef
+ enum {
+ ARM_EXIDX_CMD_FINISH=0x100,
+ ARM_EXIDX_CMD_SUB_FROM_VSP,
+ ARM_EXIDX_CMD_ADD_TO_VSP,
+ ARM_EXIDX_CMD_REG_POP,
+ ARM_EXIDX_CMD_REG_TO_SP,
+ ARM_EXIDX_CMD_VFP_POP,
+ ARM_EXIDX_CMD_WREG_POP,
+ ARM_EXIDX_CMD_WCGR_POP,
+ ARM_EXIDX_CMD_RESERVED,
+ ARM_EXIDX_CMD_REFUSED
+ }
+ ExtabCmd;
+
+static const HChar* showExtabCmd ( ExtabCmd cmd ) {
+ switch (cmd) {
+ case ARM_EXIDX_CMD_FINISH: return "FINISH";
+ case ARM_EXIDX_CMD_SUB_FROM_VSP: return "SUB_FROM_VSP";
+ case ARM_EXIDX_CMD_ADD_TO_VSP: return "ADD_TO_VSP";
+ case ARM_EXIDX_CMD_REG_POP: return "REG_POP";
+ case ARM_EXIDX_CMD_REG_TO_SP: return "REG_TO_SP";
+ case ARM_EXIDX_CMD_VFP_POP: return "VFP_POP";
+ case ARM_EXIDX_CMD_WREG_POP: return "WREG_POP";
+ case ARM_EXIDX_CMD_WCGR_POP: return "WCGR_POP";
+ case ARM_EXIDX_CMD_RESERVED: return "RESERVED";
+ case ARM_EXIDX_CMD_REFUSED: return "REFUSED";
+ default: return "???";
+ }
+}
+
+
+typedef
+ struct { ExtabCmd cmd; UInt data; }
+ ExtabData;
+
+static void ppExtabData ( const ExtabData* etd ) {
+ VG_(printf)("ExtabData{%12s 0x%08x}", showExtabCmd(etd->cmd), etd->data);
+}
+
+
+enum extab_cmd_flags {
+ ARM_EXIDX_VFP_SHIFT_16 = 1 << 16,
+ ARM_EXIDX_VFP_FSTMD = 1 << 17, // distinguishes FSTMxxD from FSTMxxX
+};
+
+
+/* Forwards */
+typedef struct _SummState SummState;
+static Int TranslateCmd(/*MOD*/SummState* state, const ExtabData* edata);
+
+
+// Take the unwind information extracted by ExtabEntryExtract
+// and parse it into frame-unwind instructions. These are as
+// specified in "Table 4, ARM-defined frame-unwinding instructions"
+// in the specification document detailed in comments at the top
+// of this file.
+//
+// This reads from |buf[0, +data_size)|. It checks for overruns of
+// the input buffer and returns a negative value if that happens, or
+// for any other failure cases. It returns zero in case of success.
+// Whilst reading the input, it dumps the result in |*state|.
+static
+Int ExtabEntryDecode(/*OUT*/SummState* state, const UChar* buf, SizeT buf_size)
+{
+ if (buf == NULL || buf_size == 0)
+ return -3;
+
+ MemoryRange mr_in;
+ Bool ok = MemoryRange__init(&mr_in, buf, buf_size);
+ if (!ok)
+ return -2;
+
+# define GET_BUF_U8(_lval) \
+ do { if (!MemoryRange__covers(&mr_in, buf, 1)) \
+ return -4; \
+ (_lval) = *(buf++); } while (0)
+
+ const UChar* end = buf + buf_size;
+
+ while (buf < end) {
+ ExtabData edata;
+ VG_(bzero_inline)(&edata, sizeof(edata));
+
+ UChar op;
+ GET_BUF_U8(op);
+ if ((op & 0xc0) == 0x00) {
+ // vsp = vsp + (xxxxxx << 2) + 4
+ edata.cmd = ARM_EXIDX_CMD_ADD_TO_VSP;
+ edata.data = (((Int)op & 0x3f) << 2) + 4;
+ }
+ else if ((op & 0xc0) == 0x40) {
+ // vsp = vsp - (xxxxxx << 2) - 4
+ edata.cmd = ARM_EXIDX_CMD_SUB_FROM_VSP;
+ edata.data = (((Int)op & 0x3f) << 2) + 4;
+ }
+ else if ((op & 0xf0) == 0x80) {
+ UChar op2;
+ GET_BUF_U8(op2);
+ if (op == 0x80 && op2 == 0x00) {
+ // Refuse to unwind
+ edata.cmd = ARM_EXIDX_CMD_REFUSED;
+ } else {
+ // Pop up to 12 integer registers under masks {r15-r12},{r11-r4}
+ edata.cmd = ARM_EXIDX_CMD_REG_POP;
+ edata.data = ((op & 0xf) << 8) | op2;
+ edata.data = edata.data << 4;
+ }
+ }
+ else if ((op & 0xf0) == 0x90) {
+ if (op == 0x9d || op == 0x9f) {
+ // 9d: Reserved as prefix for ARM register to register moves
+ // 9f: Reserved as prefix for Intel Wireless MMX reg to reg moves
+ edata.cmd = ARM_EXIDX_CMD_RESERVED;
+ } else {
+ // Set vsp = r[nnnn]
+ edata.cmd = ARM_EXIDX_CMD_REG_TO_SP;
+ edata.data = op & 0x0f;
+ }
+ }
+ else if ((op & 0xf0) == 0xa0) {
+ // Pop r4 to r[4+nnn], or
+ // Pop r4 to r[4+nnn] and r14
+ Int nnn = (op & 0x07);
+ edata.data = (1 << (nnn + 1)) - 1;
+ edata.data = edata.data << 4;
+ if (op & 0x08) edata.data |= 1 << 14;
+ edata.cmd = ARM_EXIDX_CMD_REG_POP;
+ }
+ else if (op == ARM_EXTBL_OP_FINISH) {
+ // Finish
+ edata.cmd = ARM_EXIDX_CMD_FINISH;
+ buf = end;
+ }
+ else if (op == 0xb1) {
+ UChar op2;
+ GET_BUF_U8(op2);
+ if (op2 == 0 || (op2 & 0xf0)) {
+ // Spare
+ edata.cmd = ARM_EXIDX_CMD_RESERVED;
+ } else {
+ // Pop integer registers under mask {r3,r2,r1,r0}
+ edata.cmd = ARM_EXIDX_CMD_REG_POP;
+ edata.data = op2 & 0x0f;
+ }
+ }
+ else if (op == 0xb2) {
+ // vsp = vsp + 0x204 + (uleb128 << 2)
+ ULong offset = 0;
+ UChar byte, shift = 0;
+ do {
+ GET_BUF_U8(byte);
+ offset |= (byte & 0x7f) << shift;
+ shift += 7;
+ } while ((byte & 0x80) && buf < end);
+ edata.data = offset * 4 + 0x204;
+ edata.cmd = ARM_EXIDX_CMD_ADD_TO_VSP;
+ }
+ else if (op == 0xb3 || op == 0xc8 || op == 0xc9) {
+ // b3: Pop VFP regs D[ssss] to D[ssss+cccc], FSTMFDX-ishly
+ // c8: Pop VFP regs D[16+ssss] to D[16+ssss+cccc], FSTMFDD-ishly
+ // c9: Pop VFP regs D[ssss] to D[ssss+cccc], FSTMFDD-ishly
+ edata.cmd = ARM_EXIDX_CMD_VFP_POP;
+ GET_BUF_U8(edata.data);
+ if (op == 0xc8) edata.data |= ARM_EXIDX_VFP_SHIFT_16;
+ if (op != 0xb3) edata.data |= ARM_EXIDX_VFP_FSTMD;
+ }
+ else if ((op & 0xf8) == 0xb8 || (op & 0xf8) == 0xd0) {
+ // b8: Pop VFP regs D[8] to D[8+nnn], FSTMFDX-ishly
+ // d0: Pop VFP regs D[8] to D[8+nnn], FSTMFDD-ishly
+ edata.cmd = ARM_EXIDX_CMD_VFP_POP;
+ edata.data = 0x80 | (op & 0x07);
+ if ((op & 0xf8) == 0xd0) edata.data |= ARM_EXIDX_VFP_FSTMD;
+ }
+ else if (op >= 0xc0 && op <= 0xc5) {
+ // Intel Wireless MMX pop wR[10]-wr[10+nnn], nnn != 6,7
+ edata.cmd = ARM_EXIDX_CMD_WREG_POP;
+ edata.data = 0xa0 | (op & 0x07);
+ }
+ else if (op == 0xc6) {
+ // Intel Wireless MMX pop wR[ssss] to wR[ssss+cccc]
+ edata.cmd = ARM_EXIDX_CMD_WREG_POP;
+ GET_BUF_U8(edata.data);
+ }
+ else if (op == 0xc7) {
+ UChar op2;
+ GET_BUF_U8(op2);
+ if (op2 == 0 || (op2 & 0xf0)) {
+ // Spare
+ edata.cmd = ARM_EXIDX_CMD_RESERVED;
+ } else {
+ // Intel Wireless MMX pop wCGR registers under mask {wCGR3,2,1,0}
+ edata.cmd = ARM_EXIDX_CMD_WCGR_POP;
+ edata.data = op2 & 0x0f;
+ }
+ }
+ else {
+ // Spare
+ edata.cmd = ARM_EXIDX_CMD_RESERVED;
+ }
+
+ if (0)
+ VG_(printf)(" edata: cmd %08x data %08x\n",
+ (UInt)edata.cmd, (UInt)edata.data);
+
+ Int ret = TranslateCmd ( state, &edata );
+ if (ret < 0) return ret;
+ }
+ return 0;
+
+# undef GET_BUF_U8
+}
+
+
+/*------------------------------------------------------------*/
+/*--- (Pass 3 of 3) The EXIDX summariser ---*/
+/*------------------------------------------------------------*/
+
+/* In this translation into DiCfSI_m, we're going to have the CFA play
+ the role of the VSP. That means that the VSP can be exactly any of
+ the CFA expressions, viz: {r7,r11,r12,r13) +/- offset.
+
+ All of this would be a lot simpler if the DiCfSI_m representation
+ was just a bit more expressive and orthogonal. But it isn't.
+
+ The central difficulty is that, although we can track changes
+ to the offset of VSP (via vsp_off), we can't deal with assignments
+ of an entirely new expression to it, because the existing
+ rules in |cfi| will almost certainly refer to the CFA, and so
+ changing it will make them invalid. Hence, below:
+
+ * for the case ARM_EXIDX_CMD_REG_TO_SP we simply disallow
+ assignment, and hence give up, if any rule refers to CFA
+
+ * for the case ARM_EXIDX_CMD_REG_POP, the SP (hence, VSP) is
+ updated by the pop, give up.
+
+ This is an ugly hack to work around not having a better (LUL-like)
+ expression representation. That said, these restrictions don't
+ appear to be a big problem in practice.
+*/
+
+struct _SummState {
+ // The DiCfSI_m under construction
+ DiCfSI_m cfi;
+ Int vsp_off;
+ // For generating CFI register expressions, if needed.
+ DebugInfo* di;
+};
+
+
+/* Generate a trivial CfiExpr, for the ARM(32) integer register
+ numbered |gprNo|. First ensure this DebugInfo has a cfsi_expr
+ array in which to park it. Returns -1 if |gprNo| cannot be
+ represented, otherwise returns a value >= 0. */
+static
+Int gen_CfiExpr_CfiReg_ARM_GPR ( /*MB_MOD*/DebugInfo* di, UInt gprNo )
+{
+ CfiReg creg = Creg_INVALID;
+ switch (gprNo) {
+ case 13: creg = Creg_ARM_R13; break;
+ case 12: creg = Creg_ARM_R12; break;
+ case 15: creg = Creg_ARM_R15; break;
+ case 14: creg = Creg_ARM_R14; break;
+ case 7: creg = Creg_ARM_R7; break;
+ default: break;
+ }
+ if (creg == Creg_INVALID) {
+ return -1;
+ }
+ if (!di->cfsi_exprs) {
+ di->cfsi_exprs = VG_(newXA)( ML_(dinfo_zalloc), "di.gCCAG",
+ ML_(dinfo_free), sizeof(CfiExpr) );
+ }
+ Int res = ML_(CfiExpr_CfiReg)( di->cfsi_exprs, creg );
+ vg_assert(res >= 0);
+ return res;
+}
+
+
+/* Given a DiCfSI_m, find the _how/_off pair for the given ARM(32) GPR
+ number inside |cfsi_m|, or return NULL for both if that register
+ number is not represented. */
+static
+void maybeFindExprForRegno( /*OUT*/UChar** howPP, /*OUT*/Int** offPP,
+ DiCfSI_m* cfsi_m, Int regNo )
+{
+ switch (regNo) {
+ case 15: *howPP = &cfsi_m->ra_how; *offPP = &cfsi_m->ra_off; return;
+ case 14: *howPP = &cfsi_m->r14_how; *offPP = &cfsi_m->r14_off; return;
+ case 13: *howPP = &cfsi_m->r13_how; *offPP = &cfsi_m->r13_off; return;
+ case 12: *howPP = &cfsi_m->r12_how; *offPP = &cfsi_m->r12_off; return;
+ case 11: *howPP = &cfsi_m->r11_how; *offPP = &cfsi_m->r11_off; return;
+ case 7: *howPP = &cfsi_m->r7_how; *offPP = &cfsi_m->r7_off; return;
+ default: break;
+ }
+ *howPP = NULL; *offPP = NULL;
+}
+
+
+/* Set cfi.cfa_{how,off} so as to be a copy of the expression denoted
+ by (how,off), if it is possible to do so. Returns True on
+ success. */
+static
+Bool setCFAfromCFIR( /*MOD*/DiCfSI_m* cfi, XArray*/*CfiExpr*/ cfsi_exprs,
+ UChar how, Int off )
+{
+ switch (how) {
+ case CFIR_EXPR:
+ if (!cfsi_exprs) return False;
+ CfiExpr* e = (CfiExpr*)VG_(indexXA)(cfsi_exprs, off);
+ if (e->tag != Cex_CfiReg) return False;
+ if (e->Cex.CfiReg.reg == Creg_ARM_R7) {
+ cfi->cfa_how = CFIC_ARM_R7REL;
+ cfi->cfa_off = 0;
+ return True;
+ }
+ ML_(ppCfiExpr)(cfsi_exprs, off);
+ vg_assert(0);
+ default:
+ break;
+ }
+ VG_(printf)("setCFAfromCFIR: FAIL: how %d off %d\n", (Int)how, (Int)off);
+ vg_assert(0);
+ return False;
+}
+
+
+#define ARM_EXBUF_START(x) (((x) >> 4) & 0x0f)
+#define ARM_EXBUF_COUNT(x) ((x) & 0x0f)
+#define ARM_EXBUF_END(x) (ARM_EXBUF_START(x) + ARM_EXBUF_COUNT(x))
+
+
+static Bool mentionsCFA ( DiCfSI_m* cfi )
+{
+# define MENTIONS_CFA(_how) ((_how) == CFIR_CFAREL || (_how) == CFIR_MEMCFAREL)
+ if (MENTIONS_CFA(cfi->ra_how)) return True;
+ if (MENTIONS_CFA(cfi->r14_how)) return True;
+ if (MENTIONS_CFA(cfi->r13_how)) return True;
+ if (MENTIONS_CFA(cfi->r12_how)) return True;
+ if (MENTIONS_CFA(cfi->r11_how)) return True;
+ if (MENTIONS_CFA(cfi->r7_how)) return True;
+ return False;
+# undef MENTIONS_CFA
+}
+
+
+// Translate command from extab_data to command for Module.
+static
+Int TranslateCmd(/*MOD*/SummState* state, const ExtabData* edata)
+{
+ /* Stay sane: check that the CFA has the expected form. */
+ vg_assert(state);
+ switch (state->cfi.cfa_how) {
+ case CFIC_ARM_R13REL: case CFIC_ARM_R12REL:
+ case CFIC_ARM_R11REL: case CFIC_ARM_R7REL: break;
+ default: vg_assert(0);
+ }
+
+ if (0) {
+ VG_(printf)(" TranslateCmd: ");
+ ppExtabData(edata);
+ VG_(printf)("\n");
+ }
+
+ Int ret = 0;
+ switch (edata->cmd) {
+ case ARM_EXIDX_CMD_FINISH:
+ /* Copy LR to PC if there isn't currently a rule for PC in force. */
+ if (state->cfi.ra_how == CFIR_UNKNOWN) {
+ if (state->cfi.r14_how == CFIR_UNKNOWN) {
+ state->cfi.ra_how = CFIR_EXPR;
+ state->cfi.ra_off = gen_CfiExpr_CfiReg_ARM_GPR(state->di, 14);
+ vg_assert(state->cfi.ra_off >= 0);
+ } else {
+ state->cfi.ra_how = state->cfi.r14_how;
+ state->cfi.ra_off = state->cfi.r14_off;
+ }
+ }
+ break;
+ case ARM_EXIDX_CMD_SUB_FROM_VSP:
+ state->vsp_off -= (Int)(edata->data);
+ break;
+ case ARM_EXIDX_CMD_ADD_TO_VSP:
+ state->vsp_off += (Int)(edata->data);
+ break;
+ case ARM_EXIDX_CMD_REG_POP: {
+ UInt i;
+ for (i = 0; i < 16; i++) {
+ if (edata->data & (1 << i)) {
+ // See if we're summarising for int register |i|. If so,
+ // describe how to pull it off the stack. The cast of |i| is
+ // a bit of a kludge but works because DW_REG_ARM_Rn has the
+ // value |n|, for 0 <= |n| <= 15 -- that is, for the ARM
+ // general-purpose registers.
+ UChar* rX_howP = NULL;
+ Int* rX_offP = NULL;
+ maybeFindExprForRegno(&rX_howP, &rX_offP, &state->cfi, i);
+ if (rX_howP) {
+ vg_assert(rX_offP);
+ /* rX_howP and rX_offP point at one of the rX fields
+ in |state->cfi|. Hence the following assignments
+ are really updating |state->cfi|. */
+ *rX_howP = CFIR_MEMCFAREL;
+ *rX_offP = state->vsp_off;
+ } else {
+ /* We're not tracking this register, so ignore it. */
+ vg_assert(!rX_offP);
+ }
+ state->vsp_off += 4;
+ }
+ }
+ /* Set cfa in case the SP got popped. */
+ if (edata->data & (1 << 13)) {
+ // vsp = curr_rules_.mR13expr;
+ //state->cfi.cfa_how =
+ //state->cfi.cfa_off =
+ //state->vsp_off = 0;
+ // If this happens, it would make the existing CFA references
+ // in the summary invalid. So give up instead.
+ goto cant_summarise;
+ }
+ break;
+ }
+ case ARM_EXIDX_CMD_REG_TO_SP: {
+ /* We're generating a new value for the CFA/VSP here. Hence,
+ if the summary already refers to the CFA at all, we can't
+ go any further, and have to abandon summarisation. */
+ if (mentionsCFA(&state->cfi))
+ goto cant_summarise;
+ vg_assert(edata->data < 16);
+ Int reg_no = edata->data;
+ // Same comment as above, re the casting of |reg_no|, applies.
+ UChar* rX_howP = NULL;
+ Int* rX_offP = NULL;
+ maybeFindExprForRegno(&rX_howP, &rX_offP, &state->cfi, reg_no);
+ if (rX_howP) {
+ vg_assert(rX_offP);
+ if (*rX_howP == CFIR_UNKNOWN) {
+ //curr_rules_.mR13expr = LExpr(LExpr::NODEREF, reg_no, 0);
+ Int expr_ix = gen_CfiExpr_CfiReg_ARM_GPR(state->di, reg_no);
+ if (expr_ix >= 0) {
+ state->cfi.r13_how = CFIR_EXPR;
+ state->cfi.r13_off = expr_ix;
+ } else {
+ goto cant_summarise;
+ }
+ } else {
+ //curr_rules_.mR13expr = *reg_exprP;
+ state->cfi.r13_how = *rX_howP;
+ state->cfi.r13_off = *rX_offP;
+ }
+ //vsp = curr_rules_.mR13expr;
+ Bool ok = setCFAfromCFIR( &state->cfi, state->di->cfsi_exprs,
+ state->cfi.r13_how, state->cfi.r13_off );
+ if (!ok) goto cant_summarise;
+ state->vsp_off = 0;
+ } else {
+ vg_assert(!rX_offP);
+ }
+ break;
+ }
+ case ARM_EXIDX_CMD_VFP_POP: {
+ /* Don't recover VFP registers, but be sure to adjust the stack
+ pointer. */
+ UInt i;
+ for (i = ARM_EXBUF_START(edata->data);
+ i <= ARM_EXBUF_END(edata->data); i++) {
+ state->vsp_off += 8;
+ }
+ if (!(edata->data & ARM_EXIDX_VFP_FSTMD)) {
+ state->vsp_off += 4;
+ }
+ break;
+ }
+ case ARM_EXIDX_CMD_WREG_POP: {
+ UInt i;
+ for (i = ARM_EXBUF_START(edata->data);
+ i <= ARM_EXBUF_END(edata->data); i++) {
+ state->vsp_off += 8;
+ }
+ break;
+ }
+ case ARM_EXIDX_CMD_WCGR_POP: {
+ UInt i;
+ // Pop wCGR registers under mask {wCGR3,2,1,0}, hence "i < 4"
+ for (i = 0; i < 4; i++) {
+ if (edata->data & (1 << i)) {
+ state->vsp_off += 4;
+ }
+ }
+ break;
+ }
+ case ARM_EXIDX_CMD_REFUSED:
+ case ARM_EXIDX_CMD_RESERVED:
+ ret = -1;
+ break;
+ }
+ return ret;
+
+ cant_summarise:
+ return -10;
+}
+
+
+/* Initialise the EXIDX summariser, by writing initial values in |state|. */
+static
+void AddStackFrame ( /*OUT*/SummState* state,
+ DebugInfo* di )
+{
+ VG_(bzero_inline)(state, sizeof(*state));
+ state->vsp_off = 0;
+ state->di = di;
+ /* Initialise the DiCfSI_m that we are building. */
+ state->cfi.cfa_how = CFIC_ARM_R13REL;
+ state->cfi.cfa_off = 0;
+ state->cfi.ra_how = CFIR_UNKNOWN;
+ state->cfi.r14_how = CFIR_UNKNOWN;
+ state->cfi.r13_how = CFIR_UNKNOWN;
+ state->cfi.r12_how = CFIR_UNKNOWN;
+ state->cfi.r11_how = CFIR_UNKNOWN;
+ state->cfi.r7_how = CFIR_UNKNOWN;
+}
+
+static
+void SubmitStackFrame( /*MOD*/DebugInfo* di,
+ SummState* state, Addr avma, SizeT len )
+{
+ // JRS: I'm really not sure what this means, or if it is necessary
+ // return address always winds up in pc
+ //stack_frame_entry_->initial_rules[ustr__ZDra()] // ".ra"
+ // = stack_frame_entry_->initial_rules[ustr__pc()];
+ // maybe don't need to do anything here?
+
+ // the final value of vsp is the new value of sp.
+ switch (state->cfi.cfa_how) {
+ case CFIC_ARM_R13REL: case CFIC_ARM_R12REL:
+ case CFIC_ARM_R11REL: case CFIC_ARM_R7REL: break;
+ default: vg_assert(0);
+ }
+ state->cfi.r13_how = CFIR_CFAREL;
+ state->cfi.r13_off = state->vsp_off;
+
+ // Finally, add the completed RuleSet to the SecMap
+ if (len > 0) {
+
+ // Futz with the rules for r4 .. r11 in the same way as happens
+ // with the CFI summariser:
+ /* Mark callee-saved registers (r4 .. r11) as unchanged, if there is
+ no other information about them. FIXME: do this just once, at
+ the point where the ruleset is committed. */
+ if (state->cfi.r7_how == CFIR_UNKNOWN) {
+ state->cfi.r7_how = CFIR_SAME;
+ state->cfi.r7_off = 0;
+ }
+ if (state->cfi.r11_how == CFIR_UNKNOWN) {
+ state->cfi.r11_how = CFIR_SAME;
+ state->cfi.r11_off = 0;
+ }
+ if (state->cfi.r12_how == CFIR_UNKNOWN) {
+ state->cfi.r12_how = CFIR_SAME;
+ state->cfi.r12_off = 0;
+ }
+ if (state->cfi.r14_how == CFIR_UNKNOWN) {
+ state->cfi.r14_how = CFIR_SAME;
+ state->cfi.r14_off = 0;
+ }
+
+ // And add them
+ ML_(addDiCfSI)(di, avma, len, &state->cfi);
+ if (di->trace_cfi)
+ ML_(ppDiCfSI)(di->cfsi_exprs, avma, len, &state->cfi);
+ }
+}
+
+
+/*------------------------------------------------------------*/
+/*--- Top level ---*/
+/*------------------------------------------------------------*/
+
+void ML_(read_exidx) ( /*MOD*/DebugInfo* di,
+ UChar* exidx_img, SizeT exidx_size,
+ UChar* extab_img, SizeT extab_size,
+ Addr text_last_svma,
+ PtrdiffT text_bias )
+{
+ if (di->trace_cfi)
+ VG_(printf)("BEGIN ML_(read_exidx) exidx_img=[%p, +%lu) "
+ "extab_img=[%p, +%lu) text_last_svma=%lx text_bias=%lx\n",
+ exidx_img, exidx_size, extab_img, extab_size,
+ text_last_svma, text_bias);
+ Bool ok;
+ MemoryRange mr_exidx, mr_extab;
+ ok = MemoryRange__init(&mr_exidx, exidx_img, exidx_size);
+ ok = ok && MemoryRange__init(&mr_extab, extab_img, extab_size);
+ if (!ok) {
+ complain(".exidx or .extab image area wraparound");
+ return;
+ }
+
+ const ExidxEntry* start_img = (const ExidxEntry*)exidx_img;
+ const ExidxEntry* end_img = (const ExidxEntry*)(exidx_img + exidx_size);
+
+ if (VG_(clo_verbosity) > 1)
+ VG_(message)(Vg_DebugMsg, " Reading EXIDX entries: %lu available\n",
+ exidx_size / sizeof(ExidxEntry) );
+
+ // Iterate over each of the EXIDX entries (pairs of 32-bit words).
+ // These occupy the entire .exidx section.
+ UWord n_attempted = 0, n_successful = 0;
+
+ const ExidxEntry* entry_img;
+ for (entry_img = start_img; entry_img < end_img; ++entry_img) {
+
+ n_attempted++;
+ // Figure out the code address range that this table entry_img is
+ // associated with.
+ Addr avma = (Addr)Prel31ToAddr(&entry_img->addr);
+ if (di->trace_cfi)
+ VG_(printf)("XXX1 entry: entry->addr 0x%lx, avma 0x%lx\n",
+ (UWord)entry_img->addr, avma);
+
+ Addr next_avma;
+ if (entry_img < end_img - 1) {
+ next_avma = (Addr)Prel31ToAddr(&(entry_img+1)->addr);
+ } else {
+ // This is the last EXIDX entry in the sequence, so we don't
+ // have an address for the start of the next function, to limit
+ // this one. Instead use the address of the last byte of the
+ // text section associated with this .exidx section, that we
+ // have been given. So as to avoid junking up the CFI unwind
+ // tables with absurdly large address ranges in the case where
+ // text_last_svma_ is wrong, only use the value if it is nonzero
+ // and within one page of |svma|. Otherwise assume a length of 1.
+ //
+ // In some cases, gcc has been observed to finish the exidx
+ // section with an entry of length 1 marked CANT_UNWIND,
+ // presumably exactly for the purpose of giving a definite
+ // length for the last real entry, without having to look at
+ // text segment boundaries.
+ Addr text_last_avma = text_last_svma + text_bias;
+
+ Bool plausible;
+ Addr maybe_next_avma = text_last_avma + 1;
+ if (maybe_next_avma > avma && maybe_next_avma - avma <= 4096) {
+ next_avma = maybe_next_avma;
+ plausible = True;
+ } else {
+ next_avma = avma + 1;
+ plausible = False;
+ }
+
+ if (!plausible && avma != text_last_avma + 1) {
+ HChar buf[100];
+ VG_(snprintf)(buf, sizeof(buf),
+ "Implausible EXIDX last entry size %u"
+ "; using 1 instead.", (UInt)(text_last_avma - avma));
+ buf[sizeof(buf)-1] = 0;
+ complain(buf);
+ }
+ }
+
+ // Extract the unwind info into |buf|. This might fail for
+ // various reasons. It involves reading both the .exidx and
+ // .extab sections. All accesses to those sections are
+ // bounds-checked.
+ if (di->trace_cfi)
+ VG_(printf)("XXX1 entry is for AVMA 0x%lx 0x%lx\n",
+ avma, next_avma-1);
+ UChar buf[ARM_EXIDX_TABLE_LIMIT];
+ SizeT buf_used = 0;
+ ExExtractResult res
+ = ExtabEntryExtract(&mr_exidx, &mr_extab,
+ entry_img, buf, sizeof(buf), &buf_used);
+ if (res != ExSuccess) {
+ // Couldn't extract the unwind info, for some reason. Move on.
+ switch (res) {
+ case ExInBufOverflow:
+ complain("ExtabEntryExtract: .exidx/.extab section overrun");
+ break;
+ case ExOutBufOverflow:
+ complain("ExtabEntryExtract: bytecode buffer overflow");
+ break;
+ case ExCantUnwind:
+ // Some functions are marked CantUnwind by the compiler.
+ // Don't record these as attempted, since that's just
+ // confusing, and failure to summarise them is not the fault
+ // of this code.
+ n_attempted--;
+ if (0)
+ complain("ExtabEntryExtract: function is marked CANT_UNWIND");
+ break;
+ case ExCantRepresent:
+ complain("ExtabEntryExtract: bytecode can't be represented");
+ break;
+ case ExInvalid:
+ complain("ExtabEntryExtract: index table entry is invalid");
+ break;
+ default: {
+ HChar mbuf[100];
+ VG_(snprintf)(mbuf, sizeof(mbuf),
+ "ExtabEntryExtract: unknown error: %d", (Int)res);
+ buf[sizeof(mbuf)-1] = 0;
+ complain(mbuf);
+ break;
+ }
+ }
+ continue;
+ }
+
+ // Finally, work through the unwind instructions in |buf| and
+ // create CFI entries that Valgrind can use. This can also fail.
+ // First, initialise the summariser's running state, into which
+ // ExtabEntryDecode will write the CFI entries.
+
+ SummState state;
+ AddStackFrame( &state, di );
+ Int ret = ExtabEntryDecode( &state, buf, buf_used );
+ if (ret < 0) {
+ /* Failed summarisation. Ignore and move on. */
+ HChar mbuf[100];
+ VG_(snprintf)(mbuf, sizeof(mbuf),
+ "ExtabEntryDecode: failed with error code: %d", ret);
+ mbuf[sizeof(mbuf)-1] = 0;
+ complain(mbuf);
+ } else {
+ /* Successful summarisation. Add it to the collection. */
+ SubmitStackFrame( di, &state, avma, next_avma - avma );
+ n_successful++;
+ }
+
+ } /* iterating over .exidx */
+
+ if (VG_(clo_verbosity) > 1)
+ VG_(message)(Vg_DebugMsg,
+ " Reading EXIDX entries: %lu attempted, %lu successful\n",
+ n_attempted, n_successful);
+}
+
+#endif /* defined(VGA_arm) */
+
+/*--------------------------------------------------------------------*/
+/*--- end readexidx.c ---*/
+/*--------------------------------------------------------------------*/
diff --git a/coregrind/m_debuginfo/storage.c b/coregrind/m_debuginfo/storage.c
index 50522c7..ce40ea6 100644
--- a/coregrind/m_debuginfo/storage.c
+++ b/coregrind/m_debuginfo/storage.c
@@ -696,7 +696,7 @@
if (VG_(clo_verbosity) > 1) {
VG_(message)(
Vg_DebugMsg,
- "warning: DiCfSI %#lx .. %#lx outside mapped rw segments (%s)\n",
+ "warning: DiCfSI %#lx .. %#lx outside mapped rx segments (%s)\n",
base,
base + len - 1,
di->soname
@@ -875,6 +875,7 @@
static void ppCfiReg ( CfiReg reg )
{
switch (reg) {
+ case Creg_INVALID: VG_(printf)("Creg_INVALID"); break;
case Creg_IA_SP: VG_(printf)("xSP"); break;
case Creg_IA_BP: VG_(printf)("xBP"); break;
case Creg_IA_IP: VG_(printf)("xIP"); break;
@@ -882,6 +883,7 @@
case Creg_ARM_R12: VG_(printf)("R12"); break;
case Creg_ARM_R15: VG_(printf)("R15"); break;
case Creg_ARM_R14: VG_(printf)("R14"); break;
+ case Creg_ARM_R7: VG_(printf)("R7"); break;
case Creg_ARM64_X30: VG_(printf)("X30"); break;
case Creg_MIPS_RA: VG_(printf)("RA"); break;
case Creg_S390_R14: VG_(printf)("R14"); break;