summaryrefslogtreecommitdiff
path: root/gnu/packages
diff options
context:
space:
mode:
Diffstat (limited to 'gnu/packages')
-rw-r--r--gnu/packages/base.scm13
-rw-r--r--gnu/packages/patches/glibc-hurd-signal-fpe-exception.patch67
-rw-r--r--gnu/packages/patches/glibc-hurd-xstate.patch813
3 files changed, 892 insertions, 1 deletions
diff --git a/gnu/packages/base.scm b/gnu/packages/base.scm
index 26096743192..79810bccd10 100644
--- a/gnu/packages/base.scm
+++ b/gnu/packages/base.scm
@@ -1548,7 +1548,18 @@ variety of options. It is an alternative to the shell \"type\" built-in
1548command.") 1548command.")
1549 (license gpl3+))) ; some files are under GPLv2+ 1549 (license gpl3+))) ; some files are under GPLv2+
1550 1550
1551(define-public glibc/hurd glibc) 1551(define-public glibc/hurd
1552 (hidden-package
1553 (package/inherit glibc
1554 (source
1555 (origin
1556 (inherit (package-source glibc))
1557 (patches
1558 (append (map search-patch
1559 (delete "glibc-hurd64-intr-msg-clobber.patch" %glibc-patches))
1560 (search-patches
1561 "glibc-hurd-signal-fpe-exception.patch"
1562 "glibc-hurd-xstate.patch"))))))))
1552 1563
1553(define-public glibc/hurd-headers 1564(define-public glibc/hurd-headers
1554 (package/inherit glibc/hurd 1565 (package/inherit glibc/hurd
diff --git a/gnu/packages/patches/glibc-hurd-signal-fpe-exception.patch b/gnu/packages/patches/glibc-hurd-signal-fpe-exception.patch
new file mode 100644
index 00000000000..fefd9f693c0
--- /dev/null
+++ b/gnu/packages/patches/glibc-hurd-signal-fpe-exception.patch
@@ -0,0 +1,67 @@
1Cherry picked from glibc-2.42
2
30d99714680 * hurd: Clear FP exceptions before calling signal handler
4
5From 0d9971468087c77e8727638123ae4aa60724e0ff Mon Sep 17 00:00:00 2001
6From: Samuel Thibault <samuel.thibault@ens-lyon.org>
7Date: Mon, 21 Apr 2025 19:58:04 +0200
8Subject: [PATCH] hurd: Clear FP exceptions before calling signal handler
9
10This avoids SIGFPE handlers (or code longjmp-ed to) getting disturbed by the
11exception that generated it.
12
13Note: gcc's unwinding depends on the rpc_wait_trampoline/trampoline exact
14code, so we here avoid breaking it.
15---
16 sysdeps/mach/hurd/x86/trampoline.c | 14 ++++++++++----
17 1 file changed, 10 insertions(+), 4 deletions(-)
18
19diff --git a/sysdeps/mach/hurd/x86/trampoline.c b/sysdeps/mach/hurd/x86/trampoline.c
20index 02510b178c..6f23c567da 100644
21--- a/sysdeps/mach/hurd/x86/trampoline.c
22+++ b/sysdeps/mach/hurd/x86/trampoline.c
23@@ -461,7 +461,10 @@ _hurd_setup_sighandler (struct hurd_sigstate *ss, const struct sigaction *action
24 - in gdb: gdb/i386-gnu-tdep.c gnu_sigtramp_code. */
25
26 #ifdef __x86_64__
27-asm ("rpc_wait_trampoline:\n"
28+asm ("trampoline:\n"
29+ "fnclex\n" /* Clear any pending exception. */
30+ "jmp _trampoline\n"
31+ "rpc_wait_trampoline:\n"
32 /* This is the entry point when we have an RPC reply message to receive
33 before running the handler. The MACH_MSG_SEND bit has already been
34 cleared in the OPTION argument in our %rsi. The interrupted user
35@@ -480,7 +483,7 @@ asm ("rpc_wait_trampoline:\n"
36 /* Switch to the signal stack. */
37 "movq %rbx, %rsp\n"
38
39- "trampoline:\n"
40+ "_trampoline:\n"
41 /* Entry point for running the handler normally. The arguments to the
42 handler function are on the top of the stack, same as in the i386
43 version:
44@@ -506,7 +509,10 @@ asm ("rpc_wait_trampoline:\n"
45 "movq 16(%rsp), %rdi\n"
46 "ret");
47 #else
48-asm ("rpc_wait_trampoline:\n");
49+asm ("trampoline:\n"
50+ "fnclex\n" /* Clear any pending exception. */
51+ "jmp _trampoline\n"
52+ "rpc_wait_trampoline:\n");
53 /* This is the entry point when we have an RPC reply message to receive
54 before running the handler. The MACH_MSG_SEND bit has already been
55 cleared in the OPTION argument on our stack. The interrupted user
56@@ -526,7 +532,7 @@ asm (/* Retry the interrupted mach_msg system call. */
57 /* Switch to the signal stack. */
58 "movl %ebx, %esp\n");
59
60- asm ("trampoline:\n");
61+asm ("_trampoline:\n");
62 /* Entry point for running the handler normally. The arguments to the
63 handler function are already on the top of the stack:
64
65--
662.51.0
67
diff --git a/gnu/packages/patches/glibc-hurd-xstate.patch b/gnu/packages/patches/glibc-hurd-xstate.patch
new file mode 100644
index 00000000000..f46a84adb21
--- /dev/null
+++ b/gnu/packages/patches/glibc-hurd-xstate.patch
@@ -0,0 +1,813 @@
1Cherry picked from glibc 2.42
2
36d6a6e2dd2 * hurd: save xstate during signal handling
48d54b428cf * hurd: Do not restore xstate when it is not initialized
50f2df19d95 * hurd: Do not check for xstate level if it was not initialized
6
7From 6d6a6e2dd2133908e3f5cb8a2ed817ccb2a0bb06 Mon Sep 17 00:00:00 2001
8From: Luca Dariz <luca@orpolo.org>
9Date: Wed, 19 Mar 2025 18:11:18 +0100
10Subject: [PATCH] hurd: save xstate during signal handling
11
12* hurd/Makefile: add new tests
13* hurd/test-sig-rpc-interrupted.c: check xstate save and restore in
14 the case where a signal is delivered to a thread which is waiting
15 for an rpc. This test implements the rpc interruption protocol used
16 by the hurd servers. It was so far passing on Debian thanks to the
17 local-intr-msg-clobber.diff patch, which is now obsolete.
18* hurd/test-sig-xstate.c: check xstate save and restore in the case
19 where a signal is delivered to a running thread, making sure that
20 the xstate is modified in the signal handler.
21* hurd/test-xstate.h: add helpers to test xstate
22* sysdeps/mach/hurd/i386/bits/sigcontext.h: add xstate to the
23 sigcontext structure.
24+ sysdeps/mach/hurd/i386/sigreturn.c: restore xstate from the saved
25 context
26* sysdeps/mach/hurd/x86/trampoline.c: save xstate if
27 supported. Otherwise we fall back to the previous behaviour of
28 ignoring xstate.
29* sysdeps/mach/hurd/x86_64/bits/sigcontext.h: add xstate to the
30 sigcontext structure.
31* sysdeps/mach/hurd/x86_64/sigreturn.c: restore xstate from the saved
32 context
33
34Signed-off-by: Luca Dariz <luca@orpolo.org>
35Signed-off-by: Samuel Thibault <samuel.thibault@ens-lyon.org>
36Message-ID: <20250319171118.142163-1-luca@orpolo.org>
37---
38 hurd/Makefile | 5 +
39 hurd/test-sig-rpc-interrupted.c | 185 +++++++++++++++++++++
40 hurd/test-sig-xstate.c | 94 +++++++++++
41 hurd/test-xstate.h | 40 +++++
42 sysdeps/mach/hurd/i386/bits/sigcontext.h | 2 +
43 sysdeps/mach/hurd/i386/sigreturn.c | 32 +++-
44 sysdeps/mach/hurd/x86/trampoline.c | 91 ++++++++--
45 sysdeps/mach/hurd/x86_64/bits/sigcontext.h | 2 +
46 sysdeps/mach/hurd/x86_64/sigreturn.c | 32 +++-
47 9 files changed, 458 insertions(+), 25 deletions(-)
48 create mode 100644 hurd/test-sig-rpc-interrupted.c
49 create mode 100644 hurd/test-sig-xstate.c
50 create mode 100644 hurd/test-xstate.h
51
52diff --git a/hurd/Makefile b/hurd/Makefile
53index cf70b8c65c..cbc3c23b1f 100644
54--- a/hurd/Makefile
55+++ b/hurd/Makefile
56@@ -19,6 +19,11 @@ subdir := hurd
57
58 include ../Makeconfig
59
60+tests := test-sig-xstate \
61+ test-sig-rpc-interrupted
62+$(objpfx)test-sig-xstate: $(shared-thread-library)
63+$(objpfx)test-sig-rpc-interrupted: $(shared-thread-library) $(objdir)/hurd/libhurduser.so
64+
65 headers = \
66 $(interface-headers) \
67 hurd.h \
68diff --git a/hurd/test-sig-rpc-interrupted.c b/hurd/test-sig-rpc-interrupted.c
69new file mode 100644
70index 0000000000..a89d70e5a4
71--- /dev/null
72+++ b/hurd/test-sig-rpc-interrupted.c
73@@ -0,0 +1,185 @@
74+/* Test the state save/restore procedures during signal handling when an
75+ interruptible RPC is restarted.
76+
77+ Copyright (C) 2024 Free Software Foundation, Inc.
78+ This file is part of the GNU C Library.
79+
80+ The GNU C Library is free software; you can redistribute it and/or
81+ modify it under the terms of the GNU Lesser General Public
82+ License as published by the Free Software Foundation; either
83+ version 2.1 of the License, or (at your option) any later version.
84+
85+ The GNU C Library is distributed in the hope that it will be useful,
86+ but WITHOUT ANY WARRANTY; without even the implied warranty of
87+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
88+ Lesser General Public License for more details.
89+
90+ You should have received a copy of the GNU Lesser General Public
91+ License along with the GNU C Library; if not, see
92+ <https://www.gnu.org/licenses/>. */
93+
94+
95+#include <assert.h>
96+#include <pthread.h>
97+#include <signal.h>
98+#include <stdbool.h>
99+#include <stdio.h>
100+#include <stdlib.h>
101+#include <string.h>
102+#include <unistd.h>
103+
104+#include <mach/message.h>
105+#include <mach/gnumach.h>
106+#include <mach/mach_traps.h>
107+#include <mach/mig_errors.h>
108+#include <mach-shortcuts.h>
109+#include <mach_init.h>
110+#include <hurd/io.h>
111+#include <hurd/io_reply.h>
112+
113+#include <support/check.h>
114+#include <support/xthread.h>
115+
116+#include "test-xstate.h"
117+
118+void handler (int signum, siginfo_t *info, void *context)
119+{
120+ printf ("signal %d setting a different CPU state\n", signum);
121+ char buf3[XSTATE_BUFFER_SIZE];
122+ memset (buf3, 0x77, XSTATE_BUFFER_SIZE);
123+ SET_XSTATE (buf3);
124+}
125+
126+static const mach_msg_type_t RetCodeCheck = {
127+ .msgt_name = (unsigned char) MACH_MSG_TYPE_INTEGER_32,
128+ .msgt_size = 32,
129+ .msgt_number = 1,
130+ .msgt_inline = TRUE,
131+ .msgt_longform = FALSE,
132+ .msgt_deallocate = FALSE,
133+ .msgt_unused = 0
134+};
135+
136+
137+/* Helper thread to simulate a proper RPC interruption during dignal handling */
138+void* fake_interruptor (void *arg)
139+{
140+ int err;
141+ sigset_t ss;
142+ TEST_COMPARE (sigemptyset (&ss), 0);
143+ TEST_COMPARE (sigaddset (&ss, SIGUSR1), 0);
144+ TEST_COMPARE (sigprocmask (SIG_BLOCK, &ss, NULL), 0);
145+
146+ struct {
147+ mach_msg_header_t Head;
148+ } request;
149+ mach_port_t rxport = *((mach_port_t*)arg);
150+ err = mach_msg (&request.Head, MACH_RCV_MSG, 0, sizeof (request), rxport,
151+ MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
152+ TEST_COMPARE (err, MACH_MSG_SUCCESS);
153+ TEST_COMPARE (request.Head.msgh_bits, 0x1112);
154+ TEST_COMPARE (request.Head.msgh_size, sizeof (request.Head));
155+ TEST_COMPARE (request.Head.msgh_id, 33000);
156+
157+ mig_reply_header_t reply;
158+ reply.Head = request.Head;
159+ reply.Head.msgh_id += 100;
160+ reply.RetCodeType = RetCodeCheck;
161+ reply.RetCode = KERN_SUCCESS;
162+ err = mach_msg (&reply.Head, MACH_SEND_MSG, sizeof (reply), 0, MACH_PORT_NULL,
163+ MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
164+ TEST_COMPARE (err, MACH_MSG_SUCCESS);
165+
166+ return NULL;
167+}
168+
169+
170+/* Helper thread to send a signal to the main thread in the middle of
171+ * an interruptible rpc */
172+void* signal_sender (void *arg)
173+{
174+ int err;
175+ sigset_t ss;
176+ TEST_COMPARE (sigemptyset (&ss), 0);
177+ TEST_COMPARE (sigaddset (&ss, SIGUSR1), 0);
178+ TEST_COMPARE (sigprocmask (SIG_BLOCK, &ss, NULL), 0);
179+
180+ /* Receive the first request, we won't answer to this. */
181+ struct {
182+ mach_msg_header_t head;
183+ char data[64];
184+ } m1, m2;
185+ mach_port_t rxport = *((mach_port_t*)arg);
186+ memset (&m1, 0, sizeof (m1));
187+ memset (&m2, 0, sizeof (m2));
188+ err = mach_msg (&m1.head, MACH_RCV_MSG, 0, sizeof (m1), rxport,
189+ MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
190+ TEST_COMPARE (err, MACH_MSG_SUCCESS);
191+
192+ /* interrupt the ongoing rpc with a signal, using the
193+ * interruptible rpc protocol */
194+ pthread_t thintr = xpthread_create (NULL, fake_interruptor, arg);
195+ TEST_COMPARE (kill (getpid (), SIGUSR1), 0);
196+ xpthread_join (thintr);
197+
198+ /* Complete the interruption by sending EINTR */
199+ mig_reply_header_t reply;
200+ reply.Head = m1.head;
201+ reply.Head.msgh_id += 100;
202+ reply.RetCodeType = RetCodeCheck;
203+ reply.RetCode = EINTR;
204+ err = mach_msg (&reply.Head, MACH_SEND_MSG, sizeof (reply), 0, MACH_PORT_NULL,
205+ MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
206+ TEST_COMPARE (err, MACH_MSG_SUCCESS);
207+
208+ /* Receive the retried rpc, and check that it has the same payload
209+ * as the first one. Port names might still be different. */
210+ err = mach_msg (&m2.head, MACH_RCV_MSG, 0, sizeof (m2), rxport,
211+ MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
212+ TEST_COMPARE (m1.head.msgh_bits, m2.head.msgh_bits);
213+ TEST_COMPARE (m1.head.msgh_size, m2.head.msgh_size);
214+ TEST_COMPARE (m1.head.msgh_id, m2.head.msgh_id);
215+ TEST_COMPARE_BLOB (m1.data, sizeof (m1.data), m2.data, sizeof (m2.data));
216+
217+ /* And finally make the rpc succeed by sending a valid reply */
218+ err = io_read_reply (m2.head.msgh_remote_port, MACH_MSG_TYPE_MOVE_SEND_ONCE,
219+ KERN_SUCCESS, NULL, 0);
220+ TEST_COMPARE (err, MACH_MSG_SUCCESS);
221+
222+ return NULL;
223+}
224+
225+
226+static int do_test (void)
227+{
228+#if ! XSTATE_HELPERS_SUPPORTED
229+ FAIL_UNSUPPORTED ("Test not supported on this arch.");
230+#endif
231+
232+ /* Setup signal handling; we need to handle the signal in the main
233+ * thread, the other ones will explicitely block SIGUSR1. */
234+ struct sigaction act = { 0 };
235+ act.sa_flags = SA_RESTART;
236+ act.sa_sigaction = &handler;
237+ TEST_COMPARE (sigaction (SIGUSR1, &act, NULL), 0);
238+
239+ mach_port_t fakeio;
240+ int err;
241+ err = mach_port_allocate (mach_task_self (), MACH_PORT_RIGHT_RECEIVE, &fakeio);
242+ TEST_COMPARE (err, MACH_MSG_SUCCESS);
243+
244+ err = mach_port_insert_right (mach_task_self (), fakeio, fakeio,
245+ MACH_MSG_TYPE_MAKE_SEND);
246+ TEST_COMPARE (err, MACH_MSG_SUCCESS);
247+
248+ pthread_t thsender = xpthread_create (NULL, signal_sender, &fakeio);
249+
250+ char *buf;
251+ mach_msg_type_number_t n;
252+ TEST_COMPARE (io_read (fakeio, &buf, &n, 1, 2), 0);
253+
254+ xpthread_join (thsender);
255+ return EXIT_SUCCESS;
256+}
257+
258+#include <support/test-driver.c>
259diff --git a/hurd/test-sig-xstate.c b/hurd/test-sig-xstate.c
260new file mode 100644
261index 0000000000..0a68a44fd7
262--- /dev/null
263+++ b/hurd/test-sig-xstate.c
264@@ -0,0 +1,94 @@
265+/* Test the state save/restore procedures during signal handling.
266+
267+ Copyright (C) 2025 Free Software Foundation, Inc.
268+ This file is part of the GNU C Library.
269+
270+ The GNU C Library is free software; you can redistribute it and/or
271+ modify it under the terms of the GNU Lesser General Public
272+ License as published by the Free Software Foundation; either
273+ version 2.1 of the License, or (at your option) any later version.
274+
275+ The GNU C Library is distributed in the hope that it will be useful,
276+ but WITHOUT ANY WARRANTY; without even the implied warranty of
277+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
278+ Lesser General Public License for more details.
279+
280+ You should have received a copy of the GNU Lesser General Public
281+ License along with the GNU C Library; if not, see
282+ <https://www.gnu.org/licenses/>. */
283+
284+
285+#include <assert.h>
286+#include <pthread.h>
287+#include <signal.h>
288+#include <stdbool.h>
289+#include <stdio.h>
290+#include <stdlib.h>
291+#include <string.h>
292+#include <unistd.h>
293+
294+#include <mach/message.h>
295+#include <mach/gnumach.h>
296+#include <mach/mach_traps.h>
297+#include <mach-shortcuts.h>
298+#include <mach_init.h>
299+#include <hurd/io.h>
300+#include <hurd/io_reply.h>
301+
302+#include <support/check.h>
303+#include <support/xthread.h>
304+
305+#include "test-xstate.h"
306+
307+static volatile bool loopflag = true;
308+
309+void handler (int signum, siginfo_t *info, void *context)
310+{
311+ char buf3[XSTATE_BUFFER_SIZE];
312+ memset (buf3, 0x77, XSTATE_BUFFER_SIZE);
313+ SET_XSTATE (buf3);
314+ printf ("signal %d setting a different CPU state\n", signum);
315+ loopflag = false;
316+}
317+
318+/* Helper thread to send a signal to the main thread */
319+void* signal_sender (void *arg)
320+{
321+ sigset_t ss;
322+ assert (! sigemptyset (&ss));
323+ assert (! sigaddset (&ss, SIGUSR1));
324+ assert (! sigprocmask (SIG_BLOCK, &ss, NULL));
325+
326+ TEST_COMPARE (kill (getpid (), SIGUSR1), 0);
327+
328+ return NULL;
329+}
330+
331+static int do_test (void)
332+{
333+#if ! XSTATE_HELPERS_SUPPORTED
334+ FAIL_UNSUPPORTED ("Test not supported on this arch.");
335+#endif
336+
337+ struct sigaction act = { 0 };
338+ act.sa_sigaction = &handler;
339+ TEST_COMPARE (sigaction (SIGUSR1, &act, NULL), 0);
340+
341+ pthread_t thsender = xpthread_create (NULL, signal_sender, NULL);
342+
343+ char buf1[XSTATE_BUFFER_SIZE], buf2[XSTATE_BUFFER_SIZE];
344+ memset (buf1, 0x33, XSTATE_BUFFER_SIZE);
345+
346+ SET_XSTATE (buf1);
347+
348+ while (loopflag)
349+ ;
350+
351+ GET_XSTATE (buf2);
352+ TEST_COMPARE_BLOB (buf1, sizeof (buf1), buf2, sizeof (buf2));
353+
354+ xpthread_join (thsender);
355+ return EXIT_SUCCESS;
356+}
357+
358+#include <support/test-driver.c>
359diff --git a/hurd/test-xstate.h b/hurd/test-xstate.h
360new file mode 100644
361index 0000000000..a8185dcb07
362--- /dev/null
363+++ b/hurd/test-xstate.h
364@@ -0,0 +1,40 @@
365+/* Helpers to test XSTATE during signal handling
366+
367+ Copyright (C) 2025 Free Software Foundation, Inc.
368+ This file is part of the GNU C Library.
369+
370+ The GNU C Library is free software; you can redistribute it and/or
371+ modify it under the terms of the GNU Lesser General Public
372+ License as published by the Free Software Foundation; either
373+ version 2.1 of the License, or (at your option) any later version.
374+
375+ The GNU C Library is distributed in the hope that it will be useful,
376+ but WITHOUT ANY WARRANTY; without even the implied warranty of
377+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
378+ Lesser General Public License for more details.
379+
380+ You should have received a copy of the GNU Lesser General Public
381+ License along with the GNU C Library; if not, see
382+ <https://www.gnu.org/licenses/>. */
383+
384+#ifndef _TEST_XSTATE_H
385+#define _TEST_XSTATE_H
386+
387+#if defined __x86_64__ || defined __i386__
388+#define XSTATE_HELPERS_SUPPORTED 1
389+#define XSTATE_BUFFER_SIZE 16
390+#define SET_XSTATE(b) do { \
391+ asm volatile ("movups (%0),%%xmm0" :: "r" (b)); \
392+ } while (0)
393+
394+#define GET_XSTATE(b) do { \
395+ asm volatile ("movups %%xmm0,(%0)" :: "r" (b)); \
396+ } while (0)
397+
398+#else
399+#define XSTATE_HELPERS_SUPPORTED 0
400+#define XSTATE_BUFFER_SIZE 1
401+#define SET_XSTATE(b)
402+#endif
403+
404+#endif /* _TEST_XSTATE_H */
405diff --git a/sysdeps/mach/hurd/i386/bits/sigcontext.h b/sysdeps/mach/hurd/i386/bits/sigcontext.h
406index 6e5e220e9d..c44e4deac6 100644
407--- a/sysdeps/mach/hurd/i386/bits/sigcontext.h
408+++ b/sysdeps/mach/hurd/i386/bits/sigcontext.h
409@@ -88,6 +88,8 @@ struct sigcontext
410 struct i386_fp_save sc_fpsave;
411 struct i386_fp_regs sc_fpregs;
412 int sc_fpexcsr; /* FPSR including exception bits. */
413+
414+ struct i386_xfloat_state *xstate;
415 };
416
417 /* Traditional BSD names for some members. */
418diff --git a/sysdeps/mach/hurd/i386/sigreturn.c b/sysdeps/mach/hurd/i386/sigreturn.c
419index ce8df8d02b..37fa984070 100644
420--- a/sysdeps/mach/hurd/i386/sigreturn.c
421+++ b/sysdeps/mach/hurd/i386/sigreturn.c
422@@ -21,6 +21,8 @@
423 #include <stdlib.h>
424 #include <string.h>
425
426+#include <cpuid.h>
427+
428 /* This is run on the thread stack after restoring it, to be able to
429 unlock SS off sigstack. */
430 static void
431@@ -123,10 +125,32 @@ __sigreturn (struct sigcontext *scp)
432 if (scp->sc_onstack)
433 ss->sigaltstack.ss_flags &= ~SS_ONSTACK;
434
435- if (scp->sc_fpused)
436- /* Restore the FPU state. Mach conveniently stores the state
437- in the format the i387 `frstor' instruction uses to restore it. */
438- asm volatile ("frstor %0" : : "m" (scp->sc_fpsave));
439+#ifdef i386_XFLOAT_STATE
440+ if ((scp->xstate) && (scp->xstate->initialized))
441+ {
442+ unsigned eax, ebx, ecx, edx;
443+ __cpuid_count(0xd, 0, eax, ebx, ecx, edx);
444+ switch (scp->xstate->fp_save_kind)
445+ {
446+ case 0: // FNSAVE
447+ asm volatile("frstor %0" : : "m" (scp->xstate->hw_state));
448+ break;
449+ case 1: // FXSAVE
450+ asm volatile("fxrstor %0" : : "m" (scp->xstate->hw_state), \
451+ "a" (eax), "d" (edx));
452+ break;
453+ default: // XSAVE, XSAVEOPT, XSAVEC, XSAVES
454+ asm volatile("xrstor %0" : : "m" (scp->xstate->hw_state), \
455+ "a" (eax), "d" (edx));
456+ break;
457+ }
458+ }
459+ else
460+#endif
461+ if (scp->sc_fpused)
462+ /* Restore the FPU state. Mach conveniently stores the state
463+ in the format the i387 `frstor' instruction uses to restore it. */
464+ asm volatile ("frstor %0" : : "m" (scp->sc_fpsave));
465
466 {
467 /* There are convenient instructions to pop state off the stack, so we
468diff --git a/sysdeps/mach/hurd/x86/trampoline.c b/sysdeps/mach/hurd/x86/trampoline.c
469index 8e2890f8c5..db756e8a1f 100644
470--- a/sysdeps/mach/hurd/x86/trampoline.c
471+++ b/sysdeps/mach/hurd/x86/trampoline.c
472@@ -26,7 +26,11 @@
473 #include "hurdfault.h"
474 #include <intr-msg.h>
475 #include <sys/ucontext.h>
476-
477+#ifdef __x86_64__
478+#include <mach/x86_64/mach_i386.h>
479+#else
480+#include <mach/i386/mach_i386.h>
481+#endif
482
483 /* Fill in a siginfo_t structure for SA_SIGINFO-enabled handlers. */
484 static void fill_siginfo (siginfo_t *si, int signo,
485@@ -106,6 +110,7 @@ _hurd_setup_sighandler (struct hurd_sigstate *ss, const struct sigaction *action
486 void firewall (void);
487 void *sigsp;
488 struct sigcontext *scp;
489+ vm_size_t xstate_size;
490 struct
491 {
492 union
493@@ -145,6 +150,14 @@ _hurd_setup_sighandler (struct hurd_sigstate *ss, const struct sigaction *action
494 struct hurd_userlink link;
495 ucontext_t ucontext;
496 siginfo_t siginfo;
497+#ifdef __x86_64__
498+ char _pad2[56];
499+#else
500+ char _pad2[20];
501+#endif
502+ char xstate[];
503+ /* Don't add anything after xstate, as it's dynamically
504+ sized. */
505 } *stackframe;
506
507 #ifdef __x86_64__
508@@ -170,6 +183,17 @@ _hurd_setup_sighandler (struct hurd_sigstate *ss, const struct sigaction *action
509 if (! machine_get_basic_state (ss->thread, state))
510 return NULL;
511
512+ /* Initialize the size of the CPU extended state, to be saved during
513+ * signal handling */
514+#ifdef i386_XFLOAT_STATE
515+ _Static_assert ((sizeof(*stackframe) + sizeof(struct i386_xfloat_state)) % 64 == 0,
516+ "stackframe size must be multiple of 64-byte minus "
517+ "sizeof(struct i386_xfloat_state), please adjust _pad2");
518+
519+ if (__i386_get_xstate_size(__mach_host_self(), &xstate_size))
520+#endif
521+ xstate_size = 0;
522+
523 /* Save the original SP in the gratuitous `esp' slot.
524 We may need to reset the SP (the `uesp' slot) to avoid clobbering an
525 interrupted RPC frame. */
526@@ -196,14 +220,21 @@ _hurd_setup_sighandler (struct hurd_sigstate *ss, const struct sigaction *action
527 #endif
528 }
529
530- /* Push the arguments to call `trampoline' on the stack. */
531- sigsp -= sizeof (*stackframe);
532-#ifdef __x86_64__
533- /* Align SP at 16 bytes. Coupled with the fact that sigreturn_addr is
534- 16-byte aligned within the stackframe struct, this ensures that it ends
535- up on a 16-byte aligned address, as required by the ABI. */
536- sigsp = (void *) ((uintptr_t) sigsp & ~15UL);
537-#endif
538+ /* Push the arguments to call `trampoline' on the stack.
539+ * The extended state might have a variable size depending on the platform,
540+ * so we dynamically allocate it on the stack frame.*/
541+ sigsp -= sizeof (*stackframe) + xstate_size;
542+
543+ /* Align SP at 64 bytes. This is needed for two reasons:
544+ * - sigreturn_addr is 16-byte aligned within the stackframe
545+ * struct, and this ensures that it ends up on a 16-byte aligned
546+ * address, as required by the ABI.
547+ * - the XSAVE state needs to be aligned at 64 bytes (on both i386 and
548+ * x86_64), so we align the stackframe also at 64 bytes and add the
549+ * required padding at the end, see the _pad2 field.
550+ */
551+ sigsp = (void *) ((uintptr_t) sigsp & ~63UL);
552+
553 stackframe = sigsp;
554
555 if (_hurdsig_catch_memory_fault (stackframe))
556@@ -248,14 +279,40 @@ _hurd_setup_sighandler (struct hurd_sigstate *ss, const struct sigaction *action
557 memcpy (&scp->sc_i386_thread_state,
558 &state->basic, sizeof (state->basic));
559
560- /* struct sigcontext is laid out so that starting at sc_fpkind mimics
561- a struct i386_float_state. */
562- _Static_assert (offsetof (struct sigcontext, sc_i386_float_state)
563- % __alignof__ (struct i386_float_state) == 0,
564- "sc_i386_float_state layout mismatch");
565- ok = machine_get_state (ss->thread, state, i386_FLOAT_STATE,
566- &state->fpu, &scp->sc_i386_float_state,
567- sizeof (state->fpu));
568+ scp->xstate = NULL;
569+#ifdef i386_XFLOAT_STATE
570+ if (xstate_size > 0)
571+ {
572+ mach_msg_type_number_t got = (xstate_size / sizeof (int));
573+
574+ ok = (! __thread_get_state (ss->thread, i386_XFLOAT_STATE,
575+ (thread_state_t) stackframe->xstate, &got)
576+ && got == (xstate_size / sizeof (int)));
577+
578+ if (((struct i386_xfloat_state*) stackframe->xstate)->fp_save_kind > 5)
579+ /* We support up to XSAVES */
580+ ok = 0;
581+
582+ if (ok)
583+ {
584+ scp->xstate = (struct i386_xfloat_state*) stackframe->xstate;
585+ assert((uintptr_t)scp->xstate->hw_state % 64 == 0);
586+ }
587+ }
588+ else
589+#endif
590+ ok = 0;
591+ if (!ok)
592+ {
593+ /* struct sigcontext is laid out so that starting at sc_fpkind mimics
594+ a struct i386_float_state. */
595+ _Static_assert (offsetof (struct sigcontext, sc_i386_float_state)
596+ % __alignof__ (struct i386_float_state) == 0,
597+ "sc_i386_float_state layout mismatch");
598+ ok = machine_get_state (ss->thread, state, i386_FLOAT_STATE,
599+ &state->fpu, &scp->sc_i386_float_state,
600+ sizeof (state->fpu));
601+ }
602
603 /* Set up the arguments for the signal handler. */
604 stackframe->signo = signo;
605diff --git a/sysdeps/mach/hurd/x86_64/bits/sigcontext.h b/sysdeps/mach/hurd/x86_64/bits/sigcontext.h
606index 7bac881176..d83795fcbc 100644
607--- a/sysdeps/mach/hurd/x86_64/bits/sigcontext.h
608+++ b/sysdeps/mach/hurd/x86_64/bits/sigcontext.h
609@@ -96,6 +96,8 @@ struct sigcontext
610 struct i386_fp_save sc_fpsave;
611 struct i386_fp_regs sc_fpregs;
612 int sc_fpexcsr; /* FPSR including exception bits. */
613+
614+ struct i386_xfloat_state *xstate;
615 };
616
617 /* Traditional BSD names for some members. */
618diff --git a/sysdeps/mach/hurd/x86_64/sigreturn.c b/sysdeps/mach/hurd/x86_64/sigreturn.c
619index 81a2d3ba74..dff8e76dc8 100644
620--- a/sysdeps/mach/hurd/x86_64/sigreturn.c
621+++ b/sysdeps/mach/hurd/x86_64/sigreturn.c
622@@ -20,6 +20,8 @@
623 #include <hurd/msg.h>
624 #include <stdlib.h>
625
626+#include <cpuid.h>
627+
628 /* This is run on the thread stack after restoring it, to be able to
629 unlock SS off sigstack. */
630 void
631@@ -116,10 +118,32 @@ __sigreturn (struct sigcontext *scp)
632 if (scp->sc_onstack)
633 ss->sigaltstack.ss_flags &= ~SS_ONSTACK;
634
635- if (scp->sc_fpused)
636- /* Restore the FPU state. Mach conveniently stores the state
637- in the format the i387 `frstor' instruction uses to restore it. */
638- asm volatile ("frstor %0" : : "m" (scp->sc_fpsave));
639+#ifdef i386_XFLOAT_STATE
640+ if ((scp->xstate) && (scp->xstate->initialized))
641+ {
642+ unsigned eax, ebx, ecx, edx;
643+ __cpuid_count(0xd, 0, eax, ebx, ecx, edx);
644+ switch (scp->xstate->fp_save_kind)
645+ {
646+ case 0: // FNSAVE
647+ asm volatile("frstor %0" : : "m" (scp->xstate->hw_state));
648+ break;
649+ case 1: // FXSAVE
650+ asm volatile("fxrstor %0" : : "m" (scp->xstate->hw_state), \
651+ "a" (eax), "d" (edx));
652+ break;
653+ default: // XSAVE, XSAVEOPT, XSAVEC, XSAVES
654+ asm volatile("xrstor %0" : : "m" (scp->xstate->hw_state), \
655+ "a" (eax), "d" (edx));
656+ break;
657+ }
658+ }
659+ else
660+#endif
661+ if (scp->sc_fpused)
662+ /* Restore the FPU state. Mach conveniently stores the state
663+ in the format the i387 `frstor' instruction uses to restore it. */
664+ asm volatile ("frstor %0" : : "m" (scp->sc_fpsave));
665
666 /* Copy the registers onto the user's stack, to be able to release the
667 altstack (by unlocking sigstate). Note that unless an altstack is used,
668--
6692.51.0
670
671From 8d54b428cfe98c21049f94c8af3bf302e44091e9 Mon Sep 17 00:00:00 2001
672From: Samuel Thibault <samuel.thibault@ens-lyon.org>
673Date: Mon, 21 Apr 2025 19:42:27 +0200
674Subject: [PATCH] hurd: Do not restore xstate when it is not initialized
675
676If the process has never used fp before getting a signal, xstate is set
677(and thus the x87 state is not initialized) but xstate->initialized is still
6780, and we should not restore anything.
679---
680 sysdeps/mach/hurd/i386/sigreturn.c | 37 +++++++++++++++-------------
681 sysdeps/mach/hurd/x86_64/sigreturn.c | 37 +++++++++++++++-------------
682 2 files changed, 40 insertions(+), 34 deletions(-)
683
684diff --git a/sysdeps/mach/hurd/i386/sigreturn.c b/sysdeps/mach/hurd/i386/sigreturn.c
685index 37fa984070..dc57d6122c 100644
686--- a/sysdeps/mach/hurd/i386/sigreturn.c
687+++ b/sysdeps/mach/hurd/i386/sigreturn.c
688@@ -126,24 +126,27 @@ __sigreturn (struct sigcontext *scp)
689 ss->sigaltstack.ss_flags &= ~SS_ONSTACK;
690
691 #ifdef i386_XFLOAT_STATE
692- if ((scp->xstate) && (scp->xstate->initialized))
693+ if (scp->xstate)
694 {
695- unsigned eax, ebx, ecx, edx;
696- __cpuid_count(0xd, 0, eax, ebx, ecx, edx);
697- switch (scp->xstate->fp_save_kind)
698- {
699- case 0: // FNSAVE
700- asm volatile("frstor %0" : : "m" (scp->xstate->hw_state));
701- break;
702- case 1: // FXSAVE
703- asm volatile("fxrstor %0" : : "m" (scp->xstate->hw_state), \
704- "a" (eax), "d" (edx));
705- break;
706- default: // XSAVE, XSAVEOPT, XSAVEC, XSAVES
707- asm volatile("xrstor %0" : : "m" (scp->xstate->hw_state), \
708- "a" (eax), "d" (edx));
709- break;
710- }
711+ if (scp->xstate->initialized)
712+ {
713+ unsigned eax, ebx, ecx, edx;
714+ __cpuid_count(0xd, 0, eax, ebx, ecx, edx);
715+ switch (scp->xstate->fp_save_kind)
716+ {
717+ case 0: // FNSAVE
718+ asm volatile("frstor %0" : : "m" (scp->xstate->hw_state));
719+ break;
720+ case 1: // FXSAVE
721+ asm volatile("fxrstor %0" : : "m" (scp->xstate->hw_state), \
722+ "a" (eax), "d" (edx));
723+ break;
724+ default: // XSAVE, XSAVEOPT, XSAVEC, XSAVES
725+ asm volatile("xrstor %0" : : "m" (scp->xstate->hw_state), \
726+ "a" (eax), "d" (edx));
727+ break;
728+ }
729+ }
730 }
731 else
732 #endif
733diff --git a/sysdeps/mach/hurd/x86_64/sigreturn.c b/sysdeps/mach/hurd/x86_64/sigreturn.c
734index dff8e76dc8..773c00f86d 100644
735--- a/sysdeps/mach/hurd/x86_64/sigreturn.c
736+++ b/sysdeps/mach/hurd/x86_64/sigreturn.c
737@@ -119,24 +119,27 @@ __sigreturn (struct sigcontext *scp)
738 ss->sigaltstack.ss_flags &= ~SS_ONSTACK;
739
740 #ifdef i386_XFLOAT_STATE
741- if ((scp->xstate) && (scp->xstate->initialized))
742+ if (scp->xstate)
743 {
744- unsigned eax, ebx, ecx, edx;
745- __cpuid_count(0xd, 0, eax, ebx, ecx, edx);
746- switch (scp->xstate->fp_save_kind)
747- {
748- case 0: // FNSAVE
749- asm volatile("frstor %0" : : "m" (scp->xstate->hw_state));
750- break;
751- case 1: // FXSAVE
752- asm volatile("fxrstor %0" : : "m" (scp->xstate->hw_state), \
753- "a" (eax), "d" (edx));
754- break;
755- default: // XSAVE, XSAVEOPT, XSAVEC, XSAVES
756- asm volatile("xrstor %0" : : "m" (scp->xstate->hw_state), \
757- "a" (eax), "d" (edx));
758- break;
759- }
760+ if (scp->xstate->initialized)
761+ {
762+ unsigned eax, ebx, ecx, edx;
763+ __cpuid_count(0xd, 0, eax, ebx, ecx, edx);
764+ switch (scp->xstate->fp_save_kind)
765+ {
766+ case 0: // FNSAVE
767+ asm volatile("frstor %0" : : "m" (scp->xstate->hw_state));
768+ break;
769+ case 1: // FXSAVE
770+ asm volatile("fxrstor %0" : : "m" (scp->xstate->hw_state), \
771+ "a" (eax), "d" (edx));
772+ break;
773+ default: // XSAVE, XSAVEOPT, XSAVEC, XSAVES
774+ asm volatile("xrstor %0" : : "m" (scp->xstate->hw_state), \
775+ "a" (eax), "d" (edx));
776+ break;
777+ }
778+ }
779 }
780 else
781 #endif
782--
7832.51.0
784
785From 0f2df19d9535d234b31c65f84a6c67ac2e0bd027 Mon Sep 17 00:00:00 2001
786From: Samuel Thibault <samuel.thibault@ens-lyon.org>
787Date: Mon, 21 Apr 2025 19:45:02 +0200
788Subject: [PATCH] hurd: Do not check for xstate level if it was not
789 initialized
790
791If __thread_get_state failed, there is no xstate level to check.
792ok is 0 already and the memory exists, but better not read uninitialized
793memory.
794---
795 sysdeps/mach/hurd/x86/trampoline.c | 2 +-
796 1 file changed, 1 insertion(+), 1 deletion(-)
797
798diff --git a/sysdeps/mach/hurd/x86/trampoline.c b/sysdeps/mach/hurd/x86/trampoline.c
799index db756e8a1f..02510b178c 100644
800--- a/sysdeps/mach/hurd/x86/trampoline.c
801+++ b/sysdeps/mach/hurd/x86/trampoline.c
802@@ -289,7 +289,7 @@ _hurd_setup_sighandler (struct hurd_sigstate *ss, const struct sigaction *action
803 (thread_state_t) stackframe->xstate, &got)
804 && got == (xstate_size / sizeof (int)));
805
806- if (((struct i386_xfloat_state*) stackframe->xstate)->fp_save_kind > 5)
807+ if (ok && ((struct i386_xfloat_state*) stackframe->xstate)->fp_save_kind > 5)
808 /* We support up to XSAVES */
809 ok = 0;
810
811--
8122.51.0
813