https://gcc.gnu.org/bugzilla/show_bug.cgi?id=86755

            Bug ID: 86755
           Summary: [ASAN] Libasan failed to be build for arm with -mthumb
                    and -fno-omit-frame-pointer
           Product: gcc
           Version: 9.0
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: sanitizer
          Assignee: unassigned at gcc dot gnu.org
          Reporter: d.khalikov at partner dot samsung.com
                CC: dodji at gcc dot gnu.org, dvyukov at gcc dot gnu.org,
                    jakub at gcc dot gnu.org, kcc at gcc dot gnu.org, marxin at 
gcc dot gnu.org
  Target Milestone: ---

Created attachment 44471
  --> https://gcc.gnu.org/bugzilla/attachment.cgi?id=44471&action=edit
Reduced test case

GCC fails to build libasan with -mthumb and -fno-omit-frame-pointer

../../../../libsanitizer/sanitizer_common/sanitizer_linux.cc: In function
'__sanitizer::uptr __sanitizer::internal_clone(int (*)(void*), void*, int,
void*, int*, void*, int*)':
../../../../libsanitizer/sanitizer_common/sanitizer_linux.cc:1540:1: error: r7
cannot be used in asm here
 }

The problem is inside internal_clone function defined for arm.

Reduced test case:

$cat clone.cc
#define __NR_clone 120
#define __NR_exit 1

using uptr = unsigned int;
unsigned int EINVAL = 1;
uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg,
                    int *parent_tidptr, void *newtls, int *child_tidptr) {
  unsigned int res;
  if (!fn || !child_stack)
    return -EINVAL;
  child_stack = (char *)child_stack - 2 * sizeof(unsigned int);
  ((unsigned int *)child_stack)[0] = (uptr)fn;
  ((unsigned int *)child_stack)[1] = (uptr)arg;
  register int r0 __asm__("r0") = flags;
  register void *r1 __asm__("r1") = child_stack;
  register int *r2 __asm__("r2") = parent_tidptr;
  register void *r3 __asm__("r3") = newtls;
  register int *r4 __asm__("r4") = child_tidptr;
  register int r7 __asm__("r7") = __NR_clone;

#if __ARM_ARCH > 4 || defined (__ARM_ARCH_4T__)
# define ARCH_HAS_BX
#endif
#if __ARM_ARCH > 4
# define ARCH_HAS_BLX
#endif

#ifdef ARCH_HAS_BX
# ifdef ARCH_HAS_BLX
#  define BLX(R) "blx "  #R "\n"
# else
#  define BLX(R) "mov lr, pc; bx " #R "\n"
# endif
#else
# define BLX(R)  "mov lr, pc; mov pc," #R "\n"
#endif

  __asm__ __volatile__(
                       /* %r0 = syscall(%r7 = SYSCALL(clone),
                        *               %r0 = flags,
                        *               %r1 = child_stack,
                        *               %r2 = parent_tidptr,
                        *               %r3  = new_tls,
                        *               %r4 = child_tidptr)
                        */

                       /* Do the system call */
                       "swi 0x0\n"

                       /* if (%r0 != 0)
                        *   return %r0;
                        */
                       "cmp r0, #0\n"
                       "bne 1f\n"

                       /* In the child, now. Call "fn(arg)". */
                       "ldr r0, [sp, #4]\n"
                       "ldr ip, [sp], #8\n"
                       BLX(ip)
                       /* Call _exit(%r0). */
                       "mov r7, %7\n"
                       "swi 0x0\n"
                       "1:\n"
                       "mov %0, r0\n"
                       : "=r"(res)
                       : "r"(r0), "r"(r1), "r"(r2), "r"(r3), "r"(r4), "r"(r7),
                         "i"(__NR_exit)
                       : "memory");
  return res;
} 


$armv7l-linux-gnueabi-g++ -o clone.s clone.cc -fno-omit-frame-pointer -mthumb
-S
clone.cc: In function ‘uptr internal_clone(int (*)(void*), void*, int, void*,
int*, void*, int*)’:
clone.cc:70:1: error: r7 cannot be used in asm here

Regarding to arm ABI, r7 register is using for syscall number, r0 for return
value, and r1 - r6 for syscall arguments, by the way r7 for arm with THUMB2
mode is using as frame pointer and it looks like we have a conflict in this
case. As far as I understood, GCC has a special check inside IRA 
if (!TEST_HARD_REG_BIT (crtl->asm_clobbers, HARD_FRAME_POINTER_REGNUM)), which
does not allow to have frame pointer register as clobber register. 

In other way, there is no issue with clang:
clang++ -target armv7l -S -o clone.s  clone.cc -mthumb -fno-omit-frame-pointer

So, looks like we can save syscall number in r8 register, move it to r7 before
we the interruption, and restore the value in the parent task. 

 register int r8 __asm__("r8") = __NR_clone;

  __asm__ __volatile__(
                       /* %r0 = syscall(%r7 = SYSCALL(clone),
                        *               %r0 = flags,
                        *               %r1 = child_stack,
                        *               %r2 = parent_tidptr,
                        *               %r3  = new_tls,
                        *               %r4 = child_tidptr)
                        */

                       "push {r7}\n"
                       "mov r7, r8\n"

                       /* Do the system call */
                       "swi 0x0\n"

                       /* if (%r0 != 0)
                        *   return %r0;
                        */
                       "cmp r0, #0\n"
                       "bne 1f\n"

                       /* In the child, now. Call "fn(arg)". */
                       "ldr r0, [sp, #4]\n"
                       "ldr ip, [sp], #8\n"
                       BLX(ip)
                       /* Call _exit(%r0). */
                       "mov r7, %7\n"
                       "swi 0x0\n"
                       "1:\n"
                       "pop {r7}\n"
                       "mov %0, r0\n"
                       : "=r"(res)
                       : "r"(r0), "r"(r1), "r"(r2), "r"(r3), "r"(r4), "r"(r8),
                         "i"(__NR_exit)
                       : "memory");
  return res;
}

Reply via email to