[PATCH] arm: Save/restore VFP argument registers in PLT trampolines (BZ 34144, BZ 15792)
Adhemerval Zanella
adhemerval.zanella@linaro.org
Tue May 12 20:35:55 GMT 2026
_dl_runtime_resolve and _dl_runtime_profile only preserved r0-r3 across
the inner fixup call. Recent GCC emits VFP instructions inside _dl_fixup
and _dl_profile_fixup, clobbering d0-d7 -- which under AAPCS-VFP hold the
caller's double arguments to the function being resolved.
Save d0-d7 around the call. d8-d15 are callee-saved by the C fixup
routines themselves, and d16-d31 are caller-saved scratch never used for
argument passing, so neither needs preserving here. Gate on
HAVE_ARM_PCS_VFP at build time: a hardfp ld.so cannot run without VFP,
and a softfp ld.so emits no VFP instruction.
Two new tests in sysdeps/arm/: tst-bz34144 exercises _dl_runtime_resolve;
tst-bz34144-audit installs a minimal audit module that defines
la_arm_gnu_pltenter to route lazy resolution through _dl_runtime_profile.
Both pass a function eight doubles via PLT lazy binding and verify they
arrive intact.
Checked on arm-linux-gnueabihf.
---
sysdeps/arm/Makefile | 19 ++++++++++++
sysdeps/arm/dl-trampoline.S | 50 ++++++++++++++++++++++++++++--
sysdeps/arm/tst-bz34144-audit.c | 32 +++++++++++++++++++
sysdeps/arm/tst-bz34144-auditmod.c | 50 ++++++++++++++++++++++++++++++
sysdeps/arm/tst-bz34144-mod.c | 28 +++++++++++++++++
sysdeps/arm/tst-bz34144.c | 32 +++++++++++++++++++
6 files changed, 209 insertions(+), 2 deletions(-)
create mode 100644 sysdeps/arm/tst-bz34144-audit.c
create mode 100644 sysdeps/arm/tst-bz34144-auditmod.c
create mode 100644 sysdeps/arm/tst-bz34144-mod.c
create mode 100644 sysdeps/arm/tst-bz34144.c
diff --git a/sysdeps/arm/Makefile b/sysdeps/arm/Makefile
index d08dade3c5b..0bb1b6e05be 100644
--- a/sysdeps/arm/Makefile
+++ b/sysdeps/arm/Makefile
@@ -30,6 +30,25 @@ $(objpfx)tst-armtlsdescloc: $(objpfx)tst-armtlsdesclocmod.so
$(objpfx)tst-armtlsdescextnow: $(objpfx)tst-armtlsdescextnowmod.so
$(objpfx)tst-armtlsdescextlazy: $(objpfx)tst-armtlsdescextlazymod.so
endif
+
+tests += \
+ tst-bz34144 \
+ tst-bz34144-audit \
+ # tests
+modules-names += \
+ tst-bz34144-auditmod \
+ tst-bz34144-mod \
+ # modules-names
+$(objpfx)tst-bz34144: $(objpfx)tst-bz34144-mod.so
+$(objpfx)tst-bz34144-audit: $(objpfx)tst-bz34144-mod.so
+$(objpfx)tst-bz34144-audit.out: $(objpfx)tst-bz34144-auditmod.so
+# Use lazy binding to check if _dl_runtime_resolve correctly save/restore
+# the VFP state.
+LDFLAGS-tst-bz34144 = -Wl,-z,lazy
+# With LD_AUDIT, lazy resolution goes through _dl_runtime_profile, which
+# must also save/restore VFP state (BZ 34144).
+LDFLAGS-tst-bz34144-audit = -Wl,-z,lazy
+tst-bz34144-audit-ENV = LD_AUDIT=$(objpfx)tst-bz34144-auditmod.so
endif
endif
diff --git a/sysdeps/arm/dl-trampoline.S b/sysdeps/arm/dl-trampoline.S
index eb6d464384d..7433648c1ef 100644
--- a/sysdeps/arm/dl-trampoline.S
+++ b/sysdeps/arm/dl-trampoline.S
@@ -44,6 +44,16 @@ _dl_runtime_resolve:
cfi_rel_offset (r2, 8)
cfi_rel_offset (r3, 12)
+#if defined SHARED && defined HAVE_ARM_PCS_VFP
+ @ Save the VFP argument registers d0-d7 around the call to
+ @ _dl_fixup. Under AAPCS-VFP these hold caller-supplied
+ @ floating-point arguments which must be forwarded unchanged
+ @ to the resolved function.
+ sub sp, sp, #64
+ cfi_adjust_cfa_offset (64)
+ .inst 0xec8d0b10 @ vstmia sp, {d0-d7}
+#endif
+
@ get pointer to linker struct
ldr r0, [lr, #-4]
@@ -59,8 +69,14 @@ _dl_runtime_resolve:
@ save the return
mov ip, r0
- @ get arguments and return address back. We restore r4
- @ only to realign the stack.
+#if defined SHARED && defined HAVE_ARM_PCS_VFP
+ .inst 0xec9d0b10 @ vldmia sp, {d0-d7}
+ add sp, sp, #64
+ cfi_adjust_cfa_offset (-64)
+#endif
+
+ @ get arguments and return address back. We restore r4 to
+ @ realign the stack.
pop {r0-r4,lr}
cfi_adjust_cfa_offset (-24)
@@ -124,14 +140,44 @@ _dl_runtime_profile:
add r3, sp, #8
stmia r3!, {r0,r1}
+#ifdef HAVE_ARM_PCS_VFP
+ @ Save the VFP argument registers d0-d7 around the call to
+ @ _dl_profile_fixup. Under AAPCS-VFP these hold caller-supplied
+ @ floating-point arguments which must be forwarded unchanged to
+ @ the resolved function.
+ @
+ @ Layout below new sp:
+ @ sp + 0 .. 3: outgoing arg (framesizep) for _dl_profile_fixup
+ @ sp + 4 .. 7: padding (for 8-byte alignment of the VFP area)
+ @ sp + 8 .. 71: d0-d7
+ sub sp, sp, #72
+ cfi_adjust_cfa_offset (72)
+ add ip, sp, #8
+ .inst 0xec8c0b10 @ vstmia ip, {d0-d7}
+
+ @ Set up extra args for _dl_profile_fixup.
+ @ The framesize slot is at the old sp+208, which is the new
+ @ sp + 72 + 208 = sp + 280.
+ add ip, sp, #280
+ str ip, [sp, #0]
+#else
@ Set up extra args for _dl_profile_fixup.
@ r2 and r3 are already loaded.
add ip, sp, #208
str ip, [sp, #0]
+#endif
@ call profiling fixup routine
bl _dl_profile_fixup
+#ifdef HAVE_ARM_PCS_VFP
+ @ Restore d0-d7. r0 holds the resolved function address.
+ add ip, sp, #8
+ .inst 0xec9c0b10 @ vldmia ip, {d0-d7}
+ add sp, sp, #72
+ cfi_adjust_cfa_offset (-72)
+#endif
+
@ The address to call is now in r0.
@ Check whether we're wrapping this function.
diff --git a/sysdeps/arm/tst-bz34144-audit.c b/sysdeps/arm/tst-bz34144-audit.c
new file mode 100644
index 00000000000..8f1084fa0af
--- /dev/null
+++ b/sysdeps/arm/tst-bz34144-audit.c
@@ -0,0 +1,32 @@
+/* Test that lazy PLT resolution via _dl_runtime_profile preserves
+ caller-saved VFP registers used to pass double arguments (BZ 34144).
+ Copyright (C) 2026 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <https://www.gnu.org/licenses/>. */
+
+#include <support/test-driver.h>
+
+extern void test_float_args (double a, double b, double c, double d,
+ double e, double f, double g, double h);
+
+static int
+do_test (void)
+{
+ test_float_args (2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0);
+ return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/sysdeps/arm/tst-bz34144-auditmod.c b/sysdeps/arm/tst-bz34144-auditmod.c
new file mode 100644
index 00000000000..ada9f126c28
--- /dev/null
+++ b/sysdeps/arm/tst-bz34144-auditmod.c
@@ -0,0 +1,50 @@
+/* Minimal audit module used by tst-bz34144-audit to force PLT calls
+ to go through _dl_runtime_profile instead of _dl_runtime_resolve.
+ Copyright (C) 2026 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <https://www.gnu.org/licenses/>. */
+
+#include <link.h>
+#include <stddef.h>
+#include <stdint.h>
+
+unsigned int
+la_version (unsigned int v)
+{
+ return v;
+}
+
+unsigned int
+la_objopen (struct link_map *l, Lmid_t lmid, uintptr_t *cookie)
+{
+ return LA_FLG_BINDFROM | LA_FLG_BINDTO;
+}
+
+uintptr_t
+la_symbind32 (Elf32_Sym *sym, unsigned int ndx, uintptr_t *refcook,
+ uintptr_t *defcook, unsigned int *flags, const char *symname)
+{
+ return sym->st_value;
+}
+
+Elf32_Addr
+la_arm_gnu_pltenter (Elf32_Sym *sym, unsigned int ndx, uintptr_t *refcook,
+ uintptr_t *defcook, La_arm_regs *regs,
+ unsigned int *flags, const char *symname,
+ long int *framesizep)
+{
+ return sym->st_value;
+}
diff --git a/sysdeps/arm/tst-bz34144-mod.c b/sysdeps/arm/tst-bz34144-mod.c
new file mode 100644
index 00000000000..be6b54bf91d
--- /dev/null
+++ b/sysdeps/arm/tst-bz34144-mod.c
@@ -0,0 +1,28 @@
+/* DSO used by tst-bz34144.
+ Copyright (C) 2026 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <https://www.gnu.org/licenses/>. */
+
+#include <stdlib.h>
+
+void
+test_float_args (double a, double b, double c, double d,
+ double e, double f, double g, double h)
+{
+ if (a != 2.0 || b != 3.0 || c != 4.0 || d != 5.0
+ || e != 6.0 || f != 7.0 || g != 8.0 || h != 9.0)
+ abort ();
+}
diff --git a/sysdeps/arm/tst-bz34144.c b/sysdeps/arm/tst-bz34144.c
new file mode 100644
index 00000000000..61e41b3945c
--- /dev/null
+++ b/sysdeps/arm/tst-bz34144.c
@@ -0,0 +1,32 @@
+/* Test that lazy PLT resolution preserves caller-saved VFP registers
+ used to pass double arguments (BZ 34144).
+ Copyright (C) 2026 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <https://www.gnu.org/licenses/>. */
+
+#include <support/test-driver.h>
+
+extern void test_float_args (double a, double b, double c, double d,
+ double e, double f, double g, double h);
+
+static int
+do_test (void)
+{
+ test_float_args (2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0);
+ return 0;
+}
+
+#include <support/test-driver.c>
--
2.43.0
More information about the Libc-alpha
mailing list