1 /*
2  * Copyright (c) 2016, 2017, 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 #include <stdio.h>
25 #include <string.h>
26 #include "jvmti.h"
27 
28 #ifdef __cplusplus
29 extern "C" {
30 #endif
31 
32 #ifndef JNI_ENV_ARG
33 
34 #ifdef __cplusplus
35 #define JNI_ENV_ARG(x, y) y
36 #define JNI_ENV_PTR(x) x
37 #else
38 #define JNI_ENV_ARG(x,y) x, y
39 #define JNI_ENV_PTR(x) (*x)
40 #endif
41 
42 #endif
43 
44 #define TranslateError(err) "JVMTI error"
45 
46 #define PASSED 0
47 #define FAILED 2
48 
49 static const char *EXC_CNAME = "java/lang/Exception";
50 static const char* MOD_CNAME = "Ljava/lang/Module;";
51 
52 static jvmtiEnv *jvmti = NULL;
53 static jint result = PASSED;
54 static jboolean printdump = JNI_FALSE;
55 
56 static jint Agent_Initialize(JavaVM *jvm, char *options, void *reserved);
57 
58 JNIEXPORT
Agent_OnLoad(JavaVM * jvm,char * options,void * reserved)59 jint JNICALL Agent_OnLoad(JavaVM *jvm, char *options, void *reserved) {
60     return Agent_Initialize(jvm, options, reserved);
61 }
62 
63 JNIEXPORT
Agent_OnAttach(JavaVM * jvm,char * options,void * reserved)64 jint JNICALL Agent_OnAttach(JavaVM *jvm, char *options, void *reserved) {
65     return Agent_Initialize(jvm, options, reserved);
66 }
67 
68 JNIEXPORT
JNI_OnLoad(JavaVM * jvm,void * reserved)69 jint JNICALL JNI_OnLoad(JavaVM *jvm, void *reserved) {
70     return JNI_VERSION_1_8;
71 }
72 
73 static
Agent_Initialize(JavaVM * jvm,char * options,void * reserved)74 jint Agent_Initialize(JavaVM *jvm, char *options, void *reserved) {
75     jint res;
76 
77     if (options != NULL && strcmp(options, "printdump") == 0) {
78         printdump = JNI_TRUE;
79     }
80 
81     res = JNI_ENV_PTR(jvm)->GetEnv(JNI_ENV_ARG(jvm, (void **) &jvmti),
82         JVMTI_VERSION_9);
83     if (res != JNI_OK || jvmti == NULL) {
84         printf("    Error: wrong result of a valid call to GetEnv!\n");
85         return JNI_ERR;
86     }
87 
88     return JNI_OK;
89 }
90 
91 static
throw_exc(JNIEnv * env,char * msg)92 jint throw_exc(JNIEnv *env, char *msg) {
93     jclass exc_class = JNI_ENV_PTR(env)->FindClass(JNI_ENV_ARG(env, EXC_CNAME));
94 
95     if (exc_class == NULL) {
96         printf("throw_exc: Error in FindClass(env, %s)\n", EXC_CNAME);
97         return -1;
98     }
99     return JNI_ENV_PTR(env)->ThrowNew(JNI_ENV_ARG(env, exc_class), msg);
100 }
101 
102 static
get_class_loader(jclass cls)103 jobject get_class_loader(jclass cls) {
104     jvmtiError err = JVMTI_ERROR_NONE;
105     jobject loader = NULL;
106 
107     if (printdump == JNI_TRUE) {
108         printf(">>> getting class loader ...\n");
109     }
110     err = (*jvmti)->GetClassLoader(jvmti, cls, &loader);
111     if (err != JVMTI_ERROR_NONE) {
112         printf("    Error in GetClassLoader: %s (%d)\n", TranslateError(err), err);
113     }
114     return loader;
115 }
116 
117 static
jlM(JNIEnv * env)118 jclass jlM(JNIEnv *env) {
119     jclass cls = NULL;
120 
121     cls = JNI_ENV_PTR(env)->FindClass(JNI_ENV_ARG(env, MOD_CNAME));
122     if (cls == NULL) {
123         printf("    Error in JNI FindClass: %s\n", MOD_CNAME);
124     }
125     return cls;
126 }
127 
128 jmethodID
get_method(JNIEnv * env,jclass clazz,const char * name,const char * sig)129 get_method(JNIEnv *env, jclass clazz, const char * name, const char *sig) {
130     jmethodID method = NULL;
131 
132     method = JNI_ENV_PTR(env)->GetMethodID(JNI_ENV_ARG(env, clazz), name, sig);
133     if (method == NULL) {
134         printf("    Error in JNI GetMethodID %s with signature %s", name, sig);
135     }
136     return method;
137 }
138 
139 static
get_module_loader(JNIEnv * env,jobject module)140 jobject get_module_loader(JNIEnv *env, jobject module) {
141     static jmethodID cl_method = NULL;
142     jobject loader = NULL;
143 
144     if (cl_method == NULL) {
145         cl_method = get_method(env, jlM(env), "getClassLoader", "()Ljava/lang/ClassLoader;");
146     }
147     loader = (jobject)JNI_ENV_PTR(env)->CallObjectMethod(JNI_ENV_ARG(env, module), cl_method);
148     return loader;
149 }
150 
151 static
get_module_name(JNIEnv * env,jobject module)152 const char* get_module_name(JNIEnv *env, jobject module) {
153     static jmethodID method = NULL;
154     jobject loader = NULL;
155     jstring jstr = NULL;
156     const char *name = NULL;
157     const char *nstr = NULL;
158 
159     if (method == NULL) {
160         method = get_method(env, jlM(env), "getName", "()Ljava/lang/String;");
161     }
162     jstr = (jstring)JNI_ENV_PTR(env)->CallObjectMethod(JNI_ENV_ARG(env, module), method);
163     if (jstr != NULL) {
164         name = JNI_ENV_PTR(env)->GetStringUTFChars(JNI_ENV_ARG(env, jstr), NULL);
165     }
166     loader = get_module_loader(env, module);
167     nstr = (name == NULL) ? "<UNNAMED>" : name;
168     printf("    loader: %p, module: %p, name: %s\n", loader, module, nstr);
169     return name;
170 }
171 
172 static
get_module(JNIEnv * env,jobject loader,const char * pkg_name,jobject * module_ptr,const char ** mod_name_ptr)173 jvmtiError get_module(JNIEnv *env,
174                       jobject loader,
175                       const char* pkg_name,
176                       jobject* module_ptr,
177                       const char** mod_name_ptr) {
178     jvmtiError err = JVMTI_ERROR_NONE;
179     const char* name = (pkg_name == NULL) ? "<NULL>" : pkg_name;
180 
181     printf(">>> getting module by loader %p and package \"%s\"\n", loader, name);
182     *mod_name_ptr = NULL;
183     err = (*jvmti)->GetNamedModule(jvmti, loader, pkg_name, module_ptr);
184     if (err != JVMTI_ERROR_NONE) {
185         printf("    Error in GetNamedModule for package \"%s\": %s (%d)\n",
186                name, TranslateError(err), err);
187         return err;
188     }
189     printf("    returned module: %p\n", *module_ptr);
190     if (*module_ptr == NULL) { // named module was not found
191         return err;
192     }
193     *mod_name_ptr = get_module_name(env, *module_ptr);
194     return err;
195 }
196 
197 static
get_all_modules(JNIEnv * env)198 jint get_all_modules(JNIEnv *env) {
199     jvmtiError err;
200     jint cnt = -1;
201     jint idx = 0;
202     jobject* modules;
203 
204     printf(">>> Inspecting modules with GetAllModules\n");
205     err = (*jvmti)->GetAllModules(jvmti, &cnt, &modules);
206     if (err != JVMTI_ERROR_NONE) {
207         printf("Error in GetAllModules: %d\n", err);
208         return -1;
209     }
210     for (idx = 0; idx < cnt; ++idx) {
211         get_module_name(env, modules[idx]);
212     }
213     return cnt;
214 }
215 
216 static
check_bad_loader(JNIEnv * env,jobject loader)217 jint check_bad_loader(JNIEnv *env, jobject loader) {
218     jvmtiError err = JVMTI_ERROR_NONE;
219     jobject module = NULL;
220     const char* mod_name = NULL;
221 
222     err = get_module(env, loader, "", &module, &mod_name);
223     if (err != JVMTI_ERROR_ILLEGAL_ARGUMENT) {
224         return FAILED;
225     }
226     printf("    got expected JVMTI_ERROR_ILLEGAL_ARGUMENT for bad loader\n");
227     return PASSED;
228 }
229 
230 static
check_system_loader(JNIEnv * env,jobject loader)231 jint check_system_loader(JNIEnv *env, jobject loader) {
232     jvmtiError err = JVMTI_ERROR_NONE;
233     jobject module = NULL;
234     const char* exp_name = NULL;
235     const char* mod_name = NULL;
236 
237     // NULL pointer for package name
238     err = get_module(env, loader, NULL, &module, &mod_name);
239     if (err != JVMTI_ERROR_NULL_POINTER) {
240         throw_exc(env, "check #SN1: failed to return JVMTI_ERROR_NULL_POINTER for NULL package");
241         return FAILED;
242     }
243 
244     // NULL pointer for module_ptr
245     err = (*jvmti)->GetNamedModule(jvmti, loader, "", NULL);
246     if (err != JVMTI_ERROR_NULL_POINTER) {
247         throw_exc(env, "check #SN2: failed to return JVMTI_ERROR_NULL_POINTER for NULL module_ptr");
248         return FAILED;
249     }
250 
251     // Unnamed/default package ""
252     err = get_module(env, loader, "", &module, &mod_name);
253     if (err != JVMTI_ERROR_NONE) {
254         throw_exc(env, "check #S1: failed to return JVMTI_ERROR_NONE for default package");
255         return FAILED;
256     }
257     if (module != NULL || mod_name != NULL) {
258         throw_exc(env, "check #S2: failed to return NULL-module for default package");
259         return FAILED;
260     }
261 
262     // Test package: MyPackage
263     err = get_module(env, loader, "MyPackage", &module, &mod_name);
264     if (err != JVMTI_ERROR_NONE) {
265         throw_exc(env, "check #S3: failed to return JVMTI_ERROR_NONE for MyPackage");
266         return FAILED;
267     }
268     if (module != NULL || mod_name != NULL) {
269         throw_exc(env, "check #S4: failed to return NULL-module for MyPackage");
270         return FAILED;
271     }
272 
273     // Package: com/sun/jdi
274     exp_name = "jdk.jdi";
275     err = get_module(env, loader, "com/sun/jdi", &module, &mod_name);
276     if (err != JVMTI_ERROR_NONE) {
277         throw_exc(env, "check #S5: failed to return JVMTI_ERROR_NONE for test package");
278         return FAILED;
279     }
280     if (module == NULL || mod_name == NULL) {
281         throw_exc(env, "check #S6: failed to return named module for com/sun/jdi package");
282         return FAILED;
283     }
284     if (strcmp(mod_name, exp_name) != 0) {
285         printf("check #S7: failed to return right module, expected: %s, returned: %s\n",
286                exp_name, mod_name);
287         throw_exc(env, "check #S7: failed to return jdk.jdi module for com/sun/jdi package");
288         return FAILED;
289     }
290 
291     // Non-existing package: "bad/package/name"
292     err = get_module(env, loader, "bad/package/name", &module, &mod_name);
293     if (err != JVMTI_ERROR_NONE) {
294         throw_exc(env, "check #S8: failed to return JVMTI_ERROR_NONE for bad package");
295         return FAILED;
296     }
297     if (module != NULL || mod_name != NULL) {
298         throw_exc(env, "check #S9: failed to return NULL-module for bad package");
299         return FAILED;
300     }
301     return PASSED;
302 }
303 
304 static
check_bootstrap_loader(JNIEnv * env,jobject loader)305 jint check_bootstrap_loader(JNIEnv *env, jobject loader) {
306     jvmtiError err = JVMTI_ERROR_NONE;
307     jobject module = NULL;
308     const char* exp_name = NULL;
309     const char* mod_name = NULL;
310 
311     // NULL pointer for package name
312     err = get_module(env, loader, NULL, &module, &mod_name);
313     if (err != JVMTI_ERROR_NULL_POINTER) {
314         throw_exc(env, "check #BN1: failed to return JVMTI_ERROR_NULL_POINTER for NULL package");
315         return FAILED;
316     }
317 
318     // NULL pointer for module_ptr
319     err = (*jvmti)->GetNamedModule(jvmti, loader, "", NULL);
320     if (err != JVMTI_ERROR_NULL_POINTER) {
321         throw_exc(env, "check #BN2: failed to return JVMTI_ERROR_NULL_POINTER for NULL module_ptr");
322         return FAILED;
323     }
324 
325     // Unnamed/default package ""
326     err = get_module(env, loader, "", &module, &mod_name);
327     if (err != JVMTI_ERROR_NONE) {
328         throw_exc(env, "check #B1: failed to return JVMTI_ERROR_NONE for default package");
329         return FAILED;
330     }
331     if (module != NULL || mod_name != NULL) {
332         throw_exc(env, "check #B2: failed to return NULL-module for default package");
333         return FAILED;
334     }
335 
336     // Normal package from java.base module: "java/lang"
337     exp_name = "java.base";
338     err = get_module(env, loader, "java/lang", &module, &mod_name);
339     if (err != JVMTI_ERROR_NONE) {
340         throw_exc(env, "check #B3: failed to return JVMTI_ERROR_NONE for java/lang package");
341         return FAILED;
342     }
343     if (module == NULL || mod_name == NULL) {
344         throw_exc(env, "check #B4: failed to return named module for java/lang package");
345         return FAILED;
346     }
347     if (strcmp(exp_name, mod_name) != 0) {
348         printf("check #B5: failed to return right module, expected: %s, returned: %s\n",
349                exp_name, mod_name);
350         throw_exc(env, "check #B5: failed to return expected module for java/lang package");
351         return FAILED;
352     }
353 
354     // Non-existing package: "bad/package/name"
355     err = get_module(env, loader, "bad/package/name", &module, &mod_name);
356     if (err != JVMTI_ERROR_NONE) {
357         throw_exc(env, "check #B6: failed to return JVMTI_ERROR_NONE for bad package");
358         return FAILED;
359     }
360     if (module != NULL || mod_name != NULL) {
361         throw_exc(env, "check #B7: failed to return NULL-module for bad package");
362         return FAILED;
363     }
364     return PASSED;
365 }
366 
367 JNIEXPORT jint JNICALL
Java_MyPackage_GetNamedModuleTest_check(JNIEnv * env,jclass cls)368 Java_MyPackage_GetNamedModuleTest_check(JNIEnv *env, jclass cls) {
369     jobject loader = NULL;
370 
371     if (jvmti == NULL) {
372         throw_exc(env, "JVMTI client was not properly loaded!\n");
373         return FAILED;
374     }
375 
376     get_all_modules(env);
377 
378     printf("\n*** Check for bad ClassLoader ***\n\n");
379     result = check_bad_loader(env, (jobject)cls);
380     if (result != PASSED) {
381         throw_exc(env, "check #L1: failed to return JVMTI_ERROR_ILLEGAL_ARGUMENT for bad loader");
382         return result;
383     }
384 
385     loader = get_class_loader(cls);
386     if (loader == NULL) {
387         throw_exc(env, "check #L2: failed to return non-NULL loader for valid test class");
388         return FAILED;
389     }
390 
391     printf("\n*** Checks for System ClassLoader ***\n\n");
392     result = check_system_loader(env, loader);
393     if (result != PASSED) {
394         return result;
395     }
396 
397     printf("\n*** Checks for Bootstrap ClassLoader ***\n\n");
398     result = check_bootstrap_loader(env, NULL);
399 
400     return result;
401 }
402 
403 #ifdef __cplusplus
404 }
405 #endif
406