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
17 static const uint8 kLineNumberStandardOpcodeOperands[]
18 = { 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1 };
19 static const uint32 kLineNumberStandardOpcodeCount = 12;
20
21
LineNumberProgram(uint8 addressSize,bool isBigEndian)22 LineNumberProgram::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
~LineNumberProgram()38 LineNumberProgram::~LineNumberProgram()
39 {
40 }
41
42
43 status_t
Init(const void * program,size_t programSize,uint8 minInstructionLength,bool defaultIsStatement,int8 lineBase,uint8 lineRange,uint8 opcodeBase,const uint8 * standardOpcodeLengths)44 LineNumberProgram::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
72 void
GetInitialState(State & state) const73 LineNumberProgram::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
83 bool
GetNextRow(State & state) const84 LineNumberProgram::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
207 void
_SetToInitial(State & state) const208 LineNumberProgram::_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