]> git.saurik.com Git - apple/javascriptcore.git/blobdiff - ftl/FTLUnwindInfo.cpp
JavaScriptCore-7601.1.46.3.tar.gz
[apple/javascriptcore.git] / ftl / FTLUnwindInfo.cpp
index 98c18316d3ef9a1b3eefa2f338fe7ad4d99b917d..2867a7245e89bf4f134e0c0d53624052e38ddd27 100644 (file)
@@ -1,5 +1,7 @@
 /*
  * 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
  * 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. 
+ * 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"
@@ -28,7 +96,9 @@
 
 #if ENABLE(FTL_JIT)
 
+#if OS(DARWIN)
 #include <mach-o/compact_unwind_encoding.h>
+#endif
 #include <wtf/ListDump.h>
 
 namespace JSC { namespace FTL {
@@ -36,8 +106,9 @@ namespace JSC { namespace FTL {
 UnwindInfo::UnwindInfo() { }
 UnwindInfo::~UnwindInfo() { }
 
-namespace {
 
+namespace {
+#if OS(DARWIN)
 struct CompactUnwind {
     void* function;
     uint32_t size;
@@ -45,17 +116,552 @@ struct CompactUnwind {
     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 == !!size);
+    RELEASE_ASSERT(!!section);
     if (!section)
         return false;
-    
+
+#if OS(DARWIN)
     RELEASE_ASSERT(size >= sizeof(CompactUnwind));
     
     CompactUnwind* data = bitwise_cast<CompactUnwind*>(section);
@@ -157,7 +763,260 @@ bool UnwindInfo::parse(void* section, size_t size, GeneratedFunction generatedFu
 #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;
 }