xref: /titanic_51/usr/src/lib/libsaveargs/amd64/saveargs.c (revision 702941cd97e30d600c2c1275823bc58231b2c361)
1*702941cdSRichard Lowe /*
2*702941cdSRichard Lowe  * CDDL HEADER START
3*702941cdSRichard Lowe  *
4*702941cdSRichard Lowe  * The contents of this file are subject to the terms of the
5*702941cdSRichard Lowe  * Common Development and Distribution License (the "License").
6*702941cdSRichard Lowe  * You may not use this file except in compliance with the License.
7*702941cdSRichard Lowe  *
8*702941cdSRichard Lowe  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9*702941cdSRichard Lowe  * or http://www.opensolaris.org/os/licensing.
10*702941cdSRichard Lowe  * See the License for the specific language governing permissions
11*702941cdSRichard Lowe  * and limitations under the License.
12*702941cdSRichard Lowe  *
13*702941cdSRichard Lowe  * When distributing Covered Code, include this CDDL HEADER in each
14*702941cdSRichard Lowe  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15*702941cdSRichard Lowe  * If applicable, add the following below this CDDL HEADER, with the
16*702941cdSRichard Lowe  * fields enclosed by brackets "[]" replaced with your own identifying
17*702941cdSRichard Lowe  * information: Portions Copyright [yyyy] [name of copyright owner]
18*702941cdSRichard Lowe  *
19*702941cdSRichard Lowe  * CDDL HEADER END
20*702941cdSRichard Lowe  */
21*702941cdSRichard Lowe /*
22*702941cdSRichard Lowe  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
23*702941cdSRichard Lowe  * Use is subject to license terms.
24*702941cdSRichard Lowe  */
25*702941cdSRichard Lowe 
26*702941cdSRichard Lowe 
27*702941cdSRichard Lowe /*
28*702941cdSRichard Lowe  * The Sun Studio and GCC (patched for opensolaris/illumos) compilers
29*702941cdSRichard Lowe  * implement a argument saving scheme on amd64 via the -Wu,save-args or
30*702941cdSRichard Lowe  * options.  When the option is specified, INTEGER type function arguments
31*702941cdSRichard Lowe  * passed via registers will be saved on the stack immediately after %rbp, and
32*702941cdSRichard Lowe  * will not be modified through out the life of the routine.
33*702941cdSRichard Lowe  *
34*702941cdSRichard Lowe  *				+--------+
35*702941cdSRichard Lowe  *		%rbp	-->     |  %rbp  |
36*702941cdSRichard Lowe  *				+--------+
37*702941cdSRichard Lowe  *		-0x8(%rbp)	|  %rdi  |
38*702941cdSRichard Lowe  *				+--------+
39*702941cdSRichard Lowe  *		-0x10(%rbp)	|  %rsi  |
40*702941cdSRichard Lowe  *				+--------+
41*702941cdSRichard Lowe  *		-0x18(%rbp)	|  %rdx  |
42*702941cdSRichard Lowe  *				+--------+
43*702941cdSRichard Lowe  *		-0x20(%rbp)	|  %rcx  |
44*702941cdSRichard Lowe  *				+--------+
45*702941cdSRichard Lowe  *		-0x28(%rbp)	|  %r8   |
46*702941cdSRichard Lowe  *				+--------+
47*702941cdSRichard Lowe  *		-0x30(%rbp)	|  %r9   |
48*702941cdSRichard Lowe  *				+--------+
49*702941cdSRichard Lowe  *
50*702941cdSRichard Lowe  *
51*702941cdSRichard Lowe  * For example, for the following function,
52*702941cdSRichard Lowe  *
53*702941cdSRichard Lowe  * void
54*702941cdSRichard Lowe  * foo(int a1, int a2, int a3, int a4, int a5, int a6, int a7)
55*702941cdSRichard Lowe  * {
56*702941cdSRichard Lowe  * ...
57*702941cdSRichard Lowe  * }
58*702941cdSRichard Lowe  *
59*702941cdSRichard Lowe  * Disassembled code will look something like the following:
60*702941cdSRichard Lowe  *
61*702941cdSRichard Lowe  *     pushq	%rbp
62*702941cdSRichard Lowe  *     movq	%rsp, %rbp
63*702941cdSRichard Lowe  *     subq	$imm8, %rsp			**
64*702941cdSRichard Lowe  *     movq	%rdi, -0x8(%rbp)
65*702941cdSRichard Lowe  *     movq	%rsi, -0x10(%rbp)
66*702941cdSRichard Lowe  *     movq	%rdx, -0x18(%rbp)
67*702941cdSRichard Lowe  *     movq	%rcx, -0x20(%rbp)
68*702941cdSRichard Lowe  *     movq	%r8, -0x28(%rbp)
69*702941cdSRichard Lowe  *     movq	%r9, -0x30(%rbp)
70*702941cdSRichard Lowe  *     ...
71*702941cdSRichard Lowe  * or
72*702941cdSRichard Lowe  *     pushq	%rbp
73*702941cdSRichard Lowe  *     movq	%rsp, %rbp
74*702941cdSRichard Lowe  *     subq	$imm8, %rsp			**
75*702941cdSRichard Lowe  *     movq	%r9, -0x30(%rbp)
76*702941cdSRichard Lowe  *     movq	%r8, -0x28(%rbp)
77*702941cdSRichard Lowe  *     movq	%rcx, -0x20(%rbp)
78*702941cdSRichard Lowe  *     movq	%rdx, -0x18(%rbp)
79*702941cdSRichard Lowe  *     movq	%rsi, -0x10(%rbp)
80*702941cdSRichard Lowe  *     movq	%rdi, -0x8(%rbp)
81*702941cdSRichard Lowe  *     ...
82*702941cdSRichard Lowe  * or
83*702941cdSRichard Lowe  *     pushq	%rbp
84*702941cdSRichard Lowe  *     movq	%rsp, %rbp
85*702941cdSRichard Lowe  *     pushq	%rdi
86*702941cdSRichard Lowe  *     pushq	%rsi
87*702941cdSRichard Lowe  *     pushq	%rdx
88*702941cdSRichard Lowe  *     pushq	%rcx
89*702941cdSRichard Lowe  *     pushq	%r8
90*702941cdSRichard Lowe  *     pushq	%r9
91*702941cdSRichard Lowe  *
92*702941cdSRichard Lowe  * **: The space being reserved is in addition to what the current
93*702941cdSRichard Lowe  *     function prolog already reserves.
94*702941cdSRichard Lowe  *
95*702941cdSRichard Lowe  * We loop through the first SAVEARGS_INSN_SEQ_LEN bytes of the function
96*702941cdSRichard Lowe  * looking for each argument saving instruction we would expect to see.
97*702941cdSRichard Lowe  *
98*702941cdSRichard Lowe  * If there are odd number of arguments to a function, additional space is
99*702941cdSRichard Lowe  * reserved on the stack to maintain 16-byte alignment.  For example,
100*702941cdSRichard Lowe  *
101*702941cdSRichard Lowe  *     argc == 0: no argument saving.
102*702941cdSRichard Lowe  *     argc == 3: save 3, but space for 4 is reserved
103*702941cdSRichard Lowe  *     argc == 7: save 6.
104*702941cdSRichard Lowe  */
105*702941cdSRichard Lowe 
106*702941cdSRichard Lowe #include <sys/sysmacros.h>
107*702941cdSRichard Lowe #include <sys/types.h>
108*702941cdSRichard Lowe #include <libdisasm.h>
109*702941cdSRichard Lowe #include <string.h>
110*702941cdSRichard Lowe 
111*702941cdSRichard Lowe #include <saveargs.h>
112*702941cdSRichard Lowe 
113*702941cdSRichard Lowe /*
114*702941cdSRichard Lowe  * Size of the instruction sequence arrays.  It should correspond to
115*702941cdSRichard Lowe  * the maximum number of arguments passed via registers.
116*702941cdSRichard Lowe  */
117*702941cdSRichard Lowe #define	INSTR_ARRAY_SIZE	6
118*702941cdSRichard Lowe 
119*702941cdSRichard Lowe #define	INSTR1(ins, off) (ins[(off)])
120*702941cdSRichard Lowe #define	INSTR2(ins, off) (ins[(off)] + (ins[(off) + 1] << 8))
121*702941cdSRichard Lowe #define	INSTR3(ins, off)	\
122*702941cdSRichard Lowe 	(ins[(off)] + (ins[(off) + 1] << 8) + (ins[(off + 2)] << 16))
123*702941cdSRichard Lowe #define	INSTR4(ins, off)	\
124*702941cdSRichard Lowe 	(ins[(off)] + (ins[(off) + 1] << 8) + (ins[(off + 2)] << 16) + \
125*702941cdSRichard Lowe 	(ins[(off) + 3] << 24))
126*702941cdSRichard Lowe 
127*702941cdSRichard Lowe /*
128*702941cdSRichard Lowe  * Sun Studio 10 patch implementation saves %rdi first;
129*702941cdSRichard Lowe  * GCC 3.4.3 Sun branch implementation saves them in reverse order.
130*702941cdSRichard Lowe  */
131*702941cdSRichard Lowe static const uint32_t save_instr[INSTR_ARRAY_SIZE] = {
132*702941cdSRichard Lowe 	0xf87d8948,	/* movq %rdi, -0x8(%rbp) */
133*702941cdSRichard Lowe 	0xf0758948,	/* movq %rsi, -0x10(%rbp) */
134*702941cdSRichard Lowe 	0xe8558948,	/* movq %rdx, -0x18(%rbp) */
135*702941cdSRichard Lowe 	0xe04d8948,	/* movq %rcx, -0x20(%rbp) */
136*702941cdSRichard Lowe 	0xd845894c,	/* movq %r8, -0x28(%rbp) */
137*702941cdSRichard Lowe 	0xd04d894c	/* movq %r9, -0x30(%rbp) */
138*702941cdSRichard Lowe };
139*702941cdSRichard Lowe 
140*702941cdSRichard Lowe static const uint16_t save_instr_push[] = {
141*702941cdSRichard Lowe 	0x57,	/* pushq %rdi */
142*702941cdSRichard Lowe 	0x56,	/* pushq %rsi */
143*702941cdSRichard Lowe 	0x52,	/* pushq %rdx */
144*702941cdSRichard Lowe 	0x51,	/* pushq %rcx */
145*702941cdSRichard Lowe 	0x5041,	/* pushq %r8 */
146*702941cdSRichard Lowe 	0x5141	/* pushq %r9 */
147*702941cdSRichard Lowe };
148*702941cdSRichard Lowe 
149*702941cdSRichard Lowe /*
150*702941cdSRichard Lowe  * If the return type of a function is a structure greater than 16 bytes in
151*702941cdSRichard Lowe  * size, %rdi will contain the address to which it should be stored, and
152*702941cdSRichard Lowe  * arguments will begin at %rsi.  Studio will push all of the normal argument
153*702941cdSRichard Lowe  * registers anyway, GCC will start pushing at %rsi, so we need a separate
154*702941cdSRichard Lowe  * pattern.
155*702941cdSRichard Lowe  */
156*702941cdSRichard Lowe static const uint32_t save_instr_sr[INSTR_ARRAY_SIZE-1] = {
157*702941cdSRichard Lowe 	0xf8758948,	/* movq %rsi,-0x8(%rbp) */
158*702941cdSRichard Lowe 	0xf0558948,	/* movq %rdx,-0x10(%rbp) */
159*702941cdSRichard Lowe 	0xe84d8948,	/* movq %rcx,-0x18(%rbp) */
160*702941cdSRichard Lowe 	0xe045894c,	/* movq %r8,-0x20(%rbp) */
161*702941cdSRichard Lowe 	0xd84d894c	/* movq %r9,-0x28(%rbp) */
162*702941cdSRichard Lowe };
163*702941cdSRichard Lowe 
164*702941cdSRichard Lowe static const uint8_t save_fp_pushes[] = {
165*702941cdSRichard Lowe 	0x55,	/* pushq %rbp */
166*702941cdSRichard Lowe 	0xcc	/* int $0x3 */
167*702941cdSRichard Lowe };
168*702941cdSRichard Lowe #define	NUM_FP_PUSHES (sizeof (save_fp_pushes) / sizeof (save_fp_pushes[0]))
169*702941cdSRichard Lowe 
170*702941cdSRichard Lowe static const uint32_t save_fp_movs[] = {
171*702941cdSRichard Lowe 	0x00e58948,	/* movq %rsp,%rbp, encoding 1 */
172*702941cdSRichard Lowe 	0x00ec8b48,	/* movq %rsp,%rbp, encoding 2 */
173*702941cdSRichard Lowe };
174*702941cdSRichard Lowe #define	NUM_FP_MOVS (sizeof (save_fp_movs) / sizeof (save_fp_movs[0]))
175*702941cdSRichard Lowe 
176*702941cdSRichard Lowe typedef struct {
177*702941cdSRichard Lowe 	uint8_t *data;
178*702941cdSRichard Lowe 	size_t size;
179*702941cdSRichard Lowe } text_t;
180*702941cdSRichard Lowe 
181*702941cdSRichard Lowe static int
182*702941cdSRichard Lowe do_read(void *data, uint64_t addr, void *buf, size_t len)
183*702941cdSRichard Lowe {
184*702941cdSRichard Lowe 	text_t	*t = data;
185*702941cdSRichard Lowe 
186*702941cdSRichard Lowe 	if (addr >= t->size)
187*702941cdSRichard Lowe 		return (-1);
188*702941cdSRichard Lowe 
189*702941cdSRichard Lowe 	len = MIN(len, t->size - addr);
190*702941cdSRichard Lowe 
191*702941cdSRichard Lowe 	(void) memcpy(buf, (char *)t->data + addr, len);
192*702941cdSRichard Lowe 
193*702941cdSRichard Lowe 	return (len);
194*702941cdSRichard Lowe }
195*702941cdSRichard Lowe 
196*702941cdSRichard Lowe /* ARGSUSED */
197*702941cdSRichard Lowe int
198*702941cdSRichard Lowe do_lookup(void *data, uint64_t addr, char *buf, size_t buflen, uint64_t *start,
199*702941cdSRichard Lowe     size_t *symlen)
200*702941cdSRichard Lowe {
201*702941cdSRichard Lowe 	/* We don't actually need lookup info */
202*702941cdSRichard Lowe 	return (-1);
203*702941cdSRichard Lowe }
204*702941cdSRichard Lowe 
205*702941cdSRichard Lowe static int
206*702941cdSRichard Lowe instr_size(dis_handle_t *dhp, uint8_t *ins, unsigned int i, size_t size)
207*702941cdSRichard Lowe {
208*702941cdSRichard Lowe 	text_t	t;
209*702941cdSRichard Lowe 
210*702941cdSRichard Lowe 	t.data = ins;
211*702941cdSRichard Lowe 	t.size = size;
212*702941cdSRichard Lowe 
213*702941cdSRichard Lowe 	dis_set_data(dhp, &t);
214*702941cdSRichard Lowe 	return (dis_instrlen(dhp, i));
215*702941cdSRichard Lowe }
216*702941cdSRichard Lowe 
217*702941cdSRichard Lowe static boolean_t
218*702941cdSRichard Lowe has_saved_fp(dis_handle_t *dhp, uint8_t *ins, int size)
219*702941cdSRichard Lowe {
220*702941cdSRichard Lowe 	int 		i, j;
221*702941cdSRichard Lowe 	uint32_t	n;
222*702941cdSRichard Lowe 	boolean_t	found_push = B_FALSE;
223*702941cdSRichard Lowe 	ssize_t		sz = 0;
224*702941cdSRichard Lowe 
225*702941cdSRichard Lowe 	for (i = 0; i < size; i += sz) {
226*702941cdSRichard Lowe 		if ((sz = instr_size(dhp, ins, i, size)) < 1)
227*702941cdSRichard Lowe 			return (B_FALSE);
228*702941cdSRichard Lowe 
229*702941cdSRichard Lowe 		if (found_push == B_FALSE) {
230*702941cdSRichard Lowe 			if (sz != 1)
231*702941cdSRichard Lowe 				continue;
232*702941cdSRichard Lowe 
233*702941cdSRichard Lowe 			n = INSTR1(ins, i);
234*702941cdSRichard Lowe 			for (j = 0; j <= NUM_FP_PUSHES; j++)
235*702941cdSRichard Lowe 				if (save_fp_pushes[j] == n) {
236*702941cdSRichard Lowe 					found_push = B_TRUE;
237*702941cdSRichard Lowe 					break;
238*702941cdSRichard Lowe 				}
239*702941cdSRichard Lowe 		} else {
240*702941cdSRichard Lowe 			if (sz != 3)
241*702941cdSRichard Lowe 				continue;
242*702941cdSRichard Lowe 			n = INSTR3(ins, i);
243*702941cdSRichard Lowe 			for (j = 0; j <= NUM_FP_MOVS; j++)
244*702941cdSRichard Lowe 				if (save_fp_movs[j] == n)
245*702941cdSRichard Lowe 					return (B_TRUE);
246*702941cdSRichard Lowe 		}
247*702941cdSRichard Lowe 	}
248*702941cdSRichard Lowe 
249*702941cdSRichard Lowe 	return (B_FALSE);
250*702941cdSRichard Lowe }
251*702941cdSRichard Lowe 
252*702941cdSRichard Lowe int
253*702941cdSRichard Lowe saveargs_has_args(uint8_t *ins, size_t size, uint_t argc, int start_index)
254*702941cdSRichard Lowe {
255*702941cdSRichard Lowe 	int		i, j;
256*702941cdSRichard Lowe 	uint32_t	n;
257*702941cdSRichard Lowe 	uint8_t		found = 0;
258*702941cdSRichard Lowe 	ssize_t		sz = 0;
259*702941cdSRichard Lowe 	dis_handle_t	*dhp = NULL;
260*702941cdSRichard Lowe 	int		ret = SAVEARGS_NO_ARGS;
261*702941cdSRichard Lowe 
262*702941cdSRichard Lowe 	argc = MIN((start_index + argc), INSTR_ARRAY_SIZE);
263*702941cdSRichard Lowe 
264*702941cdSRichard Lowe 	if ((dhp = dis_handle_create(DIS_X86_SIZE64, NULL, do_lookup,
265*702941cdSRichard Lowe 	    do_read)) == NULL)
266*702941cdSRichard Lowe 		return (SAVEARGS_NO_ARGS);
267*702941cdSRichard Lowe 
268*702941cdSRichard Lowe 	if (!has_saved_fp(dhp, ins, size)) {
269*702941cdSRichard Lowe 		dis_handle_destroy(dhp);
270*702941cdSRichard Lowe 		return (SAVEARGS_NO_ARGS);
271*702941cdSRichard Lowe 	}
272*702941cdSRichard Lowe 
273*702941cdSRichard Lowe 	/*
274*702941cdSRichard Lowe 	 * For each possible style of argument saving, walk the insn stream as
275*702941cdSRichard Lowe 	 * we've been given it, and set bit N in 'found' if we find the
276*702941cdSRichard Lowe 	 * instruction saving the Nth argument.
277*702941cdSRichard Lowe 	 */
278*702941cdSRichard Lowe 
279*702941cdSRichard Lowe 	/*
280*702941cdSRichard Lowe 	 * Compare against regular implementation
281*702941cdSRichard Lowe 	 */
282*702941cdSRichard Lowe 	found = 0;
283*702941cdSRichard Lowe 	for (i = 0; i < size; i += sz) {
284*702941cdSRichard Lowe 		sz = instr_size(dhp, ins, i, size);
285*702941cdSRichard Lowe 
286*702941cdSRichard Lowe 		if (sz < 1)
287*702941cdSRichard Lowe 			break;
288*702941cdSRichard Lowe 		else if (sz != 4)
289*702941cdSRichard Lowe 			continue;
290*702941cdSRichard Lowe 
291*702941cdSRichard Lowe 		n = INSTR4(ins, i);
292*702941cdSRichard Lowe 
293*702941cdSRichard Lowe 		for (j = 0; j < argc; j++) {
294*702941cdSRichard Lowe 			if (n == save_instr[j]) {
295*702941cdSRichard Lowe 				found |= (1 << j);
296*702941cdSRichard Lowe 
297*702941cdSRichard Lowe 				if (found == ((1 << argc) - 1)) {
298*702941cdSRichard Lowe 					ret = start_index ?
299*702941cdSRichard Lowe 					    SAVEARGS_STRUCT_ARGS :
300*702941cdSRichard Lowe 					    SAVEARGS_TRAD_ARGS;
301*702941cdSRichard Lowe 					goto done;
302*702941cdSRichard Lowe 				}
303*702941cdSRichard Lowe 
304*702941cdSRichard Lowe 				break;
305*702941cdSRichard Lowe 			}
306*702941cdSRichard Lowe 		}
307*702941cdSRichard Lowe 	}
308*702941cdSRichard Lowe 
309*702941cdSRichard Lowe 	/*
310*702941cdSRichard Lowe 	 * Compare against GCC push-based implementation
311*702941cdSRichard Lowe 	 */
312*702941cdSRichard Lowe 	found = 0;
313*702941cdSRichard Lowe 	for (i = 0; i < size; i += sz) {
314*702941cdSRichard Lowe 		if ((sz = instr_size(dhp, ins, i, size)) < 1)
315*702941cdSRichard Lowe 			break;
316*702941cdSRichard Lowe 
317*702941cdSRichard Lowe 		for (j = start_index; j < argc; j++) {
318*702941cdSRichard Lowe 			if (sz == 2) /* Two byte */
319*702941cdSRichard Lowe 				n = INSTR2(ins, i);
320*702941cdSRichard Lowe 			else if (sz == 1)
321*702941cdSRichard Lowe 				n = INSTR1(ins, i);
322*702941cdSRichard Lowe 			else
323*702941cdSRichard Lowe 				continue;
324*702941cdSRichard Lowe 
325*702941cdSRichard Lowe 			if (n == save_instr_push[j]) {
326*702941cdSRichard Lowe 				found |= (1 << (j - start_index));
327*702941cdSRichard Lowe 
328*702941cdSRichard Lowe 				if (found ==
329*702941cdSRichard Lowe 				    ((1 << (argc - start_index)) - 1)) {
330*702941cdSRichard Lowe 					ret = SAVEARGS_TRAD_ARGS;
331*702941cdSRichard Lowe 					goto done;
332*702941cdSRichard Lowe 				}
333*702941cdSRichard Lowe 
334*702941cdSRichard Lowe 				break;
335*702941cdSRichard Lowe 			}
336*702941cdSRichard Lowe 		}
337*702941cdSRichard Lowe 	}
338*702941cdSRichard Lowe 
339*702941cdSRichard Lowe 	/*
340*702941cdSRichard Lowe 	 * Look for a GCC-style returned structure.
341*702941cdSRichard Lowe 	 */
342*702941cdSRichard Lowe 	found = 0;
343*702941cdSRichard Lowe 	if (start_index != 0) {
344*702941cdSRichard Lowe 		for (i = 0; i < size; i += sz) {
345*702941cdSRichard Lowe 			sz = instr_size(dhp, ins, i, size);
346*702941cdSRichard Lowe 
347*702941cdSRichard Lowe 			if (sz < 1)
348*702941cdSRichard Lowe 				break;
349*702941cdSRichard Lowe 			else if (sz != 4)
350*702941cdSRichard Lowe 				continue;
351*702941cdSRichard Lowe 
352*702941cdSRichard Lowe 			n = INSTR4(ins, i);
353*702941cdSRichard Lowe 
354*702941cdSRichard Lowe 			/* argc is inclusive of start_index, allow for that */
355*702941cdSRichard Lowe 			for (j = 0; j < (argc - start_index); j++) {
356*702941cdSRichard Lowe 				if (n == save_instr_sr[j]) {
357*702941cdSRichard Lowe 					found |= (1 << j);
358*702941cdSRichard Lowe 
359*702941cdSRichard Lowe 					if (found ==
360*702941cdSRichard Lowe 					    ((1 << (argc - start_index)) - 1)) {
361*702941cdSRichard Lowe 						ret = SAVEARGS_TRAD_ARGS;
362*702941cdSRichard Lowe 						goto done;
363*702941cdSRichard Lowe 					}
364*702941cdSRichard Lowe 
365*702941cdSRichard Lowe 					break;
366*702941cdSRichard Lowe 				}
367*702941cdSRichard Lowe 			}
368*702941cdSRichard Lowe 		}
369*702941cdSRichard Lowe 	}
370*702941cdSRichard Lowe 
371*702941cdSRichard Lowe done:
372*702941cdSRichard Lowe 	dis_handle_destroy(dhp);
373*702941cdSRichard Lowe 	return (ret);
374*702941cdSRichard Lowe }
375