/* * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. * Distributed under the terms of the MIT License. */ #include "LineNumberProgram.h" #include #include #include #include "Dwarf.h" #include "Tracing.h" static const uint8 kLineNumberStandardOpcodeOperands[] = { 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1 }; static const uint32 kLineNumberStandardOpcodeCount = 12; LineNumberProgram::LineNumberProgram(uint8 addressSize, bool isBigEndian) : fProgram(NULL), fProgramSize(0), fMinInstructionLength(0), fDefaultIsStatement(0), fLineBase(0), fLineRange(0), fOpcodeBase(0), fAddressSize(addressSize), fIsBigEndian(isBigEndian), fStandardOpcodeLengths(NULL) { } LineNumberProgram::~LineNumberProgram() { } status_t LineNumberProgram::Init(const void* program, size_t programSize, uint8 minInstructionLength, bool defaultIsStatement, int8 lineBase, uint8 lineRange, uint8 opcodeBase, const uint8* standardOpcodeLengths) { // first check the operand counts for the standard opcodes uint8 standardOpcodeCount = std::min((uint32)opcodeBase - 1, kLineNumberStandardOpcodeCount); for (uint8 i = 0; i < standardOpcodeCount; i++) { if (standardOpcodeLengths[i] != kLineNumberStandardOpcodeOperands[i]) { WARNING("operand count for standard opcode %u does not what we " "expect\n", i + 1); return B_BAD_DATA; } } fProgram = program; fProgramSize = programSize; fMinInstructionLength = minInstructionLength; fDefaultIsStatement = defaultIsStatement; fLineBase = lineBase; fLineRange = lineRange; fOpcodeBase = opcodeBase; fStandardOpcodeLengths = standardOpcodeLengths; return B_OK; } void LineNumberProgram::GetInitialState(State& state) const { if (!IsValid()) return; _SetToInitial(state); state.dataReader.SetTo(fProgram, fProgramSize, fAddressSize, fIsBigEndian); } bool LineNumberProgram::GetNextRow(State& state) const { if (state.isSequenceEnd) _SetToInitial(state); DataReader& dataReader = state.dataReader; while (dataReader.BytesRemaining() > 0) { bool appendRow = false; uint8 opcode = dataReader.Read(0); if (opcode >= fOpcodeBase) { // special opcode uint adjustedOpcode = opcode - fOpcodeBase; state.address += (adjustedOpcode / fLineRange) * fMinInstructionLength; state.line += adjustedOpcode % fLineRange + fLineBase; state.isBasicBlock = false; state.isPrologueEnd = false; state.isEpilogueBegin = false; state.discriminator = 0; appendRow = true; } else if (opcode > 0) { // standard opcode switch (opcode) { case DW_LNS_copy: state.isBasicBlock = false; state.isPrologueEnd = false; state.isEpilogueBegin = false; appendRow = true; state.discriminator = 0; break; case DW_LNS_advance_pc: state.address += dataReader.ReadUnsignedLEB128(0) * fMinInstructionLength; break; case DW_LNS_advance_line: state.line += dataReader.ReadSignedLEB128(0); break; case DW_LNS_set_file: state.file = dataReader.ReadUnsignedLEB128(0); break; case DW_LNS_set_column: state.column = dataReader.ReadUnsignedLEB128(0); break; case DW_LNS_negate_stmt: state.isStatement = !state.isStatement; break; case DW_LNS_set_basic_block: state.isBasicBlock = true; break; case DW_LNS_const_add_pc: state.address += ((255 - fOpcodeBase) / fLineRange) * fMinInstructionLength; break; case DW_LNS_fixed_advance_pc: state.address += dataReader.Read(0); break; case DW_LNS_set_prologue_end: state.isPrologueEnd = true; break; case DW_LNS_set_epilogue_begin: state.isEpilogueBegin = true; break; case DW_LNS_set_isa: state.instructionSet = dataReader.ReadUnsignedLEB128(0); break; default: WARNING("unsupported standard opcode %u\n", opcode); for (int32 i = 0; i < fStandardOpcodeLengths[opcode - 1]; i++) { dataReader.ReadUnsignedLEB128(0); } } } else { // extended opcode uint32 instructionLength = dataReader.ReadUnsignedLEB128(0); off_t instructionOffset = dataReader.Offset(); uint8 extendedOpcode = dataReader.Read(0); switch (extendedOpcode) { case DW_LNE_end_sequence: state.isSequenceEnd = true; appendRow = true; break; case DW_LNE_set_address: state.address = dataReader.ReadAddress(0); break; case DW_LNE_define_file: { state.explicitFile = dataReader.ReadString(); state.explicitFileDirIndex = dataReader.ReadUnsignedLEB128(0); dataReader.ReadUnsignedLEB128(0); // modification time dataReader.ReadUnsignedLEB128(0); // file length state.file = -1; break; } case DW_LNE_set_discriminator: { state.discriminator = dataReader.ReadUnsignedLEB128(0); break; } default: WARNING("unsupported extended opcode: %u\n", extendedOpcode); break; } dataReader.Skip(instructionLength - (dataReader.Offset() - instructionOffset)); } if (dataReader.HasOverflow()) return false; if (appendRow) return true; } return false; } void LineNumberProgram::_SetToInitial(State& state) const { state.address = 0; state.file = 1; state.line = 1; state.column = 0; state.isStatement = fDefaultIsStatement; state.isBasicBlock = false; state.isSequenceEnd = false; state.isPrologueEnd = false; state.isEpilogueBegin = false; state.instructionSet = 0; state.discriminator = 0; }