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