xref: /illumos-gate/usr/src/cmd/amt/amt.c (revision a5f69788de7ac07553de47f7fec8c05a9a94c105)
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 /*
30  * Abstract Machine Test; executes memory access tests to show
31  * compliance with Common Criteria object reuse and process address
32  * space separation requirements.
33  */
34 #include <errno.h>
35 #include <fcntl.h>
36 #include <iso/stdlib_iso.h>
37 #include <libelf.h>
38 #include <libintl.h>
39 #include <locale.h>
40 #include <signal.h>
41 #include <stdio.h>
42 #include <string.h>
43 #include <sys/param.h>
44 #include <sys/resource.h>
45 #include <sys/sysmacros.h>
46 #include <sys/types.h>
47 #include <unistd.h>
48 #include <wait.h>
49 
50 #define	NOT_SILENT 0
51 #define	SILENT_MODE 1
52 
53 #define	CHILD_SLEEP_PERIOD 2
54 #define	PARENT_SLEEP_PERIOD 1
55 #define	SIG_EVENT SIGUSR1
56 
57 #define	PASS	   0		/* test passed, no SEGV */
58 #define	FAIL_ZERO  1		/* expected to read zero, didn't */
59 #define	FAIL_SEGV  2		/* expected good read or write, didn't */
60 #define	PASS_SEGV  3		/* expected SEGV, got it */
61 #define	FAIL_ABORT 4		/* test logic error */
62 
63 #define	PH_VALID   0		/* arg for probe_hole -- do valid memory */
64 				/* access */
65 #define	PH_INVALID 1		/* do illegal memory access */
66 
67 #define	WASTE_PAGES   8		/* a guess at where virgin stack space */
68 				/* is likely to exist */
69 #define	STACK_SLOP  256		/* a guess at how far below current end */
70 				/* of stack I'll find unused space */
71 
72 #if !defined(TEXT_DOMAIN)
73 #define	TEXT_DOMAIN "SYS_TEST"
74 #endif
75 
76 extern int _end;	/* first address after the end of initialized data */
77 
78 static int  data_boundary_test();
79 static void handler(int);
80 static int  memory_not_shared_after_use();
81 static int  memory_allocation_not_shared();
82 static int  memory_type(const char *);
83 static void print_message(char *);
84 static void probe_data_area(void);
85 static void probe_hole(int);
86 static void probe_stack(void);
87 static void probe_text_area(void);
88 static void segv_action(int, siginfo_t *, void *);
89 static void set_handler(int);
90 static int  test_stack_end_of_hole();
91 static int  text_area_not_writeable();
92 
93 static int done_memory_grab = 0;
94 static int silent;
95 static int handler_exit_code;
96 
97 /*
98  * Main Routine
99  */
100 int
101 main(int argc, char *argv[])
102 {
103 	int fail_count = 0;
104 	int status = 0;
105 	int bitsize;
106 
107 	/* Internationalization */
108 	(void) setlocale(LC_ALL, "");
109 	(void) textdomain(TEXT_DOMAIN);
110 
111 	silent = NOT_SILENT;
112 
113 	if (argc == 2) {
114 		/* Pull out argument provided		 */
115 		/* -s	silent mode, no status or error messages. */
116 		if (strncmp(argv[1], "-s", 4) == 0)
117 			silent = SILENT_MODE;
118 		else {
119 			/* Wrong argument */
120 			(void) fprintf(stderr, gettext(
121 			    "Wrong argument, USAGE: amt [-s]\n"));
122 			exit(EXIT_FAILURE);
123 		}
124 	} else if (argc != 1) {
125 		/* Illegal number of arguments. */
126 		(void) fprintf(stderr, gettext(
127 		    "Wrong usage, USAGE: amt [-s]\n"));
128 		exit(EXIT_FAILURE);
129 	}
130 	bitsize = memory_type(argv[0]);
131 
132 	if (silent == NOT_SILENT)
133 		(void) printf(gettext(
134 		    "\n\nAMT Test Program -- %d bit application\n"
135 		    "================\n"), bitsize);
136 	/*
137 	 * test_stack_end_of_hole must be the first test, or the stack
138 	 * is of an unknown size.
139 	 */
140 	if ((status = test_stack_end_of_hole()) == EXIT_FAILURE) {
141 		/* Normal fail */
142 		fail_count++;
143 		print_message(gettext("TEST 1 FAILED\n"));
144 	} else if (status == FAIL_ABORT) {
145 		/* Test logic failure */
146 		fail_count++;
147 		print_message(gettext("FAIL: Logic error in test\n"));
148 	} else if (status == EXIT_SUCCESS)
149 		print_message(gettext("TEST 1 PASSED\n"));
150 
151 	/* Carry out test 2 */
152 	if (data_boundary_test() != EXIT_SUCCESS) {
153 		fail_count++;
154 		print_message(gettext("TEST 2 FAILED\n"));
155 	} else
156 		print_message(gettext("TEST 2 PASSED\n"));
157 
158 	/* Carry out test 3 */
159 	if (text_area_not_writeable() != EXIT_SUCCESS) {
160 		fail_count++;
161 		print_message(gettext("TEST 3 FAILED\n"));
162 	} else
163 		print_message(gettext("TEST 3 PASSED\n"));
164 
165 	/* Carry out test 4 */
166 	if (memory_not_shared_after_use() != EXIT_SUCCESS) {
167 		fail_count++;
168 		print_message(gettext("TEST 4 FAILED\n"));
169 	} else
170 		print_message(gettext("TEST 4 PASSED\n"));
171 
172 	/* Carry out test 5 */
173 	if (memory_allocation_not_shared() != EXIT_SUCCESS) {
174 		fail_count++;
175 		print_message(gettext("TEST 5 FAILED\n"));
176 	} else
177 		print_message(gettext("TEST 5 PASSED\n"));
178 
179 	if (silent == NOT_SILENT) {
180 		if (fail_count > 0)
181 			(void) printf(gettext("\n %d TESTS FAILED\n\n"),
182 			    fail_count);
183 		else
184 			(void) printf(gettext("\nTESTS SUCCEEDED\n\n"));
185 	}
186 	return (fail_count);
187 }
188 
189 /*
190  * Test the data boundaries. First test inside the data area at the boundary
191  * of the "hole" area. Second test inside the data area at the text area
192  * boundary. Both should pass.
193  */
194 static int
195 data_boundary_test()
196 {
197 	int exit_status = EXIT_SUCCESS;
198 	pid_t pid;
199 	int status;
200 
201 	print_message(gettext("\n\nTest 2- Data Side Boundary Test.\n"));
202 
203 	if ((pid = fork()) == -1) {
204 		print_message(gettext("Fork failed\n"));
205 		return (EXIT_FAILURE);
206 	} else if (pid == 0) { /* Am I my child? */
207 		set_handler(SIGSEGV);
208 
209 		/* probe_data_area() does exit() */
210 		probe_data_area();
211 	}
212 	/* still parent */
213 	(void) wait(&status);
214 	status = WEXITSTATUS(status);
215 
216 	if (status == PASS)
217 		print_message(gettext(
218 		    "PASS: Successful read/write in data area.\n"));
219 	else if (status == FAIL_SEGV) {
220 		print_message(gettext(
221 		    "FAIL: Caught a segmentation fault while "
222 		    "attempting to write to the data area.\n"));
223 		exit_status = EXIT_FAILURE;
224 	} else {
225 		(void) printf(gettext("Test program failure: %d\n"),
226 		    status);
227 		exit_status = EXIT_FAILURE;
228 	}
229 	return (exit_status);
230 }
231 
232 static void
233 probe_data_area()
234 {
235 	int *p;
236 	/* LINTED */
237 	volatile int p1;
238 	void *address;
239 
240 	/* set handler status */
241 	handler_exit_code = FAIL_SEGV;
242 
243 	/*
244 	 * Get an address in the data area, near to the "hole".
245 	 * sbrk returns prior address value; rather than calculating
246 	 * the sbrk result, sbrk is called twice, so address points
247 	 * to the new end of data
248 	 */
249 	(void) sbrk(PAGESIZE);
250 	address = sbrk(0);
251 	/*
252 	 * Get to the inside edge of a page boundary
253 	 * two integer words short of a new page
254 	 */
255 	p = ((int *)P2ROUNDUP((uintptr_t)address, PAGESIZE)) - 2;
256 
257 	/* Try writing to it, shouldn't cause a segmentation fault. */
258 	*p = 9999;
259 
260 	/* Should be able to read back with no problems. */
261 	p1 = *p;
262 
263 	/*
264 	 * Get an address near the text area boundary, but in the data
265 	 * area.  _etext rounded up a page isn't correct since the
266 	 * initialized data area isn't writeable.
267 	 *
268 	 * Future versions should consider handling initialized data
269 	 * separately -- writing to initialized data should generate
270 	 * a fault.
271 	 */
272 	p = &_end;
273 
274 	/* Try writing to it, should succeed. */
275 	*p = 9898;
276 
277 	/* Should be able to read back with no problems. */
278 	p1 = *p;
279 
280 	exit(EXIT_SUCCESS);
281 }
282 
283 /*
284  * Test that we cannot write to the text area. An attempt to write to
285  * the text area will result in a segmentation fault. So if we catch it,
286  * test has succeed, else it has failed.
287  */
288 static int
289 text_area_not_writeable()
290 {
291 	int exit_status = EXIT_SUCCESS;
292 	pid_t pid;
293 	int status;
294 
295 	print_message(gettext(
296 	    "\n\nTest 3- Text Area Not Writeable\n"
297 	    "Verify that a write to the text space does not cause "
298 	    "a write to the executable\n"
299 	    "file from which it came, or to another process which "
300 	    "shares that text.\n"));
301 
302 	if ((pid = fork()) == -1) {
303 		print_message(gettext("Fork failed\n"));
304 		return (EXIT_FAILURE);
305 	} else if (pid == 0) { /* Am I my child? */
306 		set_handler(SIGSEGV);
307 
308 		/* probe_text_area() does exit() */
309 		probe_text_area();
310 	}
311 	/* still parent */
312 	(void) wait(&status);
313 	status = WEXITSTATUS(status);
314 
315 	if (status == PASS) {
316 		print_message(gettext(
317 		    "FAIL: We did not cause a segmentation fault.\n"));
318 		exit_status = EXIT_FAILURE;
319 	} else if (status == FAIL_SEGV) {
320 		print_message(gettext(
321 		    "PASS: Caught the segmentation fault, "
322 		    "meaning we can't write to text area.\n"));
323 	} else {
324 		(void) printf(gettext(
325 		    "Test program failure: %d\n"), status);
326 		exit_status = EXIT_FAILURE;
327 	}
328 	return (exit_status);
329 }
330 
331 /*
332  * write to text area, trigger a SEGV
333  */
334 static void
335 probe_text_area()
336 {
337 	handler_exit_code = FAIL_SEGV;
338 	*(caddr_t)probe_text_area = 0xff;
339 	exit(EXIT_FAILURE);
340 }
341 
342 /*
343  * Test that when we set some values and fork a process, when the child
344  * writes to these inherited values, the parents copies are not changed.
345  */
346 static int
347 memory_not_shared_after_use()
348 {
349 	pid_t pid;
350 	int x = 1000;
351 	int exit_status = EXIT_SUCCESS;
352 
353 	print_message(gettext("\n\nTest 4- Memory Not Shared After Write\n"
354 	    "Verify that anonymous memory initially shared by two "
355 	    "processes (e.g. after a\n"
356 	    "fork) is not shared after either process writes "
357 	    "to it.\n"));
358 
359 	if ((pid = fork()) == -1) {
360 		print_message(gettext("Fork failed\n"));
361 		return (EXIT_FAILURE);
362 	} else if (pid == 0) { /* I am the child. */
363 		/*
364 		 * Change child value; this should not change
365 		 * parent value.
366 		 */
367 		x = 2000;
368 
369 		/* Wait for parent to test value */
370 		(void) sleep(CHILD_SLEEP_PERIOD);
371 
372 		exit(EXIT_SUCCESS);
373 	}
374 	/* Wait for child to do its stuff. */
375 	(void) sleep(PARENT_SLEEP_PERIOD);
376 
377 	if (x == 1000)
378 		exit_status = EXIT_SUCCESS;
379 	else
380 		exit_status = EXIT_FAILURE;
381 
382 	return (exit_status);
383 }
384 
385 /*
386  * If we fork a process and then allocate some memory in that process,
387  * we should not see any memory changes in the parent.
388  */
389 static int
390 memory_allocation_not_shared()
391 {
392 	pid_t pid;
393 	pid_t parent_pid;
394 	int exit_status = 0;
395 	caddr_t address;
396 	caddr_t hole_start;
397 	caddr_t hole_after;
398 	void (*old_handler) ();
399 
400 	print_message(gettext(
401 	    "\n\nTest 5- Memory Allocation is Not Shared\n"
402 	    "Verify that newly allocated memory in one of two "
403 	    "processes created by forking\n"
404 	    "does not result in newly allocated memory in the other.\n"));
405 
406 	/* Save Size of data area and 1st block address of "hole" */
407 	hole_start = (caddr_t)sbrk(0);
408 
409 	if (silent == NOT_SILENT)
410 		(void) printf(gettext(
411 		    "Parent address of hole before child change: %08X\n"),
412 		    hole_start);
413 
414 	/* Set handler for signal SIG_EVENT (define at start) */
415 	old_handler = signal(SIG_EVENT, &handler);
416 	if (old_handler == SIG_ERR) {
417 		print_message(gettext(
418 		    "Can't establish signal handler, test failed\n"));
419 		return (EXIT_FAILURE);
420 	}
421 
422 	if ((pid = fork()) == -1) {
423 		print_message(gettext("Fork failed\n"));
424 		return (EXIT_FAILURE);
425 	} else if (pid == 0) { /* We are the child. */
426 		address = sbrk(0);
427 		if (silent == NOT_SILENT)
428 			(void) printf(gettext(
429 			    "Child end of hole before change:  %08X\n"),
430 			    address);
431 
432 		if (brk((address+PAGESIZE)) != 0) {
433 			print_message(gettext(
434 			    "Can't change start of hole address.\n"));
435 			exit(EXIT_FAILURE);
436 		}
437 
438 		address = sbrk(0);
439 		if (silent == NOT_SILENT)
440 			(void) printf(gettext(
441 			    "Child end of hole after change: %08X\n"),
442 			    address);
443 
444 		/* Tell the parent we're done. */
445 		parent_pid = getppid();
446 		if (sigsend(P_PID, parent_pid, SIG_EVENT) != 0) {
447 			print_message(gettext("Can't send signal to parent, "
448 			    "test failed\n"));
449 			exit(EXIT_FAILURE);
450 		}
451 
452 		/* Sleep before exiting to allow parent to finish processing. */
453 		(void) sleep(CHILD_SLEEP_PERIOD);
454 		exit(EXIT_SUCCESS);
455 	}
456 	/* Wait for child to do its work. */
457 	(void) sleep(PARENT_SLEEP_PERIOD);
458 
459 	if (done_memory_grab != 1) {
460 		print_message(gettext(
461 		    "Child failed to do memory alterations, "
462 		    "exiting\n"));
463 		return (EXIT_FAILURE);
464 	}
465 
466 	hole_after = sbrk(0);
467 	if (silent == NOT_SILENT)
468 		(void) printf(gettext(
469 		    "Parent address of hole after child change: "
470 		    "%08X\n"), hole_after);
471 
472 	/* Test size of hole and data region. */
473 	if (hole_start == hole_after)
474 		print_message(gettext(
475 		    "PASS: Hole is same size in parent.\n"));
476 	else {
477 		print_message(gettext(
478 		    "FAIL: Hole is a different size.\n"));
479 		exit_status = EXIT_FAILURE;
480 	}
481 
482 	/* Wait for child to finish. */
483 	(void) wait(0);
484 
485 	if (signal(SIG_EVENT, old_handler) == SIG_ERR) {
486 		print_message(gettext("Couldn't put back old signal handler, "
487 		    "test failed.\n"));
488 		return (EXIT_FAILURE);
489 	}
490 	return (exit_status);
491 }
492 
493 static void
494 print_message(char *message)
495 {
496 	if (silent == NOT_SILENT)
497 		(void) printf("%s", message);
498 }
499 
500 static int
501 test_stack_end_of_hole()
502 {
503 	pid_t pid;
504 	int status;
505 	int exit_status = EXIT_SUCCESS;
506 
507 	print_message(gettext("\n\nTest 1- stack Side Boundary Test\n"));
508 
509 	/* sub test 1:  the space the stack grows into is zero */
510 
511 	if ((pid = fork()) == -1) {
512 		print_message(gettext("Fork failed\n"));
513 		return (EXIT_FAILURE);
514 	} else if (pid == 0) { /* Am I my child? */
515 		set_handler(SIGSEGV);
516 
517 		/* probe_stack() does exit */
518 		probe_stack();
519 	}
520 	/* still parent */
521 	(void) wait(&status);
522 	status = WEXITSTATUS(status);
523 
524 	if (status == FAIL_ZERO) {
525 		print_message(gettext("Fail with non-zero read.\n"));
526 		exit_status = EXIT_FAILURE;
527 	} else if (status != PASS) {
528 		print_message(gettext("Test program failure\n"));
529 		exit_status = EXIT_FAILURE;
530 	}
531 	/* sub test 2:  the space in hole is not readable */
532 
533 	if ((pid = fork()) == -1) {
534 		print_message(gettext("Fork failed\n"));
535 		return (EXIT_FAILURE);
536 	} else if (pid == 0) { /* Am I my child? */
537 		set_handler(SIGSEGV);
538 
539 		/* probe_hole does exit */
540 		probe_hole(PH_INVALID);
541 	}
542 	/* still parent */
543 	(void) wait(&status);
544 	status = WEXITSTATUS(status);
545 
546 	if (status == FAIL_SEGV) {
547 		print_message(
548 		    gettext("Fail (SEGV expected, not received).\n"));
549 		exit_status = EXIT_FAILURE;
550 	} else if (status != PASS_SEGV) {
551 		print_message(gettext("Test program failure.\n"));
552 		exit_status = EXIT_FAILURE;
553 	}
554 
555 	/* sub test 3:  the space in new page below hole is zero */
556 
557 	if ((pid = fork()) == -1) {
558 		print_message(gettext("Fork failed\n"));
559 		return (EXIT_FAILURE);
560 	} else if (pid == 0) { /* Am I my child? */
561 		set_handler(SIGSEGV);
562 
563 		/* probe_hole does exit */
564 		probe_hole(PH_VALID);
565 	}
566 	/* still parent */
567 	(void) wait(&status);
568 	status = WEXITSTATUS(status);
569 
570 	if (status == FAIL_SEGV) {
571 		print_message(gettext("Fail (got SEGV).\n"));
572 		exit_status = EXIT_FAILURE;
573 	} else if (status != PASS) {
574 		print_message(gettext("Test program failure.\n"));
575 		exit_status = EXIT_FAILURE;
576 	}
577 	return (exit_status);
578 }
579 
580 
581 /*
582  * set_handler
583  */
584 static void
585 set_handler(int sig)
586 {
587 	struct sigaction act;
588 
589 	act.sa_handler = NULL;
590 	act.sa_flags = SA_SIGINFO;
591 	act.sa_sigaction = segv_action;
592 	(void) sigemptyset(&(act.sa_mask));
593 
594 	if (sigaction(sig, &act, NULL) < 0) {
595 		if (silent == NOT_SILENT) {
596 			(void) fprintf(stderr, gettext(
597 			    "sigaction() returned error: %s\n"),
598 			    strerror(errno));
599 		}
600 		exit(EXIT_FAILURE);
601 	}
602 }
603 
604 
605 /*ARGSUSED*/
606 static void
607 segv_action(int which_sig, siginfo_t *t1, void *t2)
608 {
609 	exit(handler_exit_code);
610 }
611 
612 /*
613  * probe_stack
614  *
615  * Warning -- if you do a printf or fprintf prior to the actual
616  * reading from the stack, you've changed the stack to an unknown
617  * state.  (stack memory isn't free'd automatically and this function
618  * needs to touch virgin stack space.)
619  */
620 static void
621 probe_stack(void)
622 {
623 	unsigned char *end;	/* end of stack */
624 	unsigned char probe;
625 	long i;
626 	int j;
627 	unsigned char last_fail, *last_fail_address;
628 	unsigned char mark = 0xAA;	/* roughly the end of stack */
629 	handler_exit_code = FAIL_SEGV;
630 
631 	end = &mark;
632 	/* stack growth is negative */
633 	end -= (WASTE_PAGES * PAGESIZE) + STACK_SLOP;
634 
635 	for (i = 0, j = 0; i < PAGESIZE; i++) {
636 		if ((probe = *end) != 0) {
637 			j++;
638 			last_fail = probe;
639 			last_fail_address = end;
640 		}
641 		end--;
642 	}
643 
644 	if (j != 0) {
645 		if (silent == NOT_SILENT)
646 			(void) fprintf(stderr, gettext(
647 			    "probe_stack failed. address=0x%08X; "
648 			    "probe=0x%02X; content = %d\n"),
649 			    (caddr_t)last_fail_address, last_fail, j);
650 
651 		exit(FAIL_ZERO);    /* test failed at least once */
652 	}
653 	exit(EXIT_SUCCESS);
654 }
655 
656 static void
657 probe_hole(int test_type)
658 {
659 	long i;
660 	/* LINTED */
661 	volatile unsigned char probe;
662 	unsigned char *probe_adr;
663 	void *address;
664 
665 	address = sbrk(0);  /* current end data + 1 */
666 
667 	if (address == (void *)-1) {
668 		print_message(gettext("Test program logic error\n"));
669 		exit(FAIL_ABORT);
670 	}
671 	if (test_type == PH_VALID) {
672 		/* show that access works inside the  hole */
673 		handler_exit_code = FAIL_SEGV;
674 
675 		probe_adr =  (unsigned char *)address - sizeof (char);
676 
677 		for (i = 0; i < PAGESIZE; i++)
678 			probe = *probe_adr--;
679 
680 		exit(EXIT_SUCCESS);
681 	} else {
682 		/* show that a trap occurs in the  hole */
683 		handler_exit_code = PASS_SEGV;
684 
685 		address = (void *)P2ROUNDUP((uintptr_t)address, PAGESIZE);
686 		probe_adr = (unsigned char *)address;
687 
688 		probe = *probe_adr;
689 		exit(FAIL_SEGV);	/* expected SEGV, didn't get it */
690 	}
691 }
692 
693 /*
694  * Catch signal, child to parent
695  */
696 /*ARGSUSED*/
697 void
698 handler(int signal)
699 {
700 	done_memory_grab = 1;
701 }
702 /*
703  * memory_type:  Determine whether a given executable file is compiled
704  * as 32 or 64 bit.
705  *
706  * The following code was stolen from isainfo (1)
707  */
708 
709 static int
710 memory_type(const char *path) {
711 	char *idarray;
712 	Elf *elf;
713 	int d;
714 	int bits = 0;
715 
716 	if ((d = open(path, O_RDONLY)) < 0) {
717 		(void) fprintf(stderr,
718 		    "cannot open: %s -- %s\n",
719 		    path, strerror(errno));
720 		return (bits);
721 	}
722 
723 	if (elf_version(EV_CURRENT) == EV_NONE) {
724 		(void) fprintf(stderr,
725 		    "internal error: ELF library out of date?\n");
726 		(void) close(d);
727 		return (bits);
728 	}
729 
730 	elf = elf_begin(d, ELF_C_READ, (Elf *)0);
731 	if (elf_kind(elf) != ELF_K_ELF) {
732 		(void) elf_end(elf);
733 		(void) close(d);
734 		return (bits);
735 	}
736 
737 	idarray = elf_getident(elf, 0);
738 
739 	if (idarray[EI_CLASS] == ELFCLASS32) {
740 		bits = 32;
741 	} else if (idarray[EI_CLASS] == ELFCLASS64) {
742 		bits = 64;
743 	}
744 
745 	(void) elf_end(elf);
746 	(void) close(d);
747 	return (bits);
748 }
749