PR #2136: Give externally-provided symbol declarations default visibility Imported from GitHub PR https://github.com/abseil/abseil-cpp/pull/2136 Abseil forward-declares a few symbols that are provided by something *outside* the object being linked: | Symbol(s) | File | Provided by | |---|---|---| | 14x `Annotate*` race annotations | `absl/base/dynamic_annotations.h` | sanitizer runtime | | `__tsan_read1` | `absl/synchronization/mutex.cc` | sanitizer runtime | | `__mmap2` | `absl/base/internal/direct_mmap.h` | Bionic | None of these has a public header to include — none of the `Annotate*` functions nor `__tsan_read1` appears in any compiler-rt `sanitizer/*.h`, and `__mmap2` is a private Bionic symbol — so declaring them locally is the only option. But a local declaration inherits whatever visibility is in effect at that point. When the translation unit is compiled with `-fvisibility=hidden` and abseil is linked into a shared library, those references become *hidden* undefined symbols. They then cannot be resolved from the sanitizer runtime (which lives in the executable) or from libc, and the link fails: ``` ld.lld: error: undefined hidden symbol: AnnotateIgnoreReadsBegin ld.lld: error: undefined hidden symbol: AnnotateIgnoreWritesBegin ld.lld: error: undefined hidden symbol: AnnotateIgnoreWritesEnd ld.lld: error: undefined hidden symbol: AnnotateIgnoreReadsEnd ld.lld: error: undefined hidden symbol: __tsan_read1 ld.lld: error: undefined hidden symbol: __mmap2 ``` This wraps each declaration in `#pragma GCC visibility push(default)` / `pop`. ### Verification Compiling all ~163 abseil sources with `-fsanitize=thread` and a tree-wide `#pragma GCC visibility push(hidden)`, then scanning every object for hidden undefined symbols: before this change the four `Annotate*` and `__tsan_read1` are `GLOBAL HIDDEN UND`; after, every `__tsan_*` and `Annotate*` reference is `GLOBAL DEFAULT UND`. The only hidden undefined symbols remaining are `AbslInternal*` ones that abseil defines itself, which is correct. For `__mmap2`, on a 32-bit Android shared-library link: ``` without the pragma: ld.lld: error: undefined hidden symbol: __mmap2 with the pragma: 4: FUNC GLOBAL DEFAULT UND __mmap2@LIBC ``` ### Notes - Every affected site is already inside a Bionic or sanitizer guard, so the GCC/Clang-only pragma does not reduce portability. - The `__mmap2` path only compiles on 32-bit architectures, which is why this is rarely hit — Chromium's Android builds are 64-bit. We hit it on 32-bit ARM. - Found while updating the copy of abseil vendored into Firefox, which builds with `-fvisibility=hidden` tree-wide. Merge ab78d3a7f471fdaa44f6b7bc5feece81cd1d2833 into e1cacc79e6a318108eded02261219ceabff38ce0 Merging this change closes #2136 PiperOrigin-RevId: 975244998 Change-Id: I1be645e8658b7a323fdcc34f081d3466cd9eb6ed
diff --git a/absl/base/dynamic_annotations.h b/absl/base/dynamic_annotations.h index f18b5e0..1e679a6 100644 --- a/absl/base/dynamic_annotations.h +++ b/absl/base/dynamic_annotations.h
@@ -202,6 +202,7 @@ // Function prototypes of annotations provided by the compiler-based sanitizer // implementation. +#pragma GCC visibility push(default) ABSL_INTERNAL_BEGIN_EXTERN_C void AnnotateRWLockCreate(const char* file, int line, const volatile void* lock); @@ -221,6 +222,7 @@ void AnnotateThreadName(const char* file, int line, const char* name); void AnnotateEnableRaceDetection(const char* file, int line, int enable); ABSL_INTERNAL_END_EXTERN_C +#pragma GCC visibility pop #else // ABSL_INTERNAL_RACE_ANNOTATIONS_ENABLED == 0 @@ -297,12 +299,14 @@ // Function prototypes of annotations provided by the compiler-based sanitizer // implementation. +#pragma GCC visibility push(default) ABSL_INTERNAL_BEGIN_EXTERN_C void AnnotateIgnoreReadsBegin(const char* file, int line) ABSL_INTERNAL_IGNORE_READS_BEGIN_ATTRIBUTE; void AnnotateIgnoreReadsEnd(const char* file, int line) ABSL_INTERNAL_IGNORE_READS_END_ATTRIBUTE; ABSL_INTERNAL_END_EXTERN_C +#pragma GCC visibility pop #elif defined(ABSL_INTERNAL_ANNOTALYSIS_ENABLED) @@ -353,10 +357,12 @@ // Function prototypes of annotations provided by the compiler-based sanitizer // implementation. +#pragma GCC visibility push(default) ABSL_INTERNAL_BEGIN_EXTERN_C void AnnotateIgnoreWritesBegin(const char* file, int line); void AnnotateIgnoreWritesEnd(const char* file, int line); ABSL_INTERNAL_END_EXTERN_C +#pragma GCC visibility pop #else
diff --git a/absl/base/internal/direct_mmap.h b/absl/base/internal/direct_mmap.h index f56ba23..855f51e 100644 --- a/absl/base/internal/direct_mmap.h +++ b/absl/base/internal/direct_mmap.h
@@ -54,7 +54,9 @@ // SYS_mmap and SYS_munmap are not defined in Android. #ifdef __BIONIC__ +#pragma GCC visibility push(default) extern "C" void* __mmap2(void*, size_t, int, int, int, size_t); +#pragma GCC visibility pop #if defined(__NR_mmap) && !defined(SYS_mmap) #define SYS_mmap __NR_mmap #endif
diff --git a/absl/synchronization/mutex.cc b/absl/synchronization/mutex.cc index be0d91a..d6e2aac 100644 --- a/absl/synchronization/mutex.cc +++ b/absl/synchronization/mutex.cc
@@ -2788,7 +2788,9 @@ } #ifdef ABSL_HAVE_THREAD_SANITIZER +#pragma GCC visibility push(default) extern "C" void __tsan_read1(void* addr); +#pragma GCC visibility pop #else #define __tsan_read1(addr) // do nothing if TSan not enabled #endif