1 /*
2  * Copyright (C) 2014 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package dexfuzz.rawdex;
18 
19 import dexfuzz.Log;
20 
21 public class Offset {
22   /**
23    * The absolute value of this offset as it was originally read.
24    */
25   private int originalOffset;
26 
27   /**
28    * The Offsettable that this Offset points to.
29    */
30   private Offsettable offsettable;
31 
32   /**
33    * The location of this Offset in the new file, ONLY SET IF the Offset
34    * couldn't be written because what it points to hasn't been written
35    * yet.
36    */
37   private int outputLocation;
38 
39   /**
40    * Was the output location for this Offset set?.
41    */
42   private boolean outputLocationSet;
43 
44   /**
45    * Does this Offset need to be written out using ULEB128?.
46    */
47   private boolean useUleb128;
48 
49   /**
50    * Was this Offset created after reading, during mutation?.
51    */
52   private boolean isNewOffset;
53 
54   /**
55    * Only one Offset should have this flag set, the MapItem that points
56    * to the HeaderItem.
57    */
58   private boolean pointsAtHeader;
59 
60   /**
61    * If an Offset pointed at 0 (because it is not actually a valid Offset),
62    * and it's not pointing at the header, then this is set.
63    */
64   private boolean pointsAtNull;
65 
Offset(boolean header)66   public Offset(boolean header) {
67     pointsAtHeader = header;
68   }
69 
getPointedToItem()70   public RawDexObject getPointedToItem() {
71     return offsettable.getItem();
72   }
73 
pointsToSomething()74   public boolean pointsToSomething() {
75     return offsettable != null;
76   }
77 
pointsAtNull()78   public boolean pointsAtNull() {
79     return pointsAtNull;
80   }
81 
pointsAtHeader()82   public boolean pointsAtHeader() {
83     return pointsAtHeader;
84   }
85 
86   /**
87    * Returns true if this Offset points at the provided RawDexObject.
88    */
pointsToThisItem(RawDexObject thisItem)89   public boolean pointsToThisItem(RawDexObject thisItem) {
90     if (!pointsToSomething()) {
91       return false;
92     }
93     return (offsettable.getItem().equals(thisItem));
94   }
95 
96   /**
97    * Returns true if this Offset points at the provided Offsettable.
98    */
pointsToThisOffsettable(Offsettable thisOffsettable)99   public boolean pointsToThisOffsettable(Offsettable thisOffsettable) {
100     if (!pointsToSomething()) {
101       return false;
102     }
103     return (offsettable.equals(thisOffsettable));
104   }
105 
106   /**
107    * Makes this Offset point at a new Offsettable.
108    */
pointTo(Offsettable offsettableItem)109   public void pointTo(Offsettable offsettableItem) {
110     if (offsettable != null) {
111       Log.debug("Updating what an Offset points to...");
112     }
113     offsettable = offsettableItem;
114   }
115 
116   /**
117    * Call this to make an Offset that pointed at null before now point at something.
118    * An Offset may have previously pointed at null before...
119    * Example: if there are no fields referred to in a DEX file, then header.field_ids_off
120    * will point at null. We distinguish when Offsets point at null, and are not pointing
121    * at the header (only the header MapItem should do this) with a flag. Therefore, this
122    * method is needed to indicate that this Offset now points at something.
123    */
unsetNullAndPointTo(Offsettable offsettableItem)124   public void unsetNullAndPointTo(Offsettable offsettableItem) {
125     pointsAtNull = false;
126     if (offsettable != null) {
127       Log.debug("Updating what an Offset points to...");
128     }
129     offsettable = offsettableItem;
130   }
131 
pointToNew(Offsettable offsettableItem)132   public void pointToNew(Offsettable offsettableItem) {
133     offsettable = offsettableItem;
134     isNewOffset = true;
135   }
136 
getNewPositionOfItem()137   public int getNewPositionOfItem() {
138     return offsettable.getNewPosition();
139   }
140 
usesUleb128()141   public boolean usesUleb128() {
142     return useUleb128;
143   }
144 
145   /**
146    * Mark this Offset as using the ULEB128 encoding.
147    */
setUsesUleb128()148   public void setUsesUleb128() {
149     if (useUleb128) {
150       throw new Error("Offset is already marked as using ULEB128!");
151     }
152     useUleb128 = true;
153   }
154 
isNewOffset()155   public boolean isNewOffset() {
156     return isNewOffset;
157   }
158 
setPointsAtNull()159   public void setPointsAtNull() {
160     pointsAtNull = true;
161   }
162 
setOutputLocation(int loc)163   public void setOutputLocation(int loc) {
164     outputLocation = loc;
165     outputLocationSet = true;
166   }
167 
168   /**
169    * Get the location in the output DEX file where this offset has been written.
170    * (This is used when patching Offsets when the Offsettable position was not
171    * known at the time of writing out the Offset.)
172    */
getOutputLocation()173   public int getOutputLocation() {
174     if (!outputLocationSet) {
175       throw new Error("Output location was not set yet!");
176     }
177     return outputLocation;
178   }
179 
setOriginalOffset(int offset)180   public void setOriginalOffset(int offset) {
181     originalOffset = offset;
182   }
183 
getOriginalOffset()184   public int getOriginalOffset() {
185     return originalOffset;
186   }
187 
readyForWriting()188   public boolean readyForWriting() {
189     return offsettable.readyForFinalOffsetToBeWritten();
190   }
191 }
192