1 /* 2 * Copyright (c) 2003-2005 Sun Microsystems, Inc. All Rights Reserved. 3 * Copyright (c) 2013 JogAmp Community. 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 * - Redistribution of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 12 * - Redistribution in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * Neither the name of Sun Microsystems, Inc. or the names of 17 * contributors may be used to endorse or promote products derived from 18 * this software without specific prior written permission. 19 * 20 * This software is provided "AS IS," without a warranty of any kind. ALL 21 * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, 22 * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A 23 * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN 24 * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR 25 * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR 26 * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR 27 * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR 28 * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE 29 * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, 30 * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF 31 * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 32 * 33 * You acknowledge that this software is not designed or intended for use 34 * in the design, construction, operation or maintenance of any nuclear 35 * facility. 36 */ 37 package com.jogamp.gluegen.runtime; 38 39 import com.jogamp.common.os.DynamicLookupHelper; 40 import com.jogamp.common.util.SecurityUtil; 41 42 import java.io.BufferedOutputStream; 43 import java.io.File; 44 import java.io.FileOutputStream; 45 import java.io.IOException; 46 import java.io.PrintStream; 47 import java.lang.reflect.AccessibleObject; 48 import java.lang.reflect.Field; 49 import java.security.AccessController; 50 import java.security.PrivilegedAction; 51 import java.util.Iterator; 52 import java.util.LinkedHashSet; 53 import java.util.Map; 54 import java.util.Set; 55 import java.util.SortedMap; 56 import java.util.TreeMap; 57 58 /** 59 * Superclass for all generated ProcAddressTables. 60 * 61 * A ProcAddressTable is a cache of pointers to the dynamically-linkable C 62 * functions this autogenerated Java binding has exposed. Some 63 * libraries such as OpenGL, OpenAL and others define function pointer 64 * signatures rather than statically linkable entry points for the 65 * purposes of being able to query at run-time whether a particular 66 * extension is available. This table acts as a cache of these 67 * function pointers. Each function pointer is typically looked up at 68 * run-time by a platform-dependent mechanism such as dlsym(), 69 * wgl/glXGetProcAddress(), or alGetProcAddress(). If the field containing the function 70 * pointer is 0, the function is considered to be unavailable and can 71 * not be called. 72 * 73 * @author Kenneth Russel 74 * @author Michael Bien 75 * @author Sven Gothel 76 * 77 * @see FunctionAddressResolver 78 * @see DynamicLookupHelper 79 */ 80 public abstract class ProcAddressTable { 81 82 private static final String PROCADDRESS_VAR_PREFIX = "_addressof_"; 83 private static final int PROCADDRESS_VAR_PREFIX_LEN = PROCADDRESS_VAR_PREFIX.length(); 84 85 protected static boolean DEBUG; 86 protected static String DEBUG_PREFIX; 87 protected static int debugNum; 88 89 private final FunctionAddressResolver resolver; 90 91 static { AccessController.doPrivileged(new PrivilegedAction<Object>() { @Override public Object run() { DEBUG = (System.getProperty(R) != null); if (DEBUG) { DEBUG_PREFIX = System.getProperty(R); } return null; } })92 AccessController.doPrivileged(new PrivilegedAction<Object>() { 93 @Override 94 public Object run() { 95 DEBUG = (System.getProperty("jogamp.debug.ProcAddressHelper") != null); 96 if (DEBUG) { 97 DEBUG_PREFIX = System.getProperty("jogamp.debug.ProcAddressHelper.prefix"); 98 } 99 return null; 100 } 101 }); 102 } 103 ProcAddressTable()104 public ProcAddressTable() { 105 this(new One2OneResolver()); 106 } 107 ProcAddressTable(final FunctionAddressResolver resolver)108 public ProcAddressTable(final FunctionAddressResolver resolver) { 109 this.resolver = resolver; 110 } 111 112 113 /** 114 * Resets the complete table. 115 * <p> 116 * If a {@link SecurityManager} is installed, user needs link permissions 117 * for <b>all</b> libraries, i.e. for <code>new RuntimePermission("loadLibrary.*");</code>! 118 * </p> 119 * @throws SecurityException if user is not granted access for all libraries. 120 */ reset(final DynamicLookupHelper lookup)121 public void reset(final DynamicLookupHelper lookup) throws SecurityException, RuntimeException { 122 if(null==lookup) { 123 throw new RuntimeException("Passed null DynamicLookupHelper"); 124 } 125 126 final Field[] fields = getClass().getDeclaredFields(); 127 128 final PrintStream dout; 129 if (DEBUG) { 130 dout = getDebugOutStream(); 131 dout.println(getClass().getName()+".reset() (w/ "+fields.length+" prospective fields)"); 132 } else { 133 dout = null; 134 } 135 136 // All at once - performance. 137 AccessibleObject.setAccessible(fields, true); 138 lookup.claimAllLinkPermission(); 139 try { 140 for (int i = 0; i < fields.length; ++i) { 141 final String fieldName = fields[i].getName(); 142 if ( isAddressField(fieldName) ) { 143 final String funcName = fieldToFunctionName(fieldName); 144 setEntry(fields[i], funcName, lookup); 145 } 146 } 147 } finally { 148 lookup.releaseAllLinkPermission(); 149 } 150 151 if (DEBUG) { 152 dout.flush(); 153 if (DEBUG_PREFIX != null) { 154 dout.close(); 155 } 156 } 157 } 158 159 /** 160 * Initializes the mapping for a single function. 161 * <p> 162 * If a {@link SecurityManager} is installed, user needs link permissions 163 * for <b>all</b> libraries, i.e. for <code>new RuntimePermission("loadLibrary.*");</code>! 164 * </p> 165 * 166 * @throws IllegalArgumentException if this function is not in this table. 167 * @throws SecurityException if user is not granted access for all libraries. 168 */ initEntry(final String name, final DynamicLookupHelper lookup)169 public void initEntry(final String name, final DynamicLookupHelper lookup) throws SecurityException, IllegalArgumentException { 170 final Field addressField = fieldForFunction(name); 171 addressField.setAccessible(true); 172 setEntry(addressField, name, lookup); 173 } 174 setEntry(final Field addressField, final String funcName, final DynamicLookupHelper lookup)175 private final void setEntry(final Field addressField, final String funcName, final DynamicLookupHelper lookup) throws SecurityException { 176 try { 177 assert (addressField.getType() == Long.TYPE); 178 final long newProcAddress = resolver.resolve(funcName, lookup); // issues SecurityUtil.checkLinkPermission(String) 179 addressField.setLong(this, newProcAddress); 180 if (DEBUG) { 181 getDebugOutStream().println(" " + addressField.getName() + " -> 0x" + Long.toHexString(newProcAddress)); 182 } 183 } catch (final Exception e) { 184 throw new RuntimeException("Can not get proc address for method \"" 185 + funcName + "\": Couldn't set value of field \"" + addressField, e); 186 } 187 } 188 fieldToFunctionName(final String addressFieldName)189 private final String fieldToFunctionName(final String addressFieldName) { 190 return addressFieldName.substring(PROCADDRESS_VAR_PREFIX_LEN); 191 } 192 fieldForFunction(final String name)193 private final Field fieldForFunction(final String name) throws IllegalArgumentException { 194 try { 195 return getClass().getDeclaredField(PROCADDRESS_VAR_PREFIX + name); 196 } catch (final NoSuchFieldException ex) { 197 throw new IllegalArgumentException(getClass().getName() +" has no entry for the function '"+name+"'.", ex); 198 } 199 } 200 201 /** 202 * Warning: Returns an accessible probably protected field! 203 * <p> 204 * Caller should have checked link permissions 205 * for <b>all</b> libraries, i.e. for <code>new RuntimePermission("loadLibrary.*");</code> 206 * <i>if</i> exposing the field or address! 207 * </p> 208 */ fieldForFunctionInSec(final String name)209 private final Field fieldForFunctionInSec(final String name) throws IllegalArgumentException { 210 return AccessController.doPrivileged(new PrivilegedAction<Field>() { 211 @Override 212 public Field run() { 213 try { 214 final Field addressField = ProcAddressTable.this.getClass().getDeclaredField(PROCADDRESS_VAR_PREFIX + name); 215 addressField.setAccessible(true); // we need to read the protected value! 216 return addressField; 217 } catch (final NoSuchFieldException ex) { 218 throw new IllegalArgumentException(getClass().getName() +" has no entry for the function '"+name+"'.", ex); 219 } 220 } 221 } ); 222 } 223 224 private final boolean isAddressField(final String fieldName) { 225 return fieldName.startsWith(PROCADDRESS_VAR_PREFIX); 226 } 227 228 private final static PrintStream getDebugOutStream() { 229 PrintStream out = null; 230 if (DEBUG) { 231 if (DEBUG_PREFIX != null) { 232 try { 233 out = new PrintStream(new BufferedOutputStream(new FileOutputStream(DEBUG_PREFIX + File.separatorChar 234 + "procaddresstable-" + (++debugNum) + ".txt"))); 235 } catch (final IOException e) { 236 e.printStackTrace(); 237 out = System.err; 238 } 239 } else { 240 out = System.err; 241 } 242 } 243 return out; 244 } 245 246 /** 247 * Returns this table as map with the function name as key and the address as value. 248 */ 249 private final Map<String, Long> toMap() { 250 final SortedMap<String, Long> map = new TreeMap<String, Long>(); 251 252 final Field[] fields = getClass().getFields(); 253 try { 254 for (int i = 0; i < fields.length; ++i) { 255 final String addressFieldName = fields[i].getName(); 256 if (isAddressField(addressFieldName)) { 257 map.put(fieldToFunctionName(addressFieldName), (Long)fields[i].get(this)); 258 } 259 } 260 } catch (final IllegalArgumentException ex) { 261 throw new RuntimeException(ex); 262 } catch (final IllegalAccessException ex) { 263 throw new RuntimeException(ex); 264 } 265 266 return map; 267 } 268 269 /** 270 * Returns true only if non null function pointer to this function exists. 271 */ 272 public final boolean isFunctionAvailable(final String functionName) { 273 try{ 274 return isFunctionAvailableImpl(functionName); 275 } catch (final IllegalArgumentException ex) { 276 return false; 277 } 278 } 279 280 /** 281 * This is a convenience method to query the native function existence by name. 282 * <p> 283 * It lets you avoid having to 284 * manually compute the "{@link #PROCADDRESS_VAR_PREFIX} + <functionName>" 285 * member variable name and look it up via reflection. 286 * </p> 287 * 288 * @throws IllegalArgumentException if this function is not in this table. 289 */ 290 protected boolean isFunctionAvailableImpl(final String functionName) throws IllegalArgumentException { 291 final Field addressField = fieldForFunctionInSec(functionName); 292 try { 293 return 0 != addressField.getLong(this); 294 } catch (final IllegalAccessException ex) { 295 throw new RuntimeException(ex); 296 } 297 } 298 299 /** 300 * This is a convenience method to query the native function handle by name. 301 * <p> 302 * It lets you avoid having to 303 * manually compute the "{@link #PROCADDRESS_VAR_PREFIX} + <functionName>" 304 * member variable name and look it up via reflection. 305 * </p> 306 * <p> 307 * If a {@link SecurityManager} is installed, user needs link permissions 308 * for <b>all</b> libraries, i.e. for <code>new RuntimePermission("loadLibrary.*");</code>! 309 * </p> 310 * 311 * @throws IllegalArgumentException if this function is not in this table. 312 * @throws SecurityException if user is not granted access for all libraries. 313 */ 314 public long getAddressFor(final String functionName) throws SecurityException, IllegalArgumentException { 315 SecurityUtil.checkAllLinkPermission(); 316 final Field addressField = fieldForFunctionInSec(functionName); 317 try { 318 return addressField.getLong(this); 319 } catch (final IllegalAccessException ex) { 320 throw new RuntimeException(ex); 321 } 322 } 323 324 /** 325 * Returns all functions pointing to null. 326 */ 327 public final Set<String> getNullPointerFunctions() { 328 final Map<String, Long> table = toMap(); 329 final Set<String> nullPointers = new LinkedHashSet<String>(); 330 for (final Iterator<Map.Entry<String, Long>> it = table.entrySet().iterator(); it.hasNext();) { 331 final Map.Entry<String, Long> entry = it.next(); 332 final long address = entry.getValue().longValue(); 333 if(address == 0) { 334 nullPointers.add(entry.getKey()); 335 } 336 } 337 return nullPointers; 338 } 339 340 @Override 341 public final String toString() { 342 return getClass().getName()+""+toMap(); 343 } 344 345 private static class One2OneResolver implements FunctionAddressResolver { 346 @Override 347 public long resolve(final String name, final DynamicLookupHelper lookup) throws SecurityException { 348 return lookup.dynamicLookupFunction(name); 349 } 350 } 351 352 353 } 354