Implementation:ClickHouse ClickHouse Clickhouse Add Executable Macro
| Knowledge Sources | |
|---|---|
| Domains | Build_System, C++, Systems_Programming |
| Last Updated | 2026-02-08 00:00 GMT |
Overview
Concrete tool for creating executable targets with memory allocator interposition provided by the clickhouse_add_executable macro in the root CMakeLists.txt.
Description
The clickhouse_add_executable macro (lines 504-557 of CMakeLists.txt) wraps CMake's built-in add_executable command to automatically inject memory allocator interposition into every ClickHouse executable. This macro is the single point where the critical connection between ClickHouse binaries and the custom memory allocator is established.
The macro performs three key actions:
1. Object file injection: The clickhouse_malloc object library is always included in the executable. On amd64 with glibc compatibility and ThinLTO both enabled, a memcpy object is additionally injected to prevent ThinLTO from generating calls to an incompatible system memcpy.
2. Malloc wrapping via linker options: On Linux (excluding sanitizer builds, macOS, FreeBSD, and SunOS), the macro adds --wrap linker options for nine allocation functions: malloc, free, calloc, realloc, aligned_alloc, posix_memalign, valloc, memalign, and reallocarray. On non-musl builds, pvalloc is also wrapped.
3. C++ new/delete linkage: If the target is an executable type, it links against clickhouse_new_delete, which provides custom operator new and operator delete implementations that use the wrapped allocator.
On macOS with jemalloc, an additional linker flag (-u_zone_register) ensures jemalloc's zone registration constructor is not omitted by the linker, which would cause a segmentation fault.
The primary invocation of this macro is at line 132 of programs/CMakeLists.txt:
clickhouse_add_executable (clickhouse main.cpp)
This creates the main clickhouse binary from main.cpp, which contains the dispatch table with 23+ entries mapping program names to their entry functions.
Usage
This macro must be used in place of add_executable for any ClickHouse executable that needs memory tracking. The main clickhouse binary is the primary consumer. Other executables (such as unit test runners or standalone keeper builds) also use this macro.
Code Reference
Source Location
- Repository: ClickHouse
- File:
CMakeLists.txt - Lines: 504-557
Signature
macro (clickhouse_add_executable target)
# Inject clickhouse_malloc objects (and optionally memcpy for ThinLTO)
if (ARCH_AMD64 AND GLIBC_COMPATIBILITY AND ENABLE_THINLTO)
add_executable (${ARGV} $<TARGET_OBJECTS:clickhouse_malloc> $<TARGET_OBJECTS:memcpy>)
else ()
add_executable (${ARGV} $<TARGET_OBJECTS:clickhouse_malloc>)
endif ()
# Wrap malloc/free/calloc/realloc/aligned_alloc/posix_memalign/valloc/memalign/reallocarray
# via --wrap linker options (Linux, non-sanitizer builds only)
if (NOT (SANITIZE OR SANITIZE_COVERAGE OR OS_DARWIN OR OS_FREEBSD OR OS_SUNOS))
target_link_options(${target} PRIVATE
"LINKER:--wrap=malloc"
"LINKER:--wrap=free"
"LINKER:--wrap=calloc"
"LINKER:--wrap=realloc"
"LINKER:--wrap=aligned_alloc"
"LINKER:--wrap=posix_memalign"
"LINKER:--wrap=valloc"
"LINKER:--wrap=memalign"
"LINKER:--wrap=reallocarray"
)
if (NOT USE_MUSL)
target_link_options(${target} PRIVATE
"LINKER:--wrap=pvalloc"
)
endif()
endif()
# Link custom new/delete operators
get_target_property (type ${target} TYPE)
if (${type} STREQUAL EXECUTABLE)
target_link_libraries (${target} PUBLIC clickhouse_new_delete)
# macOS jemalloc zone registration
if (ENABLE_JEMALLOC AND OS_DARWIN)
set_property(TARGET ${target} APPEND PROPERTY LINK_OPTIONS -u_zone_register)
endif()
endif()
endmacro()
Import
# Use instead of add_executable for ClickHouse binaries:
clickhouse_add_executable (clickhouse main.cpp)
# Then link component libraries and common I/O:
target_link_libraries (clickhouse PRIVATE clickhouse_common_io)
clickhouse_target_link_split_lib(clickhouse server)
clickhouse_target_link_split_lib(clickhouse client)
clickhouse_target_link_split_lib(clickhouse local)
# ... etc ...
I/O Contract
Inputs
| Name | Type | Required | Description |
|---|---|---|---|
target |
String | Yes | Name of the executable target to create (e.g., clickhouse)
|
${ARGV} |
Arguments | Yes | All arguments passed to the macro, forwarded to add_executable (target name + source files)
|
clickhouse_malloc |
Object library | Yes | Object library providing wrapped allocation functions |
memcpy |
Object library | Conditional | Custom memcpy object, required when ARCH_AMD64 AND GLIBC_COMPATIBILITY AND ENABLE_THINLTO
|
clickhouse_new_delete |
Library | Yes | Library providing custom C++ operator new and operator delete
|
| Component libraries | Static libraries | Yes | All clickhouse-{name}-lib targets linked after the executable is created
|
main.cpp |
Source file | Yes | Contains the dispatch table and main function
|
Outputs
| Name | Type | Description |
|---|---|---|
clickhouse |
Executable | Single monolithic binary containing all program modes, with memory allocator interposition |
| Symlinks | Files | Post-build symlinks: clickhouse-server, clickhouse-client, clickhouse-local, clickhouse-keeper, chc, chl, ch, and others
|
Usage Examples
Building the Main Binary
# After CMake configuration:
ninja -C build clickhouse
# The resulting binary:
ls -lh build/programs/clickhouse
# Typical size: ~300MB+ (release), ~700MB+ (debug)
Using Symlinks
# All of these invoke the same binary in different modes:
./build/programs/clickhouse server
./build/programs/clickhouse-server
./build/programs/clickhouse client
./build/programs/clickhouse-client
./build/programs/clickhouse local -q "SELECT 1"
./build/programs/clickhouse-local -q "SELECT 1"
./build/programs/chl -q "SELECT 1"
./build/programs/ch -q "SELECT 1"
Verifying Malloc Wrapping
# Check that malloc is wrapped in the binary:
nm build/programs/clickhouse | grep __wrap_malloc