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.
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.
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]
22 * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
28 * The Sun Studio and GCC (patched for opensolaris/illumos) compilers
29 * implement a argument saving scheme on amd64 via the -Wu,save-args or
30 * options. When the option is specified, INTEGER type function arguments
31 * passed via registers will be saved on the stack immediately after %rbp, and
32 * will not be modified through out the life of the routine.
39 * -0x10(%rbp) | %rsi |
41 * -0x18(%rbp) | %rdx |
43 * -0x20(%rbp) | %rcx |
51 * For example, for the following function,
54 * foo(int a1, int a2, int a3, int a4, int a5, int a6, int a7)
59 * Disassembled code will look something like the following:
64 * movq %rdi, -0x8(%rbp)
65 * movq %rsi, -0x10(%rbp)
66 * movq %rdx, -0x18(%rbp)
67 * movq %rcx, -0x20(%rbp)
68 * movq %r8, -0x28(%rbp)
69 * movq %r9, -0x30(%rbp)
75 * movq %r9, -0x30(%rbp)
76 * movq %r8, -0x28(%rbp)
77 * movq %rcx, -0x20(%rbp)
78 * movq %rdx, -0x18(%rbp)
79 * movq %rsi, -0x10(%rbp)
80 * movq %rdi, -0x8(%rbp)
92 * **: The space being reserved is in addition to what the current
93 * function prolog already reserves.
95 * We loop through the first SAVEARGS_INSN_SEQ_LEN bytes of the function
96 * looking for each argument saving instruction we would expect to see.
98 * If there are odd number of arguments to a function, additional space is
99 * reserved on the stack to maintain 16-byte alignment. For example,
101 * argc == 0: no argument saving.
102 * argc == 3: save 3, but space for 4 is reserved
106 #include <sys/sysmacros.h>
107 #include <sys/types.h>
108 #include <libdisasm.h>
111 #include <saveargs.h>
114 * Size of the instruction sequence arrays. It should correspond to
115 * the maximum number of arguments passed via registers.
117 #define INSTR_ARRAY_SIZE 6
119 #define INSTR1(ins, off) (ins[(off)])
120 #define INSTR2(ins, off) (ins[(off)] + (ins[(off) + 1] << 8))
121 #define INSTR3(ins, off) \
122 (ins[(off)] + (ins[(off) + 1] << 8) + (ins[(off + 2)] << 16))
123 #define INSTR4(ins, off) \
124 (ins[(off)] + (ins[(off) + 1] << 8) + (ins[(off + 2)] << 16) + \
125 (ins[(off) + 3] << 24))
128 * Sun Studio 10 patch implementation saves %rdi first;
129 * GCC 3.4.3 Sun branch implementation saves them in reverse order.
131 static const uint32_t save_instr
[INSTR_ARRAY_SIZE
] = {
132 0xf87d8948, /* movq %rdi, -0x8(%rbp) */
133 0xf0758948, /* movq %rsi, -0x10(%rbp) */
134 0xe8558948, /* movq %rdx, -0x18(%rbp) */
135 0xe04d8948, /* movq %rcx, -0x20(%rbp) */
136 0xd845894c, /* movq %r8, -0x28(%rbp) */
137 0xd04d894c /* movq %r9, -0x30(%rbp) */
140 static const uint16_t save_instr_push
[] = {
141 0x57, /* pushq %rdi */
142 0x56, /* pushq %rsi */
143 0x52, /* pushq %rdx */
144 0x51, /* pushq %rcx */
145 0x5041, /* pushq %r8 */
146 0x5141 /* pushq %r9 */
150 * If the return type of a function is a structure greater than 16 bytes in
151 * size, %rdi will contain the address to which it should be stored, and
152 * arguments will begin at %rsi. Studio will push all of the normal argument
153 * registers anyway, GCC will start pushing at %rsi, so we need a separate
156 static const uint32_t save_instr_sr
[INSTR_ARRAY_SIZE
-1] = {
157 0xf8758948, /* movq %rsi,-0x8(%rbp) */
158 0xf0558948, /* movq %rdx,-0x10(%rbp) */
159 0xe84d8948, /* movq %rcx,-0x18(%rbp) */
160 0xe045894c, /* movq %r8,-0x20(%rbp) */
161 0xd84d894c /* movq %r9,-0x28(%rbp) */
164 static const uint8_t save_fp_pushes
[] = {
165 0x55, /* pushq %rbp */
168 #define NUM_FP_PUSHES (sizeof (save_fp_pushes) / sizeof (save_fp_pushes[0]))
170 static const uint32_t save_fp_movs
[] = {
171 0x00e58948, /* movq %rsp,%rbp, encoding 1 */
172 0x00ec8b48, /* movq %rsp,%rbp, encoding 2 */
174 #define NUM_FP_MOVS (sizeof (save_fp_movs) / sizeof (save_fp_movs[0]))
182 do_read(void *data
, uint64_t addr
, void *buf
, size_t len
)
189 len
= MIN(len
, t
->size
- addr
);
191 (void) memcpy(buf
, (char *)t
->data
+ addr
, len
);
198 do_lookup(void *data
, uint64_t addr
, char *buf
, size_t buflen
, uint64_t *start
,
201 /* We don't actually need lookup info */
206 instr_size(dis_handle_t
*dhp
, uint8_t *ins
, unsigned int i
, size_t size
)
213 dis_set_data(dhp
, &t
);
214 return (dis_instrlen(dhp
, i
));
218 has_saved_fp(dis_handle_t
*dhp
, uint8_t *ins
, int size
)
222 boolean_t found_push
= B_FALSE
;
225 for (i
= 0; i
< size
; i
+= sz
) {
226 if ((sz
= instr_size(dhp
, ins
, i
, size
)) < 1)
229 if (found_push
== B_FALSE
) {
234 for (j
= 0; j
<= NUM_FP_PUSHES
; j
++)
235 if (save_fp_pushes
[j
] == n
) {
243 for (j
= 0; j
<= NUM_FP_MOVS
; j
++)
244 if (save_fp_movs
[j
] == n
)
253 saveargs_has_args(uint8_t *ins
, size_t size
, uint_t argc
, int start_index
)
259 dis_handle_t
*dhp
= NULL
;
260 int ret
= SAVEARGS_NO_ARGS
;
262 argc
= MIN((start_index
+ argc
), INSTR_ARRAY_SIZE
);
264 if ((dhp
= dis_handle_create(DIS_X86
| DIS_SIZE_64
, NULL
, do_lookup
,
266 return (SAVEARGS_NO_ARGS
);
268 if (!has_saved_fp(dhp
, ins
, size
)) {
269 dis_handle_destroy(dhp
);
270 return (SAVEARGS_NO_ARGS
);
274 * For each possible style of argument saving, walk the insn stream as
275 * we've been given it, and set bit N in 'found' if we find the
276 * instruction saving the Nth argument.
280 * Compare against regular implementation
283 for (i
= 0; i
< size
; i
+= sz
) {
284 sz
= instr_size(dhp
, ins
, i
, size
);
293 for (j
= 0; j
< argc
; j
++) {
294 if (n
== save_instr
[j
]) {
297 if (found
== ((1 << argc
) - 1)) {
299 SAVEARGS_STRUCT_ARGS
:
310 * Compare against GCC push-based implementation
313 for (i
= 0; i
< size
; i
+= sz
) {
314 if ((sz
= instr_size(dhp
, ins
, i
, size
)) < 1)
317 for (j
= start_index
; j
< argc
; j
++) {
318 if (sz
== 2) /* Two byte */
325 if (n
== save_instr_push
[j
]) {
326 found
|= (1 << (j
- start_index
));
329 ((1 << (argc
- start_index
)) - 1)) {
330 ret
= SAVEARGS_TRAD_ARGS
;
340 * Look for a GCC-style returned structure.
343 if (start_index
!= 0) {
344 for (i
= 0; i
< size
; i
+= sz
) {
345 sz
= instr_size(dhp
, ins
, i
, size
);
354 /* argc is inclusive of start_index, allow for that */
355 for (j
= 0; j
< (argc
- start_index
); j
++) {
356 if (n
== save_instr_sr
[j
]) {
360 ((1 << (argc
- start_index
)) - 1)) {
361 ret
= SAVEARGS_TRAD_ARGS
;
372 dis_handle_destroy(dhp
);