1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25
26 /*
27 * Copyright 2018 Joyent, Inc.
28 * Copyright (c) 2013 by Delphix. All rights reserved.
29 * Copyright 2023 Oxide Computer Company
30 */
31
32 #include <sys/types.h>
33 #include <sys/uio.h>
34 #include <string.h>
35 #include <errno.h>
36 #include <limits.h>
37
38 #include "Pcontrol.h"
39 #include "P32ton.h"
40
41 /*
42 * This file implements the routines to read and write per-lwp register
43 * information from either a live process or core file opened with libproc. We
44 * build up a few common routines for reading and writing register information,
45 * and then the public functions are all trivial calls to these. It also
46 * implements similar logic that is used with an lwp handle.
47 *
48 * The primary registers and floating point registers (e.g. regs,fpregs) are
49 * retreived from the lwp and process status files. The library caches the
50 * values of these files. When we perorm updates, we ensure that cached copies
51 * are refreshed or updated as part of this.
52 */
53
54 /*
55 * Utility function to return a pointer to the structure of cached information
56 * about an lwp in the core file, given its lwpid.
57 */
58 static lwp_info_t *
59 getlwpcore(struct ps_prochandle *P, lwpid_t lwpid)
60 {
61 core_info_t *core = P->data;
62 lwp_info_t *lwp;
63
64 for (lwp = list_head(&core->core_lwp_head); lwp != NULL;
65 lwp = list_next(&core->core_lwp_head, lwp)) {
66 if (lwp->lwp_id == lwpid)
67 return (lwp);
68 }
69
70 errno = ENOENT;
71 return (NULL);
72 }
73
74 /*
75 * Utility function to open and read the contents of a per-lwp /proc file.
76 * This function is used to slurp in lwpstatus, lwpname, lwpsinfo, spymaster,
77 * and others.
78 */
79 static int
80 getlwpfile(struct ps_prochandle *P, lwpid_t lwpid,
81 const char *fbase, void *rp, size_t n)
82 {
83 char fname[PATH_MAX];
84 int fd;
85
86 (void) snprintf(fname, sizeof (fname), "%s/%d/lwp/%d/%s",
87 procfs_path, (int)P->status.pr_pid, (int)lwpid, fbase);
88
89 if ((fd = open(fname, O_RDONLY)) >= 0) {
90 if (read(fd, rp, n) > 0) {
91 (void) close(fd);
92 return (0);
93 }
94
95 int e = errno;
96 (void) close(fd);
97 errno = e;
98 }
99 return (-1);
100 }
101
102 /*
103 * This is a variant of getlwpfile that has three different semantics:
104 *
105 * o It will stat the file to determine the size and allocate that for the
106 * caller.
107 * o If the stat size is zero (e.g. traditional xregs behavior when
108 * unsupported) then it will return the libproc ENODATA error.
109 * o It is an error if not all the data is read.
110 *
111 * Currently this is just used by xregs.
112 */
113 static int
114 getlwpfile_alloc(struct ps_prochandle *P, lwpid_t lwpid, const char *fbase,
115 void **datap, size_t *sizep)
116 {
117 char fname[PATH_MAX];
118 int fd;
119
120 (void) snprintf(fname, sizeof (fname), "%s/%d/lwp/%d/%s",
121 procfs_path, (int)P->status.pr_pid, (int)lwpid, fbase);
122
123 if ((fd = open(fname, O_RDONLY)) >= 0) {
124 int e;
125 struct stat st;
126
127 if (fstat(fd, &st) == 0) {
128 prxregset_t *prx;
129
130 if (st.st_size == 0) {
131 errno = ENODATA;
132 goto clean;
133 }
134
135 prx = malloc(st.st_size);
136 if (prx == NULL) {
137 goto clean;
138 }
139
140 if (read(fd, prx, st.st_size) == st.st_size) {
141 (void) close(fd);
142 *datap = prx;
143 *sizep = st.st_size;
144 return (0);
145 }
146
147 free(prx);
148 }
149 clean:
150 e = errno;
151 (void) close(fd);
152 errno = e;
153 }
154
155 return (-1);
156 }
157
158 /*
159 * Get the lwpstatus_t for an lwp from either the live process or our
160 * cached information from the core file. This is used to get the
161 * general-purpose registers or floating point registers.
162 */
163 int
164 getlwpstatus(struct ps_prochandle *P, lwpid_t lwpid, lwpstatus_t *lps)
165 {
166 lwp_info_t *lwp;
167
168 /*
169 * For both live processes and cores, our job is easy if the lwpid
170 * matches that of the representative lwp:
171 */
172 if (P->status.pr_lwp.pr_lwpid == lwpid) {
173 (void) memcpy(lps, &P->status.pr_lwp, sizeof (lwpstatus_t));
174 return (0);
175 }
176
177 /*
178 * If this is a live process, then just read the information out
179 * of the per-lwp status file:
180 */
181 if (P->state != PS_DEAD) {
182 return (getlwpfile(P, lwpid, "lwpstatus",
183 lps, sizeof (lwpstatus_t)));
184 }
185
186 /*
187 * If this is a core file, we need to iterate through our list of
188 * cached lwp information and then copy out the status.
189 */
190 if (P->data != NULL && (lwp = getlwpcore(P, lwpid)) != NULL) {
191 (void) memcpy(lps, &lwp->lwp_status, sizeof (lwpstatus_t));
192 return (0);
193 }
194
195 return (-1);
196 }
197
198 /*
199 * libproc caches information about the registers for representative LWPs and
200 * threads which we have the thread handle for. When we do a write to certain
201 * files, we need to refresh state and take care of both the process and the
202 * representative LWP's info. Because the xregs may or may not mutate the state
203 * of the other regsiters, we just always do a refresh of the entire cached
204 * psinfo.
205 */
206 static void
207 refresh_status(struct ps_prochandle *P, lwpid_t lwpid, struct ps_lwphandle *L,
208 long cmd, const void *rp, size_t n)
209 {
210 if (P->status.pr_lwp.pr_lwpid == lwpid) {
211 if (cmd == PCSREG)
212 (void) memcpy(P->status.pr_lwp.pr_reg, rp, n);
213 else if (cmd == PCSFPREG)
214 (void) memcpy(&P->status.pr_lwp.pr_fpreg, rp, n);
215 else if (cmd == PCSXREG)
216 (void) Pstopstatus(P, PCNULL, 0);
217 }
218
219 if (L != NULL) {
220 if (cmd == PCSREG)
221 (void) memcpy(&L->lwp_status.pr_reg, rp, n);
222 else if (cmd == PCSFPREG)
223 (void) memcpy(&L->lwp_status.pr_fpreg, rp, n);
224 else if (cmd == PCSXREG)
225 (void) Lstopstatus(L, PCNULL, 0);
226 }
227 }
228
229 /*
230 * Utility function to modify lwp registers. This is done using either the
231 * process control file or per-lwp control file as necessary. This assumes that
232 * we have a process-level hold on things, which may not always be true.
233 */
234 static int
235 setlwpregs_proc(struct ps_prochandle *P, lwpid_t lwpid, long cmd,
236 const void *rp, size_t n)
237 {
238 iovec_t iov[2];
239 char fname[PATH_MAX];
240 struct ps_lwphandle *L;
241 int fd = -1;
242
243 if (P->state != PS_STOP) {
244 errno = EBUSY;
245 return (-1);
246 }
247
248 iov[0].iov_base = (caddr_t)&cmd;
249 iov[0].iov_len = sizeof (long);
250 iov[1].iov_base = (caddr_t)rp;
251 iov[1].iov_len = n;
252
253 /*
254 * If we have an lwp handle for this thread, then make sure that we use
255 * that to update the state so cached information is updated. We sync
256 * the thread ahead of the process.
257 */
258 if ((L = Lfind(P, lwpid)) != NULL) {
259 Lsync(L);
260 fd = L->lwp_ctlfd;
261 }
262
263 /*
264 * Writing the process control file writes the representative lwp.
265 * Psync before we write to make sure we are consistent with the
266 * primary interfaces. Similarly, make sure to update P->status
267 * afterward if we are modifying one of its register sets. On some
268 * platforms the xregs modify the other register states. As a result,
269 * always refresh the representative LWP's status.
270 */
271 if (P->status.pr_lwp.pr_lwpid == lwpid) {
272 Psync(P);
273 fd = P->ctlfd;
274 }
275
276 if (fd > -1) {
277 if (writev(fd, iov, 2) == -1)
278 return (-1);
279 refresh_status(P, lwpid, L, cmd, rp, n);
280 return (0);
281 }
282
283 /*
284 * If the lwp we want is not the representative lwp, we need to
285 * open the ctl file for that specific lwp.
286 */
287 (void) snprintf(fname, sizeof (fname), "%s/%d/lwp/%d/lwpctl",
288 procfs_path, (int)P->status.pr_pid, (int)lwpid);
289
290 if ((fd = open(fname, O_WRONLY)) >= 0) {
291 if (writev(fd, iov, 2) > 0) {
292 (void) close(fd);
293 return (0);
294 }
295 int e = errno;
296 (void) close(fd);
297 errno = e;
298 }
299 return (-1);
300 }
301
302 /*
303 * This is a variant of the above that only assumes we have a hold on the thread
304 * as opposed to a process.
305 */
306 static int
307 setlwpregs_lwp(struct ps_lwphandle *L, long cmd, const void *rp, size_t n)
308 {
309 iovec_t iov[2];
310
311 if (L->lwp_state != PS_STOP) {
312 errno = EBUSY;
313 return (-1);
314 }
315
316 iov[0].iov_base = (caddr_t)&cmd;
317 iov[0].iov_len = sizeof (long);
318 iov[1].iov_base = (caddr_t)rp;
319 iov[1].iov_len = n;
320
321 Lsync(L);
322 if (writev(L->lwp_ctlfd, iov, 2) == -1)
323 return (-1);
324 refresh_status(L->lwp_proc, L->lwp_id, L, cmd, rp, n);
325
326 return (0);
327 }
328
329 int
330 Plwp_getregs(struct ps_prochandle *P, lwpid_t lwpid, prgregset_t gregs)
331 {
332 lwpstatus_t lps;
333
334 if (getlwpstatus(P, lwpid, &lps) == -1)
335 return (-1);
336
337 (void) memcpy(gregs, lps.pr_reg, sizeof (prgregset_t));
338 return (0);
339 }
340
341 int
342 Lgetregs(struct ps_lwphandle *L, prgregset_t *gregs)
343 {
344 (void) memcpy(gregs, L->lwp_status.pr_reg, sizeof (prgregset_t));
345 return (0);
346 }
347
348 int
349 Plwp_setregs(struct ps_prochandle *P, lwpid_t lwpid, const prgregset_t gregs)
350 {
351 return (setlwpregs_proc(P, lwpid, PCSREG, gregs, sizeof (prgregset_t)));
352 }
353
354 int
355 Lsetregs(struct ps_lwphandle *L, const prgregset_t *gregs)
356 {
357 return (setlwpregs_lwp(L, PCSREG, gregs, sizeof (prgregset_t)));
358 }
359
360 int
361 Plwp_getfpregs(struct ps_prochandle *P, lwpid_t lwpid, prfpregset_t *fpregs)
362 {
363 lwpstatus_t lps;
364
365 if (getlwpstatus(P, lwpid, &lps) == -1)
366 return (-1);
367
368 (void) memcpy(fpregs, &lps.pr_fpreg, sizeof (prfpregset_t));
369 return (0);
370 }
371
372 int
373 Lgetfpregs(struct ps_lwphandle *L, prfpregset_t *fpregs)
374 {
375 (void) memcpy(fpregs, &L->lwp_status.pr_fpreg, sizeof (prfpregset_t));
376 return (0);
377 }
378
379 int
380 Plwp_setfpregs(struct ps_prochandle *P, lwpid_t lwpid,
381 const prfpregset_t *fpregs)
382 {
383 return (setlwpregs_proc(P, lwpid, PCSFPREG, fpregs,
384 sizeof (prfpregset_t)));
385 }
386
387 int
388 Lsetfpregs(struct ps_lwphandle *L, const prfpregset_t *fpregs)
389 {
390 return (setlwpregs_lwp(L, PCSFPREG, fpregs, sizeof (prfpregset_t)));
391 }
392
393 /*
394 * The reason that this is structured to take both the size and the process
395 * handle is so that way we have enough information to tie this back to its
396 * underlying source and we can eventually use umem with this.
397 */
398 void
399 Plwp_freexregs(struct ps_prochandle *P __unused, prxregset_t *prx,
400 size_t size __unused)
401 {
402 free(prx);
403 }
404
405 int
406 Plwp_getxregs(struct ps_prochandle *P, lwpid_t lwpid, prxregset_t **xregs,
407 size_t *sizep)
408 {
409 lwp_info_t *lwp;
410
411 if (P->state == PS_IDLE) {
412 errno = ENODATA;
413 return (-1);
414 }
415
416 if (P->state != PS_DEAD) {
417 if (P->state != PS_STOP) {
418 errno = EBUSY;
419 return (-1);
420 }
421
422 return (getlwpfile_alloc(P, lwpid, "xregs",
423 (void **)xregs, sizep));
424 }
425
426 if ((lwp = getlwpcore(P, lwpid)) != NULL && lwp->lwp_xregs != NULL &&
427 lwp->lwp_xregsize > 0) {
428 *xregs = malloc(lwp->lwp_xregsize);
429 if (*xregs == NULL)
430 return (-1);
431 (void) memcpy(*xregs, lwp->lwp_xregs, lwp->lwp_xregsize);
432 *sizep = lwp->lwp_xregsize;
433 return (0);
434 }
435
436 if (lwp != NULL)
437 errno = ENODATA;
438 return (-1);
439 }
440
441 int
442 Lgetxregs(struct ps_lwphandle *L, prxregset_t **xregs, size_t *sizep)
443 {
444 lwp_info_t *lwp;
445
446 if (L->lwp_state != PS_DEAD) {
447 if (L->lwp_state != PS_STOP) {
448 errno = EBUSY;
449 return (-1);
450 }
451 return (getlwpfile_alloc(L->lwp_proc, L->lwp_id, "xregs",
452 (void **)xregs, sizep));
453 }
454
455 if ((lwp = getlwpcore(L->lwp_proc, L->lwp_id)) != NULL &&
456 lwp->lwp_xregs != NULL && lwp->lwp_xregsize > 0) {
457 *xregs = malloc(lwp->lwp_xregsize);
458 if (*xregs == NULL)
459 return (-1);
460 (void) memcpy(*xregs, lwp->lwp_xregs, lwp->lwp_xregsize);
461 *sizep = lwp->lwp_xregsize;
462 return (0);
463 }
464
465 if (lwp != NULL)
466 errno = ENODATA;
467 return (-1);
468 }
469
470 int
471 Plwp_setxregs(struct ps_prochandle *P, lwpid_t lwpid, const prxregset_t *xregs,
472 size_t len)
473 {
474 return (setlwpregs_proc(P, lwpid, PCSXREG, xregs, len));
475 }
476
477 int
478 Lsetxregs(struct ps_lwphandle *L, const prxregset_t *xregs, size_t len)
479 {
480 return (setlwpregs_lwp(L, PCSXREG, xregs, len));
481 }
482
483 #if defined(sparc) || defined(__sparc)
484 int
485 Plwp_getgwindows(struct ps_prochandle *P, lwpid_t lwpid, gwindows_t *gwins)
486 {
487 lwp_info_t *lwp;
488
489 if (P->state == PS_IDLE) {
490 errno = ENODATA;
491 return (-1);
492 }
493
494 if (P->state != PS_DEAD) {
495 if (P->state != PS_STOP) {
496 errno = EBUSY;
497 return (-1);
498 }
499
500 return (getlwpfile(P, lwpid, "gwindows",
501 gwins, sizeof (gwindows_t)));
502 }
503
504 if ((lwp = getlwpcore(P, lwpid)) != NULL && lwp->lwp_gwins != NULL) {
505 *gwins = *lwp->lwp_gwins;
506 return (0);
507 }
508
509 if (lwp != NULL)
510 errno = ENODATA;
511 return (-1);
512 }
513
514 #if defined(__sparcv9)
515 int
516 Plwp_getasrs(struct ps_prochandle *P, lwpid_t lwpid, asrset_t asrs)
517 {
518 lwp_info_t *lwp;
519
520 if (P->state == PS_IDLE) {
521 errno = ENODATA;
522 return (-1);
523 }
524
525 if (P->state != PS_DEAD) {
526 if (P->state != PS_STOP) {
527 errno = EBUSY;
528 return (-1);
529 }
530
531 return (getlwpfile(P, lwpid, "asrs", asrs, sizeof (asrset_t)));
532 }
533
534 if ((lwp = getlwpcore(P, lwpid)) != NULL && lwp->lwp_asrs != NULL) {
535 (void) memcpy(asrs, lwp->lwp_asrs, sizeof (asrset_t));
536 return (0);
537 }
538
539 if (lwp != NULL)
540 errno = ENODATA;
541 return (-1);
542
543 }
544
545 int
546 Plwp_setasrs(struct ps_prochandle *P, lwpid_t lwpid, const asrset_t asrs)
547 {
548 return (setlwpregs_proc(P, lwpid, PCSASRS, asrs, sizeof (asrset_t)));
549 }
550 #endif /* __sparcv9 */
551 #endif /* __sparc */
552
553 int
554 Plwp_getpsinfo(struct ps_prochandle *P, lwpid_t lwpid, lwpsinfo_t *lps)
555 {
556 lwp_info_t *lwp;
557
558 if (P->state == PS_IDLE) {
559 errno = ENODATA;
560 return (-1);
561 }
562
563 if (P->state != PS_DEAD) {
564 return (getlwpfile(P, lwpid, "lwpsinfo",
565 lps, sizeof (lwpsinfo_t)));
566 }
567
568 if ((lwp = getlwpcore(P, lwpid)) != NULL) {
569 (void) memcpy(lps, &lwp->lwp_psinfo, sizeof (lwpsinfo_t));
570 return (0);
571 }
572
573 return (-1);
574 }
575
576 int
577 Plwp_getname(struct ps_prochandle *P, lwpid_t lwpid,
578 char *buf, size_t bufsize)
579 {
580 char lwpname[THREAD_NAME_MAX];
581 char *from = NULL;
582 lwp_info_t *lwp;
583
584 if (P->state == PS_IDLE) {
585 errno = ENODATA;
586 return (-1);
587 }
588
589 if (P->state != PS_DEAD) {
590 if (getlwpfile(P, lwpid, "lwpname",
591 lwpname, sizeof (lwpname)) != 0)
592 return (-1);
593 from = lwpname;
594 } else {
595 if ((lwp = getlwpcore(P, lwpid)) == NULL)
596 return (-1);
597 from = lwp->lwp_name;
598 }
599
600 if (strlcpy(buf, from, bufsize) >= bufsize) {
601 errno = ENAMETOOLONG;
602 return (-1);
603 }
604
605 return (0);
606 }
607
608 int
609 Plwp_getspymaster(struct ps_prochandle *P, lwpid_t lwpid, psinfo_t *ps)
610 {
611 lwpstatus_t lps;
612
613 if (P->state == PS_IDLE) {
614 errno = ENODATA;
615 return (-1);
616 }
617
618 if (getlwpstatus(P, lwpid, &lps) != 0)
619 return (-1);
620
621 if (!(lps.pr_flags & PR_AGENT)) {
622 errno = EINVAL;
623 return (-1);
624 }
625
626 if (P->state != PS_DEAD) {
627 return (getlwpfile(P, lwpid, "spymaster",
628 ps, sizeof (psinfo_t)));
629 }
630
631 if (P->spymaster.pr_nlwp != 0) {
632 (void) memcpy(ps, &P->spymaster, sizeof (psinfo_t));
633 return (0);
634 }
635
636 errno = ENODATA;
637
638 return (-1);
639 }
640
641 int
642 Plwp_stack(struct ps_prochandle *P, lwpid_t lwpid, stack_t *stkp)
643 {
644 uintptr_t addr;
645
646 if (P->state == PS_IDLE) {
647 errno = ENODATA;
648 return (-1);
649 }
650
651 if (P->state != PS_DEAD) {
652 lwpstatus_t ls;
653 if (getlwpfile(P, lwpid, "lwpstatus", &ls, sizeof (ls)) != 0)
654 return (-1);
655 addr = ls.pr_ustack;
656 } else {
657 lwp_info_t *lwp;
658 if ((lwp = getlwpcore(P, lwpid)) == NULL)
659 return (-1);
660 addr = lwp->lwp_status.pr_ustack;
661 }
662
663
664 if (P->status.pr_dmodel == PR_MODEL_NATIVE) {
665 if (Pread(P, stkp, sizeof (*stkp), addr) != sizeof (*stkp))
666 return (-1);
667 #ifdef _LP64
668 } else {
669 stack32_t stk32;
670
671 if (Pread(P, &stk32, sizeof (stk32), addr) != sizeof (stk32))
672 return (-1);
673
674 stack_32_to_n(&stk32, stkp);
675 #endif
676 }
677
678 return (0);
679 }
680
681 int
682 Plwp_main_stack(struct ps_prochandle *P, lwpid_t lwpid, stack_t *stkp)
683 {
684 uintptr_t addr;
685 lwpstatus_t ls;
686
687 if (P->state == PS_IDLE) {
688 errno = ENODATA;
689 return (-1);
690 }
691
692 if (P->state != PS_DEAD) {
693 if (getlwpfile(P, lwpid, "lwpstatus", &ls, sizeof (ls)) != 0)
694 return (-1);
695 } else {
696 lwp_info_t *lwp;
697 if ((lwp = getlwpcore(P, lwpid)) == NULL)
698 return (-1);
699 ls = lwp->lwp_status;
700 }
701
702 addr = ls.pr_ustack;
703
704 /*
705 * Read out the current stack; if the SS_ONSTACK flag is set then
706 * this LWP is operating on the alternate signal stack. We can
707 * recover the original stack from pr_oldcontext.
708 */
709 if (P->status.pr_dmodel == PR_MODEL_NATIVE) {
710 if (Pread(P, stkp, sizeof (*stkp), addr) != sizeof (*stkp))
711 return (-1);
712
713 if (stkp->ss_flags & SS_ONSTACK)
714 goto on_altstack;
715 #ifdef _LP64
716 } else {
717 stack32_t stk32;
718
719 if (Pread(P, &stk32, sizeof (stk32), addr) != sizeof (stk32))
720 return (-1);
721
722 if (stk32.ss_flags & SS_ONSTACK)
723 goto on_altstack;
724
725 stack_32_to_n(&stk32, stkp);
726 #endif
727 }
728
729 return (0);
730
731 on_altstack:
732
733 if (P->status.pr_dmodel == PR_MODEL_NATIVE) {
734 ucontext_t *ctxp = (void *)ls.pr_oldcontext;
735
736 if (Pread(P, stkp, sizeof (*stkp),
737 (uintptr_t)&ctxp->uc_stack) != sizeof (*stkp))
738 return (-1);
739 #ifdef _LP64
740 } else {
741 ucontext32_t *ctxp = (void *)ls.pr_oldcontext;
742 stack32_t stk32;
743
744 if (Pread(P, &stk32, sizeof (stk32),
745 (uintptr_t)&ctxp->uc_stack) != sizeof (stk32))
746 return (-1);
747
748 stack_32_to_n(&stk32, stkp);
749 #endif
750 }
751
752 return (0);
753 }
754
755 int
756 Plwp_alt_stack(struct ps_prochandle *P, lwpid_t lwpid, stack_t *stkp)
757 {
758 if (P->state == PS_IDLE) {
759 errno = ENODATA;
760 return (-1);
761 }
762
763 if (P->state != PS_DEAD) {
764 lwpstatus_t ls;
765
766 if (getlwpfile(P, lwpid, "lwpstatus", &ls, sizeof (ls)) != 0)
767 return (-1);
768
769 if (ls.pr_altstack.ss_flags & SS_DISABLE) {
770 errno = ENODATA;
771 return (-1);
772 }
773
774 *stkp = ls.pr_altstack;
775 } else {
776 lwp_info_t *lwp;
777
778 if ((lwp = getlwpcore(P, lwpid)) == NULL)
779 return (-1);
780
781 if (lwp->lwp_status.pr_altstack.ss_flags & SS_DISABLE) {
782 errno = ENODATA;
783 return (-1);
784 }
785
786 *stkp = lwp->lwp_status.pr_altstack;
787 }
788
789 return (0);
790 }