CodeItem.java revision 2a91d72a1534dd8171d8296ce8312de4bd603451
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.dexbacked.DexReader;
37import org.jf.dexlib2.dexbacked.instruction.DexBackedInstruction;
38import org.jf.dexlib2.dexbacked.raw.util.DexAnnotator;
39import org.jf.dexlib2.iface.instruction.*;
40import org.jf.dexlib2.iface.instruction.formats.*;
41import org.jf.dexlib2.util.AnnotatedBytes;
42import org.jf.dexlib2.util.ReferenceUtil;
43import org.jf.util.NumberUtils;
44
45import javax.annotation.Nonnull;
46import javax.annotation.Nullable;
47import java.util.List;
48
49public class CodeItem {
50    public static final int REGISTERS_OFFSET = 0;
51    public static final int INS_OFFSET = 2;
52    public static final int OUTS_OFFSET = 4;
53    public static final int TRIES_SIZE_OFFSET = 6;
54    public static final int DEBUG_INFO_OFFSET = 8;
55    public static final int INSTRUCTION_COUNT_OFFSET = 12;
56    public static final int INSTRUCTION_START_OFFSET = 16;
57
58    public static class TryItem {
59        public static final int ITEM_SIZE = 8;
60
61        public static final int START_ADDRESS_OFFSET = 0;
62        public static final int CODE_UNIT_COUNT_OFFSET = 4;
63        public static final int HANDLER_OFFSET = 6;
64    }
65
66    @Nonnull
67    public static SectionAnnotator makeAnnotator(@Nonnull DexAnnotator annotator, @Nonnull MapItem mapItem) {
68        return new SectionAnnotator(annotator, mapItem) {
69            private SectionAnnotator debugInfoAnnotator = null;
70
71            @Override public void annotateSection(@Nonnull AnnotatedBytes out) {
72                debugInfoAnnotator = annotator.getAnnotator(ItemType.DEBUG_INFO_ITEM);
73                super.annotateSection(out);
74            }
75
76            @Nonnull @Override public String getItemName() {
77                return "code_item";
78            }
79
80            @Override public int getItemAlignment() {
81                return 4;
82            }
83
84            @Override
85            public void annotateItem(@Nonnull AnnotatedBytes out, int itemIndex, @Nullable String itemIdentity) {
86                DexReader reader = dexFile.readerAt(out.getCursor());
87
88                int registers = reader.readUshort();
89                out.annotate(2, "registers_size = %d", registers);
90
91                int inSize = reader.readUshort();
92                out.annotate(2, "ins_size = %d", inSize);
93
94                int outSize = reader.readUshort();
95                out.annotate(2, "outs_size = %d", outSize);
96
97                int triesCount = reader.readUshort();
98                out.annotate(2, "tries_size = %d", triesCount);
99
100                int debugInfoOffset = reader.readSmallUint();
101                out.annotate(4, "debug_info_off = 0x%x", debugInfoOffset);
102
103                if (debugInfoOffset != 0) {
104                    addDebugInfoIdentity(debugInfoOffset, itemIdentity);
105                }
106
107                int instructionSize = reader.readSmallUint();
108                out.annotate(4, "insns_size = 0x%x", instructionSize);
109
110                out.annotate(0, "instructions:");
111                out.indent();
112
113                int end = reader.getOffset() + instructionSize*2;
114                while (reader.getOffset() < end) {
115                    Instruction instruction = DexBackedInstruction.readFrom(reader);
116
117                    switch (instruction.getOpcode().format) {
118                        case Format10x:
119                            annotateInstruction10x(out, instruction);
120                            break;
121                        case Format35c:
122                            annotateInstruction35c(out, (Instruction35c)instruction);
123                            break;
124                        case Format3rc:
125                            annotateInstruction3rc(out, (Instruction3rc)instruction);
126                            break;
127                        case ArrayPayload:
128                            annotateArrayPayload(out, (ArrayPayload)instruction);
129                            break;
130                        case PackedSwitchPayload:
131                            annotatePackedSwitchPayload(out, (PackedSwitchPayload)instruction);
132                            break;
133                        case SparseSwitchPayload:
134                            annotateSparseSwitchPayload(out, (SparseSwitchPayload)instruction);
135                            break;
136                        default:
137                            annotateDefaultInstruction(out, instruction);
138                            break;
139                    }
140
141                    assert reader.getOffset() == out.getCursor();
142                }
143                out.deindent();
144
145                if (triesCount > 0) {
146                    if ((reader.getOffset() % 4) != 0) {
147                        reader.readUshort();
148                        out.annotate(2, "padding");
149                    }
150
151                    out.annotate(0, "try_items:");
152                    out.indent();
153                    for (int i=0; i<triesCount; i++) {
154                        out.annotate(0, "try_item[%d]:", i);
155                        out.indent();
156                        int startAddr = reader.readSmallUint();
157                        out.annotate(4, "start_addr = 0x%x", startAddr);
158
159                        int instructionCount = reader.readUshort();
160                        out.annotate(2, "insn_count = 0x%x", instructionCount);
161
162                        int handlerOffset = reader.readUshort();
163                        out.annotate(2, "handler_off = 0x%x", handlerOffset);
164                        out.deindent();
165                    }
166                    out.deindent();
167
168                    int handlerListCount = reader.readSmallUleb128();
169                    out.annotate(0, "encoded_catch_handler_list:");
170                    out.annotateTo(reader.getOffset(), "size = %d", handlerListCount);
171                    out.indent();
172                    for (int i=0; i<handlerListCount; i++) {
173                        out.annotate(0, "encoded_catch_handler[%d]", i);
174                        out.indent();
175                        int handlerCount = reader.readSleb128();
176                        out.annotateTo(reader.getOffset(), "size = %d", handlerCount);
177                        boolean hasCatchAll = handlerCount <= 0;
178                        handlerCount = Math.abs(handlerCount);
179                        if (handlerCount != 0) {
180                            out.annotate(0, "handlers:");
181                            out.indent();
182                            for (int j=0; j<handlerCount; j++) {
183                                out.annotate(0, "encoded_type_addr_pair[%d]", i);
184                                out.indent();
185                                int typeIndex = reader.readSmallUleb128();
186                                out.annotateTo(reader.getOffset(), TypeIdItem.getReferenceAnnotation(dexFile, typeIndex));
187
188                                int handlerAddress = reader.readSmallUleb128();
189                                out.annotateTo(reader.getOffset(), "addr = 0x%x", handlerAddress);
190                                out.deindent();
191                            }
192                            out.deindent();
193                        }
194                        if (hasCatchAll) {
195                            int catchAllAddress = reader.readSmallUleb128();
196                            out.annotateTo(reader.getOffset(), "catch_all_addr = 0x%x", catchAllAddress);
197                        }
198                        out.deindent();
199                    }
200                    out.deindent();
201                }
202            }
203
204            private String formatRegister(int registerNum) {
205                return String.format("v%d", registerNum);
206            }
207
208            private void annotateInstruction10x(@Nonnull AnnotatedBytes out, @Nonnull Instruction instruction) {
209                out.annotate(2, instruction.getOpcode().name);
210            }
211
212            private void annotateInstruction35c(@Nonnull AnnotatedBytes out, @Nonnull Instruction35c instruction) {
213                List<String> args = Lists.newArrayList();
214
215                int registerCount = instruction.getRegisterCount();
216                if (registerCount == 1) {
217                    args.add(formatRegister(instruction.getRegisterC()));
218                } else if (registerCount == 2) {
219                    args.add(formatRegister(instruction.getRegisterC()));
220                    args.add(formatRegister(instruction.getRegisterD()));
221                } else if (registerCount == 3) {
222                    args.add(formatRegister(instruction.getRegisterC()));
223                    args.add(formatRegister(instruction.getRegisterD()));
224                    args.add(formatRegister(instruction.getRegisterE()));
225                } else if (registerCount == 4) {
226                    args.add(formatRegister(instruction.getRegisterC()));
227                    args.add(formatRegister(instruction.getRegisterD()));
228                    args.add(formatRegister(instruction.getRegisterE()));
229                    args.add(formatRegister(instruction.getRegisterF()));
230                } else if (registerCount == 5) {
231                    args.add(formatRegister(instruction.getRegisterC()));
232                    args.add(formatRegister(instruction.getRegisterD()));
233                    args.add(formatRegister(instruction.getRegisterE()));
234                    args.add(formatRegister(instruction.getRegisterF()));
235                    args.add(formatRegister(instruction.getRegisterG()));
236                }
237
238                String reference = ReferenceUtil.getReferenceString(instruction.getReference());
239
240                out.annotate(6, String.format("%s {%s}, %s",
241                        instruction.getOpcode().name, Joiner.on(", ").join(args), reference));
242            }
243
244            private void annotateInstruction3rc(@Nonnull AnnotatedBytes out, @Nonnull Instruction3rc instruction) {
245                int startRegister = instruction.getStartRegister();
246                int endRegister = startRegister + instruction.getRegisterCount() - 1;
247                String reference = ReferenceUtil.getReferenceString(instruction.getReference());
248                out.annotate(6, String.format("%s {%s .. %s}, %s",
249                        instruction.getOpcode().name, formatRegister(startRegister), formatRegister(endRegister),
250                        reference));
251            }
252
253            private void annotateDefaultInstruction(@Nonnull AnnotatedBytes out, @Nonnull Instruction instruction) {
254                List<String> args = Lists.newArrayList();
255
256                if (instruction instanceof OneRegisterInstruction) {
257                    args.add(formatRegister(((OneRegisterInstruction)instruction).getRegisterA()));
258                    if (instruction instanceof TwoRegisterInstruction) {
259                        args.add(formatRegister(((TwoRegisterInstruction)instruction).getRegisterB()));
260                        if (instruction instanceof ThreeRegisterInstruction) {
261                            args.add(formatRegister(((ThreeRegisterInstruction)instruction).getRegisterC()));
262                        }
263                    }
264                }
265
266                if (instruction instanceof ReferenceInstruction) {
267                    args.add(ReferenceUtil.getReferenceString(
268                            ((ReferenceInstruction)instruction).getReference()));
269                } else if (instruction instanceof OffsetInstruction) {
270                    int offset = ((OffsetInstruction)instruction).getCodeOffset();
271                    String sign = offset>=0?"+":"-";
272                    args.add(String.format("%s0x%x", sign, offset));
273                } else if (instruction instanceof NarrowLiteralInstruction) {
274                    int value = ((NarrowLiteralInstruction)instruction).getNarrowLiteral();
275                    if (NumberUtils.isLikelyFloat(value)) {
276                        args.add(String.format("%d # %f", value, Float.intBitsToFloat(value)));
277                    } else {
278                        args.add(String.format("%d", value));
279                    }
280                } else if (instruction instanceof WideLiteralInstruction) {
281                    long value = ((WideLiteralInstruction)instruction).getWideLiteral();
282                    if (NumberUtils.isLikelyDouble(value)) {
283                        args.add(String.format("%d # %f", value, Double.longBitsToDouble(value)));
284                    } else {
285                        args.add(String.format("%d", value));
286                    }
287                } else if (instruction instanceof FieldOffsetInstruction) {
288                    int fieldOffset = ((FieldOffsetInstruction)instruction).getFieldOffset();
289                    args.add(String.format("field@0x%x", fieldOffset));
290                } else if (instruction instanceof VtableIndexInstruction) {
291                    int vtableIndex = ((VtableIndexInstruction)instruction).getVtableIndex();
292                    args.add(String.format("vtable@%d", vtableIndex));
293                } else if (instruction instanceof InlineIndexInstruction) {
294                    int inlineIndex = ((InlineIndexInstruction)instruction).getInlineIndex();
295                    args.add(String.format("inline@%d", inlineIndex));
296                }
297
298                out.annotate(instruction.getCodeUnits()*2, "%s %s",
299                        instruction.getOpcode().name, Joiner.on(", ").join(args));
300            }
301
302            private void annotateArrayPayload(@Nonnull AnnotatedBytes out, @Nonnull ArrayPayload instruction) {
303                List<Number> elements = instruction.getArrayElements();
304                int elementWidth = instruction.getElementWidth();
305
306                out.annotate(2, instruction.getOpcode().name);
307                out.indent();
308                out.annotate(2, "element_width = %d", elementWidth);
309                out.annotate(4, "size = %d", elements.size());
310                out.annotate(0, "elements:");
311                out.indent();
312                for (int i=0; i<elements.size(); i++) {
313                    if (elementWidth == 8) {
314                        long value = elements.get(i).longValue();
315                        if (NumberUtils.isLikelyDouble(value)) {
316                            out.annotate(elementWidth, "element[%d] = %d # %f", i, value, Double.longBitsToDouble(value));
317                        } else {
318                            out.annotate(elementWidth, "element[%d] = %d", i, value);
319                        }
320                    } else {
321                        int value = elements.get(i).intValue();
322                        if (NumberUtils.isLikelyFloat(value)) {
323                            out.annotate(elementWidth, "element[%d] = %d # %f", i, value, Float.intBitsToFloat(value));
324                        } else {
325                            out.annotate(elementWidth, "element[%d] = %d", i, value);
326                        }
327                    }
328                }
329                if (out.getCursor() % 2 != 0) {
330                    out.annotate(1, "padding");
331                }
332                out.deindent();
333                out.deindent();
334            }
335
336            private void annotatePackedSwitchPayload(@Nonnull AnnotatedBytes out,
337                                                     @Nonnull PackedSwitchPayload instruction) {
338                List<? extends SwitchElement> elements = instruction.getSwitchElements();
339
340                out.annotate(2, instruction.getOpcode().name);
341                out.indent();
342
343                out.annotate(2, "size = %d", elements.size());
344                out.annotate(4, "first_key = %d", elements.get(0).getKey());
345                out.annotate(0, "targets:");
346                out.indent();
347                for (int i=0; i<elements.size(); i++) {
348                    out.annotate(4, "target[%d] = %d", i, elements.get(i).getOffset());
349                }
350                out.deindent();
351                out.deindent();
352            }
353
354            private void annotateSparseSwitchPayload(@Nonnull AnnotatedBytes out,
355                                                     @Nonnull SparseSwitchPayload instruction) {
356                List<? extends SwitchElement> elements = instruction.getSwitchElements();
357
358                out.annotate(2, instruction.getOpcode().name);
359                out.indent();
360                out.annotate(2, "size = %d", elements.size());
361                out.annotate(0, "keys:");
362                out.indent();
363                for (int i=0; i<elements.size(); i++) {
364                    out.annotate(4, "key[%d] = %d", i, elements.get(i).getKey());
365                }
366                out.deindent();
367                out.annotate(0, "targets:");
368                out.indent();
369                for (int i=0; i<elements.size(); i++) {
370                    out.annotate(4, "target[%d] = %d", i, elements.get(i).getOffset());
371                }
372                out.deindent();
373                out.deindent();
374            }
375
376            private void addDebugInfoIdentity(int debugInfoOffset, String methodString) {
377                if (debugInfoAnnotator != null) {
378                    debugInfoAnnotator.setItemIdentity(debugInfoOffset, methodString);
379                }
380            }
381        };
382    }
383}
384