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