1/*
2 * Copyright 2013, Google Inc.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met:
8 *
9 *     * Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 *     * Redistributions in binary form must reproduce the above
12 * copyright notice, this list of conditions and the following disclaimer
13 * in the documentation and/or other materials provided with the
14 * distribution.
15 *     * Neither the name of Google Inc. nor the names of its
16 * contributors may be used to endorse or promote products derived from
17 * this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 */
31
32package org.jf.dexlib2.dexbacked.raw;
33
34import com.google.common.base.Joiner;
35import com.google.common.collect.Lists;
36import org.jf.dexlib2.VerificationError;
37import org.jf.dexlib2.dexbacked.DexReader;
38import org.jf.dexlib2.dexbacked.instruction.DexBackedInstruction;
39import org.jf.dexlib2.dexbacked.raw.util.DexAnnotator;
40import org.jf.dexlib2.iface.instruction.*;
41import org.jf.dexlib2.iface.instruction.formats.*;
42import org.jf.dexlib2.util.AnnotatedBytes;
43import org.jf.dexlib2.util.ReferenceUtil;
44import org.jf.util.ExceptionWithContext;
45import org.jf.util.NumberUtils;
46
47import javax.annotation.Nonnull;
48import javax.annotation.Nullable;
49import java.util.List;
50
51public class CodeItem {
52    public static final int REGISTERS_OFFSET = 0;
53    public static final int INS_OFFSET = 2;
54    public static final int OUTS_OFFSET = 4;
55    public static final int TRIES_SIZE_OFFSET = 6;
56    public static final int DEBUG_INFO_OFFSET = 8;
57    public static final int INSTRUCTION_COUNT_OFFSET = 12;
58    public static final int INSTRUCTION_START_OFFSET = 16;
59
60    public static class TryItem {
61        public static final int ITEM_SIZE = 8;
62
63        public static final int START_ADDRESS_OFFSET = 0;
64        public static final int CODE_UNIT_COUNT_OFFSET = 4;
65        public static final int HANDLER_OFFSET = 6;
66    }
67
68    @Nonnull
69    public static SectionAnnotator makeAnnotator(@Nonnull DexAnnotator annotator, @Nonnull MapItem mapItem) {
70        return new SectionAnnotator(annotator, mapItem) {
71            private SectionAnnotator debugInfoAnnotator = null;
72
73            @Override public void annotateSection(@Nonnull AnnotatedBytes out) {
74                debugInfoAnnotator = annotator.getAnnotator(ItemType.DEBUG_INFO_ITEM);
75                super.annotateSection(out);
76            }
77
78            @Nonnull @Override public String getItemName() {
79                return "code_item";
80            }
81
82            @Override public int getItemAlignment() {
83                return 4;
84            }
85
86            @Override
87            public void annotateItem(@Nonnull AnnotatedBytes out, int itemIndex, @Nullable String itemIdentity) {
88                try {
89                    DexReader reader = dexFile.readerAt(out.getCursor());
90
91                    int registers = reader.readUshort();
92                    out.annotate(2, "registers_size = %d", registers);
93
94                    int inSize = reader.readUshort();
95                    out.annotate(2, "ins_size = %d", inSize);
96
97                    int outSize = reader.readUshort();
98                    out.annotate(2, "outs_size = %d", outSize);
99
100                    int triesCount = reader.readUshort();
101                    out.annotate(2, "tries_size = %d", triesCount);
102
103                    int debugInfoOffset = reader.readSmallUint();
104                    out.annotate(4, "debug_info_off = 0x%x", debugInfoOffset);
105
106                    if (debugInfoOffset != 0) {
107                        addDebugInfoIdentity(debugInfoOffset, itemIdentity);
108                    }
109
110                    int instructionSize = reader.readSmallUint();
111                    out.annotate(4, "insns_size = 0x%x", instructionSize);
112
113                    out.annotate(0, "instructions:");
114                    out.indent();
115
116                    out.setLimit(out.getCursor(), out.getCursor() + instructionSize * 2);
117
118                    int end = reader.getOffset() + instructionSize*2;
119                    try {
120                        while (reader.getOffset() < end) {
121                            Instruction instruction = DexBackedInstruction.readFrom(reader);
122
123                            // if we read past the end of the instruction list
124                            if (reader.getOffset() > end) {
125                                out.annotateTo(end, "truncated instruction");
126                                reader.setOffset(end);
127                            } else {
128                                switch (instruction.getOpcode().format) {
129                                    case Format10x:
130                                        annotateInstruction10x(out, instruction);
131                                        break;
132                                    case Format35c:
133                                        annotateInstruction35c(out, (Instruction35c)instruction);
134                                        break;
135                                    case Format3rc:
136                                        annotateInstruction3rc(out, (Instruction3rc)instruction);
137                                        break;
138                                    case ArrayPayload:
139                                        annotateArrayPayload(out, (ArrayPayload)instruction);
140                                        break;
141                                    case PackedSwitchPayload:
142                                        annotatePackedSwitchPayload(out, (PackedSwitchPayload)instruction);
143                                        break;
144                                    case SparseSwitchPayload:
145                                        annotateSparseSwitchPayload(out, (SparseSwitchPayload)instruction);
146                                        break;
147                                    default:
148                                        annotateDefaultInstruction(out, instruction);
149                                        break;
150                                }
151                            }
152
153                            assert reader.getOffset() == out.getCursor();
154                        }
155                    } catch (ExceptionWithContext ex) {
156                        ex.printStackTrace(System.err);
157                        out.annotate(0, "annotation error: %s", ex.getMessage());
158                        out.moveTo(end);
159                        reader.setOffset(end);
160                    } finally {
161                        out.clearLimit();
162                        out.deindent();
163                    }
164
165                    if (triesCount > 0) {
166                        if ((reader.getOffset() % 4) != 0) {
167                            reader.readUshort();
168                            out.annotate(2, "padding");
169                        }
170
171                        out.annotate(0, "try_items:");
172                        out.indent();
173                        try {
174                            for (int i=0; i<triesCount; i++) {
175                                out.annotate(0, "try_item[%d]:", i);
176                                out.indent();
177                                try {
178                                    int startAddr = reader.readSmallUint();
179                                    out.annotate(4, "start_addr = 0x%x", startAddr);
180
181                                    int instructionCount = reader.readUshort();
182                                    out.annotate(2, "insn_count = 0x%x", instructionCount);
183
184                                    int handlerOffset = reader.readUshort();
185                                    out.annotate(2, "handler_off = 0x%x", handlerOffset);
186                                } finally {
187                                    out.deindent();
188                                }
189                            }
190                        } finally {
191                            out.deindent();
192                        }
193
194                        int handlerListCount = reader.readSmallUleb128();
195                        out.annotate(0, "encoded_catch_handler_list:");
196                        out.annotateTo(reader.getOffset(), "size = %d", handlerListCount);
197                        out.indent();
198                        try {
199                            for (int i=0; i<handlerListCount; i++) {
200                                out.annotate(0, "encoded_catch_handler[%d]", i);
201                                out.indent();
202                                try {
203                                    int handlerCount = reader.readSleb128();
204                                    out.annotateTo(reader.getOffset(), "size = %d", handlerCount);
205                                    boolean hasCatchAll = handlerCount <= 0;
206                                    handlerCount = Math.abs(handlerCount);
207                                    if (handlerCount != 0) {
208                                        out.annotate(0, "handlers:");
209                                        out.indent();
210                                        try {
211                                            for (int j=0; j<handlerCount; j++) {
212                                                out.annotate(0, "encoded_type_addr_pair[%d]", i);
213                                                out.indent();
214                                                try {
215                                                    int typeIndex = reader.readSmallUleb128();
216                                                    out.annotateTo(reader.getOffset(), TypeIdItem.getReferenceAnnotation(dexFile, typeIndex));
217
218                                                    int handlerAddress = reader.readSmallUleb128();
219                                                    out.annotateTo(reader.getOffset(), "addr = 0x%x", handlerAddress);
220                                                } finally {
221                                                    out.deindent();
222                                                }
223                                            }
224                                        } finally {
225                                            out.deindent();
226                                        }
227                                    }
228                                    if (hasCatchAll) {
229                                        int catchAllAddress = reader.readSmallUleb128();
230                                        out.annotateTo(reader.getOffset(), "catch_all_addr = 0x%x", catchAllAddress);
231                                    }
232                                } finally {
233                                    out.deindent();
234                                }
235                            }
236                        } finally {
237                            out.deindent();
238                        }
239                    }
240                } catch (ExceptionWithContext ex) {
241                    out.annotate(0, "annotation error: %s", ex.getMessage());
242                }
243            }
244
245            private String formatRegister(int registerNum) {
246                return String.format("v%d", registerNum);
247            }
248
249            private void annotateInstruction10x(@Nonnull AnnotatedBytes out, @Nonnull Instruction instruction) {
250                out.annotate(2, instruction.getOpcode().name);
251            }
252
253            private void annotateInstruction35c(@Nonnull AnnotatedBytes out, @Nonnull Instruction35c instruction) {
254                List<String> args = Lists.newArrayList();
255
256                int registerCount = instruction.getRegisterCount();
257                if (registerCount == 1) {
258                    args.add(formatRegister(instruction.getRegisterC()));
259                } else if (registerCount == 2) {
260                    args.add(formatRegister(instruction.getRegisterC()));
261                    args.add(formatRegister(instruction.getRegisterD()));
262                } else if (registerCount == 3) {
263                    args.add(formatRegister(instruction.getRegisterC()));
264                    args.add(formatRegister(instruction.getRegisterD()));
265                    args.add(formatRegister(instruction.getRegisterE()));
266                } else if (registerCount == 4) {
267                    args.add(formatRegister(instruction.getRegisterC()));
268                    args.add(formatRegister(instruction.getRegisterD()));
269                    args.add(formatRegister(instruction.getRegisterE()));
270                    args.add(formatRegister(instruction.getRegisterF()));
271                } else if (registerCount == 5) {
272                    args.add(formatRegister(instruction.getRegisterC()));
273                    args.add(formatRegister(instruction.getRegisterD()));
274                    args.add(formatRegister(instruction.getRegisterE()));
275                    args.add(formatRegister(instruction.getRegisterF()));
276                    args.add(formatRegister(instruction.getRegisterG()));
277                }
278
279                String reference = ReferenceUtil.getReferenceString(instruction.getReference());
280
281                out.annotate(6, String.format("%s {%s}, %s",
282                        instruction.getOpcode().name, Joiner.on(", ").join(args), reference));
283            }
284
285            private void annotateInstruction3rc(@Nonnull AnnotatedBytes out, @Nonnull Instruction3rc instruction) {
286                int startRegister = instruction.getStartRegister();
287                int endRegister = startRegister + instruction.getRegisterCount() - 1;
288                String reference = ReferenceUtil.getReferenceString(instruction.getReference());
289                out.annotate(6, String.format("%s {%s .. %s}, %s",
290                        instruction.getOpcode().name, formatRegister(startRegister), formatRegister(endRegister),
291                        reference));
292            }
293
294            private void annotateDefaultInstruction(@Nonnull AnnotatedBytes out, @Nonnull Instruction instruction) {
295                List<String> args = Lists.newArrayList();
296
297                if (instruction instanceof OneRegisterInstruction) {
298                    args.add(formatRegister(((OneRegisterInstruction)instruction).getRegisterA()));
299                    if (instruction instanceof TwoRegisterInstruction) {
300                        args.add(formatRegister(((TwoRegisterInstruction)instruction).getRegisterB()));
301                        if (instruction instanceof ThreeRegisterInstruction) {
302                            args.add(formatRegister(((ThreeRegisterInstruction)instruction).getRegisterC()));
303                        }
304                    }
305                }  else if (instruction instanceof VerificationErrorInstruction) {
306                    String verificationError = VerificationError.getVerificationErrorName(
307                        ((VerificationErrorInstruction) instruction).getVerificationError());
308                    if (verificationError != null) {
309                        args.add(verificationError);
310                    } else {
311                        args.add("invalid verification error type");
312                    }
313                }
314
315                if (instruction instanceof ReferenceInstruction) {
316                    args.add(ReferenceUtil.getReferenceString(((ReferenceInstruction)instruction).getReference()));
317                } else if (instruction instanceof OffsetInstruction) {
318                    int offset = ((OffsetInstruction)instruction).getCodeOffset();
319                    String sign = offset>=0?"+":"-";
320                    args.add(String.format("%s0x%x", sign, Math.abs(offset)));
321                } else if (instruction instanceof NarrowLiteralInstruction) {
322                    int value = ((NarrowLiteralInstruction)instruction).getNarrowLiteral();
323                    if (NumberUtils.isLikelyFloat(value)) {
324                        args.add(String.format("%d # %f", value, Float.intBitsToFloat(value)));
325                    } else {
326                        args.add(String.format("%d", value));
327                    }
328                } else if (instruction instanceof WideLiteralInstruction) {
329                    long value = ((WideLiteralInstruction)instruction).getWideLiteral();
330                    if (NumberUtils.isLikelyDouble(value)) {
331                        args.add(String.format("%d # %f", value, Double.longBitsToDouble(value)));
332                    } else {
333                        args.add(String.format("%d", value));
334                    }
335                } else if (instruction instanceof FieldOffsetInstruction) {
336                    int fieldOffset = ((FieldOffsetInstruction)instruction).getFieldOffset();
337                    args.add(String.format("field@0x%x", fieldOffset));
338                } else if (instruction instanceof VtableIndexInstruction) {
339                    int vtableIndex = ((VtableIndexInstruction)instruction).getVtableIndex();
340                    args.add(String.format("vtable@%d", vtableIndex));
341                } else if (instruction instanceof InlineIndexInstruction) {
342                    int inlineIndex = ((InlineIndexInstruction)instruction).getInlineIndex();
343                    args.add(String.format("inline@%d", inlineIndex));
344                }
345
346                out.annotate(instruction.getCodeUnits()*2, "%s %s",
347                        instruction.getOpcode().name, Joiner.on(", ").join(args));
348            }
349
350            private void annotateArrayPayload(@Nonnull AnnotatedBytes out, @Nonnull ArrayPayload instruction) {
351                List<Number> elements = instruction.getArrayElements();
352                int elementWidth = instruction.getElementWidth();
353
354                out.annotate(2, instruction.getOpcode().name);
355                out.indent();
356                out.annotate(2, "element_width = %d", elementWidth);
357                out.annotate(4, "size = %d", elements.size());
358                out.annotate(0, "elements:");
359                out.indent();
360                for (int i=0; i<elements.size(); i++) {
361                    if (elementWidth == 8) {
362                        long value = elements.get(i).longValue();
363                        if (NumberUtils.isLikelyDouble(value)) {
364                            out.annotate(elementWidth, "element[%d] = %d # %f", i, value, Double.longBitsToDouble(value));
365                        } else {
366                            out.annotate(elementWidth, "element[%d] = %d", i, value);
367                        }
368                    } else {
369                        int value = elements.get(i).intValue();
370                        if (NumberUtils.isLikelyFloat(value)) {
371                            out.annotate(elementWidth, "element[%d] = %d # %f", i, value, Float.intBitsToFloat(value));
372                        } else {
373                            out.annotate(elementWidth, "element[%d] = %d", i, value);
374                        }
375                    }
376                }
377                if (out.getCursor() % 2 != 0) {
378                    out.annotate(1, "padding");
379                }
380                out.deindent();
381                out.deindent();
382            }
383
384            private void annotatePackedSwitchPayload(@Nonnull AnnotatedBytes out,
385                                                     @Nonnull PackedSwitchPayload instruction) {
386                List<? extends SwitchElement> elements = instruction.getSwitchElements();
387
388                out.annotate(2, instruction.getOpcode().name);
389                out.indent();
390
391                out.annotate(2, "size = %d", elements.size());
392                if (elements.size() == 0) {
393                    out.annotate(4, "first_key");
394                } else {
395                    out.annotate(4, "first_key = %d", elements.get(0).getKey());
396                    out.annotate(0, "targets:");
397                    out.indent();
398                    for (int i=0; i<elements.size(); i++) {
399                        out.annotate(4, "target[%d] = %d", i, elements.get(i).getOffset());
400                    }
401                    out.deindent();
402                }
403                out.deindent();
404            }
405
406            private void annotateSparseSwitchPayload(@Nonnull AnnotatedBytes out,
407                                                     @Nonnull SparseSwitchPayload instruction) {
408                List<? extends SwitchElement> elements = instruction.getSwitchElements();
409
410                out.annotate(2, instruction.getOpcode().name);
411                out.indent();
412                out.annotate(2, "size = %d", elements.size());
413                if (elements.size() > 0) {
414                    out.annotate(0, "keys:");
415                    out.indent();
416                    for (int i=0; i<elements.size(); i++) {
417                        out.annotate(4, "key[%d] = %d", i, elements.get(i).getKey());
418                    }
419                    out.deindent();
420                    out.annotate(0, "targets:");
421                    out.indent();
422                    for (int i=0; i<elements.size(); i++) {
423                        out.annotate(4, "target[%d] = %d", i, elements.get(i).getOffset());
424                    }
425                    out.deindent();
426                }
427                out.deindent();
428            }
429
430            private void addDebugInfoIdentity(int debugInfoOffset, String methodString) {
431                if (debugInfoAnnotator != null) {
432                    debugInfoAnnotator.setItemIdentity(debugInfoOffset, methodString);
433                }
434            }
435        };
436    }
437}
438