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