1/*
2 * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Distributed under the terms of the MIT License.
4 */
5
6#include "LineNumberProgram.h"
7
8#include <algorithm>
9
10#include <stdio.h>
11#include <string.h>
12
13#include "Dwarf.h"
14#include "Tracing.h"
15
16
17static const uint8 kLineNumberStandardOpcodeOperands[]
18	= { 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1 };
19static const uint32 kLineNumberStandardOpcodeCount = 12;
20
21
22LineNumberProgram::LineNumberProgram(uint8 addressSize, bool isBigEndian)
23	:
24	fProgram(NULL),
25	fProgramSize(0),
26	fMinInstructionLength(0),
27	fDefaultIsStatement(0),
28	fLineBase(0),
29	fLineRange(0),
30	fOpcodeBase(0),
31	fAddressSize(addressSize),
32	fIsBigEndian(isBigEndian),
33	fStandardOpcodeLengths(NULL)
34{
35}
36
37
38LineNumberProgram::~LineNumberProgram()
39{
40}
41
42
43status_t
44LineNumberProgram::Init(const void* program, size_t programSize,
45	uint8 minInstructionLength, bool defaultIsStatement, int8 lineBase,
46	uint8 lineRange, uint8 opcodeBase, const uint8* standardOpcodeLengths)
47{
48	// first check the operand counts for the standard opcodes
49	uint8 standardOpcodeCount = std::min((uint32)opcodeBase - 1,
50		kLineNumberStandardOpcodeCount);
51	for (uint8 i = 0; i < standardOpcodeCount; i++) {
52		if (standardOpcodeLengths[i] != kLineNumberStandardOpcodeOperands[i]) {
53			WARNING("operand count for standard opcode %u does not what we "
54				"expect\n", i + 1);
55			return B_BAD_DATA;
56		}
57	}
58
59	fProgram = program;
60	fProgramSize = programSize;
61	fMinInstructionLength = minInstructionLength;
62	fDefaultIsStatement = defaultIsStatement;
63	fLineBase = lineBase;
64	fLineRange = lineRange;
65	fOpcodeBase = opcodeBase;
66	fStandardOpcodeLengths = standardOpcodeLengths;
67
68	return B_OK;
69}
70
71
72void
73LineNumberProgram::GetInitialState(State& state) const
74{
75	if (!IsValid())
76		return;
77
78	_SetToInitial(state);
79	state.dataReader.SetTo(fProgram, fProgramSize, fAddressSize, fIsBigEndian);
80}
81
82
83bool
84LineNumberProgram::GetNextRow(State& state) const
85{
86	if (state.isSequenceEnd)
87		_SetToInitial(state);
88
89	DataReader& dataReader = state.dataReader;
90
91	while (dataReader.BytesRemaining() > 0) {
92		bool appendRow = false;
93		uint8 opcode = dataReader.Read<uint8>(0);
94		if (opcode >= fOpcodeBase) {
95			// special opcode
96			uint adjustedOpcode = opcode - fOpcodeBase;
97			state.address += (adjustedOpcode / fLineRange)
98				* fMinInstructionLength;
99			state.line += adjustedOpcode % fLineRange + fLineBase;
100			state.isBasicBlock = false;
101			state.isPrologueEnd = false;
102			state.isEpilogueBegin = false;
103			state.discriminator = 0;
104			appendRow = true;
105		} else if (opcode > 0) {
106			// standard opcode
107			switch (opcode) {
108				case DW_LNS_copy:
109					state.isBasicBlock = false;
110					state.isPrologueEnd = false;
111					state.isEpilogueBegin = false;
112					appendRow = true;
113					state.discriminator = 0;
114					break;
115				case DW_LNS_advance_pc:
116					state.address += dataReader.ReadUnsignedLEB128(0)
117						* fMinInstructionLength;
118					break;
119				case DW_LNS_advance_line:
120					state.line += dataReader.ReadSignedLEB128(0);
121					break;
122				case DW_LNS_set_file:
123					state.file = dataReader.ReadUnsignedLEB128(0);
124					break;
125				case DW_LNS_set_column:
126					state.column = dataReader.ReadUnsignedLEB128(0);
127					break;
128				case DW_LNS_negate_stmt:
129					state.isStatement = !state.isStatement;
130					break;
131				case DW_LNS_set_basic_block:
132					state.isBasicBlock = true;
133					break;
134				case DW_LNS_const_add_pc:
135					state.address += ((255 - fOpcodeBase) / fLineRange)
136						* fMinInstructionLength;
137					break;
138				case DW_LNS_fixed_advance_pc:
139					state.address += dataReader.Read<uint16>(0);
140					break;
141				case DW_LNS_set_prologue_end:
142					state.isPrologueEnd = true;
143					break;
144				case DW_LNS_set_epilogue_begin:
145					state.isEpilogueBegin = true;
146					break;
147				case DW_LNS_set_isa:
148					state.instructionSet = dataReader.ReadUnsignedLEB128(0);
149					break;
150				default:
151					WARNING("unsupported standard opcode %u\n", opcode);
152					for (int32 i = 0; i < fStandardOpcodeLengths[opcode - 1];
153							i++) {
154						dataReader.ReadUnsignedLEB128(0);
155					}
156			}
157		} else {
158			// extended opcode
159			uint32 instructionLength = dataReader.ReadUnsignedLEB128(0);
160			off_t instructionOffset = dataReader.Offset();
161			uint8 extendedOpcode = dataReader.Read<uint8>(0);
162
163			switch (extendedOpcode) {
164				case DW_LNE_end_sequence:
165					state.isSequenceEnd = true;
166					appendRow = true;
167					break;
168				case DW_LNE_set_address:
169					state.address = dataReader.ReadAddress(0);
170					break;
171				case DW_LNE_define_file:
172				{
173					state.explicitFile = dataReader.ReadString();
174					state.explicitFileDirIndex
175						= dataReader.ReadUnsignedLEB128(0);
176					dataReader.ReadUnsignedLEB128(0);	// modification time
177					dataReader.ReadUnsignedLEB128(0);	// file length
178					state.file = -1;
179					break;
180				}
181				case DW_LNE_set_discriminator:
182				{
183					state.discriminator = dataReader.ReadUnsignedLEB128(0);
184					break;
185				}
186				default:
187					WARNING("unsupported extended opcode: %u\n",
188						extendedOpcode);
189					break;
190			}
191
192			dataReader.Skip(instructionLength
193				- (dataReader.Offset() - instructionOffset));
194		}
195
196		if (dataReader.HasOverflow())
197			return false;
198
199		if (appendRow)
200			return true;
201	}
202
203	return false;
204}
205
206
207void
208LineNumberProgram::_SetToInitial(State& state) const
209{
210	state.address = 0;
211	state.file = 1;
212	state.line = 1;
213	state.column = 0;
214	state.isStatement = fDefaultIsStatement;
215	state.isBasicBlock = false;
216	state.isSequenceEnd = false;
217	state.isPrologueEnd = false;
218	state.isEpilogueBegin = false;
219	state.instructionSet = 0;
220	state.discriminator = 0;
221}
222