| /* |
| * 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) |
| |