1/*
2 * Copyright (C) 2014 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23 * THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#include "FTLDWARFDebugLineInfo.h"
28
29#if ENABLE(FTL_JIT)
30
31#include <wtf/DataLog.h>
32
33namespace JSC { namespace FTL {
34
35DebugLineInterpreter::DebugLineInterpreter(const char* program)
36    : m_program(program)
37    , m_logResults(false)
38{
39    resetInterpreterState();
40}
41
42template <typename T> inline T read(const char*& program)
43{
44    T result = *reinterpret_cast<const T*>(program);
45    program += sizeof(T);
46    return result;
47}
48
49uint32_t DebugLineInterpreter::parseULEB128(const char*& offset)
50{
51    uint32_t result = 0;
52    uint8_t byte;
53    unsigned shiftAmount = 0;
54    do {
55        byte = read<uint8_t>(offset);
56        result |= (byte & ~0x80) << shiftAmount;
57        shiftAmount += 7;
58    } while (byte & 0x80);
59    return result;
60}
61
62int32_t DebugLineInterpreter::parseSLEB128(const char*& offset)
63{
64    int32_t result = 0;
65    uint8_t byte;
66    unsigned shiftAmount = 0;
67    do {
68        byte = read<uint8_t>(offset);
69        result |= (byte & ~0x80) << shiftAmount;
70        shiftAmount += 7;
71    } while (byte & 0x80);
72
73    // If the sign bit (in this case, the second MSB) on the last byte is set we need to zero extend.
74    if (byte & 0x40)
75        result |= -(1 << shiftAmount);
76    return result;
77}
78
79void DebugLineInterpreter::run()
80{
81    parsePrologue();
82    interpretStatementProgram();
83    if (m_logResults)
84        printLineInfo();
85}
86
87void DebugLineInterpreter::parsePrologue()
88{
89    const char* currentProgramOffset = m_program;
90    m_prologue.totalLength = read<uint32_t>(currentProgramOffset);
91    if (m_prologue.totalLength == 0xffffffff) {
92        // This is 64-bit DWARF format.
93        m_prologue.format = SixtyFourBit;
94        m_prologue.totalLength = read<uint64_t>(currentProgramOffset);
95    } else
96        m_prologue.format = ThirtyTwoBit;
97    m_prologue.version = read<uint16_t>(currentProgramOffset);
98
99    if (m_prologue.format == ThirtyTwoBit)
100        m_prologue.prologueLength = read<uint32_t>(currentProgramOffset);
101    else
102        m_prologue.prologueLength = read<uint64_t>(currentProgramOffset);
103    const char* afterLengthOffset = currentProgramOffset;
104
105    m_prologue.minimumInstructionLength = read<uint8_t>(currentProgramOffset);
106    m_prologue.defaultIsStatement = read<uint8_t>(currentProgramOffset);
107    m_prologue.lineBase = read<int8_t>(currentProgramOffset);
108    m_prologue.lineRange = read<uint8_t>(currentProgramOffset);
109    m_prologue.opcodeBase = read<uint8_t>(currentProgramOffset);
110    for (unsigned i = 1; i < m_prologue.opcodeBase; ++i)
111        m_prologue.standardOpcodeLengths.append(read<uint8_t>(currentProgramOffset));
112    parseIncludeDirectories(currentProgramOffset);
113    parseFileEntries(currentProgramOffset);
114
115    m_program = afterLengthOffset + m_prologue.prologueLength;
116
117    if (!m_logResults)
118        return;
119
120    dataLog("\nPrologue:\n");
121    dataLog("totalLength = ", m_prologue.totalLength, "\n");
122    dataLog("version = ", m_prologue.version, "\n");
123    dataLog("prologueLength = ", m_prologue.prologueLength, "\n");
124    dataLog("minimumInstructionLength = ", m_prologue.minimumInstructionLength, "\n");
125    dataLog("defaultIsStatement = ", m_prologue.defaultIsStatement, "\n");
126    dataLog("lineBase = ", m_prologue.lineBase, "\n");
127    dataLog("lineRange = ", m_prologue.lineRange, "\n");
128    dataLog("opcodeBase = ", m_prologue.opcodeBase, "\n");
129
130    dataLog("\nStandard Opcode Lengths:\n");
131    for (unsigned i = 1; i < m_prologue.opcodeBase; ++i)
132        dataLog("standardOpcodeLengths[", i - 1, "] = ", m_prologue.standardOpcodeLengths[i - 1], "\n");
133
134    dataLog("\nInclude Directories:\n");
135    for (unsigned i = 0; i < m_prologue.includeDirectories.size(); ++i)
136        dataLog("includeDirectories[", i, "] = ", m_prologue.includeDirectories[i], "\n");
137
138    dataLog("\nFiles:\n");
139    for (unsigned i = 0; i < m_prologue.fileEntries.size(); ++i) {
140        FileEntry& entry = m_prologue.fileEntries[i];
141        dataLog("fileEntries[", i, "] = {name: \"", entry.name, "\", dir_index: ", entry.directoryIndex, ", last_modified: ", entry.lastModified, ", size: ", entry.size, "}\n");
142    }
143}
144
145void DebugLineInterpreter::parseIncludeDirectories(const char*& offset)
146{
147    size_t length = 0;
148    while ((length = strlen(offset))) {
149        m_prologue.includeDirectories.append(offset);
150        offset += length + 1;
151    }
152
153    // Extra increment to get past the last null byte.
154    offset += 1;
155}
156
157void DebugLineInterpreter::parseFileEntries(const char*& offset)
158{
159    while (true) {
160        DebugLineInterpreter::FileEntry nextEntry;
161        if (!parseFileEntry(offset, nextEntry))
162            break;
163        m_prologue.fileEntries.append(nextEntry);
164    }
165}
166
167bool DebugLineInterpreter::parseFileEntry(const char*& offset, FileEntry& entry)
168{
169    size_t length = strlen(offset);
170    if (!length) {
171        offset += 1;
172        return false;
173    }
174    entry.name = offset;
175    offset += length + 1;
176    entry.directoryIndex = parseULEB128(offset);
177    entry.lastModified = parseULEB128(offset);
178    entry.size = parseULEB128(offset);
179
180    return true;
181}
182
183void DebugLineInterpreter::interpretStatementProgram()
184{
185    const char* currentProgramOffset = m_program;
186    bool keepGoing = true;
187    do {
188        keepGoing = interpretOpcode(currentProgramOffset);
189    } while (keepGoing);
190}
191
192bool DebugLineInterpreter::interpretOpcode(const char*& offset)
193{
194    uint8_t nextOpcode = read<uint8_t>(offset);
195    switch (nextOpcode) {
196    case ExtendedOpcodes: {
197        uint32_t length = parseULEB128(offset);
198        if (!length)
199            return false;
200        uint8_t extendedOpcode = read<uint8_t>(offset);
201        switch (extendedOpcode) {
202        case DW_LNE_end_sequence: {
203            m_currentState.endSequence = true;
204            m_lineInfoMatrix.append(m_currentState);
205            resetInterpreterState();
206            break;
207        }
208        case DW_LNE_set_address: {
209            m_currentState.address = read<size_t>(offset);
210            break;
211        }
212        case DW_LNE_define_file: {
213            fprintf(stderr, "Unimplemented extended opcode DW_LNE_define_file.\n");
214            RELEASE_ASSERT_NOT_REACHED();
215            break;
216        }
217        default: {
218            fprintf(stderr, "Unknown extended opcode.\n");
219            RELEASE_ASSERT_NOT_REACHED();
220            break;
221        }
222        }
223        break;
224    }
225        /* Standard opcodes */
226    case DW_LNS_copy: {
227        m_lineInfoMatrix.append(m_currentState);
228        m_currentState.isBasicBlock = false;
229        m_currentState.prologueEnd = false;
230        m_currentState.epilogueBegin = false;
231        break;
232    }
233    case DW_LNS_advance_pc: {
234        uint32_t advance = parseULEB128(offset);
235        m_currentState.address += advance * m_prologue.minimumInstructionLength;
236        break;
237    }
238    case DW_LNS_advance_line: {
239        int32_t advance = parseSLEB128(offset);
240        m_currentState.line += advance;
241        break;
242    }
243    case DW_LNS_set_file: {
244        uint32_t fileIndex = parseULEB128(offset);
245        m_currentState.file = fileIndex;
246        break;
247    }
248    case DW_LNS_set_column: {
249        m_currentState.column = parseULEB128(offset);
250        break;
251    }
252    case DW_LNS_negate_stmt: {
253        m_currentState.isStatement = !m_currentState.isStatement;
254        break;
255    }
256    case DW_LNS_set_basic_block: {
257        m_currentState.isBasicBlock = true;
258        break;
259    }
260    case DW_LNS_const_add_pc: {
261        uint8_t adjustedOpcode = nextOpcode - m_prologue.opcodeBase;
262        uint32_t addressIncrement = (adjustedOpcode / m_prologue.lineRange) * m_prologue.minimumInstructionLength;
263        m_currentState.address += addressIncrement;
264        break;
265    }
266    case DW_LNS_fixed_advance_pc: {
267        uint16_t advance = read<uint16_t>(offset);
268        m_currentState.address += advance;
269        break;
270    }
271    case DW_LNS_set_prologue_end: {
272        m_currentState.prologueEnd = true;
273        break;
274    }
275    case DW_LNS_set_epilogue_begin: {
276        m_currentState.epilogueBegin = true;
277        break;
278    }
279    case DW_LNS_set_isa: {
280        m_currentState.isa = parseULEB128(offset);
281        break;
282    }
283        /* Special opcodes */
284    default: {
285        uint8_t adjustedOpcode = nextOpcode - m_prologue.opcodeBase;
286        uint32_t addressIncrement = (adjustedOpcode / m_prologue.lineRange) * m_prologue.minimumInstructionLength;
287        int32_t lineIncrement = m_prologue.lineBase + (adjustedOpcode % m_prologue.lineRange);
288        m_currentState.address += addressIncrement;
289        m_currentState.line += lineIncrement;
290        m_lineInfoMatrix.append(m_currentState);
291        m_currentState.isBasicBlock = false;
292        m_currentState.prologueEnd = false;
293        m_currentState.epilogueBegin = false;
294        break;
295    }
296    }
297    return true;
298}
299
300void DebugLineInterpreter::printLineInfo()
301{
302    dataLog("\nLine Info Matrix:\n");
303    for (unsigned i = 0; i < m_lineInfoMatrix.size(); ++i)
304        printLineInfo(m_lineInfoMatrix[i]);
305    dataLog("\n");
306}
307
308void DebugLineInterpreter::printLineInfo(LineInfo& info)
309{
310    dataLogF("address: %p", reinterpret_cast<void*>(info.address));
311    dataLog("  file: ", info.file, "  line: ", info.line, "  column: ", info.column, "  isa: ", info.isa, "  ");
312    dataLog("  statement?: ", info.isStatement);
313    dataLog("  basic block?: ", info.isBasicBlock);
314    dataLog("  end sequence?: ", info.endSequence);
315    dataLog("  prologue end?: ", info.prologueEnd);
316    dataLog("  epilogue begin?: ", info.epilogueBegin);
317    dataLog("\n");
318}
319
320void DebugLineInterpreter::resetInterpreterState()
321{
322    m_currentState.address = 0;
323    m_currentState.file = 1;
324    m_currentState.line = 1;
325    m_currentState.column = 0;
326    m_currentState.isa = 0;
327    m_currentState.isStatement = false;
328    m_currentState.isBasicBlock = false;
329    m_currentState.endSequence = false;
330    m_currentState.prologueEnd = false;
331    m_currentState.epilogueBegin = false;
332}
333
334} } // namespace JSC::FTL
335
336#endif // ENABLE(FTL_JIT)
337
338