|
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 |
|
67 static 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 */ |
|
107 JNIEXPORT 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 */ |
|
181 typedef 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 */ |
|
191 typedef 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 */ |
|
202 static 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 */ |
|
223 static 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 */ |
|
277 JNIEXPORT 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 } |