This is an automated email from the ASF dual-hosted git repository.

yiguolei pushed a commit to branch tmp_4.1.4-rc04
in repository https://gitbox.apache.org/repos/asf/doris.git

commit ad35a140c7fd0b842f18c23300bac581f7d04326
Author: yiguolei <[email protected]>
AuthorDate: Wed Sep 9 21:18:19 2026 +0800

    [bugfix](glibc) fix glibc compatible bugs (#67694)
    
    ### What problem does this PR solve?
    pick https://github.com/apache/doris/pull/67701/changes
    
    Rust std weak-links this glibc 2.18 entry point and has an internal
    fallback when it is absent. Since the LDB sysroot exposes it, the final
    linker would otherwise record GLIBC_2.18.
    
    See https://github.com/rust-lang/rust/issues/57497 for more details.
    
    Problem Summary:
    
    ### Release note
    
    None
    
    ### Check List (For Author)
    
    - Test <!-- At least one of them must be included. -->
        - [ ] Regression test
        - [ ] Unit Test
        - [ ] Manual test (add detailed scripts or steps below)
        - [ ] No need to test or manual test. Explain why:
    - [ ] This is a refactor/code format and no logic has been changed.
            - [ ] Previous test can cover this change.
            - [ ] No code files have been changed.
            - [ ] Other reason <!-- Add your reason?  -->
    
    - Behavior changed:
        - [ ] No.
        - [ ] Yes. <!-- Explain the behavior change -->
    
    - Does this need documentation?
        - [ ] No.
    - [ ] Yes. <!-- Add document PR link here. eg:
    https://github.com/apache/doris-website/pull/1214 -->
    
    ### Check List (For Reviewer who merge this PR)
    
    - [ ] Confirm the release note
    - [ ] Confirm test cases
    - [ ] Confirm document
    - [ ] Add branch pick label <!-- Add branch pick label that this PR
    should merge into -->
---
 be/CMakeLists.txt                                  |  10 ++
 be/cmake/check_glibc_compatibility.cmake           |  70 +++++++++++++
 be/src/glibc-compatibility/lance_symbol_versions.c | 115 ++++++++++++++++++++-
 be/src/glibc-compatibility/musl/expf.c             |  80 ++++++++++++++
 be/src/service/CMakeLists.txt                      |  22 +++-
 5 files changed, 293 insertions(+), 4 deletions(-)

diff --git a/be/CMakeLists.txt b/be/CMakeLists.txt
index 389c3883b29..555b2508f9d 100644
--- a/be/CMakeLists.txt
+++ b/be/CMakeLists.txt
@@ -725,7 +725,17 @@ if (GLIBC_COMPATIBILITY)
     # Keep lance_c here instead of COMMON_THIRDPARTY: placing its required 
libm there
     # would resolve -lm symbol before Doris compatibility is scanned, 
preventing
     # the linker from selecting Doris' optimized implementations.
+    # lance_c can also introduce single-precision math references after the
+    # compatibility archive has already been scanned. As ClickHouse does for 
its
+    # compatibility archive, force the GLIBC_2.27 math symbols to be undefined 
up
+    # front so the linker extracts Doris' musl implementations instead of 
binding
+    # late references to the toolchain's libm.
     set(DORIS_LINK_LIBS ${DORIS_LINK_LIBS}
+        -Wl,-u,logf
+        -Wl,-u,powf
+        -Wl,-u,expf
+        -Wl,-u,exp2f
+        -Wl,-u,log2f
         glibc-compatibility-explicit
         glibc-compatibility
         -lm
diff --git a/be/cmake/check_glibc_compatibility.cmake 
b/be/cmake/check_glibc_compatibility.cmake
new file mode 100644
index 00000000000..435419bf07d
--- /dev/null
+++ b/be/cmake/check_glibc_compatibility.cmake
@@ -0,0 +1,70 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+if (NOT DEFINED ARTIFACT OR NOT EXISTS "${ARTIFACT}")
+    message(FATAL_ERROR "Cannot check glibc compatibility: '${ARTIFACT}' does 
not exist")
+endif()
+
+if (NOT DEFINED BASELINE OR BASELINE STREQUAL "")
+    message(FATAL_ERROR "Cannot check glibc compatibility: BASELINE is not 
set")
+endif()
+
+if (NOT DEFINED OBJDUMP OR OBJDUMP STREQUAL "" OR NOT EXISTS "${OBJDUMP}")
+    find_program(OBJDUMP NAMES objdump llvm-objdump REQUIRED)
+endif()
+
+execute_process(
+    COMMAND "${OBJDUMP}" -T "${ARTIFACT}"
+    RESULT_VARIABLE objdump_result
+    OUTPUT_VARIABLE dynamic_symbol_table
+    ERROR_VARIABLE objdump_error)
+
+if (NOT objdump_result EQUAL 0)
+    message(FATAL_ERROR
+        "Cannot read the dynamic symbol table from '${ARTIFACT}': 
${objdump_error}")
+endif()
+
+string(REPLACE "\n" ";" dynamic_symbol_lines "${dynamic_symbol_table}")
+set(incompatible_symbols)
+
+foreach (symbol_line IN LISTS dynamic_symbol_lines)
+    # Only undefined symbols are runtime requirements on the target system.
+    if (symbol_line MATCHES "\\*UND\\*")
+        string(REGEX MATCH "GLIBC_[0-9]+(\\.[0-9]+)+" symbol_version 
"${symbol_line}")
+        if (NOT "${symbol_version}" STREQUAL "")
+            string(REGEX REPLACE "^GLIBC_" "" numeric_version 
"${symbol_version}")
+            if ("${numeric_version}" VERSION_GREATER "${BASELINE}")
+                string(REGEX MATCH "[^ \t]+$" symbol_name "${symbol_line}")
+                list(APPEND incompatible_symbols "${symbol_version} 
${symbol_name}")
+            endif()
+        endif()
+    endif()
+endforeach()
+
+if (NOT "${incompatible_symbols}" STREQUAL "")
+    list(REMOVE_DUPLICATES incompatible_symbols)
+    list(SORT incompatible_symbols)
+    string(JOIN "\n  " formatted_symbols ${incompatible_symbols})
+    message(FATAL_ERROR
+        "${ARTIFACT} requires glibc symbols newer than GLIBC_${BASELINE}:\n"
+        "  ${formatted_symbols}\n"
+        "The production BE must remain runnable on CentOS 7 (glibc 
${BASELINE}).")
+endif()
+
+message(STATUS
+    "glibc compatibility check passed: ${ARTIFACT} requires no symbols newer 
than "
+    "GLIBC_${BASELINE}")
diff --git a/be/src/glibc-compatibility/lance_symbol_versions.c 
b/be/src/glibc-compatibility/lance_symbol_versions.c
index b0594dce06b..66b9cdae5cb 100644
--- a/be/src/glibc-compatibility/lance_symbol_versions.c
+++ b/be/src/glibc-compatibility/lance_symbol_versions.c
@@ -18,8 +18,12 @@
 #define _GNU_SOURCE
 
 #include <fcntl.h>
+#include <pthread.h>
 #include <spawn.h>
+#include <stdlib.h>
+#include <sys/syscall.h>
 #include <sys/uio.h>
+#include <unistd.h>
 
 #if defined(__x86_64__)
 #define DORIS_GLIBC_BASE_VERSION "GLIBC_2.2.5"
@@ -41,6 +45,76 @@
 // glibc symbol, so it cannot recurse back into the hidden wrapper.
 #define DORIS_HIDDEN __attribute__((visibility("hidden")))
 
+typedef void (*doris_tls_destructor)(void*);
+
+struct doris_tls_destructor_entry {
+    doris_tls_destructor destructor;
+    void* object;
+    struct doris_tls_destructor_entry* next;
+};
+
+static pthread_key_t doris_tls_destructor_key;
+static pthread_once_t doris_tls_destructor_once = PTHREAD_ONCE_INIT;
+
+static void doris_run_tls_destructors(void* value) {
+    struct doris_tls_destructor_entry* entry = value;
+    while (entry != NULL) {
+        struct doris_tls_destructor_entry* next = entry->next;
+        doris_tls_destructor destructor = entry->destructor;
+        void* object = entry->object;
+
+        // Publish the remainder before invoking the destructor. A destructor
+        // may register another TLS destructor, which must run before the older
+        // entries that are still pending.
+        if (pthread_setspecific(doris_tls_destructor_key, next) != 0) {
+            abort();
+        }
+        free(entry);
+        destructor(object);
+        entry = pthread_getspecific(doris_tls_destructor_key);
+    }
+}
+
+static void doris_run_main_thread_tls_destructors(void) {
+    doris_run_tls_destructors(pthread_getspecific(doris_tls_destructor_key));
+}
+
+static void doris_init_tls_destructor_key(void) {
+    if (pthread_key_create(&doris_tls_destructor_key, 
doris_run_tls_destructors) != 0) {
+        abort();
+    }
+    // pthread_key_create() arranges for doris_run_tls_destructors() to be
+    // called automatically when an ordinary worker thread exits. However,
+    // returning from main() (which is equivalent to exit()) does not run the
+    // initial thread's pthread key destructors. Register an atexit handler so
+    // TLS destructors belonging to the thread that performs normal process
+    // termination are still invoked.
+    //
+    // This relies on Doris normally terminating the process from its initial
+    // thread. If another thread calls exit(), atexit handlers execute in that
+    // thread and pthread_getspecific() observes that thread's TLS state.
+    // atexit handlers are not invoked by abort(), _exit(), fatal signals, or
+    // SIGKILL; those paths are already abnormal process termination.
+    //
+    // Unlike glibc, this fallback puts the initial thread's TLS cleanup in the
+    // same LIFO list as atexit callbacks and static-object destructors. A 
static
+    // object initialized after this handler is registered is therefore 
destroyed
+    // before the TLS objects. We accept this limitation because Doris normally
+    // terminates with _exit(), and normal destructor processing is currently 
used
+    // only when enable_graceful_exit_check is enabled for sanitizer leak 
checks.
+    // TLS destructors on that diagnostic path must not access static-lifetime
+    // objects that may already have been destroyed. If graceful exit becomes a
+    // production path, the initial thread's TLS destructors must instead be 
run
+    // explicitly before the atexit/static-destructor list.
+    //
+    // A registration failure means that normal main-thread TLS cleanup cannot
+    // be guaranteed, so fail immediately instead of continuing with a 
partially
+    // installed compatibility implementation.
+    if (atexit(doris_run_main_thread_tls_destructors) != 0) {
+        abort();
+    }
+}
+
 extern __typeof__(posix_spawnp) __doris_old_posix_spawnp;
 DORIS_GLIBC_SYMVER(__doris_old_posix_spawnp, posix_spawnp, 
DORIS_GLIBC_BASE_VERSION);
 
@@ -53,8 +127,8 @@ 
DORIS_GLIBC_SYMVER(__doris_old_posix_spawn_file_actions_destroy, posix_spawn_fil
                    DORIS_GLIBC_BASE_VERSION);
 
 extern __typeof__(posix_spawn_file_actions_adddup2) 
__doris_old_posix_spawn_file_actions_adddup2;
-DORIS_GLIBC_SYMVER(__doris_old_posix_spawn_file_actions_adddup2,
-                   posix_spawn_file_actions_adddup2, DORIS_GLIBC_BASE_VERSION);
+DORIS_GLIBC_SYMVER(__doris_old_posix_spawn_file_actions_adddup2, 
posix_spawn_file_actions_adddup2,
+                   DORIS_GLIBC_BASE_VERSION);
 
 extern __typeof__(preadv) __doris_old_preadv;
 DORIS_GLIBC_SYMVER(__doris_old_preadv, preadv, DORIS_GLIBC_PREADV_VERSION);
@@ -90,3 +164,40 @@ DORIS_HIDDEN ssize_t splice(int fd_in, off64_t* offset_in, 
int fd_out, off64_t*
                             size_t length, unsigned int flags) {
     return __doris_old_splice(fd_in, offset_in, fd_out, offset_out, length, 
flags);
 }
+
+// Rust std weak-links copy_file_range and otherwise issues the syscall itself.
+// Provide that syscall path locally so linking on glibc 2.27 does not attach a
+// GLIBC_2.27 version requirement. Old kernels return ENOSYS and Rust falls 
back
+// to its generic copy loop.
+DORIS_HIDDEN ssize_t copy_file_range(int fd_in, off64_t* offset_in, int 
fd_out, off64_t* offset_out,
+                                     size_t length, unsigned int flags) {
+    return (ssize_t)syscall(SYS_copy_file_range, fd_in, offset_in, fd_out, 
offset_out, length,
+                            flags);
+}
+
+// Rust std weak-links this glibc 2.18 entry point and has an internal fallback
+// when it is absent. Since the LDB sysroot exposes it, the final linker would
+// otherwise record GLIBC_2.18. Supply equivalent pthread-key based 
registration
+// locally. All callers are linked into doris_be, so dso_symbol tracking for
+// dlclose is intentionally unnecessary.
+// See https://github.com/rust-lang/rust/issues/57497 for more details.
+DORIS_HIDDEN int __cxa_thread_atexit_impl(doris_tls_destructor destructor, 
void* object,
+                                          void* dso_symbol) {
+    (void)dso_symbol;
+    if (pthread_once(&doris_tls_destructor_once, 
doris_init_tls_destructor_key) != 0) {
+        abort();
+    }
+
+    struct doris_tls_destructor_entry* entry = malloc(sizeof(*entry));
+    if (entry == NULL) {
+        abort();
+    }
+    entry->destructor = destructor;
+    entry->object = object;
+    entry->next = pthread_getspecific(doris_tls_destructor_key);
+    if (pthread_setspecific(doris_tls_destructor_key, entry) != 0) {
+        free(entry);
+        abort();
+    }
+    return 0;
+}
diff --git a/be/src/glibc-compatibility/musl/expf.c 
b/be/src/glibc-compatibility/musl/expf.c
new file mode 100644
index 00000000000..f9fbf8e727d
--- /dev/null
+++ b/be/src/glibc-compatibility/musl/expf.c
@@ -0,0 +1,80 @@
+/*
+ * Single-precision e^x function.
+ *
+ * Copyright (c) 2017-2018, Arm Limited.
+ * SPDX-License-Identifier: MIT
+ */
+
+#include <math.h>
+#include <stdint.h>
+#include "libm.h"
+#include "exp2f_data.h"
+
+/*
+EXP2F_TABLE_BITS = 5
+EXP2F_POLY_ORDER = 3
+
+ULP error: 0.502 (nearest rounding.)
+Relative error: 1.69 * 2^-34 in [-ln2/64, ln2/64] (before rounding.)
+Wrong count: 170635 (all nearest rounding wrong results with fma.)
+Non-nearest ULP error: 1 (rounded ULP error)
+*/
+
+#define N (1 << EXP2F_TABLE_BITS)
+#define InvLn2N __exp2f_data.invln2_scaled
+#define T __exp2f_data.tab
+#define C __exp2f_data.poly_scaled
+
+static inline uint32_t top12(float x)
+{
+       return asuint(x) >> 20;
+}
+
+float expf(float x)
+{
+       uint32_t abstop;
+       uint64_t ki, t;
+       double_t kd, xd, z, r, r2, y, s;
+
+       xd = (double_t)x;
+       abstop = top12(x) & 0x7ff;
+       if (predict_false(abstop >= top12(88.0f))) {
+               /* |x| >= 88 or x is nan.  */
+               if (asuint(x) == asuint(-INFINITY))
+                       return 0.0f;
+               if (abstop >= top12(INFINITY))
+                       return x + x;
+               if (x > 0x1.62e42ep6f) /* x > log(0x1p128) ~= 88.72 */
+                       return __math_oflowf(0);
+               if (x < -0x1.9fe368p6f) /* x < log(0x1p-150) ~= -103.97 */
+                       return __math_uflowf(0);
+       }
+
+       /* x*N/Ln2 = k + r with r in [-1/2, 1/2] and int k.  */
+       z = InvLn2N * xd;
+
+       /* Round and convert z to int, the result is in [-150*N, 128*N] and
+          ideally ties-to-even rule is used, otherwise the magnitude of r
+          can be bigger which gives larger approximation error.  */
+#if TOINT_INTRINSICS
+       kd = roundtoint(z);
+       ki = converttoint(z);
+#else
+# define SHIFT __exp2f_data.shift
+       kd = eval_as_double(z + SHIFT);
+       ki = asuint64(kd);
+       kd -= SHIFT;
+#endif
+       r = z - kd;
+
+       /* exp(x) = 2^(k/N) * 2^(r/N) ~= s * (C0*r^3 + C1*r^2 + C2*r + 1) */
+       t = T[ki % N];
+       t += ki << (52 - EXP2F_TABLE_BITS);
+       s = asdouble(t);
+       z = C[0] * r + C[1];
+       r2 = r * r;
+       y = C[2] * r + 1;
+       y = z * r2 + y;
+       y = y * s;
+       return eval_as_float(y);
+}
diff --git a/be/src/service/CMakeLists.txt b/be/src/service/CMakeLists.txt
index b9bcef0b7b8..1f52d4f7a60 100644
--- a/be/src/service/CMakeLists.txt
+++ b/be/src/service/CMakeLists.txt
@@ -46,13 +46,31 @@ if (${MAKE_TEST} STREQUAL "OFF" AND ${BUILD_BENCHMARK} 
STREQUAL "OFF")
     endif ()
     pch_reuse(doris_be)
 
-    # This permits libraries loaded by dlopen to link to the symbols in the 
program.
-    set_target_properties(doris_be PROPERTIES ENABLE_EXPORTS 1)
+    # Keep doris_be symbols private. Java UDF callbacks are registered 
explicitly through JNI and
+    # Python UDFs run out of process, so neither requires executable exports. 
Exporting all symbols
+    # would also let dlopened libraries bind to private copies from static 
dependencies.
 
     target_link_libraries(doris_be
         ${DORIS_LINK_LIBS}
     )
 
+    if (OS_LINUX AND GLIBC_COMPATIBILITY)
+        # Use TARGET_FILE rather than an install/output directory so this check
+        # follows doris_be if its build location or output name changes.
+        set(GLIBC_COMPATIBILITY_CHECK
+            "${BASE_DIR}/cmake/check_glibc_compatibility.cmake")
+        set_property(TARGET doris_be APPEND PROPERTY LINK_DEPENDS
+            "${GLIBC_COMPATIBILITY_CHECK}")
+        add_custom_command(TARGET doris_be POST_BUILD
+            COMMAND "${CMAKE_COMMAND}"
+                "-DARTIFACT=$<TARGET_FILE:doris_be>"
+                "-DOBJDUMP=${CMAKE_OBJDUMP}"
+                "-DBASELINE=2.17"
+                -P "${GLIBC_COMPATIBILITY_CHECK}"
+            COMMENT "Checking that doris_be requires no glibc symbols newer 
than GLIBC_2.17"
+            VERBATIM)
+    endif()
+
     install(DIRECTORY DESTINATION ${OUTPUT_DIR}/lib/)
     install(TARGETS doris_be DESTINATION ${OUTPUT_DIR}/lib/)
 


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to