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