bug-gnulib
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

jit/cache: Fix for arm CPUs with GCC target arm-linux-gnueabihf


From: Bruno Haible
Subject: jit/cache: Fix for arm CPUs with GCC target arm-linux-gnueabihf
Date: Wed, 10 Jan 2024 15:38:02 +0100

I see test failures on Debian 'armhf' 8 and 12.

The cause is the following:
On arm, there are two instruction sets:
  - The "ARM" instruction set, consisting of 4-byte words.
  - The "Thumb" instruction set, consisting of 2-byte words.
A pointer to a function in "ARM" instruction set is just a pointer to the
first instruction.
A pointer to a function in "Thumb" instruction set is a pointer to the
first instruction + 1.
Thus, bit 0 of a function pointer tells which instruction set the function uses:
0 for "ARM", 1 for "Thumb".
The instructions 'blx' and 'bx' are used to call a function or return from a
function, respectively, while possibly changing the current instruction set
mode. (As opposed to 'bl' and 'mov pc,lr', which don't change the instruction
set mode.)
By default, with this compiler target, 'return1' and 'return2' are encoded
as "Thumb", and thus
  memcpy (start_rw, code_of_return1, size_of_return1);
starts copying at the second byte of the 'return1' function, not the first byte.
Which leads to SIGILL or SIGSEGV.

This patch fixes it.


2024-01-10  Bruno Haible  <bruno@clisp.org>

        jit/cache: Fix for arm CPUs with GCC target arm-linux-gnueabihf.
        * tests/jit/test-cache.c (CODE): Define differently on arm.
        (SET_CODE, IS, SET_IS): New macros.
        (main): New variables is_of_return1, is_of_return2. Use the SET_CODE and
        SET_IS macros.

diff --git a/tests/jit/test-cache.c b/tests/jit/test-cache.c
index cabbc0ddfd..cad6d2a214 100644
--- a/tests/jit/test-cache.c
+++ b/tests/jit/test-cache.c
@@ -90,7 +90,25 @@ struct func
 #ifdef FUNCPTR_POINTS_TO_CODE
 /* A function pointer points directly to the code.  */
 # define COPY_FUNCPTR(funcptr) funcptr
-# define CODE(funcptr) (funcptr)
+/* On arm, bit 0 of a function pointer tells which instruction set the function
+   uses.  */
+# if defined __arm__ || defined __armhf__
+#  define CODE(funcptr) ((void *) ((uintptr_t) (funcptr) & ~(intptr_t)1))
+#  define SET_CODE(funcptr,code_addr) \
+     ((void) ((funcptr) =                                     \
+              (void *) (((uintptr_t) (funcptr) & (intptr_t)1) \
+                        | (uintptr_t) (code_addr))))
+#  define IS(funcptr)  ((uintptr_t) (funcptr) & 1)
+#  define SET_IS(funcptr,is)  \
+     ((void) ((funcptr) = \
+              (void *) (((uintptr_t) (funcptr) & ~(intptr_t)1) | (is))))
+# else
+#  define CODE(funcptr) (funcptr)
+#  define SET_CODE(funcptr,code_addr) \
+     ((void) ((funcptr) = (void *) (code_addr)))
+#  define IS(funcptr) ((void) (funcptr), 0)
+#  define SET_IS(funcptr,is) ((void) (funcptr), (void) (is))
+# endif
 #else
 /* A function pointer points to a 'struct func'.  */
 # if FUNCPTR_BIAS
@@ -119,6 +137,10 @@ xcopy_structptr (struct func *structptr)
     structptr_to_funcptr (xcopy_structptr (funcptr_to_structptr (funcptr)))
 # define CODE(funcptr) \
     ((funcptr_to_structptr (funcptr))->code_address)
+# define SET_CODE(funcptr,code_addr) \
+    ((void) (CODE (funcptr) = (code_addr)))
+# define IS(funcptr) ((void) (funcptr), 0)
+# define SET_IS(funcptr,is) ((void) (funcptr), (void) (is))
 #endif
 
 /* This test assumes that the code generated by the compiler for the
@@ -212,6 +234,8 @@ main ()
   void const *code_of_return2;
   size_t size_of_return1;
   size_t size_of_return2;
+  int is_of_return1;
+  int is_of_return2;
 #if defined __OpenBSD__ || defined _RET_PROTECTOR
   /* OpenBSD maps code with PROT_EXEC (as opposed to PROT_READ | PROT_EXEC).
      We need to use predetermined code for 'return1' and 'return2'.  */
@@ -224,12 +248,17 @@ main ()
   code_of_return2 = return2_code;
   size_of_return1 = sizeof (return1_code);
   size_of_return2 = sizeof (return2_code);
+  /* On arm, return1_code and return2_code use the "ARM" instruction set.  */
+  is_of_return1 = 0;
+  is_of_return2 = 0;
 #else
   code_of_return1 = CODE (return1);
   code_of_return2 = CODE (return2);
   /* We assume that the code is not longer than 64 bytes.  */
   size_of_return1 = 64;
   size_of_return2 = 64;
+  is_of_return1 = IS (return1);
+  is_of_return2 = IS (return2);
 #endif
 
   int const pagesize = getpagesize ();
@@ -306,13 +335,15 @@ main ()
   }
 
   int (*f) (void) = COPY_FUNCPTR (return1);
-  CODE (f) = start;
+  SET_CODE (f, start);
 
   memcpy (start_rw, code_of_return1, size_of_return1);
+  SET_IS (f, is_of_return1);
   clear_cache (start, end);
   ASSERT (f () == 1);
 
   memcpy (start_rw, code_of_return2, size_of_return2);
+  SET_IS (f, is_of_return2);
   clear_cache (start, end);
   ASSERT (f () == 2);
 






reply via email to

[Prev in Thread] Current Thread [Next in Thread]