selftests/rseq: Play nice with binaries statically linked against glibc 2.35+

Message ID 20230721223352.2333911-1-seanjc@google.com
State New
Headers
Series selftests/rseq: Play nice with binaries statically linked against glibc 2.35+ |

Commit Message

Sean Christopherson July 21, 2023, 10:33 p.m. UTC
  To allow running rseq and KVM's rseq selftests as statically linked
binaries, initialize the various "trampoline" pointers to point directly
at the expect glibc symbols, and skip the dlysm() lookups if the rseq
size is non-zero, i.e. the binary is statically linked *and* the libc
registered its own rseq.

Define weak versions of the symbols so as not to break linking against
libc versions that don't support rseq in any capacity.

The KVM selftests in particular are often statically linked so that they
can be run on targets with very limited runtime environments, i.e. test
machines.

Fixes: 233e667e1ae3 ("selftests/rseq: Uplift rseq selftests for compatibility with glibc-2.35")
Cc: Aaron Lewis <aaronlewis@google.com>
Cc: kvm@vger.kernel.org
Cc: stable@vger.kernel.org
Signed-off-by: Sean Christopherson <seanjc@google.com>
---

Note, this is very much the result of throwing noodles until something
stuck, it seems like there's gotta be a less awful way to handle this :-(

I Cc'd stable@ because I know I'm not the only person that runs statically
linked KVM selftests, and figuring all this out was quite painful.

 tools/testing/selftests/rseq/rseq.c | 28 ++++++++++++++++++++++------
 1 file changed, 22 insertions(+), 6 deletions(-)


base-commit: 88bb466c9dec4f70d682cf38c685324e7b1b3d60
  

Comments

Paolo Bonzini July 29, 2023, 3:01 p.m. UTC | #1
Queued, thanks.

Paolo
  
Mathieu Desnoyers July 29, 2023, 3:08 p.m. UTC | #2
On 7/21/23 18:33, Sean Christopherson wrote:
> To allow running rseq and KVM's rseq selftests as statically linked
> binaries, initialize the various "trampoline" pointers to point directly
> at the expect glibc symbols, and skip the dlysm() lookups if the rseq
> size is non-zero, i.e. the binary is statically linked *and* the libc
> registered its own rseq.
> 
> Define weak versions of the symbols so as not to break linking against
> libc versions that don't support rseq in any capacity.
> 
> The KVM selftests in particular are often statically linked so that they
> can be run on targets with very limited runtime environments, i.e. test
> machines.

Thanks!

Reviewed-by: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>

