• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
32 package org.jf.dexlib2.dexbacked.raw;
33 
34 import com.google.common.base.Joiner;
35 import com.google.common.collect.Lists;
36 import org.jf.dexlib2.VerificationError;
37 import org.jf.dexlib2.dexbacked.DexReader;
38 import org.jf.dexlib2.dexbacked.instruction.DexBackedInstruction;
39 import org.jf.dexlib2.dexbacked.raw.util.DexAnnotator;
40 import org.jf.dexlib2.iface.instruction.*;
41 import org.jf.dexlib2.iface.instruction.formats.*;
42 import org.jf.dexlib2.util.AnnotatedBytes;
43 import org.jf.dexlib2.util.ReferenceUtil;
44 import org.jf.util.ExceptionWithContext;
45 import org.jf.util.NumberUtils;
46 
47 import javax.annotation.Nonnull;
48 import javax.annotation.Nullable;
49 import java.util.List;
50 
51 public 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
makeAnnotator(@onnull DexAnnotator annotator, @Nonnull MapItem mapItem)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.readInt();
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