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