[PATCH, RFC] Enable libsanitizer on powerpc{,64}

Peter Bergner bergner@vnet.ibm.com
Fri Nov 16 23:08:00 GMT 2012


Attached below is an initial port of ASAN to powerpc*-linux.
With the patch below along with Jakub's ASAN testsuite patch:

  http://gcc.gnu.org/ml/gcc-patches/2012-11/msg01365.html

ASAN not only builds, but seems to be working.  The lone ASAN
test case does fail, but it seems to be related to us using
_Unwind_Backtrace() and we end up with two extra frames at the
bottom of our stack frame, so we don't match the expected
results.

==47550== ERROR: AddressSanitizer stack-buffer-overflow on address 0x0fffe65540a4 at pc 0xfff769d0d70 bp 0xfffe6553ed0 sp 0xfffe6553fe8
READ of size 1 at 0x0fffe65540a4 thread T0
    #0 0xfff769dc388 in _ZN6__asan13GetStackTraceEPN11__sanitizer10StackTraceEmmm /home/bergner/gcc/gcc-fsf-mainline-asan/libsanitizer/asan/asan_linux.cc:160
    #1 0xfff769df4f0 in __asan_report_error /home/bergner/gcc/gcc-fsf-mainline-asan/libsanitizer/asan/asan_report.cc:471 (discriminator 5)
    #2 0xfff769d0dbc in __interceptor_memcmp /home/bergner/gcc/gcc-fsf-mainline-asan/libsanitizer/asan/asan_interceptors.cc:218 (discriminator 1)
    #3 0x10000b4c in main /home/bergner/gcc/gcc-fsf-mainline-asan/gcc/testsuite/c-c++-common/asan/memcmp-1.c:12

...which doesn't match:

/* { dg-output "    #0 0x\[0-9a-f\]+ (in _*(interceptor_|)memcmp |\[(\])\[^\n\r]*(\n|\r\n|\r)" } */
/* { dg-output "    #1 0x\[0-9a-f\]+ (in _*main|\[(\])\[^\n\r]*(\n|\r\n|\r)" } */

because the frame numbers are off.  Any ideas on how to fix that?

We cannot use the FastUnwindStack, since that uses exclusively the
frame pointer which ppc almost never uses.  We also can have stack
frames above or below the stack pointer, depending on whether the
function is a leaf or not.  And with shrink wrapping, can we even
be sure if the frame pointer is even setup even on those systems
that do use the frame pointer?  I've seen others say we should
use libbacktrace, and I think I have to agree with them for the
reasons above.

One question that I have is that the toplev.c test for port support
tests for !FRAME_GROWS_DOWNWARD.  The rs6000 port has FRAME_GROWS_DOWNWARD
defined as (flag_stack_protect != 0), so ASAN only works when we use
-fstack-protector.  Is there a technical reason why FRAME_GROWS_DOWNWARD
must be false?

I should also note, the code setting the page size is very fragile.
On PPC/PPC64 kernels, you can configure the kernel to use different
page sizes.  My systems are running 64K page sizes, but could just
as easily be 4K or some other number.  Shouldn't we be calling
getpagesize() or sysconf() to query the page size and then computing
the other values from that?

I also don't like all the #ifdef's sprinkling the code.  Can't we
use some configure fragments to help remove many/most of these?

Does anyone have any thoughts on the patch?  Does it look reasonable?

Peter


gcc/
	* config/rs6000/rs6000.c (TARGET_ASAN_SHADOW_OFFSET): Define.
	(rs6000_asan_shadow_offset): New function.

libsanitizer/
	* configure.tgt: Enable build on powerpc and powerpc64 linux.
	* sanitizer_common/sanitizer_common.h (kPageSizeBits): Set for powerpc
	and powerpc64.
	(kPageSize): Likewise.
	(kCacheLineSize): Likewise.
	(kMmapGranularity): Likewise.
	* sanitizer_common/sanitizer_linux.cc (__NR_mmap) Call for powerpc64.
	(__NR_fstat): Likewise.
	* sanitizer_common/sanitizer_stacktrace.cc (patch_pc): Align to 4 bytes.
	* asan/asan_mapping.h (SHADOW_OFFSET): Define for powerpc and powerpc64.
	(kHighMemEnd): Set for powerpc and powerpc64.
	* asan/asan_linux.cc (GetPcSpBp): Add powerpc and powerpc64 support.
	(GetStackTrace): Use _Unwind_Backtrace for powerpc and powerpc64.

