1 /*
2 * Copyright (c) 1999, 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. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25 #include <string.h>
26 #include "jni.h"
27
28 #include "jdwpTransport.h"
29 #include "shmemBase.h"
30 #include "sysShmem.h"
31 #include "sys.h"
32
33 /*
34 * The Shared Memory Transport Library.
35 *
36 * This module is an implementation of the Java Debug Wire Protocol Transport
37 * Service Provider Interface - see src/share/javavm/export/jdwpTransport.h.
38 */
39
40 static SharedMemoryTransport *transport = NULL; /* maximum of 1 transport */
41 static SharedMemoryConnection *connection = NULL; /* maximum of 1 connection */
42 static jdwpTransportCallback *callbacks;
43 static jboolean initialized;
44 static struct jdwpTransportNativeInterface_ interface;
45 static jdwpTransportEnv single_env = (jdwpTransportEnv)&interface;
46
47 /*
48 * Thread-local index to the per-thread error message
49 */
50 static int tlsIndex;
51
52 /*
53 * Return an error and record the error message associated with
54 * the error. Note the if (1==1) { } usage here is to avoid
55 * compilers complaining that a statement isn't reached which
56 * will arise if the semicolon (;) appears after the macro,
57 */
58 #define RETURN_ERROR(err, msg) \
59 if (1==1) { \
60 setLastError(err, msg); \
61 return err; \
62 }
63
64 /*
65 * Return an I/O error and record the error message.
66 */
67 #define RETURN_IO_ERROR(msg) RETURN_ERROR(JDWPTRANSPORT_ERROR_IO_ERROR, msg);
68
69
70 /*
71 * Set the error message for this thread. If the error is an I/O
72 * error then augment the supplied error message with the textual
73 * representation of the I/O error.
74 */
75 static void
setLastError(int err,char * newmsg)76 setLastError(int err, char *newmsg) {
77 char buf[255];
78 char *msg;
79
80 /* get any I/O first in case any system calls override errno */
81 if (err == JDWPTRANSPORT_ERROR_IO_ERROR) {
82 if (shmemBase_getlasterror(buf, sizeof(buf)) != SYS_OK) {
83 buf[0] = '\0';
84 }
85 }
86
87 /* free any current error */
88 msg = (char *)sysTlsGet(tlsIndex);
89 if (msg != NULL) {
90 (*callbacks->free)(msg);
91 }
92
93 /*
94 * For I/O errors append the I/O error message with to the
95 * supplied message. For all other errors just use the supplied
96 * message.
97 */
98 if (err == JDWPTRANSPORT_ERROR_IO_ERROR) {
99 char *join_str = ": ";
100 int msg_len = (int)strlen(newmsg) + (int)strlen(join_str) +
101 (int)strlen(buf) + 3;
102 msg = (*callbacks->alloc)(msg_len);
103 if (msg != NULL) {
104 strcpy(msg, newmsg);
105 strcat(msg, join_str);
106 strcat(msg, buf);
107 }
108 } else {
109 msg = (*callbacks->alloc)((int)strlen(newmsg)+1);
110 if (msg != NULL) {
111 strcpy(msg, newmsg);
112 }
113 }
114
115 /* Put a pointer to the message in TLS */
116 sysTlsPut(tlsIndex, msg);
117 }
118
119 static jdwpTransportError
handshake()120 handshake()
121 {
122 char *hello = "JDWP-Handshake";
123 unsigned int i;
124
125 for (i=0; i<strlen(hello); i++) {
126 jbyte b;
127 int rv = shmemBase_receiveByte(connection, &b);
128 if (rv != 0) {
129 RETURN_IO_ERROR("receive failed during handshake");
130 }
131 if ((char)b != hello[i]) {
132 RETURN_IO_ERROR("handshake failed - debugger sent unexpected message");
133 }
134 }
135
136 for (i=0; i<strlen(hello); i++) {
137 int rv = shmemBase_sendByte(connection, (jbyte)hello[i]);
138 if (rv != 0) {
139 RETURN_IO_ERROR("write failed during handshake");
140 }
141 }
142
143 return JDWPTRANSPORT_ERROR_NONE;
144 }
145
146
147 /*
148 * Return the capabilities of the shared memory transport. The shared
149 * memory transport supports both the attach and accept timeouts but
150 * doesn't support a handshake timeout.
151 */
152 static jdwpTransportError JNICALL
shmemGetCapabilities(jdwpTransportEnv * env,JDWPTransportCapabilities * capabilitiesPtr)153 shmemGetCapabilities(jdwpTransportEnv* env, JDWPTransportCapabilities *capabilitiesPtr)
154 {
155 JDWPTransportCapabilities result;
156
157 memset(&result, 0, sizeof(result));
158 result.can_timeout_attach = JNI_TRUE;
159 result.can_timeout_accept = JNI_TRUE;
160 result.can_timeout_handshake = JNI_FALSE;
161
162 *capabilitiesPtr = result;
163
164 return JDWPTRANSPORT_ERROR_NONE;
165 }
166
167
168 static jdwpTransportError JNICALL
shmemStartListening(jdwpTransportEnv * env,const char * address,char ** actualAddress)169 shmemStartListening(jdwpTransportEnv* env, const char *address, char **actualAddress)
170 {
171 jint rc;
172
173 if (connection != NULL || transport != NULL) {
174 RETURN_ERROR(JDWPTRANSPORT_ERROR_ILLEGAL_STATE, "already connected or already listening");
175 }
176
177 rc = shmemBase_listen(address, &transport);
178
179 /*
180 * If a name was selected by the function above, find it and return
181 * it in place of the original arg.
182 */
183 if (rc == SYS_OK) {
184 char *name;
185 char *name2;
186 rc = shmemBase_name(transport, &name);
187 if (rc == SYS_OK) {
188 name2 = (callbacks->alloc)((int)strlen(name) + 1);
189 if (name2 == NULL) {
190 RETURN_ERROR(JDWPTRANSPORT_ERROR_OUT_OF_MEMORY, "out of memory");
191 } else {
192 strcpy(name2, name);
193 *actualAddress = name2;
194 }
195 }
196 } else {
197 RETURN_IO_ERROR("failed to create shared memory listener");
198 }
199 return JDWPTRANSPORT_ERROR_NONE;
200 }
201
202 static jdwpTransportError JNICALL
shmemAccept(jdwpTransportEnv * env,jlong acceptTimeout,jlong handshakeTimeout)203 shmemAccept(jdwpTransportEnv* env, jlong acceptTimeout, jlong handshakeTimeout)
204 {
205 jint rc;
206
207 if (connection != NULL) {
208 RETURN_ERROR(JDWPTRANSPORT_ERROR_ILLEGAL_STATE, "already connected");
209 }
210 if (transport == NULL) {
211 RETURN_ERROR(JDWPTRANSPORT_ERROR_ILLEGAL_STATE, "transport not listening");
212 }
213
214 rc = shmemBase_accept(transport, (long)acceptTimeout, &connection);
215 if (rc != SYS_OK) {
216 if (rc == SYS_TIMEOUT) {
217 RETURN_ERROR(JDWPTRANSPORT_ERROR_TIMEOUT, "Timed out waiting for connection");
218 } else {
219 RETURN_IO_ERROR("failed to accept shared memory connection");
220 }
221 }
222
223 rc = handshake();
224 if (rc != JDWPTRANSPORT_ERROR_NONE) {
225 shmemBase_closeConnection(connection);
226 connection = NULL;
227 }
228 return rc;
229 }
230
231 static jdwpTransportError JNICALL
shmemStopListening(jdwpTransportEnv * env)232 shmemStopListening(jdwpTransportEnv* env)
233 {
234 if (transport != NULL) {
235 shmemBase_closeTransport(transport);
236 transport = NULL;
237 }
238 return JDWPTRANSPORT_ERROR_NONE;
239 }
240
241 static jdwpTransportError JNICALL
shmemAttach(jdwpTransportEnv * env,const char * address,jlong attachTimeout,jlong handshakeTimeout)242 shmemAttach(jdwpTransportEnv* env, const char *address, jlong attachTimeout, jlong handshakeTimeout)
243 {
244 jint rc;
245
246 if (connection != NULL) {
247 RETURN_ERROR(JDWPTRANSPORT_ERROR_ILLEGAL_STATE, "already connected");
248 }
249 rc = shmemBase_attach(address, (long)attachTimeout, &connection);
250 if (rc != SYS_OK) {
251 if (rc == SYS_NOMEM) {
252 RETURN_ERROR(JDWPTRANSPORT_ERROR_OUT_OF_MEMORY, "out of memory");
253 }
254 if (rc == SYS_TIMEOUT) {
255 RETURN_ERROR(JDWPTRANSPORT_ERROR_TIMEOUT, "Timed out waiting to attach");
256 }
257 RETURN_IO_ERROR("failed to attach to shared memory connection");
258 }
259
260 rc = handshake();
261 if (rc != JDWPTRANSPORT_ERROR_NONE) {
262 shmemBase_closeConnection(connection);
263 connection = NULL;
264 }
265 return rc;
266 }
267
268 static jdwpTransportError JNICALL
shmemWritePacket(jdwpTransportEnv * env,const jdwpPacket * packet)269 shmemWritePacket(jdwpTransportEnv* env, const jdwpPacket *packet)
270 {
271 if (packet == NULL) {
272 RETURN_ERROR(JDWPTRANSPORT_ERROR_ILLEGAL_ARGUMENT, "packet is null");
273 }
274 if (packet->type.cmd.len < JDWP_HEADER_SIZE) {
275 RETURN_ERROR(JDWPTRANSPORT_ERROR_ILLEGAL_ARGUMENT, "invalid length");
276 }
277 if (connection == NULL) {
278 RETURN_ERROR(JDWPTRANSPORT_ERROR_ILLEGAL_STATE, "not connected");
279 }
280 if (shmemBase_sendPacket(connection, packet) == SYS_OK) {
281 return JDWPTRANSPORT_ERROR_NONE;
282 } else {
283 RETURN_IO_ERROR("write packet failed");
284 }
285 }
286
287 static jdwpTransportError JNICALL
shmemReadPacket(jdwpTransportEnv * env,jdwpPacket * packet)288 shmemReadPacket(jdwpTransportEnv* env, jdwpPacket *packet)
289 {
290 if (packet == NULL) {
291 RETURN_ERROR(JDWPTRANSPORT_ERROR_ILLEGAL_ARGUMENT, "packet is null");
292 }
293 if (connection == NULL) {
294 RETURN_ERROR(JDWPTRANSPORT_ERROR_ILLEGAL_STATE, "not connected");
295 }
296 if (shmemBase_receivePacket(connection, packet) == SYS_OK) {
297 return JDWPTRANSPORT_ERROR_NONE;
298 } else {
299 RETURN_IO_ERROR("receive packet failed");
300 }
301 }
302
303 static jboolean JNICALL
shmemIsOpen(jdwpTransportEnv * env)304 shmemIsOpen(jdwpTransportEnv* env)
305 {
306 if (connection != NULL) {
307 return JNI_TRUE;
308 } else {
309 return JNI_FALSE;
310 }
311 }
312
313 static jdwpTransportError JNICALL
shmemClose(jdwpTransportEnv * env)314 shmemClose(jdwpTransportEnv* env)
315 {
316 SharedMemoryConnection* current_connection = connection;
317 if (current_connection != NULL) {
318 connection = NULL;
319 shmemBase_closeConnection(current_connection);
320 }
321 return JDWPTRANSPORT_ERROR_NONE;
322 }
323
324 /*
325 * Return the error message for this thread.
326 */
327 static jdwpTransportError JNICALL
shmemGetLastError(jdwpTransportEnv * env,char ** msgP)328 shmemGetLastError(jdwpTransportEnv* env, char **msgP)
329 {
330 char *msg = (char *)sysTlsGet(tlsIndex);
331 if (msg == NULL) {
332 return JDWPTRANSPORT_ERROR_MSG_NOT_AVAILABLE;
333 }
334 *msgP = (*callbacks->alloc)((int)strlen(msg)+1);
335 if (*msgP == NULL) {
336 return JDWPTRANSPORT_ERROR_OUT_OF_MEMORY;
337 }
338 strcpy(*msgP, msg);
339 return JDWPTRANSPORT_ERROR_NONE;
340 }
341
342 JNIEXPORT jint JNICALL
jdwpTransport_OnLoad(JavaVM * vm,jdwpTransportCallback * cbTablePtr,jint version,jdwpTransportEnv ** result)343 jdwpTransport_OnLoad(JavaVM *vm, jdwpTransportCallback* cbTablePtr,
344 jint version, jdwpTransportEnv** result)
345 {
346 if (version != JDWPTRANSPORT_VERSION_1_0) {
347 return JNI_EVERSION;
348 }
349 if (initialized) {
350 /*
351 * This library doesn't support multiple environments (yet)
352 */
353 return JNI_EEXIST;
354 }
355 initialized = JNI_TRUE;
356
357 /* initialize base shared memory system */
358 (void) shmemBase_initialize(vm, cbTablePtr);
359
360 /* save callbacks */
361 callbacks = cbTablePtr;
362
363 /* initialize interface table */
364 interface.GetCapabilities = &shmemGetCapabilities;
365 interface.Attach = &shmemAttach;
366 interface.StartListening = &shmemStartListening;
367 interface.StopListening = &shmemStopListening;
368 interface.Accept = &shmemAccept;
369 interface.IsOpen = &shmemIsOpen;
370 interface.Close = &shmemClose;
371 interface.ReadPacket = &shmemReadPacket;
372 interface.WritePacket = &shmemWritePacket;
373 interface.GetLastError = &shmemGetLastError;
374 *result = &single_env;
375
376 /* initialized TLS */
377 tlsIndex = sysTlsAlloc();
378
379 return JNI_OK;
380 }
381