1 /* Emulate AT_MINSIGSTKSZ.  Linux/x86 version.
2    Copyright (C) 2020 Free Software Foundation, Inc.
3 
4    The GNU C Library is free software; you can redistribute it and/or
5    modify it under the terms of the GNU Lesser General Public
6    License as published by the Free Software Foundation; either
7    version 2.1 of the License, or (at your option) any later version.
8 
9    The GNU C Library is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12    Lesser General Public License for more details.
13 
14    You should have received a copy of the GNU Lesser General Public
15    License along with the GNU C Library; if not, see
16    <https://www.gnu.org/licenses/>.  */
17 
18 /* Emulate AT_MINSIGSTKSZ with XSAVE. */
19 
20 static inline void
dl_check_minsigstacksize(const struct cpu_features * cpu_features)21 dl_check_minsigstacksize (const struct cpu_features *cpu_features)
22 {
23   /* Return if AT_MINSIGSTKSZ is provide by kernel.  */
24   if (GLRO(dl_minsigstacksize) != 0)
25     return;
26 
27   if (cpu_features->basic.max_cpuid >= 0xd
28       && CPU_FEATURES_CPU_P (cpu_features, OSXSAVE))
29     {
30       /* Emulate AT_MINSIGSTKSZ.  In Linux kernel, the signal frame data
31 	 with XSAVE is composed of the following areas and laid out as:
32 	 ------------------------------
33 	 | alignment padding          |
34 	 ------------------------------
35 	 | xsave buffer               |
36 	 ------------------------------
37 	 | fsave header (32-bit only) |
38 	 ------------------------------
39 	 | siginfo + ucontext         |
40 	 ------------------------------
41 	 */
42 
43       unsigned int sigframe_size;
44 
45 #ifdef __x86_64__
46       /* NB: sizeof(struct rt_sigframe) + 8-byte return address in Linux
47 	 kernel.  */
48       sigframe_size = 440 + 8;
49 #else
50       /* NB: sizeof(struct sigframe_ia32) + sizeof(struct fregs_state)) +
51 	 4-byte return address + 3 * 4-byte arguments in Linux kernel.  */
52       sigframe_size = 736 + 112 + 4 + 3 * 4;
53 #endif
54 
55       /* Add 15 bytes to align the stack to 16 bytes.  */
56       sigframe_size += 15;
57 
58       /* Make the space before xsave buffer multiple of 16 bytes.  */
59       sigframe_size = ALIGN_UP (sigframe_size, 16);
60 
61       /* Add (64 - 16)-byte padding to align xsave buffer at 64 bytes.  */
62       sigframe_size += 64 - 16;
63 
64       unsigned int eax, ebx, ecx, edx;
65       __cpuid_count (0xd, 0, eax, ebx, ecx, edx);
66 
67       /* Add the size of xsave buffer.  */
68       sigframe_size += ebx;
69 
70       /* Add the size of FP_XSTATE_MAGIC2.  */
71 #define FP_XSTATE_MAGIC2 0x46505845U
72       sigframe_size += sizeof (FP_XSTATE_MAGIC2);
73 
74       GLRO(dl_minsigstacksize) = sigframe_size;
75     }
76   else
77     {
78       /* NB: Default to a constant MINSIGSTKSZ.  */
79       _Static_assert (__builtin_constant_p (MINSIGSTKSZ),
80 		      "MINSIGSTKSZ is constant");
81       GLRO(dl_minsigstacksize) = MINSIGSTKSZ;
82     }
83 }
84