CodeItem.java revision 8a151ae671f6d5c99d55779005580834b49187f0
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                }
294
295                out.annotate(instruction.getCodeUnits()*2, "%s %s",
296                        instruction.getOpcode().name, Joiner.on(", ").join(args));
297            }
298
299            private void annotateArrayPayload(@Nonnull AnnotatedBytes out, @Nonnull ArrayPayload instruction) {
300                List<Number> elements = instruction.getArrayElements();
301                int elementWidth = instruction.getElementWidth();
302
303                out.annotate(2, instruction.getOpcode().name);
304                out.indent();
305                out.annotate(2, "element_width = %d", elementWidth);
306                out.annotate(4, "size = %d", elements.size());
307                out.annotate(0, "elements:");
308                out.indent();
309                for (int i=0; i<elements.size(); i++) {
310                    if (elementWidth == 8) {
311                        long value = elements.get(i).longValue();
312                        if (NumberUtils.isLikelyDouble(value)) {
313                            out.annotate(elementWidth, "element[%d] = %d # %f", i, value, Double.longBitsToDouble(value));
314                        } else {
315                            out.annotate(elementWidth, "element[%d] = %d", i, value);
316                        }
317                    } else {
318                        int value = elements.get(i).intValue();
319                        if (NumberUtils.isLikelyFloat(value)) {
320                            out.annotate(elementWidth, "element[%d] = %d # %f", i, value, Float.intBitsToFloat(value));
321                        } else {
322                            out.annotate(elementWidth, "element[%d] = %d", i, value);
323                        }
324                    }
325                }
326                if (out.getCursor() % 2 != 0) {
327                    out.annotate(1, "padding");
328                }
329                out.deindent();
330                out.deindent();
331            }
332
333            private void annotatePackedSwitchPayload(@Nonnull AnnotatedBytes out,
334                                                     @Nonnull PackedSwitchPayload instruction) {
335                List<? extends SwitchElement> elements = instruction.getSwitchElements();
336
337                out.annotate(2, instruction.getOpcode().name);
338                out.indent();
339
340                out.annotate(2, "size = %d", elements.size());
341                out.annotate(4, "first_key = %d", elements.get(0).getKey());
342                out.annotate(0, "targets:");
343                out.indent();
344                for (int i=0; i<elements.size(); i++) {
345                    out.annotate(4, "target[%d] = %d", i, elements.get(i).getOffset());
346                }
347                out.deindent();
348                out.deindent();
349            }
350
351            private void annotateSparseSwitchPayload(@Nonnull AnnotatedBytes out,
352                                                     @Nonnull SparseSwitchPayload instruction) {
353                List<? extends SwitchElement> elements = instruction.getSwitchElements();
354
355                out.annotate(2, instruction.getOpcode().name);
356                out.indent();
357                out.annotate(2, "size = %d", elements.size());
358                out.annotate(0, "keys:");
359                out.indent();
360                for (int i=0; i<elements.size(); i++) {
361                    out.annotate(4, "key[%d] = %d", i, elements.get(i).getKey());
362                }
363                out.deindent();
364                out.annotate(0, "targets:");
365                out.indent();
366                for (int i=0; i<elements.size(); i++) {
367                    out.annotate(4, "target[%d] = %d", i, elements.get(i).getOffset());
368                }
369                out.deindent();
370                out.deindent();
371            }
372
373            private void addDebugInfoIdentity(int debugInfoOffset, String methodString) {
374                if (debugInfoAnnotator != null) {
375                    debugInfoAnnotator.setItemIdentity(debugInfoOffset, methodString);
376                }
377            }
378        };
379    }
380}
381