1 /*
   2  * Copyright (c) 2003, 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.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package sun.instrument;
  27 
  28 import java.lang.instrument.UnmodifiableModuleException;
  29 import java.lang.reflect.Method;
  30 import java.lang.reflect.AccessibleObject;
  31 import java.lang.instrument.ClassFileTransformer;
  32 import java.lang.instrument.ClassDefinition;
  33 import java.lang.instrument.Instrumentation;
  34 import java.security.AccessController;
  35 import java.security.PrivilegedAction;
  36 import java.security.ProtectionDomain;
  37 import java.util.Collections;
  38 import java.util.ArrayList;
  39 import java.util.HashMap;
  40 import java.util.HashSet;
  41 import java.util.List;
  42 import java.util.Map;
  43 import java.util.Set;
  44 import java.util.jar.JarFile;
  45 
  46 import jdk.internal.module.Modules;
  47 
  48 /*
  49  * Copyright 2003 Wily Technology, Inc.
  50  */
  51 
  52 /**
  53  * The Java side of the JPLIS implementation. Works in concert with a native JVMTI agent
  54  * to implement the JPLIS API set. Provides both the Java API implementation of
  55  * the Instrumentation interface and utility Java routines to support the native code.
  56  * Keeps a pointer to the native data structure in a scalar field to allow native
  57  * processing behind native methods.
  58  */
  59 public class InstrumentationImpl implements Instrumentation {
  60     private final     TransformerManager      mTransformerManager;
  61     private           TransformerManager      mRetransfomableTransformerManager;
  62     // needs to store a native pointer, so use 64 bits
  63     private final     long                    mNativeAgent;
  64     private final     boolean                 mEnvironmentSupportsRedefineClasses;
  65     private volatile  boolean                 mEnvironmentSupportsRetransformClassesKnown;
  66     private volatile  boolean                 mEnvironmentSupportsRetransformClasses;
  67     private final     boolean                 mEnvironmentSupportsNativeMethodPrefix;
  68 
  69     private
  70     InstrumentationImpl(long    nativeAgent,
  71                         boolean environmentSupportsRedefineClasses,
  72                         boolean environmentSupportsNativeMethodPrefix) {
  73         mTransformerManager                    = new TransformerManager(false);
  74         mRetransfomableTransformerManager      = null;
  75         mNativeAgent                           = nativeAgent;
  76         mEnvironmentSupportsRedefineClasses    = environmentSupportsRedefineClasses;
  77         mEnvironmentSupportsRetransformClassesKnown = false; // false = need to ask
  78         mEnvironmentSupportsRetransformClasses = false;      // don't know yet
  79         mEnvironmentSupportsNativeMethodPrefix = environmentSupportsNativeMethodPrefix;
  80     }
  81 
  82     public void
  83     addTransformer(ClassFileTransformer transformer) {
  84         addTransformer(transformer, false);
  85     }
  86 
  87     public synchronized void
  88     addTransformer(ClassFileTransformer transformer, boolean canRetransform) {
  89         if (transformer == null) {
  90             throw new NullPointerException("null passed as 'transformer' in addTransformer");
  91         }
  92         if (canRetransform) {
  93             if (!isRetransformClassesSupported()) {
  94                 throw new UnsupportedOperationException(
  95                   "adding retransformable transformers is not supported in this environment");
  96             }
  97             if (mRetransfomableTransformerManager == null) {
  98                 mRetransfomableTransformerManager = new TransformerManager(true);
  99             }
 100             mRetransfomableTransformerManager.addTransformer(transformer);
 101             if (mRetransfomableTransformerManager.getTransformerCount() == 1) {
 102                 setHasRetransformableTransformers(mNativeAgent, true);
 103             }
 104         } else {
 105             mTransformerManager.addTransformer(transformer);
 106             if (mTransformerManager.getTransformerCount() == 1) {
 107                 setHasTransformers(mNativeAgent, true);
 108             }
 109         }
 110     }
 111 
 112     public synchronized boolean
 113     removeTransformer(ClassFileTransformer transformer) {
 114         if (transformer == null) {
 115             throw new NullPointerException("null passed as 'transformer' in removeTransformer");
 116         }
 117         TransformerManager mgr = findTransformerManager(transformer);
 118         if (mgr != null) {
 119             mgr.removeTransformer(transformer);
 120             if (mgr.getTransformerCount() == 0) {
 121                 if (mgr.isRetransformable()) {
 122                     setHasRetransformableTransformers(mNativeAgent, false);
 123                 } else {
 124                     setHasTransformers(mNativeAgent, false);
 125                 }
 126             }
 127             return true;
 128         }
 129         return false;
 130     }
 131 
 132     public boolean
 133     isModifiableClass(Class<?> theClass) {
 134         if (theClass == null) {
 135             throw new NullPointerException(
 136                          "null passed as 'theClass' in isModifiableClass");
 137         }
 138         return isModifiableClass0(mNativeAgent, theClass);
 139     }
 140 
 141     public boolean isModifiableModule(Module module) {
 142         if (module == null) {
 143             throw new NullPointerException("'module' is null");
 144         }
 145         return true;
 146     }
 147 
 148     public boolean
 149     isRetransformClassesSupported() {
 150         // ask lazily since there is some overhead
 151         if (!mEnvironmentSupportsRetransformClassesKnown) {
 152             mEnvironmentSupportsRetransformClasses = isRetransformClassesSupported0(mNativeAgent);
 153             mEnvironmentSupportsRetransformClassesKnown = true;
 154         }
 155         return mEnvironmentSupportsRetransformClasses;
 156     }
 157 
 158     public void
 159     retransformClasses(Class<?>... classes) {
 160         if (!isRetransformClassesSupported()) {
 161             throw new UnsupportedOperationException(
 162               "retransformClasses is not supported in this environment");
 163         }
 164         if (classes.length == 0) {
 165             return; // no-op
 166         }
 167         retransformClasses0(mNativeAgent, classes);
 168     }
 169 
 170     public boolean
 171     isRedefineClassesSupported() {
 172         return mEnvironmentSupportsRedefineClasses;
 173     }
 174 
 175     public void
 176     redefineClasses(ClassDefinition...  definitions)
 177             throws  ClassNotFoundException {
 178         if (!isRedefineClassesSupported()) {
 179             throw new UnsupportedOperationException("redefineClasses is not supported in this environment");
 180         }
 181         if (definitions == null) {
 182             throw new NullPointerException("null passed as 'definitions' in redefineClasses");
 183         }
 184         for (int i = 0; i < definitions.length; ++i) {
 185             if (definitions[i] == null) {
 186                 throw new NullPointerException("element of 'definitions' is null in redefineClasses");
 187             }
 188         }
 189         if (definitions.length == 0) {
 190             return; // short-circuit if there are no changes requested
 191         }
 192 
 193         redefineClasses0(mNativeAgent, definitions);
 194     }
 195 
 196     @SuppressWarnings("rawtypes")
 197     public Class[]
 198     getAllLoadedClasses() {
 199         return getAllLoadedClasses0(mNativeAgent);
 200     }
 201 
 202     @SuppressWarnings("rawtypes")
 203     public Class[]
 204     getInitiatedClasses(ClassLoader loader) {
 205         return getInitiatedClasses0(mNativeAgent, loader);
 206     }
 207 
 208     public long
 209     getObjectSize(Object objectToSize) {
 210         if (objectToSize == null) {
 211             throw new NullPointerException("null passed as 'objectToSize' in getObjectSize");
 212         }
 213         // Call the intrinsified method instead.
 214         return Runtime.sizeOf(objectToSize);
 215     }
 216 
 217     public void
 218     appendToBootstrapClassLoaderSearch(JarFile jarfile) {
 219         appendToClassLoaderSearch0(mNativeAgent, jarfile.getName(), true);
 220     }
 221 
 222     public void
 223     appendToSystemClassLoaderSearch(JarFile jarfile) {
 224         appendToClassLoaderSearch0(mNativeAgent, jarfile.getName(), false);
 225     }
 226 
 227     public boolean
 228     isNativeMethodPrefixSupported() {
 229         return mEnvironmentSupportsNativeMethodPrefix;
 230     }
 231 
 232     public synchronized void
 233     setNativeMethodPrefix(ClassFileTransformer transformer, String prefix) {
 234         if (!isNativeMethodPrefixSupported()) {
 235             throw new UnsupportedOperationException(
 236                    "setNativeMethodPrefix is not supported in this environment");
 237         }
 238         if (transformer == null) {
 239             throw new NullPointerException(
 240                        "null passed as 'transformer' in setNativeMethodPrefix");
 241         }
 242         TransformerManager mgr = findTransformerManager(transformer);
 243         if (mgr == null) {
 244             throw new IllegalArgumentException(
 245                        "transformer not registered in setNativeMethodPrefix");
 246         }
 247         mgr.setNativeMethodPrefix(transformer, prefix);
 248         String[] prefixes = mgr.getNativeMethodPrefixes();
 249         setNativeMethodPrefixes(mNativeAgent, prefixes, mgr.isRetransformable());
 250     }
 251 
 252     @Override
 253     public void redefineModule(Module module,
 254                                Set<Module> extraReads,
 255                                Map<String, Set<Module>> extraExports,
 256                                Map<String, Set<Module>> extraOpens,
 257                                Set<Class<?>> extraUses,
 258                                Map<Class<?>, List<Class<?>>> extraProvides)
 259     {
 260         if (!module.isNamed())
 261             return;
 262 
 263         if (!isModifiableModule(module))
 264             throw new UnmodifiableModuleException(module.getName());
 265 
 266         // copy and check reads
 267         extraReads = new HashSet<>(extraReads);
 268         if (extraReads.contains(null))
 269             throw new NullPointerException("'extraReads' contains null");
 270 
 271         // copy and check exports and opens
 272         extraExports = cloneAndCheckMap(module, extraExports);
 273         extraOpens = cloneAndCheckMap(module, extraOpens);
 274 
 275         // copy and check uses
 276         extraUses = new HashSet<>(extraUses);
 277         if (extraUses.contains(null))
 278             throw new NullPointerException("'extraUses' contains null");
 279 
 280         // copy and check provides
 281         Map<Class<?>, List<Class<?>>> tmpProvides = new HashMap<>();
 282         for (Map.Entry<Class<?>, List<Class<?>>> e : extraProvides.entrySet()) {
 283             Class<?> service = e.getKey();
 284             if (service == null)
 285                 throw new NullPointerException("'extraProvides' contains null");
 286             List<Class<?>> providers = new ArrayList<>(e.getValue());
 287             if (providers.isEmpty())
 288                 throw new IllegalArgumentException("list of providers is empty");
 289             providers.forEach(p -> {
 290                 if (p.getModule() != module)
 291                     throw new IllegalArgumentException(p + " not in " + module);
 292                 if (!service.isAssignableFrom(p))
 293                     throw new IllegalArgumentException(p + " is not a " + service);
 294             });
 295             tmpProvides.put(service, providers);
 296         }
 297         extraProvides = tmpProvides;
 298 
 299 
 300         // update reads
 301         extraReads.forEach(m -> Modules.addReads(module, m));
 302 
 303         // update exports
 304         for (Map.Entry<String, Set<Module>> e : extraExports.entrySet()) {
 305             String pkg = e.getKey();
 306             Set<Module> targets = e.getValue();
 307             targets.forEach(m -> Modules.addExports(module, pkg, m));
 308         }
 309 
 310         // update opens
 311         for (Map.Entry<String, Set<Module>> e : extraOpens.entrySet()) {
 312             String pkg = e.getKey();
 313             Set<Module> targets = e.getValue();
 314             targets.forEach(m -> Modules.addOpens(module, pkg, m));
 315         }
 316 
 317         // update uses
 318         extraUses.forEach(service -> Modules.addUses(module, service));
 319 
 320         // update provides
 321         for (Map.Entry<Class<?>, List<Class<?>>> e : extraProvides.entrySet()) {
 322             Class<?> service = e.getKey();
 323             List<Class<?>> providers = e.getValue();
 324             providers.forEach(p -> Modules.addProvides(module, service, p));
 325         }
 326     }
 327 
 328     private Map<String, Set<Module>>
 329         cloneAndCheckMap(Module module, Map<String, Set<Module>> map)
 330     {
 331         if (map.isEmpty())
 332             return Collections.emptyMap();
 333 
 334         Map<String, Set<Module>> result = new HashMap<>();
 335         Set<String> packages = module.getPackages();
 336         for (Map.Entry<String, Set<Module>> e : map.entrySet()) {
 337             String pkg = e.getKey();
 338             if (pkg == null)
 339                 throw new NullPointerException("package cannot be null");
 340             if (!packages.contains(pkg))
 341                 throw new IllegalArgumentException(pkg + " not in module");
 342             Set<Module> targets = new HashSet<>(e.getValue());
 343             if (targets.isEmpty())
 344                 throw new IllegalArgumentException("set of targets is empty");
 345             if (targets.contains(null))
 346                 throw new NullPointerException("set of targets cannot include null");
 347             result.put(pkg, targets);
 348         }
 349         return result;
 350     }
 351 
 352 
 353     private TransformerManager
 354     findTransformerManager(ClassFileTransformer transformer) {
 355         if (mTransformerManager.includesTransformer(transformer)) {
 356             return mTransformerManager;
 357         }
 358         if (mRetransfomableTransformerManager != null &&
 359                 mRetransfomableTransformerManager.includesTransformer(transformer)) {
 360             return mRetransfomableTransformerManager;
 361         }
 362         return null;
 363     }
 364 
 365 
 366     /*
 367      *  Natives
 368      */
 369     private native boolean
 370     isModifiableClass0(long nativeAgent, Class<?> theClass);
 371 
 372     private native boolean
 373     isRetransformClassesSupported0(long nativeAgent);
 374 
 375     private native void
 376     setHasTransformers(long nativeAgent, boolean has);
 377 
 378     private native void
 379     setHasRetransformableTransformers(long nativeAgent, boolean has);
 380 
 381     private native void
 382     retransformClasses0(long nativeAgent, Class<?>[] classes);
 383 
 384     private native void
 385     redefineClasses0(long nativeAgent, ClassDefinition[]  definitions)
 386         throws  ClassNotFoundException;
 387 
 388     @SuppressWarnings("rawtypes")
 389     private native Class[]
 390     getAllLoadedClasses0(long nativeAgent);
 391 
 392     @SuppressWarnings("rawtypes")
 393     private native Class[]
 394     getInitiatedClasses0(long nativeAgent, ClassLoader loader);
 395 
 396     private native long
 397     getObjectSize0(long nativeAgent, Object objectToSize);
 398 
 399     private native void
 400     appendToClassLoaderSearch0(long nativeAgent, String jarfile, boolean bootLoader);
 401 
 402     private native void
 403     setNativeMethodPrefixes(long nativeAgent, String[] prefixes, boolean isRetransformable);
 404 
 405     static {
 406         System.loadLibrary("instrument");
 407     }
 408 
 409     /*
 410      *  Internals
 411      */
 412 
 413 
 414     // Enable or disable Java programming language access checks on a
 415     // reflected object (for example, a method)
 416     private static void setAccessible(final AccessibleObject ao, final boolean accessible) {
 417         AccessController.doPrivileged(new PrivilegedAction<Object>() {
 418                 public Object run() {
 419                     ao.setAccessible(accessible);
 420                     return null;
 421                 }});
 422     }
 423 
 424     // Attempt to load and start an agent
 425     private void
 426     loadClassAndStartAgent( String  classname,
 427                             String  methodname,
 428                             String  optionsString)
 429             throws Throwable {
 430 
 431         ClassLoader mainAppLoader   = ClassLoader.getSystemClassLoader();
 432         Class<?>    javaAgentClass  = mainAppLoader.loadClass(classname);
 433 
 434         Method m = null;
 435         NoSuchMethodException firstExc = null;
 436         boolean twoArgAgent = false;
 437 
 438         // The agent class must have a premain or agentmain method that
 439         // has 1 or 2 arguments. We check in the following order:
 440         //
 441         // 1) declared with a signature of (String, Instrumentation)
 442         // 2) declared with a signature of (String)
 443         // 3) inherited with a signature of (String, Instrumentation)
 444         // 4) inherited with a signature of (String)
 445         //
 446         // So the declared version of either 1-arg or 2-arg always takes
 447         // primary precedence over an inherited version. After that, the
 448         // 2-arg version takes precedence over the 1-arg version.
 449         //
 450         // If no method is found then we throw the NoSuchMethodException
 451         // from the first attempt so that the exception text indicates
 452         // the lookup failed for the 2-arg method (same as JDK5.0).
 453 
 454         try {
 455             m = javaAgentClass.getDeclaredMethod( methodname,
 456                                  new Class<?>[] {
 457                                      String.class,
 458                                      java.lang.instrument.Instrumentation.class
 459                                  }
 460                                );
 461             twoArgAgent = true;
 462         } catch (NoSuchMethodException x) {
 463             // remember the NoSuchMethodException
 464             firstExc = x;
 465         }
 466 
 467         if (m == null) {
 468             // now try the declared 1-arg method
 469             try {
 470                 m = javaAgentClass.getDeclaredMethod(methodname,
 471                                                  new Class<?>[] { String.class });
 472             } catch (NoSuchMethodException x) {
 473                 // ignore this exception because we'll try
 474                 // two arg inheritance next
 475             }
 476         }
 477 
 478         if (m == null) {
 479             // now try the inherited 2-arg method
 480             try {
 481                 m = javaAgentClass.getMethod( methodname,
 482                                  new Class<?>[] {
 483                                      String.class,
 484                                      java.lang.instrument.Instrumentation.class
 485                                  }
 486                                );
 487                 twoArgAgent = true;
 488             } catch (NoSuchMethodException x) {
 489                 // ignore this exception because we'll try
 490                 // one arg inheritance next
 491             }
 492         }
 493 
 494         if (m == null) {
 495             // finally try the inherited 1-arg method
 496             try {
 497                 m = javaAgentClass.getMethod(methodname,
 498                                              new Class<?>[] { String.class });
 499             } catch (NoSuchMethodException x) {
 500                 // none of the methods exists so we throw the
 501                 // first NoSuchMethodException as per 5.0
 502                 throw firstExc;
 503             }
 504         }
 505 
 506         // the premain method should not be required to be public,
 507         // make it accessible so we can call it
 508         // Note: The spec says the following:
 509         //     The agent class must implement a public static premain method...
 510         setAccessible(m, true);
 511 
 512         // invoke the 1 or 2-arg method
 513         if (twoArgAgent) {
 514             m.invoke(null, new Object[] { optionsString, this });
 515         } else {
 516             m.invoke(null, new Object[] { optionsString });
 517         }
 518     }
 519 
 520     // WARNING: the native code knows the name & signature of this method
 521     private void
 522     loadClassAndCallPremain(    String  classname,
 523                                 String  optionsString)
 524             throws Throwable {
 525 
 526         loadClassAndStartAgent( classname, "premain", optionsString );
 527     }
 528 
 529 
 530     // WARNING: the native code knows the name & signature of this method
 531     private void
 532     loadClassAndCallAgentmain(  String  classname,
 533                                 String  optionsString)
 534             throws Throwable {
 535 
 536         loadClassAndStartAgent( classname, "agentmain", optionsString );
 537     }
 538 
 539     // WARNING: the native code knows the name & signature of this method
 540     private byte[]
 541     transform(  Module              module,
 542                 ClassLoader         loader,
 543                 String              classname,
 544                 Class<?>            classBeingRedefined,
 545                 ProtectionDomain    protectionDomain,
 546                 byte[]              classfileBuffer,
 547                 boolean             isRetransformer) {
 548         TransformerManager mgr = isRetransformer?
 549                                         mRetransfomableTransformerManager :
 550                                         mTransformerManager;
 551         // module is null when not a class load or when loading a class in an
 552         // unnamed module and this is the first type to be loaded in the package.
 553         if (module == null) {
 554             if (classBeingRedefined != null) {
 555                 module = classBeingRedefined.getModule();
 556             } else {
 557                 module = (loader == null) ? jdk.internal.loader.BootLoader.getUnnamedModule()
 558                                           : loader.getUnnamedModule();
 559             }
 560         }
 561         if (mgr == null) {
 562             return null; // no manager, no transform
 563         } else {
 564             return mgr.transform(   module,
 565                                     loader,
 566                                     classname,
 567                                     classBeingRedefined,
 568                                     protectionDomain,
 569                                     classfileBuffer);
 570         }
 571     }
 572 
 573 
 574     /**
 575      * Invoked by the java launcher to load a java agent that is packaged with
 576      * the main application in an executable JAR file.
 577      */
 578     public static void loadAgent(String path) {
 579         loadAgent0(path);
 580     }
 581 
 582     private static native void loadAgent0(String path);
 583 }