001 /* 002 * This file is part of the Jikes RVM project (http://jikesrvm.org). 003 * 004 * This file is licensed to You under the Eclipse Public License (EPL); 005 * You may not use this file except in compliance with the License. You 006 * may obtain a copy of the License at 007 * 008 * http://www.opensource.org/licenses/eclipse-1.0.php 009 * 010 * See the COPYRIGHT.txt file distributed with this work for information 011 * regarding copyright ownership. 012 */ 013 package org.jikesrvm.jni; 014 015 import org.jikesrvm.VM; 016 import org.jikesrvm.SizeConstants; 017 import org.jikesrvm.mm.mminterface.MemoryManager; 018 import org.jikesrvm.classloader.RVMMethod; 019 import org.jikesrvm.compilers.common.CompiledMethods; 020 import org.jikesrvm.runtime.Magic; 021 import org.jikesrvm.runtime.RuntimeEntrypoints; 022 import org.jikesrvm.scheduler.RVMThread; 023 import org.vmmagic.pragma.Entrypoint; 024 import org.vmmagic.pragma.Inline; 025 import org.vmmagic.pragma.NoInline; 026 import org.vmmagic.pragma.NonMoving; 027 import org.vmmagic.pragma.NonMovingAllocation; 028 import org.vmmagic.pragma.Uninterruptible; 029 import org.vmmagic.pragma.Unpreemptible; 030 import org.vmmagic.pragma.Untraced; 031 import org.vmmagic.unboxed.Address; 032 import org.vmmagic.unboxed.AddressArray; 033 import org.vmmagic.unboxed.ObjectReference; 034 import org.vmmagic.unboxed.Offset; 035 036 /** 037 * A JNIEnvironment is created for each Java thread. 038 */ 039 @NonMoving 040 public final class JNIEnvironment implements SizeConstants { 041 042 /** 043 * initial size for JNI refs, later grow as needed 044 */ 045 protected static final int JNIREFS_ARRAY_LENGTH = 100; 046 047 /** 048 * sometimes we put stuff onto the jnirefs array bypassing the code 049 * that makes sure that it does not overflow (evil assembly code in the 050 * jni stubs that would be painful to fix). So, we keep some space 051 * between the max value in JNIRefsMax and the actual size of the 052 * array. How much is governed by this field. 053 */ 054 protected static final int JNIREFS_FUDGE_LENGTH = 50; 055 056 /** 057 * This is the shared JNI function table used by native code 058 * to invoke methods in @link{JNIFunctions}. 059 */ 060 public static FunctionTable JNIFunctions; 061 062 /** 063 * For the PowerOpenABI we need a linkage triple instead of just 064 * a function pointer. 065 * This is an array of such triples that matches JNIFunctions. 066 */ 067 public static LinkageTripletTable LinkageTriplets; 068 069 /** 070 * This is the pointer to the shared JNIFunction table. 071 * When we invoke a native method, we adjust the pointer we 072 * pass to the native code such that this field is at offset 0. 073 * In other words, we turn a JNIEnvironment into a JNIEnv* 074 * by handing the native code an interior pointer to 075 * this object that points directly to this field. 076 */ 077 @SuppressWarnings({"unused", "UnusedDeclaration"}) 078 // used by native code 079 @Entrypoint 080 private final Address externalJNIFunctions = 081 VM.BuildForPowerOpenABI ? Magic.objectAsAddress(LinkageTriplets) : Magic.objectAsAddress(JNIFunctions); 082 083 /** 084 * For saving processor register on entry to native, 085 * to be restored on JNI call from native 086 */ 087 @Entrypoint 088 @Untraced 089 protected RVMThread savedTRreg; 090 091 /** 092 * For saving JTOC register on entry to native, 093 * to be restored on JNI call from native (only used on PowerPC) 094 */ 095 @Entrypoint 096 @Untraced 097 private final Address savedJTOC = VM.BuildForPowerPC ? Magic.getTocPointer() : Address.zero(); 098 099 /** 100 * When native code doesn't maintain a base pointer we can't chain 101 * through the base pointers when walking the stack. This field 102 * holds the basePointer on entry to the native code in such a case, 103 * and is pushed onto the stack if we re-enter Java code (e.g. to 104 * handle a JNI function). This field is currently only used on IA32. 105 */ 106 @Entrypoint 107 private Address basePointerOnEntryToNative = Address.fromIntSignExtend(0xF00BAAA1); 108 109 /** 110 * When transitioning between Java and C and back, we may want to stop a thread 111 * returning into Java and executing mutator code when a GC is in progress. 112 * When in C code, the C code may never return. In these situations we need a 113 * frame pointer at which to begin scanning the stack. This field holds this 114 * value. NB. these fields don't chain together on the stack as we walk through 115 * native frames by knowing their return addresses are outside of our heaps 116 */ 117 @Entrypoint 118 private Address JNITopJavaFP; 119 120 /** 121 * Currently pending exception (null if none) 122 */ 123 private Throwable pendingException; 124 private int hasPendingException; 125 126 /** 127 * true if the bottom stack frame is native, 128 * such as thread for CreateJVM or AttachCurrentThread 129 */ 130 private boolean alwaysHasNativeFrame; 131 132 /** 133 * references passed to native code 134 */ 135 @Entrypoint 136 @Untraced 137 public AddressArray JNIRefs; 138 private AddressArray JNIRefsShadow; 139 140 /** 141 * Offset of current top ref in JNIRefs array 142 */ 143 @Entrypoint 144 public int JNIRefsTop; 145 146 /** 147 * Offset of end (last entry) of JNIRefs array 148 */ 149 @Entrypoint 150 protected int JNIRefsMax; 151 152 /** 153 * Previous frame boundary in JNIRefs array. 154 * NB unused on IA32 155 */ 156 @Entrypoint 157 public int JNIRefsSavedFP; 158 159 /** 160 * Initialize a thread specific JNI environment. 161 */ 162 @NonMovingAllocation 163 public JNIEnvironment() { 164 JNIRefs = JNIRefsShadow = AddressArray.create(JNIREFS_ARRAY_LENGTH + JNIREFS_FUDGE_LENGTH); 165 JNIRefsTop = 0; 166 JNIRefsSavedFP = 0; 167 JNIRefsMax = (JNIREFS_ARRAY_LENGTH - 1) << LOG_BYTES_IN_ADDRESS; 168 alwaysHasNativeFrame = false; 169 } 170 171 /* 172 * accessor methods 173 */ 174 @Uninterruptible 175 public boolean hasNativeStackFrame() { 176 return alwaysHasNativeFrame || JNIRefsTop != 0; 177 } 178 179 @Uninterruptible 180 public Address topJavaFP() { 181 return JNITopJavaFP; 182 } 183 184 @Uninterruptible 185 public AddressArray refsArray() { 186 return JNIRefs; 187 } 188 189 @Uninterruptible 190 public int refsTop() { 191 return JNIRefsTop; 192 } 193 194 @Uninterruptible 195 public int savedRefsFP() { 196 return JNIRefsSavedFP; 197 } 198 199 /** 200 * Check push of reference can succeed 201 * @param ref object to be pushed 202 * @param canGrow can the JNI reference array be grown? 203 */ 204 @Uninterruptible("May be called from uninterruptible code") 205 @NoInline 206 private void checkPush(Object ref, boolean canGrow) { 207 final boolean debug=true; 208 VM._assert(MemoryManager.validRef(ObjectReference.fromObject(ref))); 209 if (JNIRefsTop < 0) { 210 if (debug) { 211 VM.sysWriteln("JNIRefsTop=", JNIRefsTop); 212 VM.sysWriteln("JNIRefs.length=", JNIRefs.length()); 213 } 214 VM.sysFail("unchecked push to negative offset!"); 215 } 216 if ((JNIRefsTop >> LOG_BYTES_IN_ADDRESS) >= JNIRefs.length()) { 217 if (debug) { 218 VM.sysWriteln("JNIRefsTop=", JNIRefsTop); 219 VM.sysWriteln("JNIRefs.length=", JNIRefs.length()); 220 } 221 VM.sysFail("unchecked pushes exceeded fudge length!"); 222 } 223 if (!canGrow) { 224 if ((JNIRefsTop+BYTES_IN_ADDRESS) >= JNIRefsMax) { 225 if (debug) { 226 VM.sysWriteln("JNIRefsTop=", JNIRefsTop); 227 VM.sysWriteln("JNIRefsMax=", JNIRefsMax); 228 } 229 VM.sysFail("unchecked push can't grow JNI refs!"); 230 } 231 } 232 } 233 234 /** 235 * Push a reference onto thread local JNIRefs stack. 236 * To be used by JNI functions when returning a reference 237 * back to JNI native C code. 238 * @param ref the object to put on stack 239 * @return offset of entry in JNIRefs stack 240 */ 241 public int pushJNIRef(Object ref) { 242 if (ref == null) { 243 return 0; 244 } else { 245 if (VM.VerifyAssertions) checkPush(ref, true); 246 JNIRefsTop += BYTES_IN_ADDRESS; 247 if (JNIRefsTop >= JNIRefsMax) { 248 JNIRefsMax *= 2; 249 replaceJNIRefs(AddressArray.create((JNIRefsMax >> LOG_BYTES_IN_ADDRESS) + JNIREFS_FUDGE_LENGTH)); 250 } 251 JNIRefs.set(JNIRefsTop >> LOG_BYTES_IN_ADDRESS, Magic.objectAsAddress(ref)); 252 return JNIRefsTop; 253 } 254 } 255 256 /** 257 * Atomically copy and install a new JNIRefArray 258 */ 259 @Uninterruptible 260 private void replaceJNIRefs(AddressArray newrefs) { 261 for (int i = 0; i < JNIRefs.length(); i++) { 262 newrefs.set(i, JNIRefs.get(i)); 263 } 264 JNIRefs = JNIRefsShadow = newrefs; 265 } 266 267 /** 268 * Push a JNI ref, used on entry to JNI 269 * NB only used for Intel 270 * @param ref reference to place on stack or value of saved frame pointer 271 * @param isRef false if the reference isn't a frame pointer 272 */ 273 @Uninterruptible("Encoding arguments on stack that won't be seen by GC") 274 @Inline 275 private int uninterruptiblePushJNIRef(Address ref, boolean isRef) { 276 if (isRef && ref.isZero()) { 277 return 0; 278 } else { 279 if (VM.VerifyAssertions) checkPush(isRef ? Magic.addressAsObject(ref) : null, false); 280 // we count all slots so that releasing them is straight forward 281 JNIRefsTop += BYTES_IN_ADDRESS; 282 // ensure null is always seen as slot zero 283 JNIRefs.set(JNIRefsTop >> LOG_BYTES_IN_ADDRESS, Magic.objectAsAddress(ref)); 284 return JNIRefsTop; 285 } 286 } 287 288 /** 289 * Save data and perform necessary conversions for entry into JNI. 290 * NB only used for Intel. 291 * 292 * @param encodedReferenceOffsets 293 * bit mask marking which elements on the stack hold objects that need 294 * encoding as JNI ref identifiers 295 */ 296 @Uninterruptible("Objects on the stack won't be recognized by GC, therefore don't allow GC") 297 @Entrypoint 298 public void entryToJNI(int encodedReferenceOffsets) { 299 // Save processor 300 savedTRreg = Magic.getThreadRegister(); 301 302 // Save frame pointer of calling routine, once so that native stack frames 303 // are skipped and once for use by GC 304 Address callersFP = Magic.getCallerFramePointer(Magic.getFramePointer()); 305 basePointerOnEntryToNative = callersFP; // NB old value saved on call stack 306 JNITopJavaFP = callersFP; 307 308 if (VM.traceJNI) { 309 RVMMethod m= 310 CompiledMethods.getCompiledMethod( 311 Magic.getCompiledMethodID(callersFP)).getMethod(); 312 VM.sysWrite("calling JNI from "); 313 VM.sysWrite(m.getDeclaringClass().getDescriptor()); 314 VM.sysWrite(" "); 315 VM.sysWrite(m.getName()); 316 VM.sysWrite(m.getDescriptor()); 317 VM.sysWriteln(); 318 } 319 320 // Save current JNI ref stack pointer 321 if (JNIRefsTop > 0) { 322 uninterruptiblePushJNIRef(Address.fromIntSignExtend(JNIRefsSavedFP), false); 323 JNIRefsSavedFP = JNIRefsTop; 324 } 325 326 // Convert arguments on stack from objects to JNI references 327 Address fp = Magic.getFramePointer(); 328 Offset argOffset = Offset.fromIntSignExtend(5*BYTES_IN_ADDRESS); 329 fp.store(uninterruptiblePushJNIRef(fp.loadAddress(argOffset),true), argOffset); 330 while (encodedReferenceOffsets != 0) { 331 argOffset = argOffset.plus(BYTES_IN_ADDRESS); 332 if ((encodedReferenceOffsets & 1) != 0) { 333 fp.store(uninterruptiblePushJNIRef(fp.loadAddress(argOffset), true), argOffset); 334 } 335 encodedReferenceOffsets >>>= 1; 336 } 337 // Transition processor from IN_JAVA to IN_JNI 338 RVMThread.enterJNIFromCallIntoNative(); 339 } 340 341 /** 342 * Restore data, throw pending exceptions or convert return value for exit 343 * from JNI. NB only used for Intel. 344 * 345 * @param offset 346 * offset into JNI reference tables of result 347 * @return Object encoded by offset or null if offset is 0 348 */ 349 @Unpreemptible("Don't allow preemption when we're not in a sane state. " + 350 "Code can throw exceptions so not uninterruptible.") 351 @Entrypoint 352 public Object exitFromJNI(int offset) { 353 // Transition processor from IN_JNI to IN_JAVA 354 RVMThread.leaveJNIFromCallIntoNative(); 355 356 // Restore JNI ref top and saved frame pointer 357 JNIRefsTop = 0; 358 if (JNIRefsSavedFP > 0) { 359 JNIRefsTop = JNIRefsSavedFP - BYTES_IN_ADDRESS; 360 JNIRefsSavedFP = JNIRefs.get(JNIRefsSavedFP >> LOG_BYTES_IN_ADDRESS).toInt(); 361 } 362 363 // Throw and clear any pending exceptions 364 if (pendingException != null) { 365 throwPendingException(); 366 } 367 368 // Lookup result 369 Object result; 370 if (offset == 0) { 371 result = null; 372 } else if (offset < 0) { 373 result = JNIGlobalRefTable.ref(offset); 374 } else { 375 result = Magic.addressAsObject(JNIRefs.get(offset >> LOG_BYTES_IN_ADDRESS)); 376 } 377 return result; 378 } 379 380 /** 381 * Get a reference from the JNIRefs stack. 382 * @param offset in JNIRefs stack 383 * @return reference at that offset 384 */ 385 public Object getJNIRef(int offset) { 386 if (offset > JNIRefsTop) { 387 VM.sysWrite("JNI ERROR: getJNIRef for illegal offset > TOP, "); 388 VM.sysWrite(offset); 389 VM.sysWrite("(top is "); 390 VM.sysWrite(JNIRefsTop); 391 VM.sysWrite(")\n"); 392 RVMThread.dumpStack(); 393 return null; 394 } 395 if (offset < 0) { 396 return JNIGlobalRefTable.ref(offset); 397 } else { 398 return Magic.addressAsObject(JNIRefs.get(offset >> LOG_BYTES_IN_ADDRESS)); 399 } 400 } 401 402 /** 403 * Remove a reference from the JNIRefs stack. 404 * @param offset in JNIRefs stack 405 */ 406 public void deleteJNIRef(int offset) { 407 if (offset > JNIRefsTop) { 408 VM.sysWrite("JNI ERROR: getJNIRef for illegal offset > TOP, "); 409 VM.sysWrite(offset); 410 VM.sysWrite("(top is "); 411 VM.sysWrite(JNIRefsTop); 412 VM.sysWrite(")\n"); 413 } 414 415 JNIRefs.set(offset >> LOG_BYTES_IN_ADDRESS, Address.zero()); 416 417 if (offset == JNIRefsTop) JNIRefsTop -= BYTES_IN_ADDRESS; 418 } 419 420 /** 421 * Dump the JNIRefs stack to the sysWrite stream 422 */ 423 @Uninterruptible 424 public void dumpJniRefsStack() { 425 int jniRefOffset = JNIRefsTop; 426 VM.sysWrite("\n* * dump of JNIEnvironment JniRefs Stack * *\n"); 427 VM.sysWrite("* JNIRefs = "); 428 VM.sysWrite(Magic.objectAsAddress(JNIRefs)); 429 VM.sysWrite(" * JNIRefsTop = "); 430 VM.sysWrite(JNIRefsTop); 431 VM.sysWrite(" * JNIRefsSavedFP = "); 432 VM.sysWrite(JNIRefsSavedFP); 433 VM.sysWrite(".\n*\n"); 434 while (jniRefOffset >= 0) { 435 VM.sysWrite(jniRefOffset); 436 VM.sysWrite(" "); 437 VM.sysWrite(Magic.objectAsAddress(JNIRefs).plus(jniRefOffset)); 438 VM.sysWrite(" "); 439 MemoryManager.dumpRef(JNIRefs.get(jniRefOffset >> LOG_BYTES_IN_ADDRESS).toObjectReference()); 440 jniRefOffset -= BYTES_IN_ADDRESS; 441 } 442 VM.sysWrite("\n* * end of dump * *\n"); 443 } 444 445 /** 446 * Record an exception as pending so that it will be delivered on the return 447 * to the Java caller; clear the exception by recording null 448 * @param e An exception or error 449 */ 450 public void recordException(Throwable e) { 451 // don't overwrite the first exception except to clear it 452 if (pendingException == null || e == null) { 453 pendingException = e; 454 hasPendingException = (e != null) ? 1 : 0; 455 } 456 } 457 458 /** 459 * Return and clear the (known to be non-null) pending exception. 460 */ 461 @Entrypoint 462 @Unpreemptible 463 public static void throwPendingException() { 464 JNIEnvironment me = RVMThread.getCurrentThread().getJNIEnv(); 465 if (VM.VerifyAssertions) VM._assert(me.pendingException != null); 466 Throwable pe = me.pendingException; 467 me.pendingException = null; 468 me.hasPendingException = 0; 469 RuntimeEntrypoints.athrow(pe); 470 } 471 472 /** 473 * @return the pending exception 474 */ 475 public Throwable getException() { 476 return pendingException; 477 } 478 479 /** 480 * Initialize the array of JNI functions. 481 * This function is called during bootimage writing. 482 */ 483 public static void initFunctionTable(FunctionTable functions) { 484 JNIFunctions = functions; 485 if (VM.BuildForPowerOpenABI) { 486 // Allocate the linkage triplets in the bootimage too (so they won't move) 487 LinkageTriplets = LinkageTripletTable.allocate(functions.length()); 488 for (int i = 0; i < functions.length(); i++) { 489 LinkageTriplets.set(i, AddressArray.create(3)); 490 } 491 } 492 } 493 494 /** 495 * Initialization required during VM booting; only does something if 496 * we are on a platform that needs linkage triplets. 497 */ 498 public static void boot() { 499 if (VM.BuildForPowerOpenABI) { 500 // fill in the TOC and IP entries for each linkage triplet 501 for (int i = 0; i < JNIFunctions.length(); i++) { 502 AddressArray triplet = LinkageTriplets.get(i); 503 triplet.set(1, Magic.getTocPointer()); 504 triplet.set(0, Magic.objectAsAddress(JNIFunctions.get(i))); 505 } 506 } 507 } 508 }