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