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