diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 80de5abccf..bf31f6c35d 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1214,7 +1214,7 @@ scan-build: variables: CC: "${CLANG}" CFLAGS: "${CFLAGS_COMMON}" - EXTRA_CONFIGURE: "-Didn=enabled" + EXTRA_CONFIGURE: "-Didn=enabled --native-file ci/clang-trixie.ini" before_script: - *list_installed_package_versions script: @@ -1427,7 +1427,7 @@ clang:asan: variables: CC: ${CLANG} CFLAGS: "${CFLAGS_COMMON}" - EXTRA_CONFIGURE: "-Db_sanitize=address,undefined -Db_lundef=false -Didn=enabled -Djemalloc=disabled -Dtracing=disabled" + EXTRA_CONFIGURE: "-Db_sanitize=address,undefined -Db_lundef=false -Didn=enabled -Djemalloc=disabled -Dtracing=disabled --native-file ci/clang-trixie.ini" <<: *base_image <<: *build_job @@ -1487,7 +1487,8 @@ clang:tsan: CC: "${CLANG}" CFLAGS: "${CFLAGS_COMMON}" LDFLAGS: "-Wl,--disable-new-dtags" - EXTRA_CONFIGURE: "${TSAN_CONFIGURE_FLAGS_COMMON} -Db_lundef=false" + EXTRA_CONFIGURE: "${TSAN_CONFIGURE_FLAGS_COMMON} -Db_lundef=false -Dnamed-lto=off --native-file ci/clang-trixie.ini" + <<: *build_job system:clang:tsan: variables: @@ -1542,6 +1543,7 @@ clang:trixie:amd64: variables: CC: ${CLANG} CFLAGS: "${CFLAGS_COMMON}" + EXTRA_CONFIGURE: "--native-file ci/clang-trixie.ini" RUN_MESON_INSTALL: 1 <<: *debian_trixie_amd64_image <<: *build_job @@ -1801,7 +1803,7 @@ respdiff:tsan: CC: "${CLANG}" CFLAGS: "${CFLAGS_COMMON}" LDFLAGS: "-Wl,--disable-new-dtags" - EXTRA_CONFIGURE: "${TSAN_CONFIGURE_FLAGS_COMMON} -Db_lundef=false" + EXTRA_CONFIGURE: "${TSAN_CONFIGURE_FLAGS_COMMON} -Dnamed-lto=off -Db_lundef=false" MAX_DISAGREEMENTS_PERCENTAGE: "0.3" TSAN_OPTIONS: "${TSAN_OPTIONS_DEBIAN}" script: @@ -1928,9 +1930,13 @@ reproducible-build: before_script: - *list_installed_package_versions script: + # dnstap produces an intermediate .a file, and meson considers all .a + # files to be final results independently of whether they are installed or + # not. But the content of the .a file might be unstable under LTO due to + # -ffat-lto-objects. Hence we disable dnstap for reproducibility tests. - meson reprotest - --intermediaries -- + -Ddnstap=disabled -Ddoc=disabled -Doptimization=1 artifacts: diff --git a/ci/clang-trixie.ini b/ci/clang-trixie.ini new file mode 100644 index 0000000000..4838b51894 --- /dev/null +++ b/ci/clang-trixie.ini @@ -0,0 +1,20 @@ +# Copyright (C) Internet Systems Consortium, Inc. ("ISC") +# +# SPDX-License-Identifier: MPL-2.0 +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, you can obtain one at https://mozilla.org/MPL/2.0/. +# +# See the COPYRIGHT file distributed with this work for additional +# information regarding copyright ownership. + +# LTO builds with clang + +[binaries] +ar = 'llvm-ar-20' +c = 'clang-20' +c_ld = 'lld' + +[project options] +named-lto = 'thin' diff --git a/doc/arm/build.inc.rst b/doc/arm/build.inc.rst index 5e78871ae7..e69b618b60 100644 --- a/doc/arm/build.inc.rst +++ b/doc/arm/build.inc.rst @@ -87,6 +87,22 @@ To improve performance, use of the ``jemalloc`` library (https://jemalloc.net/) is strongly recommended. Version 4.0.0 or newer is required when in use. +To further improve performance, compilation with link-time optimization is +recommended. This is enabled by default via the ``-Dnamed-lto`` option +(default: ``thin``). Link-time optimization can be disabled if needed by +using ``-Dnamed-lto=off``. The optimization level can be controlled with +``-Dnamed-lto=thin`` or ``-Dnamed-lto=full``. + +Link-time optimization requires close coordination between the compiler and +the linker. Due to ``clang`` limitations, compiling ``named`` with ``clang`` +and link-time optimization is only supported with the ``lld`` linker. + +Meson provides an alternative way to enable link-time optimization through +the ``-Db_lto=true`` flag. However, this option is incompatible with +BIND's ``-Dnamed-lto`` option. Meson's ``-Db_lto`` may also be incompatible +with certain BIND build options and can result in lower performance and +higher compile times compared to ``-Dnamed-lto``. + To support :rfc:`DNS over HTTPS (DoH) <8484>`, the server must be linked with ``libnghttp2`` (https://nghttp2.org/). If the library is unavailable, ``-Ddoh=disabled`` can be used to disable DoH support. diff --git a/lib/dns/meson.build b/lib/dns/meson.build index 4fa3ccf914..785aa961c1 100644 --- a/lib/dns/meson.build +++ b/lib/dns/meson.build @@ -9,7 +9,8 @@ # See the COPYRIGHT file distributed with this work for additional # information regarding copyright ownership. -probe_src = [dtrace_header.process('probes.d'), files('xfrin.c')] +probe_hdr = dtrace_header.process('probes-dns.d') +probe_src = [probe_hdr, files('xfrin.c')] # dns_inc += include_directories('include') dns_inc_p += include_directories('.') @@ -57,17 +58,20 @@ endif dns_srcset.add( when: 'HAVE_DTRACE', - if_true: custom_target( - 'dns-probe', - input: [files('probes.d'), dns_probe_objects], - output: 'dns-probes.o', - command: [ - dtrace, - '-G', - '-o', '@OUTPUT@', - '-s', '@INPUT@', - ], - ), + if_true: [ + custom_target( + 'dns-probe', + input: [files('probes-dns.d'), dns_probe_objects], + output: 'dns-probes.o', + command: [ + dtrace, + '-G', + '-o', '@OUTPUT@', + '-s', '@INPUT@', + ], + ), + probe_hdr, + ], if_false: probe_src, ) diff --git a/lib/dns/probes.d b/lib/dns/probes-dns.d similarity index 100% rename from lib/dns/probes.d rename to lib/dns/probes-dns.d diff --git a/lib/dns/xfrin.c b/lib/dns/xfrin.c index 132df2aa08..e1389f0422 100644 --- a/lib/dns/xfrin.c +++ b/lib/dns/xfrin.c @@ -49,7 +49,7 @@ #include -#include "probes.h" +#include "probes-dns.h" /* * Incoming AXFR and IXFR. diff --git a/lib/isc/job.c b/lib/isc/job.c index 09ed7360e5..4536746a19 100644 --- a/lib/isc/job.c +++ b/lib/isc/job.c @@ -34,7 +34,7 @@ #include "job_p.h" #include "loop_p.h" -#include "probes.h" +#include "probes-isc.h" /* * Public: #include diff --git a/lib/isc/meson.build b/lib/isc/meson.build index 223d6796f2..0e8e9cf0b3 100644 --- a/lib/isc/meson.build +++ b/lib/isc/meson.build @@ -11,7 +11,8 @@ probe_src = [] -probe_src += [dtrace_header.process('probes.d'), files('job.c')] +probe_hdr = dtrace_header.process('probes-isc.d') +probe_src += [probe_hdr, files('job.c')] if config.get('USE_PTHREAD_RWLOCK') != 1 probe_src += files('rwlock.c') endif @@ -47,17 +48,20 @@ endif isc_srcset.add( when: 'HAVE_DTRACE', - if_true: custom_target( - 'isc-probe', - input: [files('probes.d'), isc_probe_objects], - output: 'isc-probes.o', - command: [ - dtrace, - '-G', - '-o', '@OUTPUT@', - '-s', '@INPUT@', - ], - ), + if_true: [ + custom_target( + 'isc-probe', + input: [files('probes-isc.d'), isc_probe_objects], + output: 'isc-probes.o', + command: [ + dtrace, + '-G', + '-o', '@OUTPUT@', + '-s', '@INPUT@', + ], + ), + probe_hdr, + ], if_false: probe_src, ) diff --git a/lib/isc/netmgr/netmgr.c b/lib/isc/netmgr/netmgr.c index 1b7281a8a5..b3fcf33fb2 100644 --- a/lib/isc/netmgr/netmgr.c +++ b/lib/isc/netmgr/netmgr.c @@ -44,8 +44,8 @@ #include #include "../loop_p.h" +#include "../openssl_shim.h" #include "netmgr-int.h" -#include "openssl_shim.h" isc__netmgr_t *isc__netmgr = NULL; diff --git a/lib/isc/probes.d b/lib/isc/probes-isc.d similarity index 100% rename from lib/isc/probes.d rename to lib/isc/probes-isc.d diff --git a/lib/isc/rwlock.c b/lib/isc/rwlock.c index 6c040400aa..61ffd17ac2 100644 --- a/lib/isc/rwlock.c +++ b/lib/isc/rwlock.c @@ -62,7 +62,7 @@ #include #include -#include "probes.h" +#include "probes-isc.h" static atomic_uint_fast16_t isc__crwlock_workers = 128; diff --git a/lib/ns/meson.build b/lib/ns/meson.build index 0ea769e379..ded81571ec 100644 --- a/lib/ns/meson.build +++ b/lib/ns/meson.build @@ -9,7 +9,8 @@ # See the COPYRIGHT file distributed with this work for additional # information regarding copyright ownership. -probe_src = [dtrace_header.process('probes.d'), files('query.c')] +probe_hdr = dtrace_header.process('probes-ns.d') +probe_src = [probe_hdr, files('query.c')] if config.get('HAVE_DTRACE') ns_probe_objects += static_library( @@ -25,17 +26,20 @@ endif ns_srcset.add( when: 'HAVE_DTRACE', - if_true: custom_target( - 'ns-probe', - input: [files('probes.d'), ns_probe_objects], - output: 'ns-probes.o', - command: [ - dtrace, - '-G', - '-o', '@OUTPUT@', - '-s', '@INPUT@', - ], - ), + if_true: [ + custom_target( + 'ns-probe', + input: [files('probes-ns.d'), ns_probe_objects], + output: 'ns-probes.o', + command: [ + dtrace, + '-G', + '-o', '@OUTPUT@', + '-s', '@INPUT@', + ], + ), + probe_hdr, + ], if_false: probe_src, ) diff --git a/lib/ns/probes.d b/lib/ns/probes-ns.d similarity index 100% rename from lib/ns/probes.d rename to lib/ns/probes-ns.d diff --git a/lib/ns/query.c b/lib/ns/query.c index f5f8d84709..4c781f7d44 100644 --- a/lib/ns/query.c +++ b/lib/ns/query.c @@ -75,7 +75,7 @@ #include #include -#include "probes.h" +#include "probes-ns.h" #if 0 /* diff --git a/meson.build b/meson.build index b82b35b04c..ee12ed857f 100644 --- a/meson.build +++ b/meson.build @@ -45,6 +45,7 @@ developer_mode = get_option('developer').enabled() c_std = get_option('c_std') optimization = get_option('optimization') sanitizer = get_option('b_sanitize') +meson_lto = get_option('b_lto') trace_logging = get_option('trace-logging') rcu_flavor = get_option('rcu-flavor') @@ -64,6 +65,7 @@ leak_opt = get_option('leak-detection') line_opt = get_option('line') lmdb_opt = get_option('lmdb') locktype_opt = get_option('locktype') +named_lto_opt = get_option('named-lto') stats_json_opt = get_option('stats-json') stats_xml_opt = get_option('stats-xml') tracing_opt = get_option('tracing') @@ -898,6 +900,61 @@ assert( 'tracing is requested but dtrace is not found', ) +# LTO + +static_lto_c_args = [] +static_lto_link_args = [] + +if named_lto_opt == 'full' + static_lto_c_args = ['-ffat-lto-objects', '-flto'] + static_lto_link_args = ['-flto'] +elif named_lto_opt == 'thin' and cc.get_id() == 'clang' and cc.get_linker_id() == 'ld.lld' + # Per LLVM docs [1], -ffat-lto-objects is supported only with lld and gold, + # and gold is deprecated/unmantained. + # [1]: https://llvm.org/docs/FatLTO.html + + static_lto_c_args = ['-ffat-lto-objects', '-flto=thin'] + static_lto_link_args = ['-flto=thin'] +elif named_lto_opt == 'thin' and cc.get_id() == 'gcc' + static_lto_c_args = ['-ffat-lto-objects', '-flto=auto'] + static_lto_link_args = ['-flto=auto'] +elif named_lto_opt == 'thin' + error('LTO requires clang with ld.lld, or gcc with any linker') +endif + +add_project_arguments(static_lto_c_args, language: 'c') +if named_lto_opt != 'off' and cc.get_id() == 'clang' and sanitizer.contains('address') + # Needed to suppress the + # warning: Redundant instrumentation detected, with module flag: + # nosanitize_address [-Werror,-Wbackend-plugin] + # warning in address sanitizer. This warning indicates that the object file + # has been processed already by address sanitizer instrumentation pass. + # From looking at the pass code, when address sanitizer detects that + # an object file has already been instrumented, it just skips it. + # Therefore it should be safe to suppress the warning. + + add_project_arguments('-Wno-backend-plugin', language: 'c') +endif + +if meson_lto and named_lto_opt != 'off' + # Meson's builtin LTO settings do not set -ffat-lto-objects, which can cause + # build issues. + # Since we don't want two, possibly conflicting, sets of LTO flags, we + # error out if both are set. + + error( + ''' + Meson builtin -Db_lto and BIND's -Dnamed-lto options are incompatible. + Either disable named-lto with -Dnamed-lto=off, or avoid setting + -Db_lto. + + Note that using -Db_lto is not a recommended configuration, might + yield reduced performance compared to -Dnamed-lto and conflict + with other build flags. + ''', + ) +endif + ### Finalize configuration configure_file(output: 'config.h', configuration: config) add_project_arguments('-include', meson.project_build_root() / 'config.h', language: 'c') @@ -1158,8 +1215,6 @@ libisccfg_dep = declare_dependency( include_directories: isccfg_inc, ) -named_srcconf = named_srcset.apply(config, strict: false) - executable( 'arpaname', arpaname_src, @@ -1459,22 +1514,58 @@ executable( ) -executable( - 'named', - named_srcconf.sources(), - export_dynamic: true, - implicit_include_directories: true, - include_directories: named_inc_p, - install: true, - install_dir: sbindir, - sources: bind_keys, - dependencies: [ +named_c_args = [] +named_link_args = [] +named_deps = [] + +if named_lto_opt == 'off' + named_deps = [ libdns_dep, libisc_dep, libisccc_dep, libisccfg_dep, libns_dep, + ] + named_inc = named_inc_p + named_objects = [] +else + named_deps = [ + dns_srcconf.dependencies(), + isc_srcconf.dependencies(), + isccc_srcconf.dependencies(), + isccfg_srcconf.dependencies(), + ns_srcconf.dependencies(), + ] + named_inc = [isc_inc, dns_inc, isccc_inc, isccfg_inc, ns_inc, named_inc_p] + + named_srcset.add(dns_gen_headers) + + named_objects = [ + libisc.extract_all_objects(recursive: true), + libdns.extract_all_objects(recursive: true), + libns.extract_all_objects(recursive: true), + libisccc.extract_all_objects(recursive: true), + libisccfg.extract_all_objects(recursive: true), + ] +endif + +named_srcconf = named_srcset.apply(config, strict: false) + +executable( + 'named', + named_srcconf.sources(), + objects: named_objects, + c_args: static_lto_c_args, + link_args: static_lto_link_args, + export_dynamic: true, + implicit_include_directories: true, + include_directories: named_inc, + install: true, + install_dir: sbindir, + sources: bind_keys, + dependencies: named_deps + + [ openssl_dep, cap_dep, diff --git a/meson_options.txt b/meson_options.txt index bb010924dd..5783d45f3d 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -188,3 +188,11 @@ option( value: 'disabled', description: 'enable the memory leak detection in external libraries (libxml2, libuv, OpenSSL)', ) + +option( + 'named-lto', + type: 'combo', + choices: ['off', 'thin', 'full'], + value: 'thin', + description: 'Enable Link Time Optimization for named.', +)