1  /* Test program for 64-Bit Microsoft to System V function calls.
       2     Copyright (C) 2016-2017 Free Software Foundation, Inc.
       3     Contributed by Daniel Santos <daniel.santos@pobox.com>
       4  
       5  This file is part of GCC.
       6  
       7  GCC is free software; you can redistribute it and/or modify
       8  it under the terms of the GNU General Public License as published by
       9  the Free Software Foundation; either version 3, or (at your option)
      10  any later version.
      11  
      12  GCC is distributed in the hope that it will be useful,
      13  but WITHOUT ANY WARRANTY; without even the implied warranty of
      14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      15  GNU General Public License for more details.
      16  
      17  Under Section 7 of GPL version 3, you are granted additional
      18  permissions described in the GCC Runtime Library Exception, version
      19  3.1, as published by the Free Software Foundation.
      20  
      21  You should have received a copy of the GNU General Public License and
      22  a copy of the GCC Runtime Library Exception along with this program;
      23  see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
      24  <http://www.gnu.org/licenses/>.  */
      25  
      26  /* This is a single-threaded test program for Microsoft 64-bit ABI functions.
      27     It is aimed at verifying correctness of pro/epilogues of ms_abi functions
      28     that call sysv_abi functions to assure clobbered registers are properly
      29     saved and restored and attempt to detect any flaws in the behavior of these
      30     functions.  The following variants are tested:
      31  
      32     * Either uses hard frame pointer, re-aligns the stack or neither,
      33     * Uses alloca (and thus DRAP) or not,
      34     * Uses sibling call optimization or not,
      35     * Uses variable argument list or not, and
      36     * Has shrink-wrapped code or not.
      37  
      38    In addition, an ms_abi function is generated for each of these combinations
      39    clobbering each unique combination additional registers (excluding BP when
      40    a frame pointer is used). Shrink-wrap variants are called in a way that
      41    both the fast and slow path are used. Re-aligned variants are called with
      42    an aligned and mis-aligned stack.
      43  
      44    Each ms_abi function is called via an assembly stub that first saves all
      45    volatile registers and fills them with random values. The ms_abi function
      46    is then called.  After the function returns, the value of all volatile
      47    registers is verified against the random data and then restored.  */
      48  
      49  /* { dg-do run } */
      50  /* { dg-additional-sources "do-test.S" } */
      51  /* { dg-additional-options "-Wall" } */
      52  /* { dg-require-effective-target alloca } */
      53  
      54  #include <stdio.h>
      55  #include <string.h>
      56  #include <stdlib.h>
      57  #include <signal.h>
      58  #include <unistd.h>
      59  #include <stdint.h>
      60  #include <stdarg.h>
      61  #include <assert.h>
      62  #include <errno.h>
      63  #include <ctype.h>
      64  
      65  #if !defined(__x86_64__) || !defined(__SSE2__)
      66  # error Test only valid on x86_64 with -msse2
      67  #endif
      68  
      69  enum reg_data_sets
      70  {
      71    REG_SET_SAVE,
      72    REG_SET_INPUT,
      73    REG_SET_OUTPUT,
      74  
      75    REG_SET_COUNT
      76  };
      77  
      78  enum flags
      79  {
      80    FLAG_ALLOCA			= 0x01000000,
      81    FLAG_SIBCALL			= 0x02000000,
      82    FLAG_SHRINK_WRAP_FAST_PATH	= 0x08000000,
      83    FLAG_SHRINK_WRAP_SLOW_PATH	= 0x0c000000,
      84  };
      85  
      86  enum alignment_option
      87  {
      88    ALIGNMENT_NOT_TESTED,
      89    ALIGNMENT_ALIGNED,
      90    ALIGNMENT_MISALIGNED,
      91  
      92    ALIGNMENT_COUNT,
      93  };
      94  
      95  enum shrink_wrap_option
      96  {
      97    SHRINK_WRAP_NONE,
      98    SHRINK_WRAP_FAST_PATH,
      99    SHRINK_WRAP_SLOW_PATH,
     100  
     101    SHRINK_WRAP_COUNT
     102  };
     103  
     104  union regdata {
     105    struct {
     106      __uint128_t	sseregs[10];
     107      union {
     108        uint64_t	intregs[8];
     109        struct {
     110  	uint64_t	rsi;
     111  	uint64_t	rdi;
     112  	uint64_t	rbx;
     113  	uint64_t	rbp;
     114  	uint64_t	r12;
     115  	uint64_t	r13;
     116  	uint64_t	r14;
     117  	uint64_t	r15;
     118        };
     119      };
     120    };
     121    uint32_t		u32_arr[56];
     122  } __attribute__((aligned (16)));
     123  
     124  struct test_data
     125  {
     126    union regdata regdata[REG_SET_COUNT];
     127    void *fn;
     128    void *retaddr;
     129    const char *name;
     130    enum alignment_option alignment;
     131    enum shrink_wrap_option shrink_wrap;
     132    long ret_expected;
     133  } test_data;
     134  
     135  static int shrink_wrap_global;
     136  static void __attribute((sysv_abi)) do_tests ();
     137  static void init_test (void *fn, const char *name,
     138  		       enum alignment_option alignment,
     139  		       enum shrink_wrap_option shrink_wrap, long ret_expected);
     140  static void check_results (long ret);
     141  static __attribute__((ms_abi)) long do_sibcall (long arg);
     142  static __attribute__((ms_abi)) long
     143  (*const volatile do_sibcall_noinfo) (long) = do_sibcall;
     144  
     145  /* Defines do_tests ().  */
     146  #include "ms-sysv-generated.h"
     147  
     148  static int arbitrarily_fail;
     149  static const char *argv0;
     150  
     151  
     152  #define PASTE_STR2(a)		#a
     153  #define PASTE_STR1(a, b)	PASTE_STR2(a ## b)
     154  #define PASTE_STR(a, b)		PASTE_STR1(a, b)
     155  
     156  #ifdef __USER_LABEL_PREFIX__
     157  # define ASMNAME(name)		PASTE_STR(__USER_LABEL_PREFIX__, name)
     158  #else
     159  # define ASMNAME(name)		#name
     160  #endif
     161  
     162  #ifdef __MACH__
     163  # define LOAD_TEST_DATA_ADDR(dest) \
     164  	"	mov	" ASMNAME(test_data) "@GOTPCREL(%%rip), " dest "\n"
     165  #else
     166  # define LOAD_TEST_DATA_ADDR(dest) \
     167  	"	lea	" ASMNAME(test_data) "(%%rip), " dest "\n"
     168  #endif
     169  
     170  #define TEST_DATA_OFFSET(f)	((int)__builtin_offsetof(struct test_data, f))
     171  
     172  void __attribute__((naked))
     173  do_test_body (void)
     174  {__asm__ (
     175  	"	# rax, r10 and r11 are usable here.\n"
     176  	"\n"
     177  	"	# Save registers.\n"
     178  		LOAD_TEST_DATA_ADDR("%%rax")
     179  	"	lea	%p0(%%rax), %%r10\n"
     180  	"	call	" ASMNAME(regs_to_mem) "\n"
     181  	"\n"
     182  	"	# Load registers with random data.\n"
     183  	"	lea	%p1(%%rax), %%r10\n"
     184  	"	call	" ASMNAME(mem_to_regs) "\n"
     185  	"\n"
     186  	"	# Pop and save original return address.\n"
     187  	"	pop	%%r10\n"
     188  	"	mov	%%r10, %p4(%%rax)\n"
     189  	"\n"
     190  	"	# Call the test function, after which rcx, rdx and r8-11\n"
     191  	"	# become usable.\n"
     192  	"	lea	%p3(%%rax), %%rax\n"
     193  	"	call	*(%%rax)\n"
     194  	"\n"
     195  	"	# Store resulting register values.\n"
     196  		LOAD_TEST_DATA_ADDR("%%rcx")
     197  	"	lea	%p2(%%rcx), %%r10\n"
     198  	"	call	" ASMNAME(regs_to_mem) "\n"
     199  	"\n"
     200  	"	# Push the original return address.\n"
     201  	"	lea	%p4(%%rcx), %%r10\n"
     202  	"	push	(%%r10)\n"
     203  	"\n"
     204  	"	# Restore registers.\n"
     205  	"	lea	%p0(%%rcx), %%r10\n"
     206  	"	call	" ASMNAME(mem_to_regs) "\n"
     207  	"\n"
     208  	"	retq\n"
     209  	::
     210  	"i"(TEST_DATA_OFFSET(regdata[REG_SET_SAVE])),
     211  	"i"(TEST_DATA_OFFSET(regdata[REG_SET_INPUT])),
     212  	"i"(TEST_DATA_OFFSET(regdata[REG_SET_OUTPUT])),
     213  	"i"(TEST_DATA_OFFSET(fn)),
     214  	"i"(TEST_DATA_OFFSET(retaddr)) : "memory");
     215  }
     216  
     217  static void __attribute__((noinline))
     218  init_test (void *fn, const char *name, enum alignment_option alignment,
     219  	   enum shrink_wrap_option shrink_wrap, long ret_expected)
     220  {
     221    int i;
     222    union regdata *data = &test_data.regdata[REG_SET_INPUT];
     223  
     224    assert (alignment < ALIGNMENT_COUNT);
     225    assert (shrink_wrap < SHRINK_WRAP_COUNT);
     226  
     227    memset (&test_data, 0, sizeof (test_data));
     228    for (i = 55; i >= 0; --i)
     229      data->u32_arr[i] = (uint32_t)lrand48 ();
     230    test_data.fn = fn;
     231    test_data.name = name;
     232    test_data.alignment = alignment;
     233    test_data.shrink_wrap = shrink_wrap;
     234    test_data.ret_expected = ret_expected;
     235  
     236    switch (shrink_wrap)
     237    {
     238      case SHRINK_WRAP_NONE:
     239      case SHRINK_WRAP_COUNT:
     240        break;
     241      case SHRINK_WRAP_FAST_PATH:
     242        shrink_wrap_global = FLAG_SHRINK_WRAP_FAST_PATH;
     243        break;
     244      case SHRINK_WRAP_SLOW_PATH:
     245        shrink_wrap_global = FLAG_SHRINK_WRAP_SLOW_PATH;
     246        break;
     247    }
     248  }
     249  
     250  static const char *alignment_str[ALIGNMENT_COUNT] =
     251  {
     252    "", "aligned", "misaligned"
     253  };
     254  
     255  static const char *shrink_wrap_str[SHRINK_WRAP_COUNT] =
     256  {
     257    "", "shrink-wrap fast path", "shrink-wrap slow path"
     258  };
     259  
     260  static const char *test_descr ()
     261  {
     262    static char buffer[0x400];
     263  
     264    if (test_data.alignment || test_data.shrink_wrap)
     265      snprintf (buffer, sizeof (buffer) - 1, "`%s' (%s%s%s)",
     266  	      test_data.name,
     267  	      alignment_str[test_data.alignment],
     268  	      (test_data.alignment && test_data.shrink_wrap ? ", " : ""),
     269  	      shrink_wrap_str[test_data.shrink_wrap]);
     270    else
     271      snprintf (buffer, sizeof (buffer) - 1, "`%s'", test_data.name);
     272  
     273    return buffer;
     274  }
     275  
     276  static const char *regnames[] = {
     277    "XMM6",
     278    "XMM7",
     279    "XMM8",
     280    "XMM9",
     281    "XMM10",
     282    "XMM11",
     283    "XMM12",
     284    "XMM13",
     285    "XMM14",
     286    "XMM15",
     287    "RSI",
     288    "RDI",
     289    "RBX",
     290    "RBP",
     291    "R12",
     292    "R13",
     293    "R14",
     294    "R15",
     295  };
     296  
     297  static void print_header (int *header_printed)
     298  {
     299    if (!*header_printed)
     300      fprintf (stderr, "       %-35s    %-35s\n", "Expected", "Got");
     301    *header_printed = 1;
     302  }
     303  
     304  static int compare_reg128 (const __uint128_t *a, const __uint128_t *b,
     305  			   const char *name, int *header_printed)
     306  {
     307    if (!memcmp (a, b, sizeof (*a)))
     308      return 0;
     309    else
     310      {
     311        long ha = *((long*)a);
     312        long la = *((long*)a + 16);
     313        long hb = *((long*)b);
     314        long lb = *((long*)a + 16);
     315        print_header (header_printed);
     316        fprintf (stderr, "%-5s: 0x%016lx %016lx != 0x%016lx %016lx\n",
     317  	       name, ha, la, hb, lb);
     318        return 1;
     319      }
     320  }
     321  
     322  static int compare_reg64 (long a, long b, const char *name,
     323  			  int *header_printed)
     324  {
     325    if (a == b)
     326      return 0;
     327    else
     328      {
     329        print_header (header_printed);
     330        fprintf (stderr, "%s: 0x%016lx != 0x%016lx\n", name, a, b);
     331        return 1;
     332      }
     333  }
     334  
     335  
     336  static void __attribute__((noinline)) check_results (long ret)
     337  {
     338    unsigned i;
     339    unsigned bad = 0;
     340    int header_printed = 0;
     341  
     342    union regdata *a = &test_data.regdata[REG_SET_INPUT];
     343    union regdata *b = &test_data.regdata[REG_SET_OUTPUT];
     344  
     345    a = __builtin_assume_aligned(a, 16);
     346    b = __builtin_assume_aligned(b, 16);
     347  
     348    if (arbitrarily_fail) {
     349      uint64_t u64 = lrand48 ();
     350      if (u64 % 100 == 0)
     351        b->u32_arr[u64 % 56] = 0xfdfdfdfd;
     352    }
     353  
     354    for (i = 0; i < 10; ++i)
     355      bad |= compare_reg128 (&a->sseregs[i], &b->sseregs[i], regnames[i],
     356  			   &header_printed);
     357  
     358    for (i = 0; i < 8; ++i)
     359      bad |= compare_reg64 (a->intregs[i], b->intregs[i], regnames[i + 10],
     360  			  &header_printed);
     361  
     362    if (ret != test_data.ret_expected)
     363      {
     364        fprintf (stderr, "Wrong return value: got 0x%016lx, expected 0x%016lx\n",
     365  	       ret, test_data.ret_expected);
     366        bad = 1;
     367      }
     368  
     369    if (bad)
     370      {
     371        fprintf (stderr, "Failed on test function %s\n", test_descr ());
     372        raise (SIGTRAP);
     373        exit (-1);
     374      }
     375  }
     376  
     377  static __attribute__((ms_abi, noinline)) long do_sibcall (long arg) {
     378    return arg + FLAG_SIBCALL;
     379  }
     380  
     381  void usage ()
     382  {
     383    fprintf (stderr, "Usage: %s [-s <seed>] [-f]\n", argv0);
     384    exit (-1);
     385  }
     386  
     387  static long long_optarg (const char *optarg, const char *optstr)
     388  {
     389    char *end;
     390    long ret;
     391  
     392    errno = 0;
     393    ret = strtol(optarg, &end, 0);
     394  
     395    while (isspace (*end))
     396      ++end;
     397  
     398    if (errno || *end)
     399      {
     400        fprintf (stderr, "ERROR: Bad value for %s: `%s`\n", optstr, optarg);
     401        if (errno)
     402  	fprintf (stderr, "%s\n", strerror (errno));
     403        exit (-1);
     404      }
     405  
     406    return ret;
     407  }
     408  
     409  int main (int argc, char *argv[])
     410  {
     411    long seed = 0;
     412    int c;
     413    argv0 = argv[0];
     414  
     415    assert (!((long)&test_data.regdata[REG_SET_SAVE] & 15));
     416    assert (!((long)&test_data.regdata[REG_SET_INPUT] & 15));
     417    assert (!((long)&test_data.regdata[REG_SET_OUTPUT] & 15));
     418  
     419    while ((c = getopt (argc, argv, "s:f")) != -1)
     420      {
     421        switch (c)
     422  	{
     423  	case 's':
     424  	  seed = long_optarg (optarg, "-s");
     425  	  break;
     426  
     427  	case 'f':
     428  	  arbitrarily_fail = 1;
     429  	  fprintf (stderr, "NOTE: Aribrary failure enabled (-f).\n");
     430  	  break;
     431  	}
     432      }
     433  
     434    srand48 (seed);
     435    do_tests ();
     436  
     437    /* Just in case we don't have enough tests to randomly trigger the
     438       failure.  */
     439    if (arbitrarily_fail)
     440      return -1;
     441  
     442    return 0;
     443  }