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  * Reads a string index as encoded for the debug info format,
30  * returning a string pointer or NULL as appropriate.
31  */
readStringIdx(const DexFile * pDexFile,const u1 ** pStream)32 static const char* readStringIdx(const DexFile* pDexFile,
33         const u1** pStream) {
34     u4 stringIdx = readUnsignedLeb128(pStream);
35 
36     // Remember, encoded string indicies have 1 added to them.
37     if (stringIdx == 0) {
38         return NULL;
39     } else {
40         return dexStringById(pDexFile, stringIdx - 1);
41     }
42 }
43 
44 /*
45  * Reads a type index as encoded for the debug info format, returning
46  * a string pointer for its descriptor or NULL as appropriate.
47  */
readTypeIdx(const DexFile * pDexFile,const u1 ** pStream)48 static const char* readTypeIdx(const DexFile* pDexFile,
49         const u1** pStream) {
50     u4 typeIdx = readUnsignedLeb128(pStream);
51 
52     // Remember, encoded type indicies have 1 added to them.
53     if (typeIdx == 0) {
54         return NULL;
55     } else {
56         return dexStringByTypeIdx(pDexFile, typeIdx - 1);
57     }
58 }
59 
60 struct LocalInfo {
61     const char *name;
62     const char *descriptor;
63     const char *signature;
64     u2 startAddress;
65     bool live;
66 };
67 
emitLocalCbIfLive(void * cnxt,int reg,u4 endAddress,LocalInfo * localInReg,DexDebugNewLocalCb localCb)68 static void emitLocalCbIfLive(void *cnxt, int reg, u4 endAddress,
69         LocalInfo *localInReg, DexDebugNewLocalCb localCb)
70 {
71     if (localCb != NULL && localInReg[reg].live) {
72         localCb(cnxt, reg, localInReg[reg].startAddress, endAddress,
73                 localInReg[reg].name,
74                 localInReg[reg].descriptor,
75                 localInReg[reg].signature == NULL
76                 ? "" : localInReg[reg].signature );
77     }
78 }
79 
invalidStream(const char * classDescriptor,const DexProto * proto)80 static void invalidStream(const char* classDescriptor, const DexProto* proto) {
81     IF_ALOGE() {
82         char* methodDescriptor = dexProtoCopyMethodDescriptor(proto);
83         ALOGE("Invalid debug info stream. class %s; proto %s",
84                 classDescriptor, methodDescriptor);
85         free(methodDescriptor);
86     }
87 }
88 
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)89 static void dexDecodeDebugInfo0(
90             const DexFile* pDexFile,
91             const DexCode* pCode,
92             const char* classDescriptor,
93             u4 protoIdx,
94             u4 accessFlags,
95             DexDebugNewPositionCb posCb, DexDebugNewLocalCb localCb,
96             void* cnxt,
97             const u1* stream,
98             LocalInfo* localInReg)
99 {
100     DexProto proto = { pDexFile, protoIdx };
101     u4 line = readUnsignedLeb128(&stream);
102     u4 parametersSize = readUnsignedLeb128(&stream);
103     u2 argReg = pCode->registersSize - pCode->insSize;
104     u4 address = 0;
105 
106     if ((accessFlags & ACC_STATIC) == 0) {
107         /*
108          * The code is an instance method, which means that there is
109          * an initial this parameter. Also, the proto list should
110          * contain exactly one fewer argument word than the insSize
111          * indicates.
112          */
113         assert(pCode->insSize == (dexProtoComputeArgsSize(&proto) + 1));
114         localInReg[argReg].name = "this";
115         localInReg[argReg].descriptor = classDescriptor;
116         localInReg[argReg].startAddress = 0;
117         localInReg[argReg].live = true;
118         argReg++;
119     } else {
120         assert(pCode->insSize == dexProtoComputeArgsSize(&proto));
121     }
122 
123     DexParameterIterator iterator;
124     dexParameterIteratorInit(&iterator, &proto);
125 
126     while (parametersSize-- != 0) {
127         const char* descriptor = dexParameterIteratorNextDescriptor(&iterator);
128         const char *name;
129         int reg;
130 
131         if ((argReg >= pCode->registersSize) || (descriptor == NULL)) {
132             invalidStream(classDescriptor, &proto);
133             return;
134         }
135 
136         name = readStringIdx(pDexFile, &stream);
137         reg = argReg;
138 
139         switch (descriptor[0]) {
140             case 'D':
141             case 'J':
142                 argReg += 2;
143                 break;
144             default:
145                 argReg += 1;
146                 break;
147         }
148 
149         if (name != NULL) {
150             localInReg[reg].name = name;
151             localInReg[reg].descriptor = descriptor;
152             localInReg[reg].signature = NULL;
153             localInReg[reg].startAddress = address;
154             localInReg[reg].live = true;
155         }
156     }
157 
158     for (;;)  {
159         u1 opcode = *stream++;
160         u2 reg;
161 
162         switch (opcode) {
163             case DBG_END_SEQUENCE:
164                 return;
165 
166             case DBG_ADVANCE_PC:
167                 address += readUnsignedLeb128(&stream);
168                 break;
169 
170             case DBG_ADVANCE_LINE:
171                 line += readSignedLeb128(&stream);
172                 break;
173 
174             case DBG_START_LOCAL:
175             case DBG_START_LOCAL_EXTENDED:
176                 reg = readUnsignedLeb128(&stream);
177                 if (reg > pCode->registersSize) {
178                     invalidStream(classDescriptor, &proto);
179                     return;
180                 }
181 
182                 // Emit what was previously there, if anything
183                 emitLocalCbIfLive(cnxt, reg, address,
184                     localInReg, localCb);
185 
186                 localInReg[reg].name = readStringIdx(pDexFile, &stream);
187                 localInReg[reg].descriptor = readTypeIdx(pDexFile, &stream);
188                 if (opcode == DBG_START_LOCAL_EXTENDED) {
189                     localInReg[reg].signature
190                         = readStringIdx(pDexFile, &stream);
191                 } else {
192                     localInReg[reg].signature = NULL;
193                 }
194                 localInReg[reg].startAddress = address;
195                 localInReg[reg].live = true;
196                 break;
197 
198             case DBG_END_LOCAL:
199                 reg = readUnsignedLeb128(&stream);
200                 if (reg > pCode->registersSize) {
201                     invalidStream(classDescriptor, &proto);
202                     return;
203                 }
204 
205                 emitLocalCbIfLive (cnxt, reg, address, localInReg, localCb);
206                 localInReg[reg].live = false;
207                 break;
208 
209             case DBG_RESTART_LOCAL:
210                 reg = readUnsignedLeb128(&stream);
211                 if (reg > pCode->registersSize) {
212                     invalidStream(classDescriptor, &proto);
213                     return;
214                 }
215 
216                 if (localInReg[reg].name == NULL
217                         || localInReg[reg].descriptor == NULL) {
218                     invalidStream(classDescriptor, &proto);
219                     return;
220                 }
221 
222                 /*
223                  * If the register is live, the "restart" is superfluous,
224                  * and we don't want to mess with the existing start address.
225                  */
226                 if (!localInReg[reg].live) {
227                     localInReg[reg].startAddress = address;
228                     localInReg[reg].live = true;
229                 }
230                 break;
231 
232             case DBG_SET_PROLOGUE_END:
233             case DBG_SET_EPILOGUE_BEGIN:
234             case DBG_SET_FILE:
235                 break;
236 
237             default: {
238                 int adjopcode = opcode - DBG_FIRST_SPECIAL;
239 
240                 address += adjopcode / DBG_LINE_RANGE;
241                 line += DBG_LINE_BASE + (adjopcode % DBG_LINE_RANGE);
242 
243                 if (posCb != NULL) {
244                     int done;
245                     done = posCb(cnxt, address, line);
246 
247                     if (done) {
248                         // early exit
249                         return;
250                     }
251                 }
252                 break;
253             }
254         }
255     }
256 }
257 
258 // 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)259 void dexDecodeDebugInfo(
260             const DexFile* pDexFile,
261             const DexCode* pCode,
262             const char* classDescriptor,
263             u4 protoIdx,
264             u4 accessFlags,
265             DexDebugNewPositionCb posCb, DexDebugNewLocalCb localCb,
266             void* cnxt)
267 {
268     const u1* stream = dexGetDebugInfoStream(pDexFile, pCode);
269     LocalInfo localInReg[pCode->registersSize];
270 
271     memset(localInReg, 0, sizeof(LocalInfo) * pCode->registersSize);
272 
273     if (stream != NULL) {
274         dexDecodeDebugInfo0(pDexFile, pCode, classDescriptor, protoIdx, accessFlags,
275             posCb, localCb, cnxt, stream, localInReg);
276     }
277 
278     for (int reg = 0; reg < pCode->registersSize; reg++) {
279         emitLocalCbIfLive(cnxt, reg, pCode->insnsSize, localInReg, localCb);
280     }
281 }
282