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 * disassembler. 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
75static 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 */
115JNIEXPORT 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 */
189typedef 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 */
199typedef 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 */
210static 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 */
231static 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 */
285JNIEXPORT 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}
345