xref: /titanic_41/usr/src/cmd/ptools/pflags/pflags.c (revision fd9cb95cbb2f626355a60efb9d02c5f0a33c10e6)
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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <unistd.h>
32 #include <ctype.h>
33 #include <fcntl.h>
34 #include <strings.h>
35 #include <dirent.h>
36 #include <errno.h>
37 #include <sys/types.h>
38 #include <sys/int_fmtio.h>
39 #include <libproc.h>
40 
41 typedef struct look_arg {
42 	int pflags;
43 	const char *lwps;
44 	int count;
45 } look_arg_t;
46 
47 static	int	look(char *);
48 static	int	lwplook(look_arg_t *, const lwpstatus_t *, const lwpsinfo_t *);
49 static	char	*prflags(int);
50 static	char	*prwhy(int);
51 static	char	*prwhat(int, int);
52 static	void	dumpregs(const prgregset_t, int);
53 #if defined(__sparc) && defined(_ILP32)
54 static	void	dumpregs_v8p(const prgregset_t, const prxregset_t *, int);
55 #endif
56 
57 static	char	*command;
58 static	struct	ps_prochandle *Pr;
59 
60 static	int	is64;	/* Is current process 64-bit? */
61 static	int	rflag;	/* Show registers? */
62 
63 #define	LWPFLAGS	\
64 	(PR_STOPPED|PR_ISTOP|PR_DSTOP|PR_ASLEEP|PR_PCINVAL|PR_STEP \
65 	|PR_ASLWP|PR_AGENT|PR_DETACH|PR_DAEMON)
66 
67 #define	PROCFLAGS	\
68 	(PR_ISSYS|PR_VFORKP|PR_ORPHAN|PR_FORK|PR_RLC|PR_KLC|PR_ASYNC \
69 	|PR_BPTADJ|PR_MSACCT|PR_MSFORK|PR_PTRACE)
70 
71 #define	ALLFLAGS	(LWPFLAGS|PROCFLAGS)
72 
73 int
74 main(int argc, char **argv)
75 {
76 	int rc = 0;
77 	int errflg = 0;
78 	int opt;
79 	struct rlimit rlim;
80 
81 	if ((command = strrchr(argv[0], '/')) != NULL)
82 		command++;
83 	else
84 		command = argv[0];
85 
86 	/* options */
87 	while ((opt = getopt(argc, argv, "r")) != EOF) {
88 		switch (opt) {
89 		case 'r':		/* show registers */
90 			rflag = 1;
91 			break;
92 		default:
93 			errflg = 1;
94 			break;
95 		}
96 	}
97 
98 	argc -= optind;
99 	argv += optind;
100 
101 	if (errflg || argc <= 0) {
102 		(void) fprintf(stderr,
103 		    "usage:\t%s [-r] { pid | core }[/lwps] ...\n", command);
104 		(void) fprintf(stderr, "  (report process status flags)\n");
105 		(void) fprintf(stderr, "  -r : report registers\n");
106 		return (2);
107 	}
108 
109 	/*
110 	 * Make sure we'll have enough file descriptors to handle a target
111 	 * that has many many mappings.
112 	 */
113 	if (getrlimit(RLIMIT_NOFILE, &rlim) == 0) {
114 		rlim.rlim_cur = rlim.rlim_max;
115 		(void) setrlimit(RLIMIT_NOFILE, &rlim);
116 	}
117 
118 	while (argc-- > 0)
119 		rc += look(*argv++);
120 
121 	return (rc);
122 }
123 
124 static int
125 look(char *arg)
126 {
127 	int gcode;
128 	int gcode2;
129 	pstatus_t pstatus;
130 	psinfo_t psinfo;
131 	int flags;
132 	sigset_t sigmask;
133 	fltset_t fltmask;
134 	sysset_t entryset;
135 	sysset_t exitset;
136 	uint32_t sigtrace, sigtrace2, fltbits;
137 	uint32_t sigpend, sigpend2;
138 	uint32_t *bits;
139 	char buf[PRSIGBUFSZ];
140 	look_arg_t lookarg;
141 
142 	if ((Pr = proc_arg_xgrab(arg, NULL, PR_ARG_ANY,
143 	    PGRAB_RETAIN | PGRAB_FORCE | PGRAB_RDONLY | PGRAB_NOSTOP, &gcode,
144 	    &lookarg.lwps)) == NULL) {
145 		if (gcode == G_NOPROC &&
146 		    proc_arg_psinfo(arg, PR_ARG_PIDS, &psinfo, &gcode2) > 0 &&
147 		    psinfo.pr_nlwp == 0) {
148 			(void) printf("%d:\t<defunct>\n\n", (int)psinfo.pr_pid);
149 			return (0);
150 		}
151 		(void) fprintf(stderr, "%s: cannot examine %s: %s\n",
152 			command, arg, Pgrab_error(gcode));
153 		return (1);
154 	}
155 
156 	(void) memcpy(&pstatus, Pstatus(Pr), sizeof (pstatus_t));
157 	(void) memcpy(&psinfo, Ppsinfo(Pr), sizeof (psinfo_t));
158 	proc_unctrl_psinfo(&psinfo);
159 
160 	if (psinfo.pr_nlwp == 0) {
161 		(void) printf("%d:\t<defunct>\n\n", (int)psinfo.pr_pid);
162 		Prelease(Pr, PRELEASE_RETAIN);
163 		return (0);
164 	}
165 
166 	is64 = (pstatus.pr_dmodel == PR_MODEL_LP64);
167 
168 	sigmask = pstatus.pr_sigtrace;
169 	fltmask = pstatus.pr_flttrace;
170 	entryset = pstatus.pr_sysentry;
171 	exitset = pstatus.pr_sysexit;
172 
173 	if (Pstate(Pr) == PS_DEAD) {
174 		(void) printf("core '%s' of %d:\t%.70s\n",
175 		    arg, (int)psinfo.pr_pid, psinfo.pr_psargs);
176 	} else {
177 		(void) printf("%d:\t%.70s\n",
178 		    (int)psinfo.pr_pid, psinfo.pr_psargs);
179 	}
180 
181 	(void) printf("\tdata model = %s", is64? "_LP64" : "_ILP32");
182 	if ((flags = (pstatus.pr_flags & PROCFLAGS)) != 0)
183 		(void) printf("  flags = %s", prflags(flags));
184 	(void) printf("\n");
185 
186 	fltbits = *((uint32_t *)&fltmask);
187 	if (fltbits)
188 		(void) printf("\tflttrace = 0x%.8x\n", fltbits);
189 
190 	sigtrace = *((uint32_t *)&sigmask);
191 	sigtrace2 = *((uint32_t *)&sigmask + 1);
192 	if (sigtrace | sigtrace2) {
193 		(void) printf("\tsigtrace = 0x%.8x 0x%.8x\n\t    %s\n",
194 		    sigtrace, sigtrace2,
195 		    proc_sigset2str(&sigmask, "|", 1, buf, sizeof (buf)));
196 	}
197 
198 	bits = ((uint32_t *)&entryset);
199 	if (bits[0] | bits[1] | bits[2] | bits[3] |
200 	    bits[4] | bits[5] | bits[6] | bits[7])
201 		(void) printf(
202 			"\tentryset = "
203 			"0x%.8x 0x%.8x 0x%.8x 0x%.8x\n"
204 			"\t           "
205 			"0x%.8x 0x%.8x 0x%.8x 0x%.8x\n",
206 			bits[0], bits[1], bits[2], bits[3],
207 			bits[4], bits[5], bits[6], bits[7]);
208 
209 	bits = ((uint32_t *)&exitset);
210 	if (bits[0] | bits[1] | bits[2] | bits[3] |
211 	    bits[4] | bits[5] | bits[6] | bits[7])
212 		(void) printf(
213 			"\texitset  = "
214 			"0x%.8x 0x%.8x 0x%.8x 0x%.8x\n"
215 			"\t           "
216 			"0x%.8x 0x%.8x 0x%.8x 0x%.8x\n",
217 			bits[0], bits[1], bits[2], bits[3],
218 			bits[4], bits[5], bits[6], bits[7]);
219 
220 	sigpend  = *((uint32_t *)&pstatus.pr_sigpend);
221 	sigpend2 = *((uint32_t *)&pstatus.pr_sigpend + 1);
222 	if (sigpend | sigpend2)
223 		(void) printf("\tsigpend = 0x%.8x,0x%.8x\n",
224 			sigpend, sigpend2);
225 
226 	lookarg.pflags = pstatus.pr_flags;
227 	lookarg.count = 0;
228 	(void) Plwp_iter_all(Pr, (proc_lwp_all_f *)lwplook, &lookarg);
229 
230 	if (lookarg.count == 0)
231 		(void) printf("No matching lwps found");
232 
233 	(void) printf("\n");
234 	Prelease(Pr, PRELEASE_RETAIN);
235 
236 	return (0);
237 }
238 
239 static int
240 lwplook_zombie(const lwpsinfo_t *pip)
241 {
242 	(void) printf(" /%d:\t<defunct>\n", (int)pip->pr_lwpid);
243 	return (0);
244 }
245 
246 static int
247 lwplook(look_arg_t *arg, const lwpstatus_t *psp, const lwpsinfo_t *pip)
248 {
249 	int flags;
250 	uint32_t sighold, sighold2;
251 	uint32_t sigpend, sigpend2;
252 	int cursig;
253 	char buf[32];
254 
255 	if (!proc_lwp_in_set(arg->lwps, pip->pr_lwpid))
256 		return (0);
257 
258 	arg->count++;
259 
260 	if (psp == NULL)
261 		return (lwplook_zombie(pip));
262 
263 	/*
264 	 * PR_PCINVAL is just noise if the lwp is not stopped.
265 	 * Don't bother reporting it unless the lwp is stopped.
266 	 */
267 	flags = psp->pr_flags & LWPFLAGS;
268 	if (!(flags & PR_STOPPED))
269 		flags &= ~PR_PCINVAL;
270 
271 	(void) printf(" /%d:\tflags = %s", (int)psp->pr_lwpid, prflags(flags));
272 	if ((flags & PR_ASLEEP) || (psp->pr_syscall &&
273 	    !(arg->pflags & PR_ISSYS))) {
274 		if (flags & PR_ASLEEP) {
275 			if ((flags & ~PR_ASLEEP) != 0)
276 				(void) printf("|");
277 			(void) printf("ASLEEP");
278 		}
279 		if (psp->pr_syscall && !(arg->pflags & PR_ISSYS)) {
280 			uint_t i;
281 
282 			(void) printf("  %s(",
283 			    proc_sysname(psp->pr_syscall, buf, sizeof (buf)));
284 			for (i = 0; i < psp->pr_nsysarg; i++) {
285 				if (i != 0)
286 					(void) printf(",");
287 				(void) printf("0x%lx", psp->pr_sysarg[i]);
288 			}
289 			(void) printf(")");
290 		}
291 	}
292 	(void) printf("\n");
293 
294 	if (flags & PR_STOPPED) {
295 		(void) printf("\twhy = %s", prwhy(psp->pr_why));
296 		if (psp->pr_why != PR_REQUESTED &&
297 		    psp->pr_why != PR_SUSPENDED)
298 			(void) printf("  what = %s",
299 				prwhat(psp->pr_why, psp->pr_what));
300 		(void) printf("\n");
301 	}
302 
303 	sighold  = *((uint32_t *)&psp->pr_lwphold);
304 	sighold2 = *((uint32_t *)&psp->pr_lwphold + 1);
305 	sigpend  = *((uint32_t *)&psp->pr_lwppend);
306 	sigpend2 = *((uint32_t *)&psp->pr_lwppend + 1);
307 	cursig   = psp->pr_cursig;
308 
309 	if (sighold | sighold2 | sigpend | sigpend2 | cursig) {
310 		(void) printf("\t");
311 		if (sighold | sighold2) {
312 			(void) printf("sigmask = 0x%.8x,0x%.8x",
313 				sighold, sighold2);
314 			if (sigpend | sigpend2 | cursig)
315 				(void) printf("  ");
316 		}
317 		if (sigpend | sigpend2) {
318 			(void) printf("lwppend = 0x%.8x,0x%.8x",
319 				sigpend, sigpend2);
320 			if (cursig)
321 				(void) printf("  ");
322 		}
323 		if (cursig)
324 			(void) printf("cursig = %s",
325 			    proc_signame(cursig, buf, sizeof (buf)));
326 		(void) printf("\n");
327 	}
328 
329 	if (rflag) {
330 		if (Pstate(Pr) == PS_DEAD || (arg->pflags & PR_STOPPED)) {
331 #if defined(__sparc) && defined(_ILP32)
332 			/*
333 			 * If we're SPARC/32-bit, see if we can get extra
334 			 * register state for this lwp.  If it's a v8plus
335 			 * program, print the 64-bit register values.
336 			 */
337 			prxregset_t prx;
338 
339 			if (Plwp_getxregs(Pr, psp->pr_lwpid, &prx) == 0 &&
340 			    prx.pr_type == XR_TYPE_V8P)
341 				dumpregs_v8p(psp->pr_reg, &prx, is64);
342 			else
343 #endif	/* __sparc && _ILP32 */
344 				dumpregs(psp->pr_reg, is64);
345 		} else
346 			(void) printf("\tNot stopped, can't show registers\n");
347 	}
348 
349 	return (0);
350 }
351 
352 static char *
353 prflags(int arg)
354 {
355 	static char code_buf[200];
356 	char *str = code_buf;
357 
358 	if (arg == 0)
359 		return ("0");
360 
361 	if (arg & ~ALLFLAGS)
362 		(void) sprintf(str, "0x%x", arg & ~ALLFLAGS);
363 	else
364 		*str = '\0';
365 
366 	/*
367 	 * Display the semi-permanent lwp flags first.
368 	 */
369 	if (arg & PR_DAEMON)		/* daemons are always detached so */
370 		(void) strcat(str, "|DAEMON");
371 	else if (arg & PR_DETACH)	/* report detach only if non-daemon */
372 		(void) strcat(str, "|DETACH");
373 
374 	if (arg & PR_STOPPED)
375 		(void) strcat(str, "|STOPPED");
376 	if (arg & PR_ISTOP)
377 		(void) strcat(str, "|ISTOP");
378 	if (arg & PR_DSTOP)
379 		(void) strcat(str, "|DSTOP");
380 #if 0		/* displayed elsewhere */
381 	if (arg & PR_ASLEEP)
382 		(void) strcat(str, "|ASLEEP");
383 #endif
384 	if (arg & PR_PCINVAL)
385 		(void) strcat(str, "|PCINVAL");
386 	if (arg & PR_STEP)
387 		(void) strcat(str, "|STEP");
388 	if (arg & PR_AGENT)
389 		(void) strcat(str, "|AGENT");
390 	if (arg & PR_ISSYS)
391 		(void) strcat(str, "|ISSYS");
392 	if (arg & PR_VFORKP)
393 		(void) strcat(str, "|VFORKP");
394 	if (arg & PR_ORPHAN)
395 		(void) strcat(str, "|ORPHAN");
396 	if (arg & PR_FORK)
397 		(void) strcat(str, "|FORK");
398 	if (arg & PR_RLC)
399 		(void) strcat(str, "|RLC");
400 	if (arg & PR_KLC)
401 		(void) strcat(str, "|KLC");
402 	if (arg & PR_ASYNC)
403 		(void) strcat(str, "|ASYNC");
404 	if (arg & PR_BPTADJ)
405 		(void) strcat(str, "|BPTADJ");
406 	if (arg & PR_MSACCT)
407 		(void) strcat(str, "|MSACCT");
408 	if (arg & PR_MSFORK)
409 		(void) strcat(str, "|MSFORK");
410 	if (arg & PR_PTRACE)
411 		(void) strcat(str, "|PTRACE");
412 
413 	if (*str == '|')
414 		str++;
415 
416 	return (str);
417 }
418 
419 static char *
420 prwhy(int why)
421 {
422 	static char buf[20];
423 	char *str;
424 
425 	switch (why) {
426 	case PR_REQUESTED:
427 		str = "PR_REQUESTED";
428 		break;
429 	case PR_SIGNALLED:
430 		str = "PR_SIGNALLED";
431 		break;
432 	case PR_SYSENTRY:
433 		str = "PR_SYSENTRY";
434 		break;
435 	case PR_SYSEXIT:
436 		str = "PR_SYSEXIT";
437 		break;
438 	case PR_JOBCONTROL:
439 		str = "PR_JOBCONTROL";
440 		break;
441 	case PR_FAULTED:
442 		str = "PR_FAULTED";
443 		break;
444 	case PR_SUSPENDED:
445 		str = "PR_SUSPENDED";
446 		break;
447 	default:
448 		str = buf;
449 		(void) sprintf(str, "%d", why);
450 		break;
451 	}
452 
453 	return (str);
454 }
455 
456 static char *
457 prwhat(int why, int what)
458 {
459 	static char buf[32];
460 	char *str;
461 
462 	switch (why) {
463 	case PR_SIGNALLED:
464 	case PR_JOBCONTROL:
465 		str = proc_signame(what, buf, sizeof (buf));
466 		break;
467 	case PR_SYSENTRY:
468 	case PR_SYSEXIT:
469 		str = proc_sysname(what, buf, sizeof (buf));
470 		break;
471 	case PR_FAULTED:
472 		str = proc_fltname(what, buf, sizeof (buf));
473 		break;
474 	default:
475 		(void) sprintf(str = buf, "%d", what);
476 		break;
477 	}
478 
479 	return (str);
480 }
481 
482 #if defined(__sparc)
483 static const char * const regname[NPRGREG] = {
484 	" %g0", " %g1", " %g2", " %g3", " %g4", " %g5", " %g6", " %g7",
485 	" %o0", " %o1", " %o2", " %o3", " %o4", " %o5", " %sp", " %o7",
486 	" %l0", " %l1", " %l2", " %l3", " %l4", " %l5", " %l6", " %l7",
487 	" %i0", " %i1", " %i2", " %i3", " %i4", " %i5", " %fp", " %i7",
488 #ifdef __sparcv9
489 	"%ccr", " %pc", "%npc", "  %y", "%asi", "%fprs"
490 #else
491 	"%psr", " %pc", "%npc", "  %y", "%wim", "%tbr"
492 #endif
493 };
494 #endif	/* __sparc */
495 
496 #if defined(__amd64)
497 static const char * const regname[NPRGREG] = {
498 	"%r15", "%r14", "%r13", "%r12", "%r11", "%r10", " %r9", " %r8",
499 	"%rdi", "%rsi", "%rbp", "%rbx", "%rdx", "%rcx", "%rax", "%trapno",
500 	"%err", "%rip", " %cs", "%rfl", "%rsp", " %ss", " %fs", " %gs",
501 	" %es", " %ds", "%fsbase", "%gsbase"
502 };
503 
504 static const char * const regname32[NPRGREG32] = {
505 	" %gs", " %fs", " %es", " %ds", "%edi", "%esi", "%ebp", "%esp",
506 	"%ebx", "%edx", "%ecx", "%eax", "%trapno", "%err", "%eip", " %cs",
507 	"%efl", "%uesp", " %ss"
508 };
509 
510 /* XX64 Do we want to expose this through libproc */
511 void
512 prgregset_n_to_32(const prgreg_t *src, prgreg32_t *dst)
513 {
514 	bzero(dst, NPRGREG32 * sizeof (prgreg32_t));
515 	dst[GS] = src[REG_GS];
516 	dst[FS] = src[REG_FS];
517 	dst[DS] = src[REG_DS];
518 	dst[ES] = src[REG_ES];
519 	dst[EDI] = src[REG_RDI];
520 	dst[ESI] = src[REG_RSI];
521 	dst[EBP] = src[REG_RBP];
522 	dst[EBX] = src[REG_RBX];
523 	dst[EDX] = src[REG_RDX];
524 	dst[ECX] = src[REG_RCX];
525 	dst[EAX] = src[REG_RAX];
526 	dst[TRAPNO] = src[REG_TRAPNO];
527 	dst[ERR] = src[REG_ERR];
528 	dst[EIP] = src[REG_RIP];
529 	dst[CS] = src[REG_CS];
530 	dst[EFL] = src[REG_RFL];
531 	dst[UESP] = src[REG_RSP];
532 	dst[SS] = src[REG_SS];
533 }
534 
535 #elif defined(__i386)
536 static const char * const regname[NPRGREG] = {
537 	" %gs", " %fs", " %es", " %ds", "%edi", "%esi", "%ebp", "%esp",
538 	"%ebx", "%edx", "%ecx", "%eax", "%trapno", "%err", "%eip", " %cs",
539 	"%efl", "%uesp", " %ss"
540 };
541 #endif /* __i386 */
542 
543 #if defined(__amd64) && defined(_LP64)
544 static void
545 dumpregs32(const prgregset_t reg)
546 {
547 	prgregset32_t reg32;
548 	int i;
549 
550 	prgregset_n_to_32(reg, reg32);
551 
552 	for (i = 0; i < NPRGREG32; i++) {
553 		(void) printf("  %s = 0x%.8X",
554 			regname32[i], reg32[i]);
555 		if ((i+1) % 4 == 0)
556 			(void) putchar('\n');
557 	}
558 	if (i % 4 != 0)
559 		(void) putchar('\n');
560 }
561 #endif
562 
563 static void
564 dumpregs(const prgregset_t reg, int is64)
565 {
566 	int width = is64? 16 : 8;
567 	int cols = is64? 2 : 4;
568 	int i;
569 
570 #if defined(__amd64) && defined(_LP64)
571 	if (!is64) {
572 		dumpregs32(reg);
573 		return;
574 	}
575 #endif
576 
577 	for (i = 0; i < NPRGREG; i++) {
578 		(void) printf("  %s = 0x%.*lX",
579 			regname[i], width, (long)reg[i]);
580 		if ((i+1) % cols == 0)
581 			(void) putchar('\n');
582 	}
583 	if (i % cols != 0)
584 		(void) putchar('\n');
585 }
586 
587 #if defined(__sparc) && defined(_ILP32)
588 static void
589 dumpregs_v8p(const prgregset_t reg, const prxregset_t *xreg, int is64)
590 {
591 	static const uint32_t zero[8] = { 0 };
592 	int gr, xr, cols = 2;
593 	uint64_t xval;
594 
595 	if (memcmp(xreg->pr_un.pr_v8p.pr_xg, zero, sizeof (zero)) == 0 &&
596 	    memcmp(xreg->pr_un.pr_v8p.pr_xo, zero, sizeof (zero)) == 0) {
597 		dumpregs(reg, is64);
598 		return;
599 	}
600 
601 	for (gr = R_G0, xr = XR_G0; gr <= R_G7; gr++, xr++) {
602 		xval = (uint64_t)xreg->pr_un.pr_v8p.pr_xg[xr] << 32 |
603 		    (uint64_t)(uint32_t)reg[gr];
604 		(void) printf("  %s = 0x%.16" PRIX64, regname[gr], xval);
605 		if ((gr + 1) % cols == 0)
606 			(void) putchar('\n');
607 	}
608 
609 	for (gr = R_O0, xr = XR_O0; gr <= R_O7; gr++, xr++) {
610 		xval = (uint64_t)xreg->pr_un.pr_v8p.pr_xo[xr] << 32 |
611 		    (uint64_t)(uint32_t)reg[gr];
612 		(void) printf("  %s = 0x%.16" PRIX64, regname[gr], xval);
613 		if ((gr + 1) % cols == 0)
614 			(void) putchar('\n');
615 	}
616 
617 	for (gr = R_L0; gr < NPRGREG; gr++) {
618 		(void) printf("  %s =         0x%.8lX",
619 		    regname[gr], (long)reg[gr]);
620 		if ((gr + 1) % cols == 0)
621 			(void) putchar('\n');
622 	}
623 
624 	if (gr % cols != 0)
625 		(void) putchar('\n');
626 }
627 #endif	/* __sparc && _ILP32 */
628