[compiler-rt] 37445e9 - [compiler-rt] Allow 3 simultaneous interceptors on Linux

Marco Elver via llvm-commits llvm-commits at lists.llvm.org
Fri Jun 9 02:31:58 PDT 2023


Author: Marco Elver
Date: 2023-06-09T11:30:41+02:00
New Revision: 37445e96d867f4266993085e821fbd4c4d8fa401

URL: https://github.com/llvm/llvm-project/commit/37445e96d867f4266993085e821fbd4c4d8fa401
DIFF: https://github.com/llvm/llvm-project/commit/37445e96d867f4266993085e821fbd4c4d8fa401.diff

LOG: [compiler-rt] Allow 3 simultaneous interceptors on Linux

Rework Linux (and *BSD) interceptors to allow for up to 3 (2 for *BSD)
simultaneous interceptors. See code comments for details.

The main motivation is to support new sampling sanitizers (in the spirit
of GWP-ASan), that have to intercept few functions. Unfortunately, the
reality is that there are user interceptors that exist in the wild.

To support foreign user interceptors, foreign dynamic analysis
interceptors, and compiler-rt interceptors all at the same time,
including any combination of them, this change enables up to 3
interceptors on Linux (2 on *BSD).

v2:
* Revert to to the simpler "weak wrapper -(alias)-> __interceptor"
  scheme on architectures that cannot implement a trampoline efficiently
  due to complexities of resolving a preemptible symbol (PowerPC64
  ELFv2 global entry, and i386 PIC).
* Avoid duplicate intercepted functions in gen_dynamic_list.py, due to
  matching __interceptor_X and ___interceptor_X.
* Fix s390 __tls_get_offset.

Reviewed By: dvyukov, MaskRay, vitalybuka

Differential Revision: https://reviews.llvm.org/D151085

Added: 
    compiler-rt/lib/interception/tests/interception_linux_foreign_test.cpp

Modified: 
    compiler-rt/lib/interception/interception.h
    compiler-rt/lib/interception/tests/CMakeLists.txt
    compiler-rt/lib/interception/tests/interception_linux_test.cpp
    compiler-rt/lib/sanitizer_common/sanitizer_asm.h
    compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors.inc
    compiler-rt/lib/sanitizer_common/sanitizer_stacktrace_printer.cpp
    compiler-rt/lib/sanitizer_common/scripts/gen_dynamic_list.py
    compiler-rt/test/asan/TestCases/Posix/asan-symbolize-sanity-test.cpp

Removed: 
    


################################################################################
diff  --git a/compiler-rt/lib/interception/interception.h b/compiler-rt/lib/interception/interception.h
index 91358617762ad..3ae698fb9ae11 100644
--- a/compiler-rt/lib/interception/interception.h
+++ b/compiler-rt/lib/interception/interception.h
@@ -14,6 +14,7 @@
 #ifndef INTERCEPTION_H
 #define INTERCEPTION_H
 
+#include "sanitizer_common/sanitizer_asm.h"
 #include "sanitizer_common/sanitizer_internal_defs.h"
 
 #if !SANITIZER_LINUX && !SANITIZER_FREEBSD && !SANITIZER_APPLE &&    \
@@ -67,24 +68,50 @@ typedef __sanitizer::OFF64_T OFF64_T;
 //           for more details). To intercept such functions you need to use the
 //           INTERCEPTOR_WITH_SUFFIX(...) macro.
 
-// How it works:
-// To replace system functions on Linux we just need to declare functions
-// with same names in our library and then obtain the real function pointers
+// How it works on Linux
+// ---------------------
+//
+// To replace system functions on Linux we just need to declare functions with
+// the same names in our library and then obtain the real function pointers
 // using dlsym().
-// There is one complication. A user may also intercept some of the functions
-// we intercept. To resolve this we declare our interceptors with __interceptor_
-// prefix, and then make actual interceptors weak aliases to __interceptor_
-// functions.
 //
