blob: 2867a7245e89bf4f134e0c0d53624052e38ddd27 [file] [log] [blame]
/*
* Copyright (C) 2013, 2014 Apple Inc. All rights reserved.
* Copyright (C) 2014 Samsung Electronics
* Copyright (C) 2014 University of Szeged
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. 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.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE INC. 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.
*
* ==============================================================================
*
* University of Illinois/NCSA
* Open Source License
*
* Copyright (c) 2009-2014 by the contributors of LLVM/libc++abi project.
*
* All rights reserved.
*
* Developed by:
*
* LLVM Team
*
* University of Illinois at Urbana-Champaign
*
* http://llvm.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal with
* 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:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimers.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimers in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the names of the LLVM Team, University of Illinois at
* Urbana-Champaign, nor the names of its contributors may be used to
* endorse or promote products derived from this Software without specific
* prior written permission.
*
* 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
* CONTRIBUTORS 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 WITH THE
* SOFTWARE.
*
* ==============================================================================
*
* Copyright (c) 2009-2014 by the contributors of LLVM/libc++abi project.
*
* 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.
*/
#include "config.h"
#include "FTLUnwindInfo.h"
#if ENABLE(FTL_JIT)
#if OS(DARWIN)
#include <mach-o/compact_unwind_encoding.h>
#endif
#include <wtf/ListDump.h>
namespace JSC { namespace FTL {
UnwindInfo::UnwindInfo() { }
UnwindInfo::~UnwindInfo() { }
namespace {
#if OS(DARWIN)
struct CompactUnwind {
void* function;
uint32_t size;
compact_unwind_encoding_t encoding;
void* personality;
void* lsda;
};
#elif OS(LINUX)
// DWARF unwind instructions
enum {
DW_CFA_nop = 0x0,
DW_CFA_set_loc = 0x1,
DW_CFA_advance_loc1 = 0x2,
DW_CFA_advance_loc2 = 0x3,
DW_CFA_advance_loc4 = 0x4,
DW_CFA_offset_extended = 0x5,
DW_CFA_def_cfa = 0xC,
DW_CFA_def_cfa_register = 0xD,
DW_CFA_def_cfa_offset = 0xE,
DW_CFA_offset_extended_sf = 0x11,
DW_CFA_def_cfa_sf = 0x12,
DW_CFA_def_cfa_offset_sf = 0x13,
// high 2 bits are 0x1, lower 6 bits are delta
DW_CFA_advance_loc = 0x40,
// high 2 bits are 0x2, lower 6 bits are register
DW_CFA_offset = 0x80
};
enum {
DW_CFA_operand_mask = 0x3F // low 6 bits mask for opcode-encoded operands in DW_CFA_advance_loc and DW_CFA_offset
};
// FSF exception handling Pointer-Encoding constants
enum {
DW_EH_PE_ptr = 0x00,
DW_EH_PE_uleb128 = 0x01,
DW_EH_PE_udata2 = 0x02,
DW_EH_PE_udata4 = 0x03,
DW_EH_PE_udata8 = 0x04,
DW_EH_PE_sleb128 = 0x09,
DW_EH_PE_sdata2 = 0x0A,
DW_EH_PE_sdata4 = 0x0B,
DW_EH_PE_sdata8 = 0x0C,
DW_EH_PE_absptr = 0x00,
DW_EH_PE_pcrel = 0x10,
DW_EH_PE_indirect = 0x80
};
enum {
DW_EH_PE_relative_mask = 0x70
};
// 64-bit x86_64 registers
enum {
UNW_X86_64_rbx = 3,
UNW_X86_64_rbp = 6,
UNW_X86_64_r12 = 12,
UNW_X86_64_r13 = 13,
UNW_X86_64_r14 = 14,
UNW_X86_64_r15 = 15
};
enum {
DW_X86_64_RET_addr = 16
};
enum {
UNW_ARM64_x0 = 0,
UNW_ARM64_x1 = 1,
UNW_ARM64_x2 = 2,
UNW_ARM64_x3 = 3,
UNW_ARM64_x4 = 4,
UNW_ARM64_x5 = 5,
UNW_ARM64_x6 = 6,
UNW_ARM64_x7 = 7,
UNW_ARM64_x8 = 8,
UNW_ARM64_x9 = 9,
UNW_ARM64_x10 = 10,
UNW_ARM64_x11 = 11,
UNW_ARM64_x12 = 12,
UNW_ARM64_x13 = 13,
UNW_ARM64_x14 = 14,
UNW_ARM64_x15 = 15,
UNW_ARM64_x16 = 16,
UNW_ARM64_x17 = 17,
UNW_ARM64_x18 = 18,
UNW_ARM64_x19 = 19,
UNW_ARM64_x20 = 20,
UNW_ARM64_x21 = 21,
UNW_ARM64_x22 = 22,
UNW_ARM64_x23 = 23,
UNW_ARM64_x24 = 24,
UNW_ARM64_x25 = 25,
UNW_ARM64_x26 = 26,
UNW_ARM64_x27 = 27,
UNW_ARM64_x28 = 28,
UNW_ARM64_fp = 29,
UNW_ARM64_x30 = 30,
UNW_ARM64_sp = 31,
UNW_ARM64_v0 = 64,
UNW_ARM64_v1 = 65,
UNW_ARM64_v2 = 66,
UNW_ARM64_v3 = 67,
UNW_ARM64_v4 = 68,
UNW_ARM64_v5 = 69,
UNW_ARM64_v6 = 70,
UNW_ARM64_v7 = 71,
UNW_ARM64_v8 = 72,
UNW_ARM64_v9 = 73,
UNW_ARM64_v10 = 74,
UNW_ARM64_v11 = 75,
UNW_ARM64_v12 = 76,
UNW_ARM64_v13 = 77,
UNW_ARM64_v14 = 78,
UNW_ARM64_v15 = 79,
UNW_ARM64_v16 = 80,
UNW_ARM64_v17 = 81,
UNW_ARM64_v18 = 82,
UNW_ARM64_v19 = 83,
UNW_ARM64_v20 = 84,
UNW_ARM64_v21 = 85,
UNW_ARM64_v22 = 86,
UNW_ARM64_v23 = 87,
UNW_ARM64_v24 = 88,
UNW_ARM64_v25 = 89,
UNW_ARM64_v26 = 90,
UNW_ARM64_v27 = 91,
UNW_ARM64_v28 = 92,
UNW_ARM64_v29 = 93,
UNW_ARM64_v30 = 94,
UNW_ARM64_v31 = 95
};
static uint8_t get8(uintptr_t addr) { return *((uint8_t*)addr); }
static uint16_t get16(uintptr_t addr) { return *((uint16_t*)addr); }
static uint32_t get32(uintptr_t addr) { return *((uint32_t*)addr); }
static uint64_t get64(uintptr_t addr) { return *((uint64_t*)addr); }
static uintptr_t getP(uintptr_t addr)
{
// FIXME: add support for 32 bit pointers on 32 bit architectures
return get64(addr);
}
static uint64_t getULEB128(uintptr_t& addr, uintptr_t end)
{
const uint8_t* p = (uint8_t*)addr;
const uint8_t* pend = (uint8_t*)end;
uint64_t result = 0;
int bit = 0;
do {
uint64_t b;
RELEASE_ASSERT(p != pend); // truncated uleb128 expression
b = *p & 0x7f;
RELEASE_ASSERT(!(bit >= 64 || b << bit >> bit != b)); // malformed uleb128 expression
result |= b << bit;
bit += 7;
} while (*p++ >= 0x80);
addr = (uintptr_t)p;
return result;
}
static int64_t getSLEB128(uintptr_t& addr, uintptr_t end)
{
const uint8_t* p = (uint8_t*)addr;
const uint8_t* pend = (uint8_t*)end;
int64_t result = 0;
int bit = 0;
uint8_t byte;
do {
RELEASE_ASSERT(p != pend); // truncated sleb128 expression
byte = *p++;
result |= ((byte & 0x7f) << bit);
bit += 7;
} while (byte & 0x80);
// sign extend negative numbers
if ((byte & 0x40))
result |= (-1LL) << bit;
addr = (uintptr_t)p;
return result;
}
static uintptr_t getEncodedP(uintptr_t& addr, uintptr_t end, uint8_t encoding)
{
uintptr_t startAddr = addr;
const uint8_t* p = (uint8_t*)addr;
uintptr_t result;
// first get value
switch (encoding & 0x0F) {
case DW_EH_PE_ptr:
result = getP(addr);
p += sizeof(uintptr_t);
addr = (uintptr_t)p;
break;
case DW_EH_PE_uleb128:
result = getULEB128(addr, end);
break;
case DW_EH_PE_udata2:
result = get16(addr);
p += 2;
addr = (uintptr_t)p;
break;
case DW_EH_PE_udata4:
result = get32(addr);
p += 4;
addr = (uintptr_t)p;
break;
case DW_EH_PE_udata8:
result = get64(addr);
p += 8;
addr = (uintptr_t)p;
break;
case DW_EH_PE_sleb128:
result = getSLEB128(addr, end);
break;
case DW_EH_PE_sdata2:
result = (int16_t)get16(addr);
p += 2;
addr = (uintptr_t)p;
break;
case DW_EH_PE_sdata4:
result = (int32_t)get32(addr);
p += 4;
addr = (uintptr_t)p;
break;
case DW_EH_PE_sdata8:
result = get64(addr);
p += 8;
addr = (uintptr_t)p;
break;
default:
RELEASE_ASSERT_NOT_REACHED(); // unknown pointer encoding
}
// then add relative offset
switch (encoding & DW_EH_PE_relative_mask) {
case DW_EH_PE_absptr:
// do nothing
break;
case DW_EH_PE_pcrel:
result += startAddr;
break;
default:
RELEASE_ASSERT_NOT_REACHED(); // unsupported or unknown pointer encoding
}
if (encoding & DW_EH_PE_indirect)
result = getP(result);
return result;
}
// Information encoded in a CIE (Common Information Entry)
struct CIE_Info {
uintptr_t cieStart;
uintptr_t cieLength;
uintptr_t cieInstructions;
uint8_t pointerEncoding;
uint8_t lsdaEncoding;
uint8_t personalityEncoding;
uint8_t personalityOffsetInCIE;
uintptr_t personality;
int dataAlignFactor;
bool fdesHaveAugmentationData;
};
// Information about an FDE (Frame Description Entry)
struct FDE_Info {
uintptr_t fdeStart;
uintptr_t fdeLength;
uintptr_t fdeInstructions;
uintptr_t lsda;
};
// Information about a frame layout and registers saved determined
// by "running" the dwarf FDE "instructions"
#if CPU(ARM64)
enum { MaxRegisterNumber = 120 };
#elif CPU(X86_64)
enum { MaxRegisterNumber = 17 };
#else
#error "Unrecognized architecture"
#endif
struct RegisterLocation {
bool saved;
int64_t offset;
};
struct PrologInfo {
uint32_t cfaRegister;
int32_t cfaRegisterOffset; // CFA = (cfaRegister)+cfaRegisterOffset
RegisterLocation savedRegisters[MaxRegisterNumber]; // from where to restore registers
};
static void parseCIE(uintptr_t cie, CIE_Info* cieInfo)
{
cieInfo->pointerEncoding = 0;
cieInfo->lsdaEncoding = 0;
cieInfo->personalityEncoding = 0;
cieInfo->personalityOffsetInCIE = 0;
cieInfo->personality = 0;
cieInfo->dataAlignFactor = 0;
cieInfo->fdesHaveAugmentationData = false;
cieInfo->cieStart = cie;
uintptr_t p = cie;
uint64_t cieLength = get32(p);
p += 4;
uintptr_t cieContentEnd = p + cieLength;
if (cieLength == 0xffffffff) {
// 0xffffffff means length is really next 8 bytes
cieLength = get64(p);
p += 8;
cieContentEnd = p + cieLength;
}
RELEASE_ASSERT(cieLength);
// CIE ID is always 0
RELEASE_ASSERT(!get32(p)); // CIE ID is not zero
p += 4;
// Version is always 1 or 3
uint8_t version = get8(p);
RELEASE_ASSERT((version == 1) || (version == 3)); // CIE version is not 1 or 3
++p;
// save start of augmentation string and find end
uintptr_t strStart = p;
while (get8(p))
++p;
++p;
// parse code aligment factor
getULEB128(p, cieContentEnd);
// parse data alignment factor
cieInfo->dataAlignFactor = getSLEB128(p, cieContentEnd);
// parse return address register
getULEB128(p, cieContentEnd);
// parse augmentation data based on augmentation string
if (get8(strStart) == 'z') {
// parse augmentation data length
getULEB128(p, cieContentEnd);
for (uintptr_t s = strStart; get8(s) != '\0'; ++s) {
switch (get8(s)) {
case 'z':
cieInfo->fdesHaveAugmentationData = true;
break;
case 'P': // FIXME: should assert on personality (just to keep in sync with the CU behaviour)
cieInfo->personalityEncoding = get8(p);
++p;
cieInfo->personalityOffsetInCIE = p - cie;
cieInfo->personality = getEncodedP(p, cieContentEnd, cieInfo->personalityEncoding);
break;
case 'L': // FIXME: should assert on LSDA (just to keep in sync with the CU behaviour)
cieInfo->lsdaEncoding = get8(p);
++p;
break;
case 'R':
cieInfo->pointerEncoding = get8(p);
++p;
break;
default:
// ignore unknown letters
break;
}
}
}
cieInfo->cieLength = cieContentEnd - cieInfo->cieStart;
cieInfo->cieInstructions = p;
}
static void findFDE(uintptr_t pc, uintptr_t ehSectionStart, uint32_t sectionLength, FDE_Info* fdeInfo, CIE_Info* cieInfo)
{
uintptr_t p = ehSectionStart;
const uintptr_t ehSectionEnd = p + sectionLength;
while (p < ehSectionEnd) {
uintptr_t currentCFI = p;
uint64_t cfiLength = get32(p);
p += 4;
if (cfiLength == 0xffffffff) {
// 0xffffffff means length is really next 8 bytes
cfiLength = get64(p);
p += 8;
}
RELEASE_ASSERT(cfiLength); // end marker reached before finding FDE for pc
uint32_t id = get32(p);
if (!id) {
// skip over CIEs
p += cfiLength;
} else {
// process FDE to see if it covers pc
uintptr_t nextCFI = p + cfiLength;
uint32_t ciePointer = get32(p);
uintptr_t cieStart = p - ciePointer;
// validate pointer to CIE is within section
RELEASE_ASSERT((ehSectionStart <= cieStart) && (cieStart < ehSectionEnd)); // malformed FDE. CIE is bad
parseCIE(cieStart, cieInfo);
p += 4;
// parse pc begin and range
uintptr_t pcStart = getEncodedP(p, nextCFI, cieInfo->pointerEncoding);
uintptr_t pcRange = getEncodedP(p, nextCFI, cieInfo->pointerEncoding & 0x0F);
// test if pc is within the function this FDE covers
// if pc is not in begin/range, skip this FDE
if ((pcStart <= pc) && (pc < pcStart+pcRange)) {
// parse rest of info
fdeInfo->lsda = 0;
// check for augmentation length
if (cieInfo->fdesHaveAugmentationData) {
uintptr_t augLen = getULEB128(p, nextCFI);
uintptr_t endOfAug = p + augLen;
if (cieInfo->lsdaEncoding) {
// peek at value (without indirection). Zero means no lsda
uintptr_t lsdaStart = p;
if (getEncodedP(p, nextCFI, cieInfo->lsdaEncoding & 0x0F)) {
// reset pointer and re-parse lsda address
p = lsdaStart;
fdeInfo->lsda = getEncodedP(p, nextCFI, cieInfo->lsdaEncoding);
}
}
p = endOfAug;
}
fdeInfo->fdeStart = currentCFI;
fdeInfo->fdeLength = nextCFI - currentCFI;
fdeInfo->fdeInstructions = p;
return; // FDE found
}
p = nextCFI;
}
}
RELEASE_ASSERT_NOT_REACHED(); // no FDE found for pc
}
static void executeDefCFARegister(uint64_t reg, PrologInfo* results)
{
RELEASE_ASSERT(reg <= MaxRegisterNumber); // reg too big
results->cfaRegister = reg;
}
static void executeDefCFAOffset(int64_t offset, PrologInfo* results)
{
RELEASE_ASSERT(offset <= 0x80000000); // cfa has negative offset (dwarf might contain epilog)
results->cfaRegisterOffset = offset;
}
static void executeOffset(uint64_t reg, int64_t offset, PrologInfo *results)
{
if (reg > MaxRegisterNumber)
return;
RELEASE_ASSERT(!results->savedRegisters[reg].saved);
results->savedRegisters[reg].saved = true;
results->savedRegisters[reg].offset = offset;
}
static void parseInstructions(uintptr_t instructions, uintptr_t instructionsEnd, const CIE_Info& cieInfo, PrologInfo* results)
{
uintptr_t p = instructions;
// see Dwarf Spec, section 6.4.2 for details on unwind opcodes
while ((p < instructionsEnd)) {
uint64_t reg;
uint8_t opcode = get8(p);
uint8_t operand;
++p;
switch (opcode) {
case DW_CFA_nop:
break;
case DW_CFA_set_loc:
getEncodedP(p, instructionsEnd, cieInfo.pointerEncoding);
break;
case DW_CFA_advance_loc1:
p += 1;
break;
case DW_CFA_advance_loc2:
p += 2;
break;
case DW_CFA_advance_loc4:
p += 4;
break;
case DW_CFA_def_cfa:
executeDefCFARegister(getULEB128(p, instructionsEnd), results);
executeDefCFAOffset(getULEB128(p, instructionsEnd), results);
break;
case DW_CFA_def_cfa_sf:
executeDefCFARegister(getULEB128(p, instructionsEnd), results);
executeDefCFAOffset(getSLEB128(p, instructionsEnd) * cieInfo.dataAlignFactor, results);
break;
case DW_CFA_def_cfa_register:
executeDefCFARegister(getULEB128(p, instructionsEnd), results);
break;
case DW_CFA_def_cfa_offset:
executeDefCFAOffset(getULEB128(p, instructionsEnd), results);
break;
case DW_CFA_def_cfa_offset_sf:
executeDefCFAOffset(getSLEB128(p, instructionsEnd) * cieInfo.dataAlignFactor, results);
break;
case DW_CFA_offset_extended:
reg = getULEB128(p, instructionsEnd);
executeOffset(reg, getULEB128(p, instructionsEnd) * cieInfo.dataAlignFactor, results);
break;
case DW_CFA_offset_extended_sf:
reg = getULEB128(p, instructionsEnd);
executeOffset(reg, getSLEB128(p, instructionsEnd) * cieInfo.dataAlignFactor, results);
break;
default:
operand = opcode & DW_CFA_operand_mask;
switch (opcode & ~DW_CFA_operand_mask) {
case DW_CFA_offset:
executeOffset(operand, getULEB128(p, instructionsEnd) * cieInfo.dataAlignFactor, results);
break;
case DW_CFA_advance_loc:
break;
default:
RELEASE_ASSERT_NOT_REACHED(); // unknown or unsupported CFA opcode
}
}
}
}
static void parseFDEInstructions(const FDE_Info& fdeInfo, const CIE_Info& cieInfo, PrologInfo* results)
{
// clear results
bzero(results, sizeof(PrologInfo));
// parse CIE then FDE instructions
parseInstructions(cieInfo.cieInstructions, cieInfo.cieStart + cieInfo.cieLength, cieInfo, results);
parseInstructions(fdeInfo.fdeInstructions, fdeInfo.fdeStart + fdeInfo.fdeLength, cieInfo, results);
}
#endif
} // anonymous namespace
bool UnwindInfo::parse(void* section, size_t size, GeneratedFunction generatedFunction)
{
m_registers.clear();
RELEASE_ASSERT(!!section);
if (!section)
return false;
#if OS(DARWIN)
RELEASE_ASSERT(size >= sizeof(CompactUnwind));
CompactUnwind* data = bitwise_cast<CompactUnwind*>(section);
RELEASE_ASSERT(!data->personality); // We don't know how to handle this.
RELEASE_ASSERT(!data->lsda); // We don't know how to handle this.
RELEASE_ASSERT(data->function == generatedFunction); // The unwind data better be for our function.
compact_unwind_encoding_t encoding = data->encoding;
RELEASE_ASSERT(!(encoding & UNWIND_IS_NOT_FUNCTION_START));
RELEASE_ASSERT(!(encoding & UNWIND_HAS_LSDA));
RELEASE_ASSERT(!(encoding & UNWIND_PERSONALITY_MASK));
#if CPU(X86_64)
RELEASE_ASSERT((encoding & UNWIND_X86_64_MODE_MASK) == UNWIND_X86_64_MODE_RBP_FRAME);
int32_t offset = -((encoding & UNWIND_X86_64_RBP_FRAME_OFFSET) >> 16) * 8;
uint32_t nextRegisters = encoding;
for (unsigned i = 5; i--;) {
uint32_t currentRegister = nextRegisters & 7;
nextRegisters >>= 3;
switch (currentRegister) {
case UNWIND_X86_64_REG_NONE:
break;
case UNWIND_X86_64_REG_RBX:
m_registers.append(RegisterAtOffset(X86Registers::ebx, offset));
break;
case UNWIND_X86_64_REG_R12:
m_registers.append(RegisterAtOffset(X86Registers::r12, offset));
break;
case UNWIND_X86_64_REG_R13:
m_registers.append(RegisterAtOffset(X86Registers::r13, offset));
break;
case UNWIND_X86_64_REG_R14:
m_registers.append(RegisterAtOffset(X86Registers::r14, offset));
break;
case UNWIND_X86_64_REG_R15:
m_registers.append(RegisterAtOffset(X86Registers::r15, offset));
break;
case UNWIND_X86_64_REG_RBP:
m_registers.append(RegisterAtOffset(X86Registers::ebp, offset));
break;
default:
RELEASE_ASSERT_NOT_REACHED();
}
offset += 8;
}
#elif CPU(ARM64)
RELEASE_ASSERT((encoding & UNWIND_ARM64_MODE_MASK) == UNWIND_ARM64_MODE_FRAME);
m_registers.append(RegisterAtOffset(ARM64Registers::fp, 0));
int32_t offset = 0;
if (encoding & UNWIND_ARM64_FRAME_X19_X20_PAIR) {
m_registers.append(RegisterAtOffset(ARM64Registers::x19, offset -= 8));
m_registers.append(RegisterAtOffset(ARM64Registers::x20, offset -= 8));
}
if (encoding & UNWIND_ARM64_FRAME_X21_X22_PAIR) {
m_registers.append(RegisterAtOffset(ARM64Registers::x21, offset -= 8));
m_registers.append(RegisterAtOffset(ARM64Registers::x22, offset -= 8));
}
if (encoding & UNWIND_ARM64_FRAME_X23_X24_PAIR) {
m_registers.append(RegisterAtOffset(ARM64Registers::x23, offset -= 8));
m_registers.append(RegisterAtOffset(ARM64Registers::x24, offset -= 8));
}
if (encoding & UNWIND_ARM64_FRAME_X25_X26_PAIR) {
m_registers.append(RegisterAtOffset(ARM64Registers::x25, offset -= 8));
m_registers.append(RegisterAtOffset(ARM64Registers::x26, offset -= 8));
}
if (encoding & UNWIND_ARM64_FRAME_X27_X28_PAIR) {
m_registers.append(RegisterAtOffset(ARM64Registers::x27, offset -= 8));
m_registers.append(RegisterAtOffset(ARM64Registers::x28, offset -= 8));
}
if (encoding & UNWIND_ARM64_FRAME_D8_D9_PAIR) {
m_registers.append(RegisterAtOffset(ARM64Registers::q8, offset -= 8));
m_registers.append(RegisterAtOffset(ARM64Registers::q9, offset -= 8));
}
if (encoding & UNWIND_ARM64_FRAME_D10_D11_PAIR) {
m_registers.append(RegisterAtOffset(ARM64Registers::q10, offset -= 8));
m_registers.append(RegisterAtOffset(ARM64Registers::q11, offset -= 8));
}
if (encoding & UNWIND_ARM64_FRAME_D12_D13_PAIR) {
m_registers.append(RegisterAtOffset(ARM64Registers::q12, offset -= 8));
m_registers.append(RegisterAtOffset(ARM64Registers::q13, offset -= 8));
}
if (encoding & UNWIND_ARM64_FRAME_D14_D15_PAIR) {
m_registers.append(RegisterAtOffset(ARM64Registers::q14, offset -= 8));
m_registers.append(RegisterAtOffset(ARM64Registers::q15, offset -= 8));
}
#else
#error "Unrecognized architecture"
#endif
#elif OS(LINUX)
FDE_Info fdeInfo;
CIE_Info cieInfo;
PrologInfo prolog;
findFDE((uintptr_t)generatedFunction, (uintptr_t)section, size, &fdeInfo, &cieInfo);
parseFDEInstructions(fdeInfo, cieInfo, &prolog);
#if CPU(X86_64)
RELEASE_ASSERT(prolog.cfaRegister == UNW_X86_64_rbp);
RELEASE_ASSERT(prolog.cfaRegisterOffset == 16);
RELEASE_ASSERT(prolog.savedRegisters[UNW_X86_64_rbp].saved);
RELEASE_ASSERT(prolog.savedRegisters[UNW_X86_64_rbp].offset == -prolog.cfaRegisterOffset);
for (int i = 0; i < MaxRegisterNumber; ++i) {
if (prolog.savedRegisters[i].saved) {
switch (i) {
case UNW_X86_64_rbx:
m_registers.append(RegisterAtOffset(X86Registers::ebx, prolog.savedRegisters[i].offset + prolog.cfaRegisterOffset));
break;
case UNW_X86_64_r12:
m_registers.append(RegisterAtOffset(X86Registers::r12, prolog.savedRegisters[i].offset + prolog.cfaRegisterOffset));
break;
case UNW_X86_64_r13:
m_registers.append(RegisterAtOffset(X86Registers::r13, prolog.savedRegisters[i].offset + prolog.cfaRegisterOffset));
break;
case UNW_X86_64_r14:
m_registers.append(RegisterAtOffset(X86Registers::r14, prolog.savedRegisters[i].offset + prolog.cfaRegisterOffset));
break;
case UNW_X86_64_r15:
m_registers.append(RegisterAtOffset(X86Registers::r15, prolog.savedRegisters[i].offset + prolog.cfaRegisterOffset));
break;
case UNW_X86_64_rbp:
m_registers.append(RegisterAtOffset(X86Registers::ebp, prolog.savedRegisters[i].offset + prolog.cfaRegisterOffset));
break;
case DW_X86_64_RET_addr:
break;
default:
RELEASE_ASSERT_NOT_REACHED(); // non-standard register being saved in prolog
}
}
}
#elif CPU(ARM64)
RELEASE_ASSERT(prolog.cfaRegister == UNW_ARM64_fp);
RELEASE_ASSERT(prolog.cfaRegisterOffset == 16);
RELEASE_ASSERT(prolog.savedRegisters[UNW_ARM64_fp].saved);
RELEASE_ASSERT(prolog.savedRegisters[UNW_ARM64_fp].offset == -prolog.cfaRegisterOffset);
for (int i = 0; i < MaxRegisterNumber; ++i) {
if (prolog.savedRegisters[i].saved) {
switch (i) {
case UNW_ARM64_x0:
m_registers.append(RegisterAtOffset(ARM64Registers::x0, prolog.savedRegisters[i].offset + prolog.cfaRegisterOffset));
break;
case UNW_ARM64_x1:
m_registers.append(RegisterAtOffset(ARM64Registers::x1, prolog.savedRegisters[i].offset + prolog.cfaRegisterOffset));
break;
case UNW_ARM64_x2:
m_registers.append(RegisterAtOffset(ARM64Registers::x2, prolog.savedRegisters[i].offset + prolog.cfaRegisterOffset));
break;
case UNW_ARM64_x3:
m_registers.append(RegisterAtOffset(ARM64Registers::x3, prolog.savedRegisters[i].offset + prolog.cfaRegisterOffset));
break;
case UNW_ARM64_x4:
m_registers.append(RegisterAtOffset(ARM64Registers::x4, prolog.savedRegisters[i].offset + prolog.cfaRegisterOffset));
break;
case UNW_ARM64_x5:
m_registers.append(RegisterAtOffset(ARM64Registers::x5, prolog.savedRegisters[i].offset + prolog.cfaRegisterOffset));
break;
case UNW_ARM64_x6:
m_registers.append(RegisterAtOffset(ARM64Registers::x6, prolog.savedRegisters[i].offset + prolog.cfaRegisterOffset));
break;
case UNW_ARM64_x7:
m_registers.append(RegisterAtOffset(ARM64Registers::x7, prolog.savedRegisters[i].offset + prolog.cfaRegisterOffset));
break;
case UNW_ARM64_x8:
m_registers.append(RegisterAtOffset(ARM64Registers::x8, prolog.savedRegisters[i].offset + prolog.cfaRegisterOffset));
break;
case UNW_ARM64_x9:
m_registers.append(RegisterAtOffset(ARM64Registers::x9, prolog.savedRegisters[i].offset + prolog.cfaRegisterOffset));
break;
case UNW_ARM64_x10:
m_registers.append(RegisterAtOffset(ARM64Registers::x10, prolog.savedRegisters[i].offset + prolog.cfaRegisterOffset));
break;
case UNW_ARM64_x11:
m_registers.append(RegisterAtOffset(ARM64Registers::x11, prolog.savedRegisters[i].offset + prolog.cfaRegisterOffset));
break;
case UNW_ARM64_x12:
m_registers.append(RegisterAtOffset(ARM64Registers::x12, prolog.savedRegisters[i].offset + prolog.cfaRegisterOffset));
break;
case UNW_ARM64_x13:
m_registers.append(RegisterAtOffset(ARM64Registers::x13, prolog.savedRegisters[i].offset + prolog.cfaRegisterOffset));
break;
case UNW_ARM64_x14:
m_registers.append(RegisterAtOffset(ARM64Registers::x14, prolog.savedRegisters[i].offset + prolog.cfaRegisterOffset));
break;
case UNW_ARM64_x15:
m_registers.append(RegisterAtOffset(ARM64Registers::x15, prolog.savedRegisters[i].offset + prolog.cfaRegisterOffset));
break;
case UNW_ARM64_x16:
m_registers.append(RegisterAtOffset(ARM64Registers::x16, prolog.savedRegisters[i].offset + prolog.cfaRegisterOffset));
break;
case UNW_ARM64_x17:
m_registers.append(RegisterAtOffset(ARM64Registers::x17, prolog.savedRegisters[i].offset + prolog.cfaRegisterOffset));
break;
case UNW_ARM64_x18:
m_registers.append(RegisterAtOffset(ARM64Registers::x18, prolog.savedRegisters[i].offset + prolog.cfaRegisterOffset));
break;
case UNW_ARM64_x19:
m_registers.append(RegisterAtOffset(ARM64Registers::x19, prolog.savedRegisters[i].offset + prolog.cfaRegisterOffset));
break;
case UNW_ARM64_x20:
m_registers.append(RegisterAtOffset(ARM64Registers::x20, prolog.savedRegisters[i].offset + prolog.cfaRegisterOffset));
break;
case UNW_ARM64_x21:
m_registers.append(RegisterAtOffset(ARM64Registers::x21, prolog.savedRegisters[i].offset + prolog.cfaRegisterOffset));
break;
case UNW_ARM64_x22:
m_registers.append(RegisterAtOffset(ARM64Registers::x22, prolog.savedRegisters[i].offset + prolog.cfaRegisterOffset));
break;
case UNW_ARM64_x23:
m_registers.append(RegisterAtOffset(ARM64Registers::x23, prolog.savedRegisters[i].offset + prolog.cfaRegisterOffset));
break;
case UNW_ARM64_x24:
m_registers.append(RegisterAtOffset(ARM64Registers::x24, prolog.savedRegisters[i].offset + prolog.cfaRegisterOffset));
break;
case UNW_ARM64_x25:
m_registers.append(RegisterAtOffset(ARM64Registers::x25, prolog.savedRegisters[i].offset + prolog.cfaRegisterOffset));
break;
case UNW_ARM64_x26:
m_registers.append(RegisterAtOffset(ARM64Registers::x26, prolog.savedRegisters[i].offset + prolog.cfaRegisterOffset));
break;
case UNW_ARM64_x27:
m_registers.append(RegisterAtOffset(ARM64Registers::x27, prolog.savedRegisters[i].offset + prolog.cfaRegisterOffset));
break;
case UNW_ARM64_x28:
m_registers.append(RegisterAtOffset(ARM64Registers::x28, prolog.savedRegisters[i].offset + prolog.cfaRegisterOffset));
break;
case UNW_ARM64_fp:
m_registers.append(RegisterAtOffset(ARM64Registers::fp, prolog.savedRegisters[i].offset + prolog.cfaRegisterOffset));
break;
case UNW_ARM64_x30:
m_registers.append(RegisterAtOffset(ARM64Registers::x30, prolog.savedRegisters[i].offset + prolog.cfaRegisterOffset));
break;
case UNW_ARM64_sp:
m_registers.append(RegisterAtOffset(ARM64Registers::sp, prolog.savedRegisters[i].offset + prolog.cfaRegisterOffset));
break;
case UNW_ARM64_v0:
m_registers.append(RegisterAtOffset(ARM64Registers::q0, prolog.savedRegisters[i].offset + prolog.cfaRegisterOffset));
break;
case UNW_ARM64_v1:
m_registers.append(RegisterAtOffset(ARM64Registers::q1, prolog.savedRegisters[i].offset + prolog.cfaRegisterOffset));
break;
case UNW_ARM64_v2:
m_registers.append(RegisterAtOffset(ARM64Registers::q2, prolog.savedRegisters[i].offset + prolog.cfaRegisterOffset));
break;
case UNW_ARM64_v3:
m_registers.append(RegisterAtOffset(ARM64Registers::q3, prolog.savedRegisters[i].offset + prolog.cfaRegisterOffset));
break;
case UNW_ARM64_v4:
m_registers.append(RegisterAtOffset(ARM64Registers::q4, prolog.savedRegisters[i].offset + prolog.cfaRegisterOffset));
break;
case UNW_ARM64_v5:
m_registers.append(RegisterAtOffset(ARM64Registers::q5, prolog.savedRegisters[i].offset + prolog.cfaRegisterOffset));
break;
case UNW_ARM64_v6:
m_registers.append(RegisterAtOffset(ARM64Registers::q6, prolog.savedRegisters[i].offset + prolog.cfaRegisterOffset));
break;
case UNW_ARM64_v7:
m_registers.append(RegisterAtOffset(ARM64Registers::q7, prolog.savedRegisters[i].offset + prolog.cfaRegisterOffset));
break;
case UNW_ARM64_v8:
m_registers.append(RegisterAtOffset(ARM64Registers::q8, prolog.savedRegisters[i].offset + prolog.cfaRegisterOffset));
break;
case UNW_ARM64_v9:
m_registers.append(RegisterAtOffset(ARM64Registers::q9, prolog.savedRegisters[i].offset + prolog.cfaRegisterOffset));
break;
case UNW_ARM64_v10:
m_registers.append(RegisterAtOffset(ARM64Registers::q10, prolog.savedRegisters[i].offset + prolog.cfaRegisterOffset));
break;
case UNW_ARM64_v11:
m_registers.append(RegisterAtOffset(ARM64Registers::q11, prolog.savedRegisters[i].offset + prolog.cfaRegisterOffset));
break;
case UNW_ARM64_v12:
m_registers.append(RegisterAtOffset(ARM64Registers::q12, prolog.savedRegisters[i].offset + prolog.cfaRegisterOffset));
break;
case UNW_ARM64_v13:
m_registers.append(RegisterAtOffset(ARM64Registers::q13, prolog.savedRegisters[i].offset + prolog.cfaRegisterOffset));
break;
case UNW_ARM64_v14:
m_registers.append(RegisterAtOffset(ARM64Registers::q14, prolog.savedRegisters[i].offset + prolog.cfaRegisterOffset));
break;
case UNW_ARM64_v15:
m_registers.append(RegisterAtOffset(ARM64Registers::q15, prolog.savedRegisters[i].offset + prolog.cfaRegisterOffset));
break;
case UNW_ARM64_v16:
m_registers.append(RegisterAtOffset(ARM64Registers::q16, prolog.savedRegisters[i].offset + prolog.cfaRegisterOffset));
break;
case UNW_ARM64_v17:
m_registers.append(RegisterAtOffset(ARM64Registers::q17, prolog.savedRegisters[i].offset + prolog.cfaRegisterOffset));
break;
case UNW_ARM64_v18:
m_registers.append(RegisterAtOffset(ARM64Registers::q18, prolog.savedRegisters[i].offset + prolog.cfaRegisterOffset));
break;
case UNW_ARM64_v19:
m_registers.append(RegisterAtOffset(ARM64Registers::q19, prolog.savedRegisters[i].offset + prolog.cfaRegisterOffset));
break;
case UNW_ARM64_v20:
m_registers.append(RegisterAtOffset(ARM64Registers::q20, prolog.savedRegisters[i].offset + prolog.cfaRegisterOffset));
break;
case UNW_ARM64_v21:
m_registers.append(RegisterAtOffset(ARM64Registers::q21, prolog.savedRegisters[i].offset + prolog.cfaRegisterOffset));
break;
case UNW_ARM64_v22:
m_registers.append(RegisterAtOffset(ARM64Registers::q22, prolog.savedRegisters[i].offset + prolog.cfaRegisterOffset));
break;
case UNW_ARM64_v23:
m_registers.append(RegisterAtOffset(ARM64Registers::q23, prolog.savedRegisters[i].offset + prolog.cfaRegisterOffset));
break;
case UNW_ARM64_v24:
m_registers.append(RegisterAtOffset(ARM64Registers::q24, prolog.savedRegisters[i].offset + prolog.cfaRegisterOffset));
break;
case UNW_ARM64_v25:
m_registers.append(RegisterAtOffset(ARM64Registers::q25, prolog.savedRegisters[i].offset + prolog.cfaRegisterOffset));
break;
case UNW_ARM64_v26:
m_registers.append(RegisterAtOffset(ARM64Registers::q26, prolog.savedRegisters[i].offset + prolog.cfaRegisterOffset));
break;
case UNW_ARM64_v27:
m_registers.append(RegisterAtOffset(ARM64Registers::q27, prolog.savedRegisters[i].offset + prolog.cfaRegisterOffset));
break;
case UNW_ARM64_v28:
m_registers.append(RegisterAtOffset(ARM64Registers::q28, prolog.savedRegisters[i].offset + prolog.cfaRegisterOffset));
break;
case UNW_ARM64_v29:
m_registers.append(RegisterAtOffset(ARM64Registers::q29, prolog.savedRegisters[i].offset + prolog.cfaRegisterOffset));
break;
case UNW_ARM64_v30:
m_registers.append(RegisterAtOffset(ARM64Registers::q30, prolog.savedRegisters[i].offset + prolog.cfaRegisterOffset));
break;
case UNW_ARM64_v31:
m_registers.append(RegisterAtOffset(ARM64Registers::q31, prolog.savedRegisters[i].offset + prolog.cfaRegisterOffset));
break;
default:
RELEASE_ASSERT_NOT_REACHED(); // non-standard register being saved in prolog
}
}
}
#else
#error "Unrecognized architecture"
#endif
#endif
std::sort(m_registers.begin(), m_registers.end());
return true;
}
void UnwindInfo::dump(PrintStream& out) const
{
out.print(listDump(m_registers));
}
RegisterAtOffset* UnwindInfo::find(Reg reg) const
{
return tryBinarySearch<RegisterAtOffset, Reg>(m_registers, m_registers.size(), reg, RegisterAtOffset::getReg);
}
unsigned UnwindInfo::indexOf(Reg reg) const
{
if (RegisterAtOffset* pointer = find(reg))
return pointer - m_registers.begin();
return UINT_MAX;
}
} } // namespace JSC::FTL
#endif // ENABLE(FTL_JIT)