mirror of
https://github.com/Zenithsiz/ftmemsim-valgrind.git
synced 2026-02-10 05:37:06 +00:00
1098 lines
39 KiB
C
1098 lines
39 KiB
C
/* -*- 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-2015 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 const 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 ((const 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), (const void*)(_addr), 4)) \
|
|
return ExInBufOverflow; \
|
|
(_lval) = *(const 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
|
|
const 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 = Prel31ToAddr(&entry->data);
|
|
GET_EXTAB_U32(data, extbl_data);
|
|
if (!(data & ARM_EXIDX_COMPACT)) {
|
|
// This denotes a "generic model" handler. That will involve
|
|
// executing arbitrary 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 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, 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", how, 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, (UWord)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%x, avma 0x%lx\n",
|
|
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 %lu"
|
|
"; using 1 instead.", 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 ---*/
|
|
/*--------------------------------------------------------------------*/
|