> 
> Fixes: 233e667e1ae3 ("selftests/rseq: Uplift rseq selftests for compatibility with glibc-2.35")
> Cc: Aaron Lewis <aaronlewis@google.com>
> Cc: kvm@vger.kernel.org
> Cc: stable@vger.kernel.org
> Signed-off-by: Sean Christopherson <seanjc@google.com>
> ---
> 
> Note, this is very much the result of throwing noodles until something
> stuck, it seems like there's gotta be a less awful way to handle this :-(
> 
> I Cc'd stable@ because I know I'm not the only person that runs statically
> linked KVM selftests, and figuring all this out was quite painful.
> 
>   tools/testing/selftests/rseq/rseq.c | 28 ++++++++++++++++++++++------
>   1 file changed, 22 insertions(+), 6 deletions(-)
> 
> diff --git a/tools/testing/selftests/rseq/rseq.c b/tools/testing/selftests/rseq/rseq.c
> index 4e4aa006004c..a723da253244 100644
> --- a/tools/testing/selftests/rseq/rseq.c
> +++ b/tools/testing/selftests/rseq/rseq.c
> @@ -34,9 +34,17 @@
>   #include "../kselftest.h"
>   #include "rseq.h"
>   
> -static const ptrdiff_t *libc_rseq_offset_p;
> -static const unsigned int *libc_rseq_size_p;
> -static const unsigned int *libc_rseq_flags_p;
> +/*
> + * Define weak versions to play nice with binaries that are statically linked
> + * against a libc that doesn't support registering its own rseq.
> + */
> +__weak ptrdiff_t __rseq_offset;
> +__weak unsigned int __rseq_size;
> +__weak unsigned int __rseq_flags;
> +
> +static const ptrdiff_t *libc_rseq_offset_p = &__rseq_offset;
> +static const unsigned int *libc_rseq_size_p = &__rseq_size;
> +static const unsigned int *libc_rseq_flags_p = &__rseq_flags;
>   
>   /* Offset from the thread pointer to the rseq area. */
>   ptrdiff_t rseq_offset;
> @@ -155,9 +163,17 @@ unsigned int get_rseq_feature_size(void)
>   static __attribute__((constructor))
>   void rseq_init(void)
>   {
> -	libc_rseq_offset_p = dlsym(RTLD_NEXT, "__rseq_offset");
> -	libc_rseq_size_p = dlsym(RTLD_NEXT, "__rseq_size");
> -	libc_rseq_flags_p = dlsym(RTLD_NEXT, "__rseq_flags");
> +	/*
> +	 * If the libc's registered rseq size isn't already valid, it may be
> +	 * because the binary is dynamically linked and not necessarily due to
> +	 * libc not having registered a restartable sequence.  Try to find the
> +	 * symbols if that's the case.
> +	 */
> +	if (!*libc_rseq_size_p) {
> +		libc_rseq_offset_p = dlsym(RTLD_NEXT, "__rseq_offset");
> +		libc_rseq_size_p = dlsym(RTLD_NEXT, "__rseq_size");
> +		libc_rseq_flags_p = dlsym(RTLD_NEXT, "__rseq_flags");
> +	}
>   	if (libc_rseq_size_p && libc_rseq_offset_p && libc_rseq_flags_p &&
>   			*libc_rseq_size_p != 0) {
>   		/* rseq registration owned by glibc */
> 
> base-commit: 88bb466c9dec4f70d682cf38c685324e7b1b3d60
  

Patch

diff --git a/tools/testing/selftests/rseq/rseq.c b/tools/testing/selftests/rseq/rseq.c
index 4e4aa006004c..a723da253244 100644
--- a/tools/testing/selftests/rseq/rseq.c
+++ b/tools/testing/selftests/rseq/rseq.c
@@ -34,9 +34,17 @@ 
 #include "../kselftest.h"
 #include "rseq.h"
 
-static const ptrdiff_t *libc_rseq_offset_p;
-static const unsigned int *libc_rseq_size_p;
-static const unsigned int *libc_rseq_flags_p;
+/*
+ * Define weak versions to play nice with binaries that are statically linked
+ * against a libc that doesn't support registering its own rseq.
+ */
+__weak ptrdiff_t __rseq_offset;
+__weak unsigned int __rseq_size;
+__weak unsigned int __rseq_flags;
+
+static const ptrdiff_t *libc_rseq_offset_p = &__rseq_offset;
+static const unsigned int *libc_rseq_size_p = &__rseq_size;
+static const unsigned int *libc_rseq_flags_p = &__rseq_flags;
 
 /* Offset from the thread pointer to the rseq area. */
 ptrdiff_t rseq_offset;
@@ -155,9 +163,17 @@  unsigned int get_rseq_feature_size(void)
 static __attribute__((constructor))
 void rseq_init(void)
 {
-	libc_rseq_offset_p = dlsym(RTLD_NEXT, "__rseq_offset");
-	libc_rseq_size_p = dlsym(RTLD_NEXT, "__rseq_size");
-	libc_rseq_flags_p = dlsym(RTLD_NEXT, "__rseq_flags");
+	/*
+	 * If the libc's registered rseq size isn't already valid, it may be
+	 * because the binary is dynamically linked and not necessarily due to
+	 * libc not having registered a restartable sequence.  Try to find the
+	 * symbols if that's the case.
+	 */
+	if (!*libc_rseq_size_p) {
+		libc_rseq_offset_p = dlsym(RTLD_NEXT, "__rseq_offset");
+		libc_rseq_size_p = dlsym(RTLD_NEXT, "__rseq_size");
+		libc_rseq_flags_p = dlsym(RTLD_NEXT, "__rseq_flags");
+	}
 	if (libc_rseq_size_p && libc_rseq_offset_p && libc_rseq_flags_p &&
 			*libc_rseq_size_p != 0) {
 		/* rseq registration owned by glibc */