1 /* 2 * Copyright (c) 2012, 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 #include "sun_jvm_hotspot_asm_Disassembler.h" 26 27 /* 28 * This file implements a binding between Java and the hsdis 29 * dissasembler. It should compile on Linux/Solaris and Windows. 30 * The only platform dependent pieces of the code for doing 31 * dlopen/dlsym to find the entry point in hsdis. All the rest is 32 * standard JNI code. 33 */ 34 35 #ifdef _WINDOWS 36 // Disable CRT security warning against _snprintf 37 #pragma warning (disable : 4996) 38 39 #define snprintf _snprintf 40 #define vsnprintf _vsnprintf 41 42 #include <windows.h> 43 #include <sys/types.h> 44 #include <sys/stat.h> 45 #ifdef _DEBUG 46 #include <crtdbg.h> 47 #endif 48 49 #else 50 51 #include <string.h> 52 #include <dlfcn.h> 53 54 #ifndef __APPLE__ 55 #include <link.h> 56 #endif 57 58 #endif 59 60 #include <limits.h> 61 #include <stdio.h> 62 #include <stdarg.h> 63 #include <stdlib.h> 64 #include <errno.h> 65 66 #ifdef _WINDOWS 67 #define JVM_MAXPATHLEN _MAX_PATH 68 #else 69 #include <sys/param.h> 70 #define JVM_MAXPATHLEN MAXPATHLEN 71 #endif 72 73 74 #ifdef _WINDOWS 75 static int getLastErrorString(char *buf, size_t len) 76 { 77 long errval; 78 79 if ((errval = GetLastError()) != 0) 80 { 81 /* DOS error */ 82 size_t n = (size_t)FormatMessage( 83 FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS, 84 NULL, 85 errval, 86 0, 87 buf, 88 (DWORD)len, 89 NULL); 90 if (n > 3) { 91 /* Drop final '.', CR, LF */ 92 if (buf[n - 1] == '\n') n--; 93 if (buf[n - 1] == '\r') n--; 94 if (buf[n - 1] == '.') n--; 95 buf[n] = '\0'; 96 } 97 return (int)n; 98 } 99 100 if (errno != 0) 101 { 102 /* C runtime error that has no corresponding DOS error code */ 103 strerror_s(buf, len, errno); 104 return strlen(buf); 105 } 106 return 0; 107 } 108 #endif /* _WINDOWS */ 109 110 /* 111 * Class: sun_jvm_hotspot_asm_Disassembler 112 * Method: load_library 113 * Signature: (Ljava/lang/String;)L 114 */ 115 JNIEXPORT jlong JNICALL Java_sun_jvm_hotspot_asm_Disassembler_load_1library(JNIEnv * env, 116 jclass disclass, 117 jstring jrepath_s, 118 jstring libname_s) { 119 uintptr_t func = 0; 120 const char *error_message = NULL; 121 const char *jrepath = NULL; 122 const char *libname = NULL; 123 char buffer[JVM_MAXPATHLEN]; 124 125 #ifdef _WINDOWS 126 HINSTANCE hsdis_handle = (HINSTANCE) NULL; 127 #else 128 void* hsdis_handle = NULL; 129 #endif 130 131 jrepath = (*env)->GetStringUTFChars(env, jrepath_s, NULL); // like $JAVA_HOME/jre/lib/sparc/ 132 if (jrepath == NULL || (*env)->ExceptionOccurred(env)) { 133 return 0; 134 } 135 136 libname = (*env)->GetStringUTFChars(env, libname_s, NULL); 137 if (libname == NULL || (*env)->ExceptionOccurred(env)) { 138 (*env)->ReleaseStringUTFChars(env, jrepath_s, jrepath); 139 return 0; 140 } 141 142 /* Load the hsdis library */ 143 #ifdef _WINDOWS 144 hsdis_handle = LoadLibrary(libname); 145 if (hsdis_handle == NULL) { 146 snprintf(buffer, sizeof(buffer), "%s%s", jrepath, libname); 147 hsdis_handle = LoadLibrary(buffer); 148 } 149 if (hsdis_handle != NULL) { 150 func = (uintptr_t)GetProcAddress(hsdis_handle, "decode_instructions_virtual"); 151 } 152 if (func == 0) { 153 getLastErrorString(buffer, sizeof(buffer)); 154 error_message = buffer; 155 } 156 #else 157 hsdis_handle = dlopen(libname, RTLD_LAZY | RTLD_GLOBAL); 158 if (hsdis_handle == NULL) { 159 snprintf(buffer, sizeof(buffer), "%s%s", jrepath, libname); 160 hsdis_handle = dlopen(buffer, RTLD_LAZY | RTLD_GLOBAL); 161 } 162 if (hsdis_handle != NULL) { 163 func = (uintptr_t)dlsym(hsdis_handle, "decode_instructions_virtual"); 164 } 165 if (func == 0) { 166 error_message = dlerror(); 167 } 168 #endif 169 170 (*env)->ReleaseStringUTFChars(env, libname_s, libname); 171 (*env)->ReleaseStringUTFChars(env, jrepath_s, jrepath); 172 173 if (func == 0) { 174 /* Couldn't find entry point. error_message should contain some 175 * platform dependent error message. 176 */ 177 jclass eclass = (*env)->FindClass(env, "sun/jvm/hotspot/debugger/DebuggerException"); 178 if ((*env)->ExceptionOccurred(env)) { 179 /* Can't throw exception, probably OOM, so silently return 0 */ 180 return (jlong) 0; 181 } 182 183 (*env)->ThrowNew(env, eclass, error_message); 184 } 185 return (jlong)func; 186 } 187 188 /* signature of decode_instructions_virtual from hsdis.h */ 189 typedef void* (*decode_func)(uintptr_t start_va, uintptr_t end_va, 190 unsigned char* start, uintptr_t length, 191 void* (*event_callback)(void*, const char*, void*), 192 void* event_stream, 193 int (*printf_callback)(void*, const char*, ...), 194 void* printf_stream, 195 const char* options, 196 int newline); 197 198 /* container for call back state when decoding instructions */ 199 typedef struct { 200 JNIEnv* env; 201 jobject dis; 202 jobject visitor; 203 jmethodID handle_event; 204 jmethodID raw_print; 205 char buffer[4096]; 206 } decode_env; 207 208 209 /* event callback binding to Disassembler.handleEvent */ 210 static void* event_to_env(void* env_pv, const char* event, void* arg) { 211 jlong result = 0; 212 decode_env* denv = (decode_env*)env_pv; 213 JNIEnv* env = denv->env; 214 jstring event_string = (*env)->NewStringUTF(env, event); 215 if ((*env)->ExceptionOccurred(env)) { 216 return NULL; 217 } 218 219 result = (*env)->CallLongMethod(env, denv->dis, denv->handle_event, denv->visitor, 220 event_string, (jlong) (uintptr_t)arg); 221 if ((*env)->ExceptionOccurred(env)) { 222 /* ignore exceptions for now */ 223 (*env)->ExceptionClear(env); 224 return NULL; 225 } 226 227 return (void*)(uintptr_t)result; 228 } 229 230 /* printing callback binding to Disassembler.rawPrint */ 231 static int printf_to_env(void* env_pv, const char* format, ...) { 232 jstring output; 233 va_list ap; 234 int cnt; 235 decode_env* denv = (decode_env*)env_pv; 236 JNIEnv* env = denv->env; 237 size_t flen = strlen(format); 238 const char* raw = NULL; 239 240 if (flen == 0) return 0; 241 if (flen < 2 || 242 strchr(format, '%') == NULL) { 243 raw = format; 244 } else if (format[0] == '%' && format[1] == '%' && 245 strchr(format+2, '%') == NULL) { 246 // happens a lot on machines with names like %foo 247 flen--; 248 raw = format+1; 249 } 250 if (raw != NULL) { 251 jstring output = (*env)->NewStringUTF(env, raw); 252 if (!(*env)->ExceptionOccurred(env)) { 253 /* make sure that UTF allocation doesn't cause OOM */ 254 (*env)->CallVoidMethod(env, denv->dis, denv->raw_print, denv->visitor, output); 255 } 256 if ((*env)->ExceptionOccurred(env)) { 257 /* ignore exceptions for now */ 258 (*env)->ExceptionClear(env); 259 } 260 return (int) flen; 261 } 262 va_start(ap, format); 263 cnt = vsnprintf(denv->buffer, sizeof(denv->buffer), format, ap); 264 va_end(ap); 265 266 output = (*env)->NewStringUTF(env, denv->buffer); 267 if (!(*env)->ExceptionOccurred(env)) { 268 /* make sure that UTF allocation doesn't cause OOM */ 269 (*env)->CallVoidMethod(env, denv->dis, denv->raw_print, denv->visitor, output); 270 } 271 272 if ((*env)->ExceptionOccurred(env)) { 273 /* ignore exceptions for now */ 274 (*env)->ExceptionClear(env); 275 } 276 277 return cnt; 278 } 279 280 /* 281 * Class: sun_jvm_hotspot_asm_Disassembler 282 * Method: decode 283 * Signature: (Lsun/jvm/hotspot/asm/InstructionVisitor;J[BLjava/lang/String;J)V 284 */ 285 JNIEXPORT void JNICALL Java_sun_jvm_hotspot_asm_Disassembler_decode(JNIEnv * env, 286 jobject dis, 287 jobject visitor, 288 jlong startPc, 289 jbyteArray code, 290 jstring options_s, 291 jlong decode_instructions_virtual) { 292 jbyte *start = NULL; 293 jbyte *end = NULL; 294 jclass disclass = NULL; 295 const char *options = NULL; 296 decode_env denv; 297 298 start = (*env)->GetByteArrayElements(env, code, NULL); 299 if ((*env)->ExceptionOccurred(env)) { 300 return; 301 } 302 end = start + (*env)->GetArrayLength(env, code); 303 options = (*env)->GetStringUTFChars(env, options_s, NULL); 304 if ((*env)->ExceptionOccurred(env)) { 305 (*env)->ReleaseByteArrayElements(env, code, start, JNI_ABORT); 306 return; 307 } 308 disclass = (*env)->GetObjectClass(env, dis); 309 310 denv.env = env; 311 denv.dis = dis; 312 denv.visitor = visitor; 313 314 /* find Disassembler.handleEvent callback */ 315 denv.handle_event = (*env)->GetMethodID(env, disclass, "handleEvent", 316 "(Lsun/jvm/hotspot/asm/InstructionVisitor;Ljava/lang/String;J)J"); 317 if ((*env)->ExceptionOccurred(env)) { 318 (*env)->ReleaseByteArrayElements(env, code, start, JNI_ABORT); 319 (*env)->ReleaseStringUTFChars(env, options_s, options); 320 return; 321 } 322 323 /* find Disassembler.rawPrint callback */ 324 denv.raw_print = (*env)->GetMethodID(env, disclass, "rawPrint", 325 "(Lsun/jvm/hotspot/asm/InstructionVisitor;Ljava/lang/String;)V"); 326 if ((*env)->ExceptionOccurred(env)) { 327 (*env)->ReleaseByteArrayElements(env, code, start, JNI_ABORT); 328 (*env)->ReleaseStringUTFChars(env, options_s, options); 329 return; 330 } 331 332 /* decode the buffer */ 333 (*(decode_func)(uintptr_t)decode_instructions_virtual)((uintptr_t) startPc, 334 startPc + end - start, 335 (unsigned char*)start, 336 end - start, 337 &event_to_env, (void*) &denv, 338 &printf_to_env, (void*) &denv, 339 options, 0 /* newline */); 340 341 /* cleanup */ 342 (*env)->ReleaseByteArrayElements(env, code, start, JNI_ABORT); 343 (*env)->ReleaseStringUTFChars(env, options_s, options); 344 }