-// This is not so on Mac OS, where the two-level namespace makes
-// our replacement functions invisible to other libraries. This may be overcomed
-// using the DYLD_FORCE_FLAT_NAMESPACE, but some errors loading the shared
-// libraries in Chromium were noticed when doing so.
+// There is one complication: a user may also intercept some of the functions we
+// intercept. To allow for up to 3 interceptors (including ours) of a given
+// function "func", the interceptor implementation is in ___interceptor_func,
+// which is aliased by a weak function __interceptor_func, which in turn is
+// aliased (via a trampoline) by weak wrapper function "func".
+//
+// Most user interceptors should define a foreign interceptor as follows:
+//
+//  - provide a non-weak function "func" that performs interception;
+//  - if __interceptor_func exists, call it to perform the real functionality;
+//  - if it does not exist, figure out the real function and call it instead.
+//
+// In rare cases, a foreign interceptor (of another dynamic analysis runtime)
+// may be defined as follows (on supported architectures):
+//
+//  - provide a non-weak function __interceptor_func that performs interception;
+//  - if ___interceptor_func exists, call it to perform the real functionality;
+//  - if it does not exist, figure out the real function and call it instead;
+//  - provide a weak function "func" that is an alias to __interceptor_func.
+//
+// With this protocol, sanitizer interceptors, foreign user interceptors, and
+// foreign interceptors of other dynamic analysis runtimes, or any combination
+// thereof, may co-exist simultaneously.
+//
+// How it works on Mac OS
+// ----------------------
+//
+// This is not so on Mac OS, where the two-level namespace makes our replacement
+// functions invisible to other libraries. This may be overcomed using the
+// DYLD_FORCE_FLAT_NAMESPACE, but some errors loading the shared libraries in
+// Chromium were noticed when doing so.
+//
 // Instead we create a dylib containing a __DATA,__interpose section that
 // associates library functions with their wrappers. When this dylib is
-// preloaded before an executable using DYLD_INSERT_LIBRARIES, it routes all
-// the calls to interposed functions done through stubs to the wrapper
-// functions.
+// preloaded before an executable using DYLD_INSERT_LIBRARIES, it routes all the
+// calls to interposed functions done through stubs to the wrapper functions.
+//
 // As it's decided at compile time which functions are to be intercepted on Mac,
 // INTERCEPT_FUNCTION() is effectively a no-op on this system.
 
@@ -131,20 +158,71 @@ const interpose_substitution substitution_##func_name[]             \
 # define DECLARE_WRAPPER_WINAPI(ret_type, func, ...)  \
     extern "C" __declspec(dllimport) ret_type __stdcall func(__VA_ARGS__);
 #elif !SANITIZER_FUCHSIA  // LINUX, FREEBSD, NETBSD, SOLARIS
-# define WRAP(x) __interceptor_ ## x
-# define TRAMPOLINE(x) WRAP(x)
 # define INTERCEPTOR_ATTRIBUTE __attribute__((visibility("default")))
-# if SANITIZER_FREEBSD || SANITIZER_NETBSD
+# if ASM_INTERCEPTOR_TRAMPOLINE_SUPPORT
+// Weak aliases of weak aliases do not work, therefore we need to set up a
+// trampoline function. The function "func" is a weak alias to the trampoline
+// (so that we may check if "func" was overridden), which calls the weak
+// function __interceptor_func, which in turn aliases the actual interceptor
+// implementation ___interceptor_func:
+//
+//    [wrapper "func": weak] --(alias)--> [TRAMPOLINE(func)]
+//                                                |
+//                     +--------(tail call)-------+
+//                     |
+//                     v
+//      [__interceptor_func: weak] --(alias)--> [WRAP(func)]
+//
+// We use inline assembly to define most of this, because not all compilers
+// support functions with the "naked" attribute with every architecture.
+#  define WRAP(x) ___interceptor_ ## x
+#  define TRAMPOLINE(x) __interceptor_trampoline_ ## x
+#  if SANITIZER_FREEBSD || SANITIZER_NETBSD
 // FreeBSD's dynamic linker (incompliantly) gives non-weak symbols higher
 // priority than weak ones so weak aliases won't work for indirect calls
 // in position-independent (-fPIC / -fPIE) mode.
