1 /*
2 * Copyright (C) 2011 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 /*
18 * Handling of method debug info in a .dex file.
19 */
20
21 #include "DexDebugInfo.h"
22 #include "DexProto.h"
23 #include "Leb128.h"
24
25 #include <stdlib.h>
26 #include <string.h>
27
28 /*
29 * Decode the arguments in a method signature, which looks something
30 * like "(ID[Ljava/lang/String;)V".
31 *
32 * Returns the type signature letter for the next argument, or ')' if
33 * there are no more args. Advances "pSig" to point to the character
34 * after the one returned.
35 */
decodeSignature(const char ** pSig)36 static char decodeSignature(const char** pSig)
37 {
38 const char* sig = *pSig;
39
40 if (*sig == '(')
41 sig++;
42
43 if (*sig == 'L') {
44 /* object ref */
45 while (*++sig != ';')
46 ;
47 *pSig = sig+1;
48 return 'L';
49 }
50 if (*sig == '[') {
51 /* array; advance past array type */
52 while (*++sig == '[')
53 ;
54 if (*sig == 'L') {
55 while (*++sig != ';')
56 ;
57 }
58 *pSig = sig+1;
59 return '[';
60 }
61 if (*sig == '\0')
62 return *sig; /* don't advance further */
63
64 *pSig = sig+1;
65 return *sig;
66 }
67
68 /*
69 * returns the length of a type string, given the start of the
70 * type string. Used for the case where the debug info format
71 * references types that are inside a method type signature.
72 */
typeLength(const char * type)73 static int typeLength(const char *type) {
74 // Assumes any leading '(' has already been gobbled
75 const char *end = type;
76 decodeSignature(&end);
77 return end - type;
78 }
79
80 /*
81 * Reads a string index as encoded for the debug info format,
82 * returning a string pointer or NULL as appropriate.
83 */
readStringIdx(const DexFile * pDexFile,const u1 ** pStream)84 static const char* readStringIdx(const DexFile* pDexFile,
85 const u1** pStream) {
86 u4 stringIdx = readUnsignedLeb128(pStream);
87
88 // Remember, encoded string indicies have 1 added to them.
89 if (stringIdx == 0) {
90 return NULL;
91 } else {
92 return dexStringById(pDexFile, stringIdx - 1);
93 }
94 }
95
96 /*
97 * Reads a type index as encoded for the debug info format, returning
98 * a string pointer for its descriptor or NULL as appropriate.
99 */
readTypeIdx(const DexFile * pDexFile,const u1 ** pStream)100 static const char* readTypeIdx(const DexFile* pDexFile,
101 const u1** pStream) {
102 u4 typeIdx = readUnsignedLeb128(pStream);
103
104 // Remember, encoded type indicies have 1 added to them.
105 if (typeIdx == 0) {
106 return NULL;
107 } else {
108 return dexStringByTypeIdx(pDexFile, typeIdx - 1);
109 }
110 }
111
112 struct LocalInfo {
113 const char *name;
114 const char *descriptor;
115 const char *signature;
116 u2 startAddress;
117 bool live;
118 };
119
emitLocalCbIfLive(void * cnxt,int reg,u4 endAddress,LocalInfo * localInReg,DexDebugNewLocalCb localCb)120 static void emitLocalCbIfLive(void *cnxt, int reg, u4 endAddress,
121 LocalInfo *localInReg, DexDebugNewLocalCb localCb)
122 {
123 if (localCb != NULL && localInReg[reg].live) {
124 localCb(cnxt, reg, localInReg[reg].startAddress, endAddress,
125 localInReg[reg].name,
126 localInReg[reg].descriptor,
127 localInReg[reg].signature == NULL
128 ? "" : localInReg[reg].signature );
129 }
130 }
131
invalidStream(const char * classDescriptor,const DexProto * proto)132 static void invalidStream(const char* classDescriptor, const DexProto* proto) {
133 IF_ALOGE() {
134 char* methodDescriptor = dexProtoCopyMethodDescriptor(proto);
135 ALOGE("Invalid debug info stream. class %s; proto %s",
136 classDescriptor, methodDescriptor);
137 free(methodDescriptor);
138 }
139 }
140
dexDecodeDebugInfo0(const DexFile * pDexFile,const DexCode * pCode,const char * classDescriptor,u4 protoIdx,u4 accessFlags,DexDebugNewPositionCb posCb,DexDebugNewLocalCb localCb,void * cnxt,const u1 * stream,LocalInfo * localInReg)141 static void dexDecodeDebugInfo0(
142 const DexFile* pDexFile,
143 const DexCode* pCode,
144 const char* classDescriptor,
145 u4 protoIdx,
146 u4 accessFlags,
147 DexDebugNewPositionCb posCb, DexDebugNewLocalCb localCb,
148 void* cnxt,
149 const u1* stream,
150 LocalInfo* localInReg)
151 {
152 DexProto proto = { pDexFile, protoIdx };
153 u4 insnsSize = pCode->insnsSize;
154 u4 line = readUnsignedLeb128(&stream);
155 u4 parametersSize = readUnsignedLeb128(&stream);
156 u2 argReg = pCode->registersSize - pCode->insSize;
157 u4 address = 0;
158
159 if ((accessFlags & ACC_STATIC) == 0) {
160 /*
161 * The code is an instance method, which means that there is
162 * an initial this parameter. Also, the proto list should
163 * contain exactly one fewer argument word than the insSize
164 * indicates.
165 */
166 assert(pCode->insSize == (dexProtoComputeArgsSize(&proto) + 1));
167 localInReg[argReg].name = "this";
168 localInReg[argReg].descriptor = classDescriptor;
169 localInReg[argReg].startAddress = 0;
170 localInReg[argReg].live = true;
171 argReg++;
172 } else {
173 assert(pCode->insSize == dexProtoComputeArgsSize(&proto));
174 }
175
176 DexParameterIterator iterator;
177 dexParameterIteratorInit(&iterator, &proto);
178
179 while (parametersSize-- != 0) {
180 const char* descriptor = dexParameterIteratorNextDescriptor(&iterator);
181 const char *name;
182 int reg;
183
184 if ((argReg >= pCode->registersSize) || (descriptor == NULL)) {
185 invalidStream(classDescriptor, &proto);
186 return;
187 }
188
189 name = readStringIdx(pDexFile, &stream);
190 reg = argReg;
191
192 switch (descriptor[0]) {
193 case 'D':
194 case 'J':
195 argReg += 2;
196 break;
197 default:
198 argReg += 1;
199 break;
200 }
201
202 if (name != NULL) {
203 localInReg[reg].name = name;
204 localInReg[reg].descriptor = descriptor;
205 localInReg[reg].signature = NULL;
206 localInReg[reg].startAddress = address;
207 localInReg[reg].live = true;
208 }
209 }
210
211 for (;;) {
212 u1 opcode = *stream++;
213 u2 reg;
214
215 switch (opcode) {
216 case DBG_END_SEQUENCE:
217 return;
218
219 case DBG_ADVANCE_PC:
220 address += readUnsignedLeb128(&stream);
221 break;
222
223 case DBG_ADVANCE_LINE:
224 line += readSignedLeb128(&stream);
225 break;
226
227 case DBG_START_LOCAL:
228 case DBG_START_LOCAL_EXTENDED:
229 reg = readUnsignedLeb128(&stream);
230 if (reg > pCode->registersSize) {
231 invalidStream(classDescriptor, &proto);
232 return;
233 }
234
235 // Emit what was previously there, if anything
236 emitLocalCbIfLive(cnxt, reg, address,
237 localInReg, localCb);
238
239 localInReg[reg].name = readStringIdx(pDexFile, &stream);
240 localInReg[reg].descriptor = readTypeIdx(pDexFile, &stream);
241 if (opcode == DBG_START_LOCAL_EXTENDED) {
242 localInReg[reg].signature
243 = readStringIdx(pDexFile, &stream);
244 } else {
245 localInReg[reg].signature = NULL;
246 }
247 localInReg[reg].startAddress = address;
248 localInReg[reg].live = true;
249 break;
250
251 case DBG_END_LOCAL:
252 reg = readUnsignedLeb128(&stream);
253 if (reg > pCode->registersSize) {
254 invalidStream(classDescriptor, &proto);
255 return;
256 }
257
258 emitLocalCbIfLive (cnxt, reg, address, localInReg, localCb);
259 localInReg[reg].live = false;
260 break;
261
262 case DBG_RESTART_LOCAL:
263 reg = readUnsignedLeb128(&stream);
264 if (reg > pCode->registersSize) {
265 invalidStream(classDescriptor, &proto);
266 return;
267 }
268
269 if (localInReg[reg].name == NULL
270 || localInReg[reg].descriptor == NULL) {
271 invalidStream(classDescriptor, &proto);
272 return;
273 }
274
275 /*
276 * If the register is live, the "restart" is superfluous,
277 * and we don't want to mess with the existing start address.
278 */
279 if (!localInReg[reg].live) {
280 localInReg[reg].startAddress = address;
281 localInReg[reg].live = true;
282 }
283 break;
284
285 case DBG_SET_PROLOGUE_END:
286 case DBG_SET_EPILOGUE_BEGIN:
287 case DBG_SET_FILE:
288 break;
289
290 default: {
291 int adjopcode = opcode - DBG_FIRST_SPECIAL;
292
293 address += adjopcode / DBG_LINE_RANGE;
294 line += DBG_LINE_BASE + (adjopcode % DBG_LINE_RANGE);
295
296 if (posCb != NULL) {
297 int done;
298 done = posCb(cnxt, address, line);
299
300 if (done) {
301 // early exit
302 return;
303 }
304 }
305 break;
306 }
307 }
308 }
309 }
310
311 // TODO optimize localCb == NULL case
dexDecodeDebugInfo(const DexFile * pDexFile,const DexCode * pCode,const char * classDescriptor,u4 protoIdx,u4 accessFlags,DexDebugNewPositionCb posCb,DexDebugNewLocalCb localCb,void * cnxt)312 void dexDecodeDebugInfo(
313 const DexFile* pDexFile,
314 const DexCode* pCode,
315 const char* classDescriptor,
316 u4 protoIdx,
317 u4 accessFlags,
318 DexDebugNewPositionCb posCb, DexDebugNewLocalCb localCb,
319 void* cnxt)
320 {
321 const u1* stream = dexGetDebugInfoStream(pDexFile, pCode);
322 LocalInfo localInReg[pCode->registersSize];
323
324 memset(localInReg, 0, sizeof(LocalInfo) * pCode->registersSize);
325
326 if (stream != NULL) {
327 dexDecodeDebugInfo0(pDexFile, pCode, classDescriptor, protoIdx, accessFlags,
328 posCb, localCb, cnxt, stream, localInReg);
329 }
330
331 for (int reg = 0; reg < pCode->registersSize; reg++) {
332 emitLocalCbIfLive(cnxt, reg, pCode->insnsSize, localInReg, localCb);
333 }
334 }
335