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 (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 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. 12 * 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] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 27 /* 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. 33 * 34 * +--------+ 35 * %rbp --> | %rbp | 36 * +--------+ 37 * -0x8(%rbp) | %rdi | 38 * +--------+ 39 * -0x10(%rbp) | %rsi | 40 * +--------+ 41 * -0x18(%rbp) | %rdx | 42 * +--------+ 43 * -0x20(%rbp) | %rcx | 44 * +--------+ 45 * -0x28(%rbp) | %r8 | 46 * +--------+ 47 * -0x30(%rbp) | %r9 | 48 * +--------+ 49 * 50 * 51 * For example, for the following function, 52 * 53 * void 54 * foo(int a1, int a2, int a3, int a4, int a5, int a6, int a7) 55 * { 56 * ... 57 * } 58 * 59 * Disassembled code will look something like the following: 60 * 61 * pushq %rbp 62 * movq %rsp, %rbp 63 * subq $imm8, %rsp ** 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) 70 * ... 71 * or 72 * pushq %rbp 73 * movq %rsp, %rbp 74 * subq $imm8, %rsp ** 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) 81 * ... 82 * or 83 * pushq %rbp 84 * movq %rsp, %rbp 85 * pushq %rdi 86 * pushq %rsi 87 * pushq %rdx 88 * pushq %rcx 89 * pushq %r8 90 * pushq %r9 91 * 92 * **: The space being reserved is in addition to what the current 93 * function prolog already reserves. 94 * 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. We 97 * loop byte-by-byte, rather than doing anything smart about insn lengths, 98 * only deviating from this when we know we have our insn, and can skip the 99 * rest of it. 100 * 101 * If there are odd number of arguments to a function, additional space is 102 * reserved on the stack to maintain 16-byte alignment. For example, 103 * 104 * argc == 0: no argument saving. 105 * argc == 3: save 3, but space for 4 is reserved 106 * argc == 7: save 6. 107 */ 108 109 #include <sys/sysmacros.h> 110 #include <sys/types.h> 111 #include <saveargs.h> 112 113 /* 114 * Size of the instruction sequence arrays. It should correspond to 115 * the maximum number of arguments passed via registers. 116 */ 117 #define INSTR_ARRAY_SIZE 6 118 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)) 126 127 /* 128 * Sun Studio 10 patch implementation saves %rdi first; 129 * GCC 3.4.3 Sun branch implementation saves them in reverse order. 130 */ 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) */ 138 }; 139 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 */ 147 }; 148 149 /* 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 154 * pattern. 155 */ 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) */ 162 }; 163 164 static const uint8_t save_fp_pushes[] = { 165 0x55, /* pushq %rbp */ 166 0xcc /* int $0x3 */ 167 }; 168 #define NUM_FP_PUSHES (sizeof (save_fp_pushes) / sizeof (save_fp_pushes[0])) 169 170 static const uint32_t save_fp_movs[] = { 171 0x00e58948, /* movq %rsp,%rbp, encoding 1 */ 172 0x00ec8b48, /* movq %rsp,%rbp, encoding 2 */ 173 }; 174 #define NUM_FP_MOVS (sizeof (save_fp_movs) / sizeof (save_fp_movs[0])) 175 176 static int 177 has_saved_fp(uint8_t *ins, int size) 178 { 179 int i, j; 180 uint32_t n; 181 int found_push = 0; 182 183 for (i = 0; i < size; i++) { 184 if (found_push == 0) { 185 n = INSTR1(ins, i); 186 for (j = 0; j <= NUM_FP_PUSHES; j++) 187 if (save_fp_pushes[j] == n) { 188 found_push = 1; 189 break; 190 } 191 } else { 192 n = INSTR3(ins, i); 193 for (j = 0; j <= NUM_FP_MOVS; j++) 194 if (save_fp_movs[j] == n) 195 return (1); 196 } 197 } 198 199 return (0); 200 } 201 202 int 203 saveargs_has_args(uint8_t *ins, size_t size, uint_t argc, int start_index) 204 { 205 int i, j; 206 uint32_t n; 207 208 argc = MIN((start_index + argc), INSTR_ARRAY_SIZE); 209 210 if (!has_saved_fp(ins, size)) 211 return (SAVEARGS_NO_ARGS); 212 213 /* 214 * Compare against Sun Studio implementation 215 */ 216 for (i = 4, j = 0; i <= size - 4; i++) { 217 n = INSTR4(ins, i); 218 219 if (n == save_instr[j]) { 220 i += 3; 221 if (++j >= argc) 222 return (start_index ? SAVEARGS_STRUCT_ARGS : 223 SAVEARGS_TRAD_ARGS); 224 } 225 } 226 227 /* 228 * Compare against GCC implementation 229 */ 230 for (i = 4, j = argc - 1; i <= size - 4; i++) { 231 n = INSTR4(ins, i); 232 233 if (n == save_instr[j]) { 234 i += 3; 235 if (--j < start_index) 236 return (SAVEARGS_TRAD_ARGS); 237 } 238 } 239 240 /* 241 * Compare against GCC push-based implementation 242 */ 243 for (i = 4, j = start_index; i <= size - 2; i += 1) { 244 n = (i >= (8 - start_index)) ? INSTR2(ins, i) : INSTR1(ins, i); 245 246 if (n == save_instr_push[j]) { 247 if (i >= (8 - start_index)) 248 i += 1; 249 if (++j >= argc) 250 return (SAVEARGS_TRAD_ARGS); 251 } 252 } 253 254 /* Look for a GCC-style returned structure */ 255 if (start_index != 0) { 256 for (i = 4, j = argc - 2; i <= size - 4; i++) { 257 n = INSTR4(ins, i); 258 259 if (n == save_instr_sr[j]) { 260 i += 3; 261 if (--j >= (argc - 1)) 262 return (SAVEARGS_TRAD_ARGS); 263 } 264 } 265 } 266 267 return (SAVEARGS_NO_ARGS); 268 }