[sanitizer] Use confstr to check libc version in InitTlsSize

Reviewers: Lekensteyn, jakubjelinek

Subscribers: kubamracek, llvm-commits

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

git-svn-id: https://llvm.org/svn/llvm-project/compiler-rt/trunk@334912 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/msan/msan.cc b/lib/msan/msan.cc
index 8bd2174..06bcbdf 100644
--- a/lib/msan/msan.cc
+++ b/lib/msan/msan.cc
@@ -395,7 +395,6 @@
   SanitizerToolName = "MemorySanitizer";
 
   AvoidCVE_2016_2143();
-  InitTlsSize();
 
   CacheBinaryName();
   CheckASLR();
@@ -407,6 +406,7 @@
   __sanitizer_set_report_path(common_flags()->log_path);
 
   InitializeInterceptors();
+  InitTlsSize();
   InstallDeadlySignalHandlers(MsanOnDeadlySignal);
   InstallAtExitHandler(); // Needs __cxa_atexit interceptor.
 
diff --git a/lib/sanitizer_common/sanitizer_linux_libcdep.cc b/lib/sanitizer_common/sanitizer_linux_libcdep.cc
index 73c2141..70599b0 100644
--- a/lib/sanitizer_common/sanitizer_linux_libcdep.cc
+++ b/lib/sanitizer_common/sanitizer_linux_libcdep.cc
@@ -193,6 +193,44 @@
   CHECK_NE(get_tls, 0);
   get_tls(size, align);
 }
+
+bool GetLibcVersion(int *major, int *minor, int *patch) {
+#ifdef _CS_GNU_LIBC_VERSION
+  char buf[64];
+  uptr len = confstr(_CS_GNU_LIBC_VERSION, buf, sizeof(buf));
+  if (len >= sizeof(buf))
+    return false;
+  buf[len] = 0;
+  static const char kGLibC[] = "glibc ";
+  if (internal_strncmp(buf, kGLibC, sizeof(kGLibC) - 1) != 0)
+    return false;
+  const char *p = buf + sizeof(kGLibC) - 1;
+  *major = internal_simple_strtoll(p, &p, 10);
+  *minor = (*p == '.') ? internal_simple_strtoll(p + 1, &p, 10) : 0;
+  *patch = (*p == '.') ? internal_simple_strtoll(p + 1, &p, 10) : 0;
+  return true;
+#else
+  return false;
+#endif
+}
+
+bool CmpLibcVersion(int major, int minor, int patch) {
+  int ma;
+  int mi;
+  int pa;
+  if (!GetLibcVersion(&ma, &mi, &pa))
+    return false;
+  if (ma > major)
+    return true;
+  if (ma < major)
+    return false;
+  if (mi > minor)
+    return true;
+  if (mi < minor)
+    return false;
+  return pa >= patch;
+}
+
 }  // namespace
 
 void InitTlsSize() {
@@ -204,8 +242,7 @@
   // On i?86, _dl_get_tls_static_info used to be internal_function, i.e.
   // __attribute__((regparm(3), stdcall)) before glibc 2.27 and is normal
   // function in 2.27 and later.
-  if (CHECK_GET_TLS_STATIC_INFO_VERSION &&
-      !dlvsym(RTLD_NEXT, "glob", "GLIBC_2.27"))
+  if (CHECK_GET_TLS_STATIC_INFO_VERSION && !CmpLibcVersion(2, 27, 0))
     CallGetTls<GetTlsStaticInfoRegparmCall>(get_tls_static_info_ptr,
                                             &tls_size, &tls_align);
   else
@@ -232,43 +269,33 @@
   if (val)
     return val;
 #if defined(__x86_64__) || defined(__i386__) || defined(__arm__)
-#ifdef _CS_GNU_LIBC_VERSION
-  char buf[64];
-  uptr len = confstr(_CS_GNU_LIBC_VERSION, buf, sizeof(buf));
-  if (len < sizeof(buf) && internal_strncmp(buf, "glibc 2.", 8) == 0) {
-    const char *end;
-    int minor = internal_simple_strtoll(buf + 8, &end, 10);
-    if (end != buf + 8 && (*end == '\0' || *end == '.' || *end == '-')) {
-      int patch = 0;
-      if (*end == '.')
-        // strtoll will return 0 if no valid conversion could be performed
-        patch = internal_simple_strtoll(end + 1, nullptr, 10);
-
-      /* sizeof(struct pthread) values from various glibc versions.  */
-      if (SANITIZER_X32)
-        val = 1728;  // Assume only one particular version for x32.
-      // For ARM sizeof(struct pthread) changed in Glibc 2.23.
-      else if (SANITIZER_ARM)
-        val = minor <= 22 ? 1120 : 1216;
-      else if (minor <= 3)
-        val = FIRST_32_SECOND_64(1104, 1696);
-      else if (minor == 4)
-        val = FIRST_32_SECOND_64(1120, 1728);
-      else if (minor == 5)
-        val = FIRST_32_SECOND_64(1136, 1728);
-      else if (minor <= 9)
-        val = FIRST_32_SECOND_64(1136, 1712);
-      else if (minor == 10)
-        val = FIRST_32_SECOND_64(1168, 1776);
-      else if (minor == 11 || (minor == 12 && patch == 1))
-        val = FIRST_32_SECOND_64(1168, 2288);
-      else if (minor <= 13)
-        val = FIRST_32_SECOND_64(1168, 2304);
-      else
-        val = FIRST_32_SECOND_64(1216, 2304);
-    }
+  int major;
+  int minor;
+  int patch;
+  if (GetLibcVersion(&major, &minor, &patch) && major == 2) {
+    /* sizeof(struct pthread) values from various glibc versions.  */
+    if (SANITIZER_X32)
+      val = 1728; // Assume only one particular version for x32.
+    // For ARM sizeof(struct pthread) changed in Glibc 2.23.
+    else if (SANITIZER_ARM)
+      val = minor <= 22 ? 1120 : 1216;
+    else if (minor <= 3)
+      val = FIRST_32_SECOND_64(1104, 1696);
+    else if (minor == 4)
+      val = FIRST_32_SECOND_64(1120, 1728);
+    else if (minor == 5)
+      val = FIRST_32_SECOND_64(1136, 1728);
+    else if (minor <= 9)
+      val = FIRST_32_SECOND_64(1136, 1712);
+    else if (minor == 10)
+      val = FIRST_32_SECOND_64(1168, 1776);
+    else if (minor == 11 || (minor == 12 && patch == 1))
+      val = FIRST_32_SECOND_64(1168, 2288);
+    else if (minor <= 13)
+      val = FIRST_32_SECOND_64(1168, 2304);
+    else
+      val = FIRST_32_SECOND_64(1216, 2304);
   }
-#endif
 #elif defined(__mips__)
   // TODO(sagarthakur): add more values as per different glibc versions.
   val = FIRST_32_SECOND_64(1152, 1776);