6782
|
1 /*
|
|
2 * Copyright 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 <strings.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 }
|