diff --git a/src/rt/backtrace/dwarf.d b/src/rt/backtrace/dwarf.d index 5dc2f6ef05..45388eca52 100644 --- a/src/rt/backtrace/dwarf.d +++ b/src/rt/backtrace/dwarf.d @@ -35,9 +35,10 @@ else import rt.backtrace.elf; import rt.util.container.array; -import core.stdc.string : strlen, memchr; +import core.stdc.string : strlen, memchr, memcpy; //debug = DwarfDebugMachine; +debug(DwarfDebugMachine) import core.stdc.stdio : printf; struct Location { @@ -126,88 +127,13 @@ void resolveAddresses(const(ubyte)[] debugLineSectionData, Location[] locations, while (dbg.length > 0) { debug(DwarfDebugMachine) printf("new debug program\n"); - const(LPHeader)* lph = cast(const(LPHeader)*) dbg.ptr; - - if (lph.unitLength == 0xffff_ffff) // is 64-bit dwarf? - return; // unable to read 64-bit dwarf - - const(ubyte)[] program = dbg[ - lph.headerLength + LPHeader.minimumInstructionLength.offsetof .. - lph.unitLength + LPHeader.dwarfVersion.offsetof - ]; - - const(ubyte)[] standardOpcodeLengths = dbg[ - LPHeader.sizeof .. LPHeader.sizeof + lph.opcodeBase - 1 - ]; - - const(ubyte)[] pathData = dbg[ - LPHeader.sizeof + lph.opcodeBase - 1 .. $ - ]; - - Array!(const(char)[]) directories; - directories.length = (const(ubyte)[] bytes) { - // count number of directories - int count = 0; - foreach (i; 0 .. bytes.length - 1) - { - if (bytes[i] == 0) - { - count++; - if (bytes[i + 1] == 0) return count; - } - } - return count; - }(pathData); - - // fill directories array from dwarf section - int currentDirectoryIndex = 0; - while (pathData[0] != 0) - { - directories[currentDirectoryIndex] = cast(const(char)[]) pathData[0 .. strlen(cast(char*) (pathData.ptr))]; - debug(DwarfDebugMachine) printf("dir: %s\n", pathData.ptr); - pathData = pathData[directories[currentDirectoryIndex].length + 1 .. $]; - currentDirectoryIndex++; - } - - pathData = pathData[1 .. $]; - - Array!(const(char)[]) filenames; - filenames.length = (const(ubyte)[] bytes) - { - // count number of files - int count = 0; - while (bytes[0] != 0) - { - auto filename = cast(const(char)[]) bytes[0 .. strlen(cast(char*) (bytes.ptr))]; - bytes = bytes[filename.length + 1 .. $]; - bytes.readULEB128(); // dir index - bytes.readULEB128(); // last mod - bytes.readULEB128(); // file len - count++; - } - return count; - }(pathData); - - // fill filenames array from dwarf section - int currentFileIndex = 0; - while (pathData[0] != 0) - { - filenames[currentFileIndex] = cast(const(char)[]) pathData[0 .. strlen(cast(char*) (pathData.ptr))]; - debug(DwarfDebugMachine) printf("file: %s\n", pathData.ptr); - pathData = pathData[filenames[currentFileIndex].length + 1 .. $]; - - auto dirIndex = pathData.readULEB128(); // unused - auto lastMod = pathData.readULEB128(); // unused - auto fileLen = pathData.readULEB128(); // unused - - currentFileIndex++; - } + const lp = readLineNumberProgram(dbg); LocationInfo lastLoc = LocationInfo(-1, -1); size_t lastAddress = 0x0; debug(DwarfDebugMachine) printf("program:\n"); - runStateMachine(lph, program, standardOpcodeLengths, + runStateMachine(lp, (size_t address, LocationInfo locInfo, bool isEndSequence) { // adjust to ASLR offset @@ -222,18 +148,18 @@ void resolveAddresses(const(ubyte)[] debugLineSectionData, Location[] locations, if (loc.address == address) { debug(DwarfDebugMachine) printf("-- found for [0x%x]:\n", loc.address); - debug(DwarfDebugMachine) printf("-- file: %.*s\n", filenames[locInfo.file - 1].length, filenames[locInfo.file - 1].ptr); + debug(DwarfDebugMachine) printf("-- file: %.*s\n", cast(int) lp.fileNames[locInfo.file - 1].length, lp.fileNames[locInfo.file - 1].ptr); debug(DwarfDebugMachine) printf("-- line: %d\n", locInfo.line); - loc.file = filenames[locInfo.file - 1]; + loc.file = lp.fileNames[locInfo.file - 1]; loc.line = locInfo.line; numberOfLocationsFound++; } else if (loc.address < address && lastAddress < loc.address && lastAddress != 0) { debug(DwarfDebugMachine) printf("-- found for [0x%x]:\n", loc.address); - debug(DwarfDebugMachine) printf("-- file: %.*s\n", filenames[lastLoc.file - 1].length, filenames[lastLoc.file - 1].ptr); + debug(DwarfDebugMachine) printf("-- file: %.*s\n", cast(int) lp.fileNames[lastLoc.file - 1].length, lp.fileNames[lastLoc.file - 1].ptr); debug(DwarfDebugMachine) printf("-- line: %d\n", lastLoc.line); - loc.file = filenames[lastLoc.file - 1]; + loc.file = lp.fileNames[lastLoc.file - 1]; loc.line = lastLoc.line; numberOfLocationsFound++; } @@ -254,22 +180,28 @@ void resolveAddresses(const(ubyte)[] debugLineSectionData, Location[] locations, ); if (numberOfLocationsFound == locations.length) return; - dbg = dbg[lph.unitLength + LPHeader.dwarfVersion.offsetof .. $]; } } alias RunStateMachineCallback = bool delegate(size_t, LocationInfo, bool) @nogc nothrow; -bool runStateMachine(const(LPHeader)* lpHeader, const(ubyte)[] program, const(ubyte)[] standardOpcodeLengths, scope RunStateMachineCallback callback) @nogc nothrow +bool runStateMachine(ref const(LineNumberProgram) lp, scope RunStateMachineCallback callback) @nogc nothrow { - debug(DwarfDebugMachine) import core.stdc.stdio; - StateMachine machine; - machine.isStatement = lpHeader.defaultIsStatement; + machine.isStatement = lp.defaultIsStatement; + const(ubyte)[] program = lp.program; while (program.length > 0) { + size_t advanceAddressAndOpIndex(size_t operationAdvance) + { + const addressIncrement = lp.minimumInstructionLength * ((machine.operationIndex + operationAdvance) / lp.maximumOperationsPerInstruction); + machine.address += addressIncrement; + machine.operationIndex = (machine.operationIndex + operationAdvance) % lp.maximumOperationsPerInstruction; + return addressIncrement; + } + ubyte opcode = program.read!ubyte(); - if (opcode < lpHeader.opcodeBase) + if (opcode < lp.opcodeBase) { switch (opcode) with (StandardOpcode) { @@ -284,13 +216,14 @@ bool runStateMachine(const(LPHeader)* lpHeader, const(ubyte)[] program, const(ub debug(DwarfDebugMachine) printf("endSequence 0x%x\n", machine.address); if (!callback(machine.address, LocationInfo(machine.fileIndex, machine.line), true)) return true; machine = StateMachine.init; - machine.isStatement = lpHeader.defaultIsStatement; + machine.isStatement = lp.defaultIsStatement; break; case setAddress: size_t address = program.read!size_t(); debug(DwarfDebugMachine) printf("setAddress 0x%x\n", address); machine.address = address; + machine.operationIndex = 0; break; case defineFile: // TODO: add proper implementation @@ -298,6 +231,12 @@ bool runStateMachine(const(LPHeader)* lpHeader, const(ubyte)[] program, const(ub program = program[len - 1 .. $]; break; + case setDiscriminator: + const discriminator = cast(uint) program.readULEB128(); + debug(DwarfDebugMachine) printf("setDiscriminator %d\n", discriminator); + machine.discriminator = discriminator; + break; + default: // unknown opcode debug(DwarfDebugMachine) printf("unknown extended opcode %d\n", cast(int) eopcode); @@ -313,12 +252,13 @@ bool runStateMachine(const(LPHeader)* lpHeader, const(ubyte)[] program, const(ub machine.isBasicBlock = false; machine.isPrologueEnd = false; machine.isEpilogueBegin = false; + machine.discriminator = 0; break; case advancePC: - ulong op = readULEB128(program); - machine.address += op * lpHeader.minimumInstructionLength; - debug(DwarfDebugMachine) printf("advancePC %d to 0x%x\n", cast(int) (op * lpHeader.minimumInstructionLength), machine.address); + const operationAdvance = cast(size_t) readULEB128(program); + advanceAddressAndOpIndex(operationAdvance); + debug(DwarfDebugMachine) printf("advancePC %d to 0x%x\n", cast(int) operationAdvance, machine.address); break; case advanceLine: @@ -350,13 +290,15 @@ bool runStateMachine(const(LPHeader)* lpHeader, const(ubyte)[] program, const(ub break; case constAddPC: - machine.address += (255 - lpHeader.opcodeBase) / lpHeader.lineRange * lpHeader.minimumInstructionLength; + const operationAdvance = (255 - lp.opcodeBase) / lp.lineRange; + advanceAddressAndOpIndex(operationAdvance); debug(DwarfDebugMachine) printf("constAddPC 0x%x\n", machine.address); break; case fixedAdvancePC: - uint add = program.read!uint(); + const add = program.read!ushort(); machine.address += add; + machine.operationIndex = 0; debug(DwarfDebugMachine) printf("fixedAdvancePC %d to 0x%x\n", cast(int) add, machine.address); break; @@ -376,20 +318,25 @@ bool runStateMachine(const(LPHeader)* lpHeader, const(ubyte)[] program, const(ub break; default: - // unimplemented/invalid opcode + debug(DwarfDebugMachine) printf("unknown opcode %d\n", cast(int) opcode); return false; } } else { - opcode -= lpHeader.opcodeBase; - auto ainc = (opcode / lpHeader.lineRange) * lpHeader.minimumInstructionLength; - machine.address += ainc; - auto linc = lpHeader.lineBase + (opcode % lpHeader.lineRange); - machine.line += linc; + opcode -= lp.opcodeBase; + const operationAdvance = opcode / lp.lineRange; + const addressIncrement = advanceAddressAndOpIndex(operationAdvance); + const lineIncrement = lp.lineBase + (opcode % lp.lineRange); + machine.line += lineIncrement; - debug(DwarfDebugMachine) printf("special %d %d to 0x%x line %d\n", cast(int) ainc, cast(int) linc, machine.address, cast(int) machine.line); + debug(DwarfDebugMachine) printf("special %d %d to 0x%x line %d\n", cast(int) addressIncrement, cast(int) lineIncrement, machine.address, machine.line); if (!callback(machine.address, LocationInfo(machine.fileIndex, machine.line), false)) return true; + + machine.isBasicBlock = false; + machine.isPrologueEnd = false; + machine.isEpilogueBegin = false; + machine.discriminator = 0; } } @@ -494,7 +441,20 @@ const(char)[] extractSymbol(const(char)[] btSymbol) @nogc nothrow T read(T)(ref const(ubyte)[] buffer) @nogc nothrow { - T result = *(cast(T*) buffer[0 .. T.sizeof].ptr); + version (X86) enum hasUnalignedLoads = true; + else version (X86_64) enum hasUnalignedLoads = true; + else enum hasUnalignedLoads = false; + + static if (hasUnalignedLoads || T.alignof == 1) + { + T result = *(cast(T*) buffer.ptr); + } + else + { + T result = void; + memcpy(&result, buffer.ptr, T.sizeof); + } + buffer = buffer[T.sizeof .. $]; return result; } @@ -567,6 +527,7 @@ enum ExtendedOpcode : ubyte endSequence = 1, setAddress = 2, defineFile = 3, + setDiscriminator = 4, } struct StateMachine @@ -591,17 +552,110 @@ struct LocationInfo int line; } -// 32-bit DWARF -align(1) -struct LPHeader +struct LineNumberProgram { -align(1): - uint unitLength; + ulong unitLength; ushort dwarfVersion; - uint headerLength; + ulong headerLength; ubyte minimumInstructionLength; + ubyte maximumOperationsPerInstruction; bool defaultIsStatement; byte lineBase; ubyte lineRange; ubyte opcodeBase; + const(ubyte)[] standardOpcodeLengths; + Array!(const(char)[]) includeDirectories; + Array!(const(char)[]) fileNames; + const(ubyte)[] program; +} + +LineNumberProgram readLineNumberProgram(ref const(ubyte)[] data) @nogc nothrow +{ + const originalData = data; + + LineNumberProgram lp; + + bool is64bitDwarf = false; + lp.unitLength = data.read!uint(); + if (lp.unitLength == uint.max) + { + is64bitDwarf = true; + lp.unitLength = data.read!ulong(); + } + + const dwarfVersionFieldOffset = cast(size_t) (data.ptr - originalData.ptr); + lp.dwarfVersion = data.read!ushort(); + assert(lp.dwarfVersion < 5, "DWARF v5+ not supported yet"); + + lp.headerLength = (is64bitDwarf ? data.read!ulong() : data.read!uint()); + + const minimumInstructionLengthFieldOffset = cast(size_t) (data.ptr - originalData.ptr); + lp.minimumInstructionLength = data.read!ubyte(); + + lp.maximumOperationsPerInstruction = (lp.dwarfVersion >= 4 ? data.read!ubyte() : 1); + lp.defaultIsStatement = (data.read!ubyte() != 0); + lp.lineBase = data.read!byte(); + lp.lineRange = data.read!ubyte(); + lp.opcodeBase = data.read!ubyte(); + + lp.standardOpcodeLengths = data[0 .. lp.opcodeBase - 1]; + data = data[lp.opcodeBase - 1 .. $]; + + // A sequence ends with a null-byte. + static Array!(const(char)[]) readSequence(alias ReadEntry)(ref const(ubyte)[] data) + { + static size_t count(const(ubyte)[] data) + { + size_t count = 0; + while (data.length && data[0] != 0) + { + ReadEntry(data); + ++count; + } + return count; + } + + const numEntries = count(data); + + Array!(const(char)[]) result; + result.length = numEntries; + + foreach (i; 0 .. numEntries) + result[i] = ReadEntry(data); + + data = data[1 .. $]; // skip over sequence-terminating null + + return result; + } + + static const(char)[] readIncludeDirectoryEntry(ref const(ubyte)[] data) + { + const length = strlen(cast(char*) data.ptr); + auto result = cast(const(char)[]) data[0 .. length]; + debug(DwarfDebugMachine) printf("dir: %.*s\n", cast(int) length, result.ptr); + data = data[length + 1 .. $]; + return result; + } + lp.includeDirectories = readSequence!readIncludeDirectoryEntry(data); + + static const(char)[] readFileNameEntry(ref const(ubyte)[] data) + { + const length = strlen(cast(char*) data.ptr); + auto result = cast(const(char)[]) data[0 .. length]; + debug(DwarfDebugMachine) printf("file: %.*s\n", cast(int) length, result.ptr); + data = data[length + 1 .. $]; + data.readULEB128(); // dir index + data.readULEB128(); // last mod + data.readULEB128(); // file len + return result; + } + lp.fileNames = readSequence!readFileNameEntry(data); + + const programStart = cast(size_t) (minimumInstructionLengthFieldOffset + lp.headerLength); + const programEnd = cast(size_t) (dwarfVersionFieldOffset + lp.unitLength); + lp.program = originalData[programStart .. programEnd]; + + data = originalData[programEnd .. $]; + + return lp; }