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