-# define OVERRIDE_ATTRIBUTE
-# else  // SANITIZER_FREEBSD || SANITIZER_NETBSD
-# define OVERRIDE_ATTRIBUTE __attribute__((weak))
-# endif  // SANITIZER_FREEBSD || SANITIZER_NETBSD
-# define DECLARE_WRAPPER(ret_type, func, ...)                                  \
-    extern "C" ret_type func(__VA_ARGS__) INTERCEPTOR_ATTRIBUTE                \
-      OVERRIDE_ATTRIBUTE ALIAS(WRAP(func));
+#   define __ASM_WEAK_WRAPPER(func)
+#  else
+#   define __ASM_WEAK_WRAPPER(func) ".weak " #func "\n"
+#  endif  // SANITIZER_FREEBSD || SANITIZER_NETBSD
+// Keep trampoline implementation in sync with sanitizer_common/sanitizer_asm.h
+#  define DECLARE_WRAPPER(ret_type, func, ...)                                 \
+     extern "C" ret_type func(__VA_ARGS__);                                    \
+     extern "C" ret_type TRAMPOLINE(func)(__VA_ARGS__);                        \
+     extern "C" ret_type __interceptor_##func(__VA_ARGS__)                     \
+       INTERCEPTOR_ATTRIBUTE __attribute__((weak)) ALIAS(WRAP(func));          \
+     asm(                                                                      \
+       ".text\n"                                                               \
+       __ASM_WEAK_WRAPPER(func)                                                \
+       ".set " #func ", " SANITIZER_STRINGIFY(TRAMPOLINE(func)) "\n"           \
+       ".globl " SANITIZER_STRINGIFY(TRAMPOLINE(func)) "\n"                    \
+       ".type  " SANITIZER_STRINGIFY(TRAMPOLINE(func)) ", @function\n"         \
+       SANITIZER_STRINGIFY(TRAMPOLINE(func)) ":\n"                             \
+       SANITIZER_STRINGIFY(CFI_STARTPROC) "\n"                                 \
+       SANITIZER_STRINGIFY(ASM_TAIL_CALL) " __interceptor_"                    \
+         SANITIZER_STRINGIFY(ASM_PREEMPTIBLE_SYM(func)) "\n"                   \
+       SANITIZER_STRINGIFY(CFI_ENDPROC) "\n"                                   \
+       ".size  " SANITIZER_STRINGIFY(TRAMPOLINE(func)) ", "                    \
+            ".-" SANITIZER_STRINGIFY(TRAMPOLINE(func)) "\n"                    \
+     );
+# else  // ASM_INTERCEPTOR_TRAMPOLINE_SUPPORT
+// Some architectures cannot implement efficient interceptor trampolines with
+// just a plain jump due to complexities of resolving a preemptible symbol. In
+// those cases, revert to just this scheme:
+//
+//    [wrapper "func": weak] --(alias)--> [WRAP(func)]
+//
+#  define WRAP(x) __interceptor_ ## x
+#  define TRAMPOLINE(x) WRAP(x)
+#  if SANITIZER_FREEBSD || SANITIZER_NETBSD
+#   define __ATTRIBUTE_WEAK_WRAPPER
+#  else
+#   define __ATTRIBUTE_WEAK_WRAPPER __attribute__((weak))
+#  endif  // SANITIZER_FREEBSD || SANITIZER_NETBSD
+#  define DECLARE_WRAPPER(ret_type, func, ...)                                 \
+     extern "C" ret_type func(__VA_ARGS__)                                     \
+       INTERCEPTOR_ATTRIBUTE __ATTRIBUTE_WEAK_WRAPPER ALIAS(WRAP(func));
+# endif  // ASM_INTERCEPTOR_TRAMPOLINE_SUPPORT
 #endif
 
 #if SANITIZER_FUCHSIA

