12d4be7aaSRichard Lowe /*
22d4be7aaSRichard Lowe  * CDDL HEADER START
32d4be7aaSRichard Lowe  *
42d4be7aaSRichard Lowe  * The contents of this file are subject to the terms of the
52d4be7aaSRichard Lowe  * Common Development and Distribution License (the "License").
62d4be7aaSRichard Lowe  * You may not use this file except in compliance with the License.
72d4be7aaSRichard Lowe  *
82d4be7aaSRichard Lowe  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
92d4be7aaSRichard Lowe  * or http://www.opensolaris.org/os/licensing.
102d4be7aaSRichard Lowe  * See the License for the specific language governing permissions
112d4be7aaSRichard Lowe  * and limitations under the License.
122d4be7aaSRichard Lowe  *
132d4be7aaSRichard Lowe  * When distributing Covered Code, include this CDDL HEADER in each
142d4be7aaSRichard Lowe  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
152d4be7aaSRichard Lowe  * If applicable, add the following below this CDDL HEADER, with the
162d4be7aaSRichard Lowe  * fields enclosed by brackets "[]" replaced with your own identifying
172d4be7aaSRichard Lowe  * information: Portions Copyright [yyyy] [name of copyright owner]
182d4be7aaSRichard Lowe  *
192d4be7aaSRichard Lowe  * CDDL HEADER END
202d4be7aaSRichard Lowe  */
212d4be7aaSRichard Lowe /*
222d4be7aaSRichard Lowe  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
232d4be7aaSRichard Lowe  * Use is subject to license terms.
242d4be7aaSRichard Lowe  */
252d4be7aaSRichard Lowe 
26*92c1a611SBryan Cantrill /*
27*92c1a611SBryan Cantrill  * Copyright 2019 Joyent, Inc.
28*92c1a611SBryan Cantrill  */
292d4be7aaSRichard Lowe 
302d4be7aaSRichard Lowe /*
312d4be7aaSRichard Lowe  * The Sun Studio and GCC (patched for opensolaris/illumos) compilers
322d4be7aaSRichard Lowe  * implement a argument saving scheme on amd64 via the -Wu,save-args or
332d4be7aaSRichard Lowe  * options.  When the option is specified, INTEGER type function arguments
342d4be7aaSRichard Lowe  * passed via registers will be saved on the stack immediately after %rbp, and
352d4be7aaSRichard Lowe  * will not be modified through out the life of the routine.
362d4be7aaSRichard Lowe  *
372d4be7aaSRichard Lowe  *				+--------+
382d4be7aaSRichard Lowe  *		%rbp	-->     |  %rbp  |
392d4be7aaSRichard Lowe  *				+--------+
402d4be7aaSRichard Lowe  *		-0x8(%rbp)	|  %rdi  |
412d4be7aaSRichard Lowe  *				+--------+
422d4be7aaSRichard Lowe  *		-0x10(%rbp)	|  %rsi  |
432d4be7aaSRichard Lowe  *				+--------+
442d4be7aaSRichard Lowe  *		-0x18(%rbp)	|  %rdx  |
452d4be7aaSRichard Lowe  *				+--------+
462d4be7aaSRichard Lowe  *		-0x20(%rbp)	|  %rcx  |
472d4be7aaSRichard Lowe  *				+--------+
482d4be7aaSRichard Lowe  *		-0x28(%rbp)	|  %r8   |
492d4be7aaSRichard Lowe  *				+--------+
502d4be7aaSRichard Lowe  *		-0x30(%rbp)	|  %r9   |
512d4be7aaSRichard Lowe  *				+--------+
522d4be7aaSRichard Lowe  *
532d4be7aaSRichard Lowe  *
542d4be7aaSRichard Lowe  * For example, for the following function,
552d4be7aaSRichard Lowe  *
562d4be7aaSRichard Lowe  * void
572d4be7aaSRichard Lowe  * foo(int a1, int a2, int a3, int a4, int a5, int a6, int a7)
582d4be7aaSRichard Lowe  * {
592d4be7aaSRichard Lowe  * ...
602d4be7aaSRichard Lowe  * }
612d4be7aaSRichard Lowe  *
622d4be7aaSRichard Lowe  * Disassembled code will look something like the following:
632d4be7aaSRichard Lowe  *
642d4be7aaSRichard Lowe  *     pushq	%rbp
652d4be7aaSRichard Lowe  *     movq	%rsp, %rbp
662d4be7aaSRichard Lowe  *     subq	$imm8, %rsp			**
672d4be7aaSRichard Lowe  *     movq	%rdi, -0x8(%rbp)
682d4be7aaSRichard Lowe  *     movq	%rsi, -0x10(%rbp)
692d4be7aaSRichard Lowe  *     movq	%rdx, -0x18(%rbp)
702d4be7aaSRichard Lowe  *     movq	%rcx, -0x20(%rbp)
712d4be7aaSRichard Lowe  *     movq	%r8, -0x28(%rbp)
722d4be7aaSRichard Lowe  *     movq	%r9, -0x30(%rbp)
732d4be7aaSRichard Lowe  *     ...
742d4be7aaSRichard Lowe  * or
752d4be7aaSRichard Lowe  *     pushq	%rbp
762d4be7aaSRichard Lowe  *     movq	%rsp, %rbp
772d4be7aaSRichard Lowe  *     subq	$imm8, %rsp			**
782d4be7aaSRichard Lowe  *     movq	%r9, -0x30(%rbp)
792d4be7aaSRichard Lowe  *     movq	%r8, -0x28(%rbp)
802d4be7aaSRichard Lowe  *     movq	%rcx, -0x20(%rbp)
812d4be7aaSRichard Lowe  *     movq	%rdx, -0x18(%rbp)
822d4be7aaSRichard Lowe  *     movq	%rsi, -0x10(%rbp)
832d4be7aaSRichard Lowe  *     movq	%rdi, -0x8(%rbp)
842d4be7aaSRichard Lowe  *     ...
852d4be7aaSRichard Lowe  * or
862d4be7aaSRichard Lowe  *     pushq	%rbp
872d4be7aaSRichard Lowe  *     movq	%rsp, %rbp
882d4be7aaSRichard Lowe  *     pushq	%rdi
892d4be7aaSRichard Lowe  *     pushq	%rsi
902d4be7aaSRichard Lowe  *     pushq	%rdx
912d4be7aaSRichard Lowe  *     pushq	%rcx
922d4be7aaSRichard Lowe  *     pushq	%r8
932d4be7aaSRichard Lowe  *     pushq	%r9
942d4be7aaSRichard Lowe  *
952d4be7aaSRichard Lowe  * **: The space being reserved is in addition to what the current
962d4be7aaSRichard Lowe  *     function prolog already reserves.
972d4be7aaSRichard Lowe  *
982d4be7aaSRichard Lowe  * We loop through the first SAVEARGS_INSN_SEQ_LEN bytes of the function
99702941cdSRichard Lowe  * looking for each argument saving instruction we would expect to see.
1002d4be7aaSRichard Lowe  *
1012d4be7aaSRichard Lowe  * If there are odd number of arguments to a function, additional space is
1022d4be7aaSRichard Lowe  * reserved on the stack to maintain 16-byte alignment.  For example,
1032d4be7aaSRichard Lowe  *
1042d4be7aaSRichard Lowe  *     argc == 0: no argument saving.
1052d4be7aaSRichard Lowe  *     argc == 3: save 3, but space for 4 is reserved
1062d4be7aaSRichard Lowe  *     argc == 7: save 6.
1072d4be7aaSRichard Lowe  */
1082d4be7aaSRichard Lowe 
1092d4be7aaSRichard Lowe #include <sys/sysmacros.h>
1102d4be7aaSRichard Lowe #include <sys/types.h>
111702941cdSRichard Lowe #include <libdisasm.h>
112702941cdSRichard Lowe #include <string.h>
113702941cdSRichard Lowe 
1142d4be7aaSRichard Lowe #include <saveargs.h>
1152d4be7aaSRichard Lowe 
1162d4be7aaSRichard Lowe /*
1172d4be7aaSRichard Lowe  * Size of the instruction sequence arrays.  It should correspond to
1182d4be7aaSRichard Lowe  * the maximum number of arguments passed via registers.
1192d4be7aaSRichard Lowe  */
1202d4be7aaSRichard Lowe #define	INSTR_ARRAY_SIZE	6
1212d4be7aaSRichard Lowe 
1222d4be7aaSRichard Lowe #define	INSTR1(ins, off) (ins[(off)])
1232d4be7aaSRichard Lowe #define	INSTR2(ins, off) (ins[(off)] + (ins[(off) + 1] << 8))
1242d4be7aaSRichard Lowe #define	INSTR3(ins, off)	\
1252d4be7aaSRichard Lowe 	(ins[(off)] + (ins[(off) + 1] << 8) + (ins[(off + 2)] << 16))
1262d4be7aaSRichard Lowe #define	INSTR4(ins, off)	\
1272d4be7aaSRichard Lowe 	(ins[(off)] + (ins[(off) + 1] << 8) + (ins[(off + 2)] << 16) + \
1282d4be7aaSRichard Lowe 	(ins[(off) + 3] << 24))
1292d4be7aaSRichard Lowe 
1302d4be7aaSRichard Lowe /*
1312d4be7aaSRichard Lowe  * Sun Studio 10 patch implementation saves %rdi first;
1322d4be7aaSRichard Lowe  * GCC 3.4.3 Sun branch implementation saves them in reverse order.
1332d4be7aaSRichard Lowe  */
1342d4be7aaSRichard Lowe static const uint32_t save_instr[INSTR_ARRAY_SIZE] = {
1352d4be7aaSRichard Lowe 	0xf87d8948,	/* movq %rdi, -0x8(%rbp) */
1362d4be7aaSRichard Lowe 	0xf0758948,	/* movq %rsi, -0x10(%rbp) */
1372d4be7aaSRichard Lowe 	0xe8558948,	/* movq %rdx, -0x18(%rbp) */
1382d4be7aaSRichard Lowe 	0xe04d8948,	/* movq %rcx, -0x20(%rbp) */
1392d4be7aaSRichard Lowe 	0xd845894c,	/* movq %r8, -0x28(%rbp) */
1402d4be7aaSRichard Lowe 	0xd04d894c	/* movq %r9, -0x30(%rbp) */
1412d4be7aaSRichard Lowe };
1422d4be7aaSRichard Lowe 
1432d4be7aaSRichard Lowe static const uint16_t save_instr_push[] = {
1442d4be7aaSRichard Lowe 	0x57,	/* pushq %rdi */
1452d4be7aaSRichard Lowe 	0x56,	/* pushq %rsi */
1462d4be7aaSRichard Lowe 	0x52,	/* pushq %rdx */
1472d4be7aaSRichard Lowe 	0x51,	/* pushq %rcx */
1482d4be7aaSRichard Lowe 	0x5041,	/* pushq %r8 */
1492d4be7aaSRichard Lowe 	0x5141	/* pushq %r9 */
1502d4be7aaSRichard Lowe };
1512d4be7aaSRichard Lowe 
1522d4be7aaSRichard Lowe /*
1532d4be7aaSRichard Lowe  * If the return type of a function is a structure greater than 16 bytes in
1542d4be7aaSRichard Lowe  * size, %rdi will contain the address to which it should be stored, and
1552d4be7aaSRichard Lowe  * arguments will begin at %rsi.  Studio will push all of the normal argument
1562d4be7aaSRichard Lowe  * registers anyway, GCC will start pushing at %rsi, so we need a separate
1572d4be7aaSRichard Lowe  * pattern.
1582d4be7aaSRichard Lowe  */
1592d4be7aaSRichard Lowe static const uint32_t save_instr_sr[INSTR_ARRAY_SIZE-1] = {
1602d4be7aaSRichard Lowe 	0xf8758948,	/* movq %rsi,-0x8(%rbp) */
1612d4be7aaSRichard Lowe 	0xf0558948,	/* movq %rdx,-0x10(%rbp) */
1622d4be7aaSRichard Lowe 	0xe84d8948,	/* movq %rcx,-0x18(%rbp) */
1632d4be7aaSRichard Lowe 	0xe045894c,	/* movq %r8,-0x20(%rbp) */
1642d4be7aaSRichard Lowe 	0xd84d894c	/* movq %r9,-0x28(%rbp) */
1652d4be7aaSRichard Lowe };
1662d4be7aaSRichard Lowe 
1672d4be7aaSRichard Lowe static const uint8_t save_fp_pushes[] = {
168702941cdSRichard Lowe 	0x55,	/* pushq %rbp */
169702941cdSRichard Lowe 	0xcc	/* int $0x3 */
1702d4be7aaSRichard Lowe };
1712d4be7aaSRichard Lowe #define	NUM_FP_PUSHES (sizeof (save_fp_pushes) / sizeof (save_fp_pushes[0]))
1722d4be7aaSRichard Lowe 
1732d4be7aaSRichard Lowe static const uint32_t save_fp_movs[] = {
1742d4be7aaSRichard Lowe 	0x00e58948,	/* movq %rsp,%rbp, encoding 1 */
1752d4be7aaSRichard Lowe 	0x00ec8b48,	/* movq %rsp,%rbp, encoding 2 */
1762d4be7aaSRichard Lowe };
1772d4be7aaSRichard Lowe #define	NUM_FP_MOVS (sizeof (save_fp_movs) / sizeof (save_fp_movs[0]))
1782d4be7aaSRichard Lowe 
179702941cdSRichard Lowe typedef struct {
180702941cdSRichard Lowe 	uint8_t *data;
181702941cdSRichard Lowe 	size_t size;
182702941cdSRichard Lowe } text_t;
183702941cdSRichard Lowe 
1842d4be7aaSRichard Lowe static int
do_read(void * data,uint64_t addr,void * buf,size_t len)185702941cdSRichard Lowe do_read(void *data, uint64_t addr, void *buf, size_t len)
1862d4be7aaSRichard Lowe {
187702941cdSRichard Lowe 	text_t	*t = data;
188702941cdSRichard Lowe 
189702941cdSRichard Lowe 	if (addr >= t->size)
190702941cdSRichard Lowe 		return (-1);
191702941cdSRichard Lowe 
192702941cdSRichard Lowe 	len = MIN(len, t->size - addr);
193702941cdSRichard Lowe 
194702941cdSRichard Lowe 	(void) memcpy(buf, (char *)t->data + addr, len);
195702941cdSRichard Lowe 
196702941cdSRichard Lowe 	return (len);
197702941cdSRichard Lowe }
198702941cdSRichard Lowe 
199702941cdSRichard Lowe /* ARGSUSED */
200702941cdSRichard Lowe int
do_lookup(void * data,uint64_t addr,char * buf,size_t buflen,uint64_t * start,size_t * symlen)201702941cdSRichard Lowe do_lookup(void *data, uint64_t addr, char *buf, size_t buflen, uint64_t *start,
202702941cdSRichard Lowe     size_t *symlen)
203702941cdSRichard Lowe {
204702941cdSRichard Lowe 	/* We don't actually need lookup info */
205702941cdSRichard Lowe 	return (-1);
206702941cdSRichard Lowe }
207702941cdSRichard Lowe 
208702941cdSRichard Lowe static int
instr_size(dis_handle_t * dhp,uint8_t * ins,unsigned int i,size_t size)209702941cdSRichard Lowe instr_size(dis_handle_t *dhp, uint8_t *ins, unsigned int i, size_t size)
210702941cdSRichard Lowe {
211702941cdSRichard Lowe 	text_t	t;
212702941cdSRichard Lowe 
213702941cdSRichard Lowe 	t.data = ins;
214702941cdSRichard Lowe 	t.size = size;
215702941cdSRichard Lowe 
216702941cdSRichard Lowe 	dis_set_data(dhp, &t);
217702941cdSRichard Lowe 	return (dis_instrlen(dhp, i));
218702941cdSRichard Lowe }
219702941cdSRichard Lowe 
220702941cdSRichard Lowe static boolean_t
has_saved_fp(dis_handle_t * dhp,uint8_t * ins,int size)221702941cdSRichard Lowe has_saved_fp(dis_handle_t *dhp, uint8_t *ins, int size)
222702941cdSRichard Lowe {
223*92c1a611SBryan Cantrill 	int		i, j;
224702941cdSRichard Lowe 	uint32_t	n;
225702941cdSRichard Lowe 	boolean_t	found_push = B_FALSE;
226702941cdSRichard Lowe 	ssize_t		sz = 0;
227702941cdSRichard Lowe 
228702941cdSRichard Lowe 	for (i = 0; i < size; i += sz) {
229702941cdSRichard Lowe 		if ((sz = instr_size(dhp, ins, i, size)) < 1)
230702941cdSRichard Lowe 			return (B_FALSE);
231702941cdSRichard Lowe 
232702941cdSRichard Lowe 		if (found_push == B_FALSE) {
233702941cdSRichard Lowe 			if (sz != 1)
234702941cdSRichard Lowe 				continue;
2352d4be7aaSRichard Lowe 
2362d4be7aaSRichard Lowe 			n = INSTR1(ins, i);
237*92c1a611SBryan Cantrill 			for (j = 0; j < NUM_FP_PUSHES; j++)
2382d4be7aaSRichard Lowe 				if (save_fp_pushes[j] == n) {
239702941cdSRichard Lowe 					found_push = B_TRUE;
2402d4be7aaSRichard Lowe 					break;
2412d4be7aaSRichard Lowe 				}
2422d4be7aaSRichard Lowe 		} else {
243702941cdSRichard Lowe 			if (sz != 3)
244702941cdSRichard Lowe 				continue;
2452d4be7aaSRichard Lowe 			n = INSTR3(ins, i);
246*92c1a611SBryan Cantrill 			for (j = 0; j < NUM_FP_MOVS; j++)
2472d4be7aaSRichard Lowe 				if (save_fp_movs[j] == n)
248702941cdSRichard Lowe 					return (B_TRUE);
2492d4be7aaSRichard Lowe 		}
2502d4be7aaSRichard Lowe 	}
2512d4be7aaSRichard Lowe 
252702941cdSRichard Lowe 	return (B_FALSE);
2532d4be7aaSRichard Lowe }
2542d4be7aaSRichard Lowe 
2552d4be7aaSRichard Lowe int
saveargs_has_args(uint8_t * ins,size_t size,uint_t argc,int start_index)2562d4be7aaSRichard Lowe saveargs_has_args(uint8_t *ins, size_t size, uint_t argc, int start_index)
2572d4be7aaSRichard Lowe {
2582d4be7aaSRichard Lowe 	int		i, j;
2592d4be7aaSRichard Lowe 	uint32_t	n;
260702941cdSRichard Lowe 	uint8_t		found = 0;
261702941cdSRichard Lowe 	ssize_t		sz = 0;
262702941cdSRichard Lowe 	dis_handle_t	*dhp = NULL;
263702941cdSRichard Lowe 	int		ret = SAVEARGS_NO_ARGS;
2642d4be7aaSRichard Lowe 
2652d4be7aaSRichard Lowe 	argc = MIN((start_index + argc), INSTR_ARRAY_SIZE);
2662d4be7aaSRichard Lowe 
267702941cdSRichard Lowe 	if ((dhp = dis_handle_create(DIS_X86_SIZE64, NULL, do_lookup,
268702941cdSRichard Lowe 	    do_read)) == NULL)
2692d4be7aaSRichard Lowe 		return (SAVEARGS_NO_ARGS);
2702d4be7aaSRichard Lowe 
271702941cdSRichard Lowe 	if (!has_saved_fp(dhp, ins, size)) {
272702941cdSRichard Lowe 		dis_handle_destroy(dhp);
273702941cdSRichard Lowe 		return (SAVEARGS_NO_ARGS);
274702941cdSRichard Lowe 	}
275702941cdSRichard Lowe 
2762d4be7aaSRichard Lowe 	/*
277702941cdSRichard Lowe 	 * For each possible style of argument saving, walk the insn stream as
278702941cdSRichard Lowe 	 * we've been given it, and set bit N in 'found' if we find the
279702941cdSRichard Lowe 	 * instruction saving the Nth argument.
2802d4be7aaSRichard Lowe 	 */
2812d4be7aaSRichard Lowe 
2822d4be7aaSRichard Lowe 	/*
283702941cdSRichard Lowe 	 * Compare against regular implementation
2842d4be7aaSRichard Lowe 	 */
285702941cdSRichard Lowe 	found = 0;
286702941cdSRichard Lowe 	for (i = 0; i < size; i += sz) {
287702941cdSRichard Lowe 		sz = instr_size(dhp, ins, i, size);
288702941cdSRichard Lowe 
289702941cdSRichard Lowe 		if (sz < 1)
290702941cdSRichard Lowe 			break;
291702941cdSRichard Lowe 		else if (sz != 4)
292702941cdSRichard Lowe 			continue;
293702941cdSRichard Lowe 
2942d4be7aaSRichard Lowe 		n = INSTR4(ins, i);
2952d4be7aaSRichard Lowe 
296702941cdSRichard Lowe 		for (j = 0; j < argc; j++) {
297702941cdSRichard Lowe 			if (n == save_instr[j]) {
298702941cdSRichard Lowe 				found |= (1 << j);
299702941cdSRichard Lowe 
300702941cdSRichard Lowe 				if (found == ((1 << argc) - 1)) {
301702941cdSRichard Lowe 					ret = start_index ?
302702941cdSRichard Lowe 					    SAVEARGS_STRUCT_ARGS :
303702941cdSRichard Lowe 					    SAVEARGS_TRAD_ARGS;
304702941cdSRichard Lowe 					goto done;
305702941cdSRichard Lowe 				}
306702941cdSRichard Lowe 
307702941cdSRichard Lowe 				break;
308702941cdSRichard Lowe 			}
3092d4be7aaSRichard Lowe 		}
3102d4be7aaSRichard Lowe 	}
3112d4be7aaSRichard Lowe 
3122d4be7aaSRichard Lowe 	/*
3132d4be7aaSRichard Lowe 	 * Compare against GCC push-based implementation
3142d4be7aaSRichard Lowe 	 */
315702941cdSRichard Lowe 	found = 0;
316702941cdSRichard Lowe 	for (i = 0; i < size; i += sz) {
317702941cdSRichard Lowe 		if ((sz = instr_size(dhp, ins, i, size)) < 1)
318702941cdSRichard Lowe 			break;
319702941cdSRichard Lowe 
320702941cdSRichard Lowe 		for (j = start_index; j < argc; j++) {
321702941cdSRichard Lowe 			if (sz == 2) /* Two byte */
322702941cdSRichard Lowe 				n = INSTR2(ins, i);
323702941cdSRichard Lowe 			else if (sz == 1)
324702941cdSRichard Lowe 				n = INSTR1(ins, i);
325702941cdSRichard Lowe 			else
326702941cdSRichard Lowe 				continue;
327702941cdSRichard Lowe 
328702941cdSRichard Lowe 			if (n == save_instr_push[j]) {
329702941cdSRichard Lowe 				found |= (1 << (j - start_index));
330702941cdSRichard Lowe 
331702941cdSRichard Lowe 				if (found ==
332702941cdSRichard Lowe 				    ((1 << (argc - start_index)) - 1)) {
333702941cdSRichard Lowe 					ret = SAVEARGS_TRAD_ARGS;
334702941cdSRichard Lowe 					goto done;
335702941cdSRichard Lowe 				}
336702941cdSRichard Lowe 
337702941cdSRichard Lowe 				break;
338702941cdSRichard Lowe 			}
3392d4be7aaSRichard Lowe 		}
3402d4be7aaSRichard Lowe 	}
3412d4be7aaSRichard Lowe 
342702941cdSRichard Lowe 	/*
343702941cdSRichard Lowe 	 * Look for a GCC-style returned structure.
344702941cdSRichard Lowe 	 */
345702941cdSRichard Lowe 	found = 0;
3462d4be7aaSRichard Lowe 	if (start_index != 0) {
347702941cdSRichard Lowe 		for (i = 0; i < size; i += sz) {
348702941cdSRichard Lowe 			sz = instr_size(dhp, ins, i, size);
349702941cdSRichard Lowe 
350702941cdSRichard Lowe 			if (sz < 1)
351702941cdSRichard Lowe 				break;
352702941cdSRichard Lowe 			else if (sz != 4)
353702941cdSRichard Lowe 				continue;
354702941cdSRichard Lowe 
3552d4be7aaSRichard Lowe 			n = INSTR4(ins, i);
3562d4be7aaSRichard Lowe 
357702941cdSRichard Lowe 			/* argc is inclusive of start_index, allow for that */
358702941cdSRichard Lowe 			for (j = 0; j < (argc - start_index); j++) {
359702941cdSRichard Lowe 				if (n == save_instr_sr[j]) {
360702941cdSRichard Lowe 					found |= (1 << j);
361702941cdSRichard Lowe 
362702941cdSRichard Lowe 					if (found ==
363702941cdSRichard Lowe 					    ((1 << (argc - start_index)) - 1)) {
364702941cdSRichard Lowe 						ret = SAVEARGS_TRAD_ARGS;
365702941cdSRichard Lowe 						goto done;
366702941cdSRichard Lowe 					}
367702941cdSRichard Lowe 
368702941cdSRichard Lowe 					break;
369702941cdSRichard Lowe 				}
3702d4be7aaSRichard Lowe 			}
3712d4be7aaSRichard Lowe 		}
3722d4be7aaSRichard Lowe 	}
3732d4be7aaSRichard Lowe 
374702941cdSRichard Lowe done:
375702941cdSRichard Lowe 	dis_handle_destroy(dhp);
376702941cdSRichard Lowe 	return (ret);
3772d4be7aaSRichard Lowe }
378