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 */
36static 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 */
73static 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 */
84static 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 */
100static 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
112struct LocalInfo {
113    const char *name;
114    const char *descriptor;
115    const char *signature;
116    u2 startAddress;
117    bool live;
118};
119
120static 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
132static 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
141static 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
312void 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