diff  --git a/compiler-rt/lib/interception/tests/CMakeLists.txt b/compiler-rt/lib/interception/tests/CMakeLists.txt
index f6840e194be49..0db7cfa9c7533 100644
--- a/compiler-rt/lib/interception/tests/CMakeLists.txt
+++ b/compiler-rt/lib/interception/tests/CMakeLists.txt
@@ -4,6 +4,7 @@ filter_available_targets(INTERCEPTION_UNITTEST_SUPPORTED_ARCH x86_64 i386 mips64
 
 set(INTERCEPTION_UNITTESTS
   interception_linux_test.cpp
+  interception_linux_foreign_test.cpp
   interception_test_main.cpp
   interception_win_test.cpp
   )
@@ -19,6 +20,10 @@ set(INTERCEPTION_TEST_CFLAGS_COMMON
   -I${COMPILER_RT_SOURCE_DIR}/lib/interception
   -DSANITIZER_COMMON_NO_REDEFINE_BUILTINS
   -fno-rtti
+  -fno-builtin-isdigit
+  -fno-builtin-isalpha
+  -fno-builtin-isalnum
+  -fno-builtin-islower
   -O2
   -Werror=sign-compare)
 

diff  --git a/compiler-rt/lib/interception/tests/interception_linux_foreign_test.cpp b/compiler-rt/lib/interception/tests/interception_linux_foreign_test.cpp
new file mode 100644
index 0000000000000..8d3ba9eb505e2
--- /dev/null
+++ b/compiler-rt/lib/interception/tests/interception_linux_foreign_test.cpp
@@ -0,0 +1,96 @@
+//===-- interception_linux_foreign_test.cpp -------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer/AddressSanitizer runtime.
+//
+// Tests that foreign interceptors work.
+//
+//===----------------------------------------------------------------------===//
+
+// Do not declare functions in ctype.h.
+#define __NO_CTYPE
+
+#include "gtest/gtest.h"
+#include "sanitizer_common/sanitizer_asm.h"
+#include "sanitizer_common/sanitizer_internal_defs.h"
+
+#if SANITIZER_LINUX
+
+extern "C" int isalnum(int d);
+extern "C" int __interceptor_isalpha(int d);
+extern "C" int ___interceptor_isalnum(int d);  // the sanitizer interceptor
+extern "C" int ___interceptor_islower(int d);  // the sanitizer interceptor
+
+namespace __interception {
+extern int isalpha_called;
+extern int isalnum_called;
+extern int islower_called;
+}  // namespace __interception
+using namespace __interception;
+
+// Direct foreign interceptor. This is the "normal" protocol that other
+// interceptors should follow.
+extern "C" int isalpha(int d) {
+  // Use non-commutative arithmetic to verify order of calls.
+  isalpha_called = isalpha_called * 10 + 1;
+  return __interceptor_isalpha(d);
+}
+
+#if ASM_INTERCEPTOR_TRAMPOLINE_SUPPORT
+// Indirect foreign interceptor. This pattern should only be used to co-exist
+// with direct foreign interceptors and sanitizer interceptors.
+extern "C" int __interceptor_isalnum(int d) {
+  isalnum_called = isalnum_called * 10 + 1;
+  return ___interceptor_isalnum(d);
+}
+
+extern "C" int __interceptor_islower(int d) {
+  islower_called = islower_called * 10 + 2;
+  return ___interceptor_islower(d);
+}
+
+extern "C" int islower(int d) {
+  islower_called = islower_called * 10 + 1;
+  return __interceptor_islower(d);
+}
+#endif  // ASM_INTERCEPTOR_TRAMPOLINE_SUPPORT
+
+namespace __interception {
+
+TEST(ForeignInterception, ForeignOverrideDirect) {
+  isalpha_called = 0;
+  EXPECT_NE(0, isalpha('a'));
+  EXPECT_EQ(13, isalpha_called);
+  isalpha_called = 0;
+  EXPECT_EQ(0, isalpha('_'));
+  EXPECT_EQ(13, isalpha_called);
+}
+
+#if ASM_INTERCEPTOR_TRAMPOLINE_SUPPORT
+TEST(ForeignInterception, ForeignOverrideIndirect) {
+  isalnum_called = 0;
+  EXPECT_NE(0, isalnum('a'));
+  EXPECT_EQ(13, isalnum_called);
+  isalnum_called = 0;
+  EXPECT_EQ(0, isalnum('_'));
+  EXPECT_EQ(13, isalnum_called);
+}
+
+TEST(ForeignInterception, ForeignOverrideThree) {
+  islower_called = 0;
+  EXPECT_NE(0, islower('a'));
+  EXPECT_EQ(123, islower_called);
+  islower_called = 0;
+  EXPECT_EQ(0, islower('_'));
+  EXPECT_EQ(123, islower_called);
+}
+#endif  // ASM_INTERCEPTOR_TRAMPOLINE_SUPPORT
+
+}  // namespace __interception
+
+#endif  // SANITIZER_LINUX

diff  --git a/compiler-rt/lib/interception/tests/interception_linux_test.cpp b/compiler-rt/lib/interception/tests/interception_linux_test.cpp
index c79fbf2fa8651..c204274fb3297 100644
--- a/compiler-rt/lib/interception/tests/interception_linux_test.cpp
+++ b/compiler-rt/lib/interception/tests/interception_linux_test.cpp
@@ -11,36 +11,68 @@
 //
 //===----------------------------------------------------------------------===//
 
-// Do not declare isdigit in ctype.h.
+// Do not declare functions in ctype.h.
 #define __NO_CTYPE
 
 #include "interception/interception.h"
 
+#include <stdlib.h>
+
 #include "gtest/gtest.h"
 
-// Too slow for debug build
-#if !SANITIZER_DEBUG
 #if SANITIZER_LINUX
 
-static int InterceptorFunctionCalled;
+static int isdigit_called;
+namespace __interception {
+int isalpha_called;
+int isalnum_called;
+int islower_called;
+}  // namespace __interception
+using namespace __interception;
 
 DECLARE_REAL(int, isdigit, int);
+DECLARE_REAL(int, isalpha, int);
+DECLARE_REAL(int, isalnum, int);
+DECLARE_REAL(int, islower, int);
+
+INTERCEPTOR(void *, malloc, SIZE_T s) { return calloc(1, s); }
+INTERCEPTOR(void, dummy_doesnt_exist__, ) { __builtin_trap(); }
 
 INTERCEPTOR(int, isdigit, int d) {
-  ++InterceptorFunctionCalled;
+  ++isdigit_called;
   return d >= '0' && d <= '9';
 }
 
+INTERCEPTOR(int, isalpha, int d) {
+  // Use non-commutative arithmetic to verify order of calls.
+  isalpha_called = isalpha_called * 10 + 3;
+  return (d >= 'a' && d <= 'z') || (d >= 'A' && d <= 'Z');
+}
+
+INTERCEPTOR(int, isalnum, int d) {
+  isalnum_called = isalnum_called * 10 + 3;
+  return __interceptor_isalpha(d) || __interceptor_isdigit(d);
+}
+
+INTERCEPTOR(int, islower, int d) {
+  islower_called = islower_called * 10 + 3;
+  return d >= 'a' && d <= 'z';
+}
+
 namespace __interception {
 
 TEST(Interception, InterceptFunction) {
   uptr malloc_address = 0;
-  EXPECT_TRUE(InterceptFunction("malloc", &malloc_address, 0, 0));
+  EXPECT_TRUE(InterceptFunction("malloc", &malloc_address, (uptr)&malloc,
+                                (uptr)&TRAMPOLINE(malloc)));
   EXPECT_NE(0U, malloc_address);
-  EXPECT_FALSE(InterceptFunction("malloc", &malloc_address, 0, 1));
+  EXPECT_FALSE(InterceptFunction("malloc", &malloc_address, (uptr)&calloc,
+                                 (uptr)&TRAMPOLINE(malloc)));
 
   uptr dummy_address = 0;
-  EXPECT_FALSE(InterceptFunction("dummy_doesnt_exist__", &dummy_address, 0, 0));
+  EXPECT_FALSE(InterceptFunction("dummy_doesnt_exist__", &dummy_address,
+                                 (uptr)&dummy_doesnt_exist__,
+                                 (uptr)&TRAMPOLINE(dummy_doesnt_exist__)));
   EXPECT_EQ(0U, dummy_address);
 }
 
@@ -48,20 +80,72 @@ TEST(Interception, Basic) {
   EXPECT_TRUE(INTERCEPT_FUNCTION(isdigit));
 
   // After interception, the counter should be incremented.
-  InterceptorFunctionCalled = 0;
+  isdigit_called = 0;
   EXPECT_NE(0, isdigit('1'));
-  EXPECT_EQ(1, InterceptorFunctionCalled);
+  EXPECT_EQ(1, isdigit_called);
   EXPECT_EQ(0, isdigit('a'));
-  EXPECT_EQ(2, InterceptorFunctionCalled);
+  EXPECT_EQ(2, isdigit_called);
 
   // Calling the REAL function should not affect the counter.
-  InterceptorFunctionCalled = 0;
+  isdigit_called = 0;
   EXPECT_NE(0, REAL(isdigit)('1'));
   EXPECT_EQ(0, REAL(isdigit)('a'));
-  EXPECT_EQ(0, InterceptorFunctionCalled);
+  EXPECT_EQ(0, isdigit_called);
+}
+
+TEST(Interception, ForeignOverrideDirect) {
+  // Actual interceptor is overridden.
+  EXPECT_FALSE(INTERCEPT_FUNCTION(isalpha));
+
+  isalpha_called = 0;
+  EXPECT_NE(0, isalpha('a'));
+  EXPECT_EQ(13, isalpha_called);
+  isalpha_called = 0;
+  EXPECT_EQ(0, isalpha('_'));
+  EXPECT_EQ(13, isalpha_called);
+
+  isalpha_called = 0;
+  EXPECT_NE(0, REAL(isalpha)('a'));
+  EXPECT_EQ(0, REAL(isalpha)('_'));
+  EXPECT_EQ(0, isalpha_called);
+}
+
+#if ASM_INTERCEPTOR_TRAMPOLINE_SUPPORT
+TEST(Interception, ForeignOverrideIndirect) {
+  // Actual interceptor is _not_ overridden.
+  EXPECT_TRUE(INTERCEPT_FUNCTION(isalnum));
+
+  isalnum_called = 0;
+  EXPECT_NE(0, isalnum('a'));
+  EXPECT_EQ(13, isalnum_called);
+  isalnum_called = 0;
+  EXPECT_EQ(0, isalnum('_'));
+  EXPECT_EQ(13, isalnum_called);
+
+  isalnum_called = 0;
+  EXPECT_NE(0, REAL(isalnum)('a'));
+  EXPECT_EQ(0, REAL(isalnum)('_'));
+  EXPECT_EQ(0, isalnum_called);
+}
+
+TEST(Interception, ForeignOverrideThree) {
+  // Actual interceptor is overridden.
+  EXPECT_FALSE(INTERCEPT_FUNCTION(islower));
+
+  islower_called = 0;
+  EXPECT_NE(0, islower('a'));
+  EXPECT_EQ(123, islower_called);
+  islower_called = 0;
+  EXPECT_EQ(0, islower('A'));
+  EXPECT_EQ(123, islower_called);
+
+  islower_called = 0;
+  EXPECT_NE(0, REAL(islower)('a'));
+  EXPECT_EQ(0, REAL(islower)('A'));
+  EXPECT_EQ(0, islower_called);
 }
+#endif  // ASM_INTERCEPTOR_TRAMPOLINE_SUPPORT
 
 }  // namespace __interception
 
 #endif  // SANITIZER_LINUX
-#endif  // #if !SANITIZER_DEBUG

diff  --git a/compiler-rt/lib/sanitizer_common/sanitizer_asm.h b/compiler-rt/lib/sanitizer_common/sanitizer_asm.h
index 632c4bdd4c262..fc96847b5c211 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_asm.h
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_asm.h
@@ -42,17 +42,56 @@
 # define CFI_RESTORE(reg)
 #endif
 
+#if defined(__x86_64__) || defined(__i386__) || defined(__sparc__)
+# define ASM_TAIL_CALL jmp
+#elif defined(__arm__) || defined(__aarch64__) || defined(__mips__) || \
+    defined(__powerpc__) || defined(__loongarch_lp64)
+# define ASM_TAIL_CALL b
+#elif defined(__s390__)
+# define ASM_TAIL_CALL jg
+#elif defined(__riscv)
+# define ASM_TAIL_CALL tail
+#endif
+
+#if defined(__ELF__) && defined(__x86_64__) || defined(__i386__) || \
+    defined(__riscv)
+# define ASM_PREEMPTIBLE_SYM(sym) sym at plt
+#else
+# define ASM_PREEMPTIBLE_SYM(sym) sym
+#endif
+
 #if !defined(__APPLE__)
 # define ASM_HIDDEN(symbol) .hidden symbol
 # define ASM_TYPE_FUNCTION(symbol) .type symbol, %function
 # define ASM_SIZE(symbol) .size symbol, .-symbol
 # define ASM_SYMBOL(symbol) symbol
 # define ASM_SYMBOL_INTERCEPTOR(symbol) symbol
-# define ASM_WRAPPER_NAME(symbol) __interceptor_##symbol
-# define ASM_TRAMPOLINE_ALIAS(symbol, name)                     \
-        .weak symbol;                                           \
-        .set symbol, ASM_WRAPPER_NAME(name)
-# define ASM_INTERCEPTOR_TRAMPOLINE(name)
+# if defined(__i386__) || defined(__powerpc__)
+// For details, see interception.h
+#  define ASM_WRAPPER_NAME(symbol) __interceptor_##symbol
+#  define ASM_TRAMPOLINE_ALIAS(symbol, name)                                   \
+         .weak symbol;                                                         \
+         .set symbol, ASM_WRAPPER_NAME(name)
+#  define ASM_INTERCEPTOR_TRAMPOLINE(name)
+#  define ASM_INTERCEPTOR_TRAMPOLINE_SUPPORT 0
+# else  // Architecture supports interceptor trampoline
+// Keep trampoline implementation in sync with interception/interception.h
+#  define ASM_WRAPPER_NAME(symbol) ___interceptor_##symbol
+#  define ASM_TRAMPOLINE_ALIAS(symbol, name)                                   \
+         .weak symbol;                                                         \
+         .set symbol, __interceptor_trampoline_##name
+#  define ASM_INTERCEPTOR_TRAMPOLINE(name)                                     \
+         .weak __interceptor_##name;                                           \
+         .set __interceptor_##name, ASM_WRAPPER_NAME(name);                    \
+         .globl __interceptor_trampoline_##name;                               \
+         ASM_TYPE_FUNCTION(__interceptor_trampoline_##name);                   \
+         __interceptor_trampoline_##name:                                      \
+                 CFI_STARTPROC;                                                \
+                 ASM_TAIL_CALL ASM_PREEMPTIBLE_SYM(__interceptor_##name);      \
+                 CFI_ENDPROC;                                                  \
+         ASM_SIZE(__interceptor_trampoline_##name)
+#  define ASM_INTERCEPTOR_TRAMPOLINE_SUPPORT 1
+# endif  // Architecture supports interceptor trampoline
 #else
 # define ASM_HIDDEN(symbol)
 # define ASM_TYPE_FUNCTION(symbol)

diff  --git a/compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors.inc b/compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors.inc
index e01dc4c87ffb8..de307ff4e3061 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors.inc
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors.inc
@@ -5426,16 +5426,20 @@ extern "C" __attribute__((visibility("hidden"))) uptr __tls_get_addr_hidden(
 extern "C" uptr __tls_get_offset(void *arg);
 extern "C" uptr TRAMPOLINE(__tls_get_offset)(void *arg);
 extern "C" uptr WRAP(__tls_get_offset)(void *arg);
-// Now carefully intercept __tls_get_offset.
+// Now carefully intercept __tls_get_offset. See DECLARE_WRAPPER for details.
 asm(
   ".text\n"
-// The __intercept_ version has to exist, so that gen_dynamic_list.py
-// exports our symbol.
   ".weak __tls_get_offset\n"
-  ".set __tls_get_offset, __interceptor___tls_get_offset\n"
-  ".global __interceptor___tls_get_offset\n"
-  ".type __interceptor___tls_get_offset, @function\n"
-  "__interceptor___tls_get_offset:\n"
+  ".set __tls_get_offset, __interceptor_trampoline___tls_get_offset\n"
+  ".global __interceptor_trampoline___tls_get_offset\n"
+  ".type __interceptor_trampoline___tls_get_offset, @function\n"
+  "__interceptor_trampoline___tls_get_offset:\n"
+  "jg __interceptor___tls_get_offset\n"
+  ".weak __interceptor___tls_get_offset\n"
+  ".set __interceptor___tls_get_offset, ___interceptor___tls_get_offset\n"
+  ".global ___interceptor___tls_get_offset\n"
+  ".type ___interceptor___tls_get_offset, @function\n"
+  "___interceptor___tls_get_offset:\n"
 #ifdef __s390x__
   "la %r2, 0(%r2,%r12)\n"
   "jg __tls_get_addr_hidden\n"
@@ -5446,7 +5450,7 @@ asm(
   "b 0(%r4,%r3)\n"
   "1: .long __tls_get_addr_hidden - 0b\n"
 #endif
-  ".size __interceptor___tls_get_offset, .-__interceptor___tls_get_offset\n"
+  ".size ___interceptor___tls_get_offset, .-___interceptor___tls_get_offset\n"
 // Assembly wrapper to call REAL(__tls_get_offset)(arg)
   ".type __tls_get_offset_wrapper, @function\n"
   "__tls_get_offset_wrapper:\n"

diff  --git a/compiler-rt/lib/sanitizer_common/sanitizer_stacktrace_printer.cpp b/compiler-rt/lib/sanitizer_common/sanitizer_stacktrace_printer.cpp
index f6e1edc102968..45c480d225c7f 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_stacktrace_printer.cpp
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_stacktrace_printer.cpp
@@ -36,6 +36,8 @@ const char *StripFunctionName(const char *function) {
     if (const char *s = try_strip("__asan_wrap_"))
       return s;
   } else {
+    if (const char *s = try_strip("___interceptor_"))
+      return s;
     if (const char *s = try_strip("__interceptor_"))
       return s;
   }

diff  --git a/compiler-rt/lib/sanitizer_common/scripts/gen_dynamic_list.py b/compiler-rt/lib/sanitizer_common/scripts/gen_dynamic_list.py
index 8c0a1ea84a8d5..fdbceae165e0d 100755
--- a/compiler-rt/lib/sanitizer_common/scripts/gen_dynamic_list.py
+++ b/compiler-rt/lib/sanitizer_common/scripts/gen_dynamic_list.py
@@ -113,7 +113,7 @@ def main(argv):
     parser.add_argument("-o", "--output", required=True)
     args = parser.parse_args()
 
-    result = []
+    result = set()
 
     all_functions = []
     for library in args.libraries:
@@ -122,36 +122,35 @@ def main(argv):
     for func in all_functions:
         # Export new/delete operators.
         if func in new_delete:
-            result.append(func)
+            result.add(func)
             continue
         # Export interceptors.
-        match = re.match("__interceptor_(.*)", func)
+        match = re.match("_?__interceptor_(.*)", func)
         if match:
-            result.append(func)
+            result.add(func)
             # We have to avoid exporting the interceptors for versioned library
             # functions due to gold internal error.
             orig_name = match.group(1)
             if orig_name in function_set and (
                 args.version_list or orig_name not in versioned_functions
             ):
-                result.append(orig_name)
+                result.add(orig_name)
             continue
         # Export sanitizer interface functions.
         if re.match("__sanitizer_(.*)", func):
-            result.append(func)
+            result.add(func)
 
     # Additional exported functions from files.
     for fname in args.extra:
         f = open(fname, "r")
         for line in f:
-            result.append(line.rstrip())
+            result.add(line.rstrip())
     # Print the resulting list in the format recognized by ld.
     with open(args.output, "w") as f:
         print("{", file=f)
         if args.version_list:
             print("global:", file=f)
-        result.sort()
-        for sym in result:
+        for sym in sorted(result):
             print("  %s;" % sym, file=f)
         if args.version_list:
             print("local:", file=f)

diff  --git a/compiler-rt/test/asan/TestCases/Posix/asan-symbolize-sanity-test.cpp b/compiler-rt/test/asan/TestCases/Posix/asan-symbolize-sanity-test.cpp
index 6e24a08adfed0..5d3e7a767e45a 100644
--- a/compiler-rt/test/asan/TestCases/Posix/asan-symbolize-sanity-test.cpp
+++ b/compiler-rt/test/asan/TestCases/Posix/asan-symbolize-sanity-test.cpp
@@ -41,7 +41,7 @@ int main(int argc, char *argv[]) {
   // CHECK: #0 {{.*}} in inc2 {{.*}}asan-symbolize-sanity-test.cpp:[[@LINE+21]]
   // CHECK: #1 {{.*}} in main {{.*}}asan-symbolize-sanity-test.cpp:[[@LINE-4]]
   // CHECK: allocated by thread T{{.*}} here:
-  // CHECK: #{{.*}} in {{(wrap_|__interceptor_)?}}malloc
+  // CHECK: #{{.*}} in {{(wrap_|_?__interceptor_)?}}malloc
   // CHECK: #{{.*}} in main {{.*}}asan-symbolize-sanity-test.cpp:[[@LINE-9]]
   return 0;
 }


        


More information about the llvm-commits mailing list