1 /*
2  * Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.
8  *
9  * This code is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12  * version 2 for more details (a copy is included in the LICENSE file that
13  * accompanied this code).
14  *
15  * You should have received a copy of the GNU General Public License version
16  * 2 along with this work; if not, write to the Free Software Foundation,
17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18  *
19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20  * or visit www.oracle.com if you need additional information or have any
21  * questions.
22  *
23  */
24 
25 package sun.jvm.hotspot.types.basic;
26 
27 import java.util.HashMap;
28 import java.util.Iterator;
29 import java.util.Map;
30 
31 import sun.jvm.hotspot.debugger.Address;
32 import sun.jvm.hotspot.debugger.MachineDescription;
33 import sun.jvm.hotspot.runtime.VM;
34 import sun.jvm.hotspot.types.Type;
35 import sun.jvm.hotspot.types.TypeDataBase;
36 import sun.jvm.hotspot.memory.FileMapInfo;
37 
38 /** <P> This is a basic implementation of the TypeDataBase interface.
39     It allows an external type database builder to add types to be
40     consumed by a client through the Type interfaces. It has no
41     knowledge of symbol lookup; for example, the builder is
42     responsible for providing the addresses of static fields. </P>
43 
44     <P> Among other things, the database builder is responsible for
45     providing the Types for the Java primitive types, as well as their
46     sizes. </P>
47 */
48 
49 public class BasicTypeDataBase implements TypeDataBase {
50   private MachineDescription machDesc;
51   private VtblAccess vtblAccess;
52   /** Maps strings to Type objects. This does not contain the primitive types. */
53   private Map nameToTypeMap = new HashMap();
54   /** Maps strings to Integers, used for enums, etc. */
55   private Map nameToIntConstantMap = new HashMap();
56   /** Maps strings to Longs, used for 32/64-bit constants, etc. */
57   private Map nameToLongConstantMap = new HashMap();
58   /** Primitive types. */
59   private Type jbooleanType;
60   private Type jbyteType;
61   private Type jcharType;
62   private Type jdoubleType;
63   private Type jfloatType;
64   private Type jintType;
65   private Type jlongType;
66   private Type jshortType;
67 
68   /** For debugging */
69   private static final boolean DEBUG;
70   static {
71     DEBUG = System.getProperty("sun.jvm.hotspot.types.basic.BasicTypeDataBase.DEBUG") != null;
72   }
73 
BasicTypeDataBase(MachineDescription machDesc, VtblAccess vtblAccess)74   public BasicTypeDataBase(MachineDescription machDesc, VtblAccess vtblAccess) {
75     this.machDesc   = machDesc;
76     this.vtblAccess = vtblAccess;
77   }
78 
lookupType(String cTypeName)79   public Type lookupType(String cTypeName) {
80     return lookupType(cTypeName, true);
81   }
82 
lookupType(String cTypeName, boolean throwException)83   public Type lookupType(String cTypeName, boolean throwException) {
84     Type type = (Type) nameToTypeMap.get(cTypeName);
85     if (type == null && throwException) {
86       throw new RuntimeException("No type named \"" + cTypeName + "\" in database");
87     }
88     return type;
89   }
90 
lookupIntConstant(String constantName)91   public Integer lookupIntConstant(String constantName) {
92     return lookupIntConstant(constantName, true);
93   }
94 
lookupIntConstant(String constantName, boolean throwException)95   public Integer lookupIntConstant(String constantName, boolean throwException) {
96     Integer i = (Integer) nameToIntConstantMap.get(constantName);
97     if (i == null) {
98       if (throwException) {
99         throw new RuntimeException("No integer constant named \"" + constantName + "\" present in type database");
100       }
101     }
102     return i;
103   }
104 
lookupLongConstant(String constantName)105   public Long lookupLongConstant(String constantName) {
106     return lookupLongConstant(constantName, true);
107   }
108 
lookupLongConstant(String constantName, boolean throwException)109   public Long lookupLongConstant(String constantName, boolean throwException) {
110     Long i = (Long) nameToLongConstantMap.get(constantName);
111     if (i == null) {
112       if (throwException) {
113         throw new RuntimeException("No long constant named \"" + constantName + "\" present in type database");
114       }
115     }
116     return i;
117   }
118 
getJBooleanType()119   public Type getJBooleanType() {
120     return jbooleanType;
121   }
122 
getJByteType()123   public Type getJByteType() {
124     return jbyteType;
125   }
126 
getJCharType()127   public Type getJCharType() {
128     return jcharType;
129   }
130 
getJDoubleType()131   public Type getJDoubleType() {
132     return jdoubleType;
133   }
134 
getJFloatType()135   public Type getJFloatType() {
136     return jfloatType;
137   }
138 
getJIntType()139   public Type getJIntType() {
140     return jintType;
141   }
142 
getJLongType()143   public Type getJLongType() {
144     return jlongType;
145   }
146 
getJShortType()147   public Type getJShortType() {
148     return jshortType;
149   }
150 
getAddressSize()151   public long getAddressSize() {
152     return machDesc.getAddressSize();
153   }
154 
getOopSize()155   public long getOopSize() {
156     return VM.getVM().getOopSize();
157   }
158 
159   HashMap typeToVtbl = new HashMap();
160 
vtblForType(Type type)161   private Address vtblForType(Type type) {
162     Address vtblAddr = (Address)typeToVtbl.get(type);
163     if (vtblAddr == null) {
164       vtblAddr = vtblAccess.getVtblForType(type);
165       if (vtblAddr != null) {
166         typeToVtbl.put(type, vtblAddr);
167       }
168     }
169     return vtblAddr;
170   }
171 
addressTypeIsEqualToType(Address addr, Type type)172   public boolean addressTypeIsEqualToType(Address addr, Type type) {
173     if (addr == null) {
174       return false;
175     }
176 
177     // This implementation should be suitably platform-independent; we
178     // search nearby memory for the vtbl value of the given type.
179 
180     Address vtblAddr = vtblForType(type);
181 
182     if (vtblAddr == null) {
183       // Type was not polymorphic, or an error occurred during lookup
184       if (DEBUG) {
185         System.err.println("BasicTypeDataBase.addressTypeIsEqualToType: vtblAddr == null");
186       }
187 
188       return false;
189     }
190 
191     // The first implementation searched three locations for this vtbl
192     // value; scanning through the entire object was considered, but
193     // we thought we knew where we were looking, and looking only in
194     // these specific locations should reduce the probability of
195     // mistaking random bits as a pointer (although, realistically
196     // speaking, the likelihood of finding a match between the bit
197     // pattern of, for example, a double and the vtbl is vanishingly
198     // small.)
199     //    1. The first word of the object (should handle MSVC++ as
200     //    well as the SparcWorks compilers with compatibility set to
201     //    v5.0 or greater)
202     //    2. and 3. The last two Address-aligned words of the part of
203     //    the object defined by its topmost polymorphic superclass.
204     //    This should handle the SparcWorks compilers, v4.2 or
205     //    earlier, as well as any other compilers which place the vptr
206     //    at the end of the user-defined fields of the first base
207     //    class with virtual functions.
208     //
209     // Unfortunately this algorithm did not work properly for the
210     // specific case of the ThreadShadow/Thread inheritance situation,
211     // because the Solaris compiler seems to cleverly eliminate the
212     // vtbl for ThreadShadow since the only virtual is empty. (We
213     // should get rid of the ThreadShadow and fix the include
214     // databases, but need to postpone this for the present.) The
215     // current solution performs the three-location check for this
216     // class and all of its known superclasses rather than just the
217     // topmost polymorphic one.
218 
219     Type curType = type;
220 
221     try {
222       while (curType != null) {
223         // Using the size information we have for this type, check the
224         // three locations described above.
225 
226         // (1)
227         if (vtblAddr.equals(addr.getAddressAt(0))) {
228           return true;
229         }
230 
231         // (2)
232         long offset = curType.getSize();
233         // I don't think this should be misaligned under any
234         // circumstances, but I'm not sure (FIXME: also not sure which
235         // way to go here, up or down -- assuming down)
236         offset -= (offset % getAddressSize());
237         if (offset <= 0) {
238           return false;
239         }
240         if (vtblAddr.equals(addr.getAddressAt(offset))) {
241           return true;
242         }
243         offset -= getAddressSize();
244         if (offset <= 0) {
245           return false;
246         }
247         if (vtblAddr.equals(addr.getAddressAt(offset))) {
248           return true;
249         }
250 
251         curType = curType.getSuperclass();
252       }
253     }
254     catch (Exception e) {
255       // Any UnmappedAddressExceptions, etc. are a good indication
256       // that the pointer is not of the specified type
257       if (DEBUG) {
258         System.err.println("BasicTypeDataBase.addressTypeIsEqualToType: exception occurred during lookup:");
259         e.printStackTrace();
260       }
261 
262       return false;
263     }
264 
265     if (DEBUG) {
266       System.err.println("BasicTypeDataBase.addressTypeIsEqualToType: all vptr tests failed for type " +
267                          type.getName());
268     }
269 
270     return false;
271   }
272 
findDynamicTypeForAddress(Address addr, Type baseType)273   public Type findDynamicTypeForAddress(Address addr, Type baseType) {
274     // This implementation should be suitably platform-independent; we
275     // search nearby memory for the vtbl value of the given type.
276 
277     if (vtblForType(baseType) == null) {
278       // Type was not polymorphic which is an error of some sort
279       throw new InternalError(baseType + " does not appear to be polymorphic");
280     }
281 
282     // This is a more restricted version of guessTypeForAddress since
283     // that function has some limitations since it doesn't really know
284     // where in the hierarchy a virtual type starts and just poking
285     // around in memory is likely to trip over some vtable address,
286     // resulting in false positives.  Eventually all uses should
287     // switch to this logic but in the interests of stability it will
288     // be separate for the moment.
289 
290     // Assuming that the base type is truly the first polymorphic type
291     // then the vtbl for all subclasss should be at several defined
292     // locations so only those locations will be checked.  It's also
293     // required that the caller knows that the static type is at least
294     // baseType.  See the notes in guessTypeForAddress for the logic of
295     // the locations searched.
296 
297     Address loc1 = addr.getAddressAt(0);
298 
299     if (VM.getVM().isSharingEnabled()) {
300       // Check if the value falls in the _md_region
301       FileMapInfo cdsFileMapInfo = VM.getVM().getFileMapInfo();
302       if (cdsFileMapInfo.inCopiedVtableSpace(loc1)) {
303          return cdsFileMapInfo.getTypeForVptrAddress(loc1);
304       }
305     }
306 
307     Address loc2 = null;
308     Address loc3 = null;
309     long offset2 = baseType.getSize();
310     // I don't think this should be misaligned under any
311     // circumstances, but I'm not sure (FIXME: also not sure which
312     // way to go here, up or down -- assuming down)
313     offset2 = offset2 - (offset2 % getAddressSize()) - getAddressSize();
314     if (offset2 > 0) {
315       loc2 = addr.getAddressAt(offset2);
316     }
317     long offset3 = offset2 - getAddressSize();
318     if (offset3 > 0) {
319       loc3 = addr.getAddressAt(offset3);
320     }
321 
322     Type loc2Match = null;
323     Type loc3Match = null;
324     for (Iterator iter = getTypes(); iter.hasNext(); ) {
325       Type type = (Type) iter.next();
326       Type superClass = type;
327       while (superClass != baseType && superClass != null) {
328         superClass = superClass.getSuperclass();
329       }
330       if (superClass == null) continue;
331       Address vtblAddr = vtblForType(type);
332       if (vtblAddr == null) {
333         // This occurs sometimes for intermediate types that are never
334         // instantiated.
335         if (DEBUG) {
336           System.err.println("null vtbl for " + type);
337         }
338         continue;
339       }
340       // Prefer loc1 match
341       if (vtblAddr.equals(loc1)) return type;
342       if (loc2 != null && loc2Match == null && vtblAddr.equals(loc2)) {
343           loc2Match = type;
344       }
345       if (loc3 != null && loc3Match == null && vtblAddr.equals(loc3)) {
346           loc3Match = type;
347       }
348     }
349     if (loc2Match != null) return loc2Match;
350     if (loc3Match != null) return loc3Match;
351     return null;
352   }
353 
guessTypeForAddress(Address addr)354   public Type guessTypeForAddress(Address addr) {
355     for (Iterator iter = getTypes(); iter.hasNext(); ) {
356       Type t = (Type) iter.next();
357       if (addressTypeIsEqualToType(addr, t)) {
358         return t;
359       }
360     }
361     return null;
362   }
363 
cIntegerTypeMaxValue(long sizeInBytes, boolean isUnsigned)364   public long cIntegerTypeMaxValue(long sizeInBytes, boolean isUnsigned) {
365     return machDesc.cIntegerTypeMaxValue(sizeInBytes, isUnsigned);
366   }
367 
cIntegerTypeMinValue(long sizeInBytes, boolean isUnsigned)368   public long cIntegerTypeMinValue(long sizeInBytes, boolean isUnsigned) {
369     return machDesc.cIntegerTypeMinValue(sizeInBytes, isUnsigned);
370   }
371 
getTypes()372   public Iterator getTypes() {
373     return nameToTypeMap.values().iterator();
374   }
375 
getIntConstants()376   public Iterator getIntConstants() {
377     return nameToIntConstantMap.keySet().iterator();
378   }
379 
getLongConstants()380   public Iterator getLongConstants() {
381     return nameToLongConstantMap.keySet().iterator();
382   }
383 
384   //--------------------------------------------------------------------------------
385   // Public routines only for use by the database builder
386   //
387 
388   /** This method should only be called by the builder of the
389       TypeDataBase and at most once */
setJBooleanType(Type type)390   public void setJBooleanType(Type type) {
391     jbooleanType = type;
392   }
393 
394   /** This method should only be called by the builder of the
395       TypeDataBase and at most once */
setJByteType(Type type)396   public void setJByteType(Type type) {
397     jbyteType = type;
398   }
399 
400   /** This method should only be called by the builder of the
401       TypeDataBase and at most once */
setJCharType(Type type)402   public void setJCharType(Type type) {
403     jcharType = type;
404   }
405 
406   /** This method should only be called by the builder of the
407       TypeDataBase and at most once */
setJDoubleType(Type type)408   public void setJDoubleType(Type type) {
409     jdoubleType = type;
410   }
411 
412   /** This method should only be called by the builder of the
413       TypeDataBase and at most once */
setJFloatType(Type type)414   public void setJFloatType(Type type) {
415     jfloatType = type;
416   }
417 
418   /** This method should only be called by the builder of the
419       TypeDataBase and at most once */
setJIntType(Type type)420   public void setJIntType(Type type) {
421     jintType = type;
422   }
423 
424   /** This method should only be called by the builder of the
425       TypeDataBase and at most once */
setJLongType(Type type)426   public void setJLongType(Type type) {
427     jlongType = type;
428   }
429 
430   /** This method should only be called by the builder of the
431       TypeDataBase and at most once */
setJShortType(Type type)432   public void setJShortType(Type type) {
433     jshortType = type;
434   }
435 
436   /** This method should only be used by the builder of the
437       TypeDataBase. Throws a RuntimeException if a class with this
438       name was already present. */
addType(Type type)439   public void addType(Type type) {
440     if (nameToTypeMap.get(type.getName()) != null) {
441       throw new RuntimeException("type of name \"" + type.getName() + "\" already present");
442     }
443 
444     nameToTypeMap.put(type.getName(), type);
445   }
446 
447   /** This method should only be used by the builder of the
448       TypeDataBase. Throws a RuntimeException if this class was not
449       present. */
removeType(Type type)450   public void removeType(Type type) {
451     Type curType = (Type) nameToTypeMap.get(type.getName());
452     if (curType == null) {
453       throw new RuntimeException("type of name \"" + type.getName() + "\" not present");
454     }
455 
456     if (!curType.equals(type)) {
457       throw new RuntimeException("a different type of name \"" + type.getName() + "\" was present");
458     }
459 
460     nameToTypeMap.remove(type.getName());
461   }
462 
463   /** This method should only be used by the builder of the
464       TypeDataBase. Throws a RuntimeException if an integer constant
465       with this name was already present. */
addIntConstant(String name, int value)466   public void addIntConstant(String name, int value) {
467     if (nameToIntConstantMap.get(name) != null) {
468       throw new RuntimeException("int constant of name \"" + name + "\" already present");
469     }
470 
471     nameToIntConstantMap.put(name, new Integer(value));
472   }
473 
474   /** This method should only be used by the builder of the
475       TypeDataBase. Throws a RuntimeException if an integer constant
476       with this name was not present. */
removeIntConstant(String name)477   public void removeIntConstant(String name) {
478     Integer curConstant = (Integer) nameToIntConstantMap.get(name);
479     if (curConstant == null) {
480       throw new RuntimeException("int constant of name \"" + name + "\" not present");
481     }
482 
483     nameToIntConstantMap.remove(name);
484   }
485 
486   /** This method should only be used by the builder of the
487       TypeDataBase. Throws a RuntimeException if a long constant with
488       this name was already present. */
addLongConstant(String name, long value)489   public void addLongConstant(String name, long value) {
490     if (nameToLongConstantMap.get(name) != null) {
491       throw new RuntimeException("long constant of name \"" + name + "\" already present");
492     }
493 
494     nameToLongConstantMap.put(name, new Long(value));
495   }
496 
497   /** This method should only be used by the builder of the
498       TypeDataBase. Throws a RuntimeException if a long constant with
499       this name was not present. */
removeLongConstant(String name)500   public void removeLongConstant(String name) {
501     Long curConstant = (Long) nameToLongConstantMap.get(name);
502     if (curConstant == null) {
503       throw new RuntimeException("long constant of name \"" + name + "\" not present");
504     }
505 
506     nameToLongConstantMap.remove(name);
507   }
508 }
509