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