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