1 /* 2 * Copyright 2013, Google LLC 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions are 6 * met: 7 * 8 * * Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * * Redistributions in binary form must reproduce the above 11 * copyright notice, this list of conditions and the following disclaimer 12 * in the documentation and/or other materials provided with the 13 * distribution. 14 * * Neither the name of Google LLC nor the names of its 15 * contributors may be used to endorse or promote products derived from 16 * this software without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 */ 30 31 package com.android.tools.smali.dexlib2.dexbacked.raw.util; 32 33 import com.android.tools.smali.dexlib2.dexbacked.raw.AnnotationDirectoryItem; 34 import com.android.tools.smali.dexlib2.dexbacked.raw.AnnotationItem; 35 import com.android.tools.smali.dexlib2.dexbacked.raw.AnnotationSetItem; 36 import com.android.tools.smali.dexlib2.dexbacked.raw.AnnotationSetRefList; 37 import com.android.tools.smali.dexlib2.dexbacked.raw.CallSiteIdItem; 38 import com.android.tools.smali.dexlib2.dexbacked.raw.CdexDebugOffsetTable; 39 import com.android.tools.smali.dexlib2.dexbacked.raw.ClassDataItem; 40 import com.android.tools.smali.dexlib2.dexbacked.raw.ClassDefItem; 41 import com.android.tools.smali.dexlib2.dexbacked.raw.CodeItem; 42 import com.android.tools.smali.dexlib2.dexbacked.raw.DebugInfoItem; 43 import com.android.tools.smali.dexlib2.dexbacked.raw.EncodedArrayItem; 44 import com.android.tools.smali.dexlib2.dexbacked.raw.FieldIdItem; 45 import com.android.tools.smali.dexlib2.dexbacked.raw.HeaderItem; 46 import com.android.tools.smali.dexlib2.dexbacked.raw.HiddenApiClassDataItem; 47 import com.android.tools.smali.dexlib2.dexbacked.raw.ItemType; 48 import com.android.tools.smali.dexlib2.dexbacked.raw.MapItem; 49 import com.android.tools.smali.dexlib2.dexbacked.raw.MethodHandleItem; 50 import com.android.tools.smali.dexlib2.dexbacked.raw.MethodIdItem; 51 import com.android.tools.smali.dexlib2.dexbacked.raw.ProtoIdItem; 52 import com.android.tools.smali.dexlib2.dexbacked.raw.SectionAnnotator; 53 import com.android.tools.smali.dexlib2.dexbacked.raw.StringDataItem; 54 import com.android.tools.smali.dexlib2.dexbacked.raw.StringIdItem; 55 import com.android.tools.smali.dexlib2.dexbacked.raw.TypeIdItem; 56 import com.android.tools.smali.dexlib2.dexbacked.raw.TypeListItem; 57 import com.android.tools.smali.dexlib2.util.AnnotatedBytes; 58 59 import com.android.tools.smali.dexlib2.dexbacked.CDexBackedDexFile; 60 import com.android.tools.smali.dexlib2.dexbacked.DexBackedDexFile; 61 62 import javax.annotation.Nonnull; 63 import javax.annotation.Nullable; 64 import java.io.IOException; 65 import java.io.Writer; 66 import java.util.Arrays; 67 import java.util.Comparator; 68 import java.util.HashMap; 69 import java.util.List; 70 import java.util.Map; 71 72 public class DexAnnotator extends AnnotatedBytes { 73 @Nonnull public final DexBackedDexFile dexFile; 74 75 private final Map<Integer, SectionAnnotator> annotators = new HashMap<>(); 76 private static final Map<Integer, Integer> sectionAnnotationOrder = new HashMap<>(); 77 78 static { 79 int[] sectionOrder = new int[] { 80 ItemType.MAP_LIST, 81 82 ItemType.HEADER_ITEM, 83 ItemType.STRING_ID_ITEM, 84 ItemType.TYPE_ID_ITEM, 85 ItemType.PROTO_ID_ITEM, 86 ItemType.FIELD_ID_ITEM, 87 ItemType.METHOD_ID_ITEM, 88 ItemType.CALL_SITE_ID_ITEM, 89 ItemType.METHOD_HANDLE_ITEM, 90 91 // these need to be ordered like this, so the item identities can be propagated 92 ItemType.CLASS_DEF_ITEM, 93 ItemType.CLASS_DATA_ITEM, 94 ItemType.CODE_ITEM, 95 ItemType.DEBUG_INFO_ITEM, 96 97 ItemType.TYPE_LIST, 98 ItemType.ANNOTATION_SET_REF_LIST, 99 ItemType.ANNOTATION_SET_ITEM, 100 ItemType.STRING_DATA_ITEM, 101 ItemType.ANNOTATION_ITEM, 102 ItemType.ENCODED_ARRAY_ITEM, 103 ItemType.ANNOTATION_DIRECTORY_ITEM, 104 105 ItemType.HIDDENAPI_CLASS_DATA_ITEM 106 }; 107 108 for (int i=0; i<sectionOrder.length; i++) { sectionAnnotationOrder.put(sectionOrder[i], i)109 sectionAnnotationOrder.put(sectionOrder[i], i); 110 } 111 } 112 DexAnnotator(@onnull DexBackedDexFile dexFile, int width)113 public DexAnnotator(@Nonnull DexBackedDexFile dexFile, int width) { 114 super(width); 115 116 this.dexFile = dexFile; 117 118 for (MapItem mapItem: dexFile.getMapItems()) { 119 switch (mapItem.getType()) { 120 case ItemType.HEADER_ITEM: 121 annotators.put(mapItem.getType(), HeaderItem.makeAnnotator(this, mapItem)); 122 break; 123 case ItemType.STRING_ID_ITEM: 124 annotators.put(mapItem.getType(), StringIdItem.makeAnnotator(this, mapItem)); 125 break; 126 case ItemType.TYPE_ID_ITEM: 127 annotators.put(mapItem.getType(), TypeIdItem.makeAnnotator(this, mapItem)); 128 break; 129 case ItemType.PROTO_ID_ITEM: 130 annotators.put(mapItem.getType(), ProtoIdItem.makeAnnotator(this, mapItem)); 131 break; 132 case ItemType.FIELD_ID_ITEM: 133 annotators.put(mapItem.getType(), FieldIdItem.makeAnnotator(this, mapItem)); 134 break; 135 case ItemType.METHOD_ID_ITEM: 136 annotators.put(mapItem.getType(), MethodIdItem.makeAnnotator(this, mapItem)); 137 break; 138 case ItemType.CLASS_DEF_ITEM: 139 annotators.put(mapItem.getType(), ClassDefItem.makeAnnotator(this, mapItem)); 140 break; 141 case ItemType.MAP_LIST: 142 annotators.put(mapItem.getType(), MapItem.makeAnnotator(this, mapItem)); 143 break; 144 case ItemType.TYPE_LIST: 145 annotators.put(mapItem.getType(), TypeListItem.makeAnnotator(this, mapItem)); 146 break; 147 case ItemType.ANNOTATION_SET_REF_LIST: 148 annotators.put(mapItem.getType(), AnnotationSetRefList.makeAnnotator(this, mapItem)); 149 break; 150 case ItemType.ANNOTATION_SET_ITEM: 151 annotators.put(mapItem.getType(), AnnotationSetItem.makeAnnotator(this, mapItem)); 152 break; 153 case ItemType.CLASS_DATA_ITEM: 154 annotators.put(mapItem.getType(), ClassDataItem.makeAnnotator(this, mapItem)); 155 break; 156 case ItemType.CODE_ITEM: 157 annotators.put(mapItem.getType(), CodeItem.makeAnnotator(this, mapItem)); 158 break; 159 case ItemType.STRING_DATA_ITEM: 160 annotators.put(mapItem.getType(), StringDataItem.makeAnnotator(this, mapItem)); 161 break; 162 case ItemType.DEBUG_INFO_ITEM: 163 annotators.put(mapItem.getType(), DebugInfoItem.makeAnnotator(this, mapItem)); 164 break; 165 case ItemType.ANNOTATION_ITEM: 166 annotators.put(mapItem.getType(), AnnotationItem.makeAnnotator(this, mapItem)); 167 break; 168 case ItemType.ENCODED_ARRAY_ITEM: 169 annotators.put(mapItem.getType(), EncodedArrayItem.makeAnnotator(this, mapItem)); 170 break; 171 case ItemType.ANNOTATION_DIRECTORY_ITEM: 172 annotators.put(mapItem.getType(), AnnotationDirectoryItem.makeAnnotator(this, mapItem)); 173 break; 174 case ItemType.CALL_SITE_ID_ITEM: 175 annotators.put(mapItem.getType(), CallSiteIdItem.makeAnnotator(this, mapItem)); 176 break; 177 case ItemType.METHOD_HANDLE_ITEM: 178 annotators.put(mapItem.getType(), MethodHandleItem.makeAnnotator(this, mapItem)); 179 break; 180 case ItemType.HIDDENAPI_CLASS_DATA_ITEM: 181 annotators.put(mapItem.getType(), HiddenApiClassDataItem.makeAnnotator(this, mapItem)); 182 break; 183 default: 184 throw new RuntimeException(String.format("Unrecognized item type: 0x%x", mapItem.getType())); 185 } 186 } 187 } 188 writeAnnotations(Writer out)189 public void writeAnnotations(Writer out) throws IOException { 190 List<MapItem> mapItems = dexFile.getMapItems(); 191 // sort the map items based on the order defined by sectionAnnotationOrder 192 Comparator<MapItem> comparator = new Comparator<MapItem>() { 193 @Override public int compare(MapItem o1, MapItem o2) { 194 return Integer.compare(sectionAnnotationOrder.get(o1.getType()), sectionAnnotationOrder.get(o2.getType())); 195 } 196 }; 197 198 MapItem[] mapItemsArray = mapItems.toArray(new MapItem[mapItems.size()]); 199 Arrays.sort(mapItemsArray, comparator); 200 201 try { 202 // Need to annotate the debug info offset table first, to propagate the debug info identities 203 if (dexFile instanceof CDexBackedDexFile) { 204 moveTo(dexFile.getBaseDataOffset() + ((CDexBackedDexFile) dexFile).getDebugInfoOffsetsPos()); 205 CdexDebugOffsetTable.annotate(this, dexFile.getBuffer()); 206 } 207 208 for (MapItem mapItem: mapItemsArray) { 209 try { 210 SectionAnnotator annotator = annotators.get(mapItem.getType()); 211 annotator.annotateSection(this); 212 } catch (Exception ex) { 213 System.err.println(String.format("There was an error while dumping the %s section", 214 ItemType.getItemTypeName(mapItem.getType()))); 215 ex.printStackTrace(System.err); 216 } 217 } 218 } finally { 219 writeAnnotations(out, dexFile.getBuffer().getBuf(), dexFile.getBuffer().getBaseOffset()); 220 } 221 } 222 223 @Nullable getAnnotator(int itemType)224 public SectionAnnotator getAnnotator(int itemType) { 225 return annotators.get(itemType); 226 } 227 } 228