Index: libsanitizer/configure.tgt
===================================================================
--- libsanitizer/configure.tgt	(revision 193575)
+++ libsanitizer/configure.tgt	(working copy)
@@ -20,6 +20,8 @@
 
 # Filter out unsupported systems.
 case "${target}" in
+  powerpc*-*-linux*)
+	;;
   x86_64-*-linux* | i?86-*-linux* | sparc*-*-linux*)
 	;;
   *)
Index: libsanitizer/sanitizer_common/sanitizer_common.h
===================================================================
--- libsanitizer/sanitizer_common/sanitizer_common.h	(revision 193575)
+++ libsanitizer/sanitizer_common/sanitizer_common.h	(working copy)
@@ -21,12 +21,24 @@
 // Constants.
 const uptr kWordSize = __WORDSIZE / 8;
 const uptr kWordSizeInBits = 8 * kWordSize;
+#if defined(__powerpc__) || defined(__powerpc64__)
+// Current PPC64 kernels use 64K pages sizes, but they can be
+// configured with 4K or even other sizes, so we should probably
+// use getpagesize() or sysconf(_SC_PAGESIZE) here rather than
+// hardcoding the values.
+const uptr kPageSizeBits = 16;
+const uptr kPageSize = 1UL << kPageSizeBits;
+const uptr kCacheLineSize = 128;
+const uptr kMmapGranularity = kPageSize;
+#elif !defined( _WIN32)
 const uptr kPageSizeBits = 12;
 const uptr kPageSize = 1UL << kPageSizeBits;
 const uptr kCacheLineSize = 64;
-#ifndef _WIN32
 const uptr kMmapGranularity = kPageSize;
 #else
+const uptr kPageSizeBits = 12;
+const uptr kPageSize = 1UL << kPageSizeBits;
+const uptr kCacheLineSize = 64;
 const uptr kMmapGranularity = 1UL << 16;
 #endif
 
Index: libsanitizer/sanitizer_common/sanitizer_linux.cc
===================================================================
--- libsanitizer/sanitizer_common/sanitizer_linux.cc	(revision 193575)
+++ libsanitizer/sanitizer_common/sanitizer_linux.cc	(working copy)
@@ -34,7 +34,7 @@
 // --------------- sanitizer_libc.h
 void *internal_mmap(void *addr, uptr length, int prot, int flags,
                     int fd, u64 offset) {
-#if defined __x86_64__
+#if defined(__x86_64__) || defined(__powerpc64__)
   return (void *)syscall(__NR_mmap, addr, length, prot, flags, fd, offset);
 #else
   return (void *)syscall(__NR_mmap2, addr, length, prot, flags, fd, offset);
@@ -67,7 +67,7 @@
 }
 
 uptr internal_filesize(fd_t fd) {
-#if defined __x86_64__
+#if defined(__x86_64__) || defined(__powerpc64__)
   struct stat st;
   if (syscall(__NR_fstat, fd, &st))
     return -1;
Index: libsanitizer/sanitizer_common/sanitizer_stacktrace.cc
===================================================================
--- libsanitizer/sanitizer_common/sanitizer_stacktrace.cc	(revision 193575)
+++ libsanitizer/sanitizer_common/sanitizer_stacktrace.cc	(working copy)
@@ -31,7 +31,12 @@
   // Cancel Thumb bit.
   pc = pc & (~1);
 #endif
+#if defined(__powerpc__) || defined(__powerpc64__)
+  // PCs are always 4 byte aligned.
+  return pc - 4;
+#else
   return pc - 1;
+#endif
 }
 
 static void PrintStackFramePrefix(uptr frame_num, uptr pc) {
Index: libsanitizer/asan/asan_mapping.h
===================================================================
--- libsanitizer/asan/asan_mapping.h	(revision 193575)
+++ libsanitizer/asan/asan_mapping.h	(working copy)
@@ -31,7 +31,11 @@
 #  if __WORDSIZE == 32
 #   define SHADOW_OFFSET (1 << 29)
 #  else
-#   define SHADOW_OFFSET (1ULL << 44)
+#   if defined(__powerpc64__)
+#    define SHADOW_OFFSET (1ULL << 41)
+#   else
+#    define SHADOW_OFFSET (1ULL << 44)
+#   endif
 #  endif
 # endif
 #endif  // ASAN_FLEXIBLE_MAPPING_AND_OFFSET
@@ -41,7 +45,11 @@
 #define SHADOW_TO_MEM(shadow) (((shadow) - SHADOW_OFFSET) << SHADOW_SCALE)
 
 #if __WORDSIZE == 64
+# if defined(__powerpc64__)
+  static const uptr kHighMemEnd = 0x00000fffffffffffUL;
+# else
   static const uptr kHighMemEnd = 0x00007fffffffffffUL;
+# endif
 #else  // __WORDSIZE == 32
   static const uptr kHighMemEnd = 0xffffffff;
 #endif  // __WORDSIZE
Index: libsanitizer/asan/asan_linux.cc
===================================================================
--- libsanitizer/asan/asan_linux.cc	(revision 193575)
+++ libsanitizer/asan/asan_linux.cc	(working copy)
@@ -66,6 +66,13 @@
   *pc = ucontext->uc_mcontext.gregs[REG_EIP];
   *bp = ucontext->uc_mcontext.gregs[REG_EBP];
   *sp = ucontext->uc_mcontext.gregs[REG_ESP];
+# elif defined(__powerpc__) || defined(__powerpc64__)
+  ucontext_t *ucontext = (ucontext_t*)context;
+  *pc = ucontext->uc_mcontext.regs->nip;
+  *sp = ucontext->uc_mcontext.regs->gpr[PT_R1];
+  // The powerpc{,64}-linux ABIs do not specify r31 as the frame
+  // pointer, but GCC always uses r31 when we need a frame pointer.
+  *bp = ucontext->uc_mcontext.regs->gpr[PT_R31];
 # elif defined(__sparc__)
   ucontext_t *ucontext = (ucontext_t*)context;
   uptr *stk_ptr;
@@ -149,7 +156,7 @@
   stack->trace[0] = pc;
   if ((max_s) > 1) {
     stack->max_size = max_s;
-#ifdef __arm__
+#if defined(__arm__) || defined(__powerpc__) || defined(__powerpc64__)
     _Unwind_Backtrace(Unwind_Trace, stack);
 #else
     if (!asan_inited) return;
Index: gcc/config/rs6000/rs6000.c
===================================================================
--- gcc/config/rs6000/rs6000.c	(revision 193575)
+++ gcc/config/rs6000/rs6000.c	(working copy)
@@ -1186,6 +1186,9 @@
 #undef TARGET_DELEGITIMIZE_ADDRESS
 #define TARGET_DELEGITIMIZE_ADDRESS rs6000_delegitimize_address
 
+#undef TARGET_ASAN_SHADOW_OFFSET
+#define TARGET_ASAN_SHADOW_OFFSET rs6000_asan_shadow_offset
+
 #undef TARGET_CONST_NOT_OK_FOR_DEBUG_P
 #define TARGET_CONST_NOT_OK_FOR_DEBUG_P rs6000_const_not_ok_for_debug_p
 
@@ -27372,6 +27375,13 @@
     }
 }
 
+/* Implement the TARGET_ASAN_SHADOW_OFFSET hook.  */
+
+static unsigned HOST_WIDE_INT
+rs6000_asan_shadow_offset (void)
+{
+  return (unsigned HOST_WIDE_INT) 1 << (TARGET_64BIT ? 41 : 29);
+}
 

 /* Mask options that we want to support inside of attribute((target)) and
    #pragma GCC target operations.  Note, we do not include things like




More information about the Gcc-patches mailing list