⛏️ index : haiku.git

/*
 * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de.
 * Distributed under the terms of the MIT License.
 */

#include "LineNumberProgram.h"

#include <algorithm>

#include <stdio.h>
#include <string.h>

#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<uint8>(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<uint16>(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<uint8>(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;
}