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