1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126
|
/* Copyright (C) 2006 Free Software Foundation, Inc. */
/* Contributed by Carlos O'Donell on 2006-03-14 */
/* Test that GCC follows the SPARC 32-bit psABI with regards to
structure return checking in a callee. When -mstd-struct-return
is specificed then gcc will emit code to skip the unimp insn. */
/* Origin: Carlos O'Donell <carlos@codesourcery.com> */
/* { dg-do run { target sparc*-*-solaris* sparc*-*-linux* sparc*-*-*bsd* } } */
/* { dg-options "-mstd-struct-return -fno-pie" } */
/* { dg-require-effective-target ilp32 } */
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
/* Local declaration of div_t structure */
struct mydiv_t {
int rem;
int quot;
};
/* Global check variable used by signal handler */
int check = 1;
struct mydiv_t dcheck;
struct mydiv_t foo (void)
{
struct mydiv_t bar;
bar.rem = 3;
bar.quot = 4;
return bar;
}
void handle_sigill (int signum)
{
if (signum == SIGILL && check == 2)
{
/* We expected a SIGILL due to a mismatch in unimp size
and struct mydiv_t size */
exit (0);
}
else
abort ();
}
/* Implement 3 checks to validate SPARC 32-bit psABI callee
returns struct
Test1: Save area is valid. unimp size is valid.
Success: Save area modified correctly.
Failure: Save area unmodified.
Test2: Save area is valid. unimp size is invalid (invalid insn).
Success: Save area unmodified. check == 2.
Failure: Save area modified or check == 1.
Test3: Save area is invalid. unimp size is invalid (invalid size).
Success: Will raise a SIGILL.
Failure: SIGSEGV caused by write to invalid save area. */
int main (void)
{
dcheck.rem = 1;
dcheck.quot = 2;
/*** Test1 ***/
/* Insert a call, insert unimp by hand */
__asm__ ("st %1, [ %%sp + 0x40 ]\n\t"
"call foo\n\t"
" nop\n\t"
"unimp %2\n\t"
: "=m" (dcheck)
: "r" (&dcheck), "i" (sizeof(struct mydiv_t))
: "memory");
/* If the caller doesn't adjust the return, then it crashes.
Check the result too. */
if ((dcheck.rem != 3) || (dcheck.quot !=4))
abort ();
/*** Test 2 ***/
dcheck.rem = 1;
dcheck.quot = 2;
/* Ignore the return of the function */
__asm__ ("st %3, [ %%sp + 0x40 ]\n\t"
"call foo\n\t"
" nop\n\t"
"mov %2, %0\n\t"
: "+r" (check), "=m" (dcheck)
: "i" (0x2), "r" (&dcheck)
: "memory");
/* If the caller does an unconditional adjustment it will skip
the mov, and then we can fail the test based on check's value
We pass a valid pointer to a save area in order to check if
caller incorrectly wrote to the save area as well. There may
be a case where the unimp check and skip is correct, but the
write to the save area still occurs. */
if (check != 2)
abort ();
if ((dcheck.rem != 1) || (dcheck.quot != 2))
abort ();
/*** Test 3 ***/
/* Prepare a test that must SIGILL. According to the spec
if the sizes of the save area and return don't match then
the copy is ignored and we return to the unimp. */
signal (SIGILL, handle_sigill);
__asm__ ("st %%g0, [ %%sp + 0x40 ]\n\t"
"call foo\n\t"
" nop\n\t"
"unimp %0\n\t"
: /* No outputs */
: "i" (sizeof(struct mydiv_t)-1)
: "memory");
/* NEVER REACHED */
exit (0);
}
|