diff --git a/BUILD b/BUILD index 0a492e93..dfd6e1c6 100644 --- a/BUILD +++ b/BUILD @@ -36,18 +36,34 @@ this file by hand, before proceeding with the build. Note that the the generated a symbolic link to a system-specific default config file. It is not necessary to edit the file; you can override variables on the "make" command line if you prefer. +You can also create and edit the "mconfig" file completely by hand (or start by copying one for a +particular OS from the "configs" directory) to choose appropriate values for the configuration +variables defined within. + + +"configure" script +=-=-=-=-=-=-=-=-=- + An alternative to "make mconfig" is to use the provided "configure" script. It will try to generate a suitable "mconfig" file, based on sensible defaults and options provided on the command -line when the script is run. +line when the script is run. It also has checks for the current system environment to +enable/disable certain features based on the availability of requirements (such as headers). + +The "configure" also checks for usability of the "-fno-rtti" compiler flag. This is achieved by +testing the flag itself and running a test source code to see if it works correctly. However, +because of the nature of cross-compiling, it's not possible to compile and run the test source +code on the building machine, and instead, "configure" disables "-fno-rtti" in this case and prints +out this warning: + + Cross-compilation detected. Disabling -fno-rtti to make sure exceptions will always work. + +See "Special note for run-time type information", below, for more information about the "-fno-rtti" +flag. For more information on available options from the configure script, run: ./configure --help -You can also create and edit the "mconfig" file completely by hand (or start by copying one for a -particular OS from the "configs" directory) to choose appropriate values for the configuration -variables defined within. - Main build variables =-=-=-=-=-=-=-=-=-=- @@ -140,9 +156,8 @@ Dinit should generally build fine with no additional options, other than: Recommended options, supported by at least GCC and Clang, are: -Os : optimise for size - -fno-rtti : disable RTTI (run-time type information), it is not required by Dinit. However, on - some platforms such as Mac OS (and historically FreeBSD, IIRC), this prevents - exceptions working correctly. + -fno-rtti : see "Special note for run-time type information", below. May cause Dinit to crash on + errors when compiled with Clang+libcxxrt on some platforms like FreeBSD. -fno-plt : enables better code generation for non-static builds, but may cause unit test failures on some older versions of FreeBSD (eg 11.2-RELEASE-p4 with clang++ 6.0.0). -flto : perform link-time optimisation (option required at compile and link). @@ -292,3 +307,26 @@ upgrading GCC. If you have libstdc++ corresponding to GCC 5.x or 6.x, you *must* old ABI, but Dinit will be broken if you upgrade to GCC 7. If you have libstdc++ from GCC 7, you *must* build with the new ABI. If the wrong ABI is used, Dinit may still run successfully but any attempt to load a non-existing service, for example, will cause Dinit to crash. + + +Special note for run-time type information +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + +RTTI, or run-time type information, is a feature of C++ that exposes information about an object's +data type at runtime. This can be used for "dynamic_cast<>" and to manipulate type information at +runtime using the "typeid" operator. Exception handling also uses the same info. + +GCC and Clang provide a compiler flag ("-fno-rtti") which would disable RTTI generation. However +GCC would still generate RTTI for exception handling and therefore "-fno-rtti" is a viable option +for saving output binary size. + +Clang on some platforms (such as FreeBSD) would break handling of certain exceptions when using +"-fno-rtti" and this will result in Dinit crashing when an error occurs (such as when loading a +non-existing service). + +Details regarding the issue with Clang can be found here: + + https://github.com/llvm/llvm-project/issues/66117 + +To avoid Dinit crashes when using Clang+libcxxrt it's recommended to not use the "-fno-rtti" flag. +Clang+libcxxrt is the default compiler+runtime-library on platforms like FreeBSD and macOS. diff --git a/configure b/configure index 199168b1..b3ed5e6f 100755 --- a/configure +++ b/configure @@ -83,6 +83,34 @@ cxx_works() fi } +# Test if the compiler successfully compiles the specified source file. If the optional compilation +# argument is specified ($3) then pass it to the compiler along with the already-established +# arguments (this can be used to test if the compiler accepts the argument). +# $1 - the name of the source file to compile +# $2 - the name of the result object file +# $3 - the argument to test (optional) +# CXX - the name of the compiler +# CXXFLAGS, CXXFLAGS_EXTRA - any predetermined flags +try_compile_source() +{ + eval $CXX $CXXFLAGS ${3:-} $CXXFLAGS_EXTRA '"$configtmpdir"/"$1"' \ + -c -o '"$configtmpdir"/"$2"' > /dev/null 2>&1 +} + +# Test if the specified object file can be linked into an executable. If the optional link argument +# is specified ($3) then pass it to the compiler driver along with the already-established +# arguments (this can be used to test if the linker accepts the argument). +# $1 - the name of the object file to link +# $2 - the name of the result executable file +# $3 - the argument to test (optional) +# CXX - the name of the compiler +# LDFLAGS, LDFLAGS_EXTRA - any predetermined flags +try_link_object() +{ + eval $CXX $LDFLAGS $LDFLAGS_EXTRA ${3:-} '"$configtmpdir"/"$1"' \ + -o '"$configtmpdir"/"$2"' > /dev/null 2>&1 +} + # Test if the compiler accepts an argument (for compilation only); if so add it to named variable. # Note: this function will return failure if the flag is unsupported (use try_optional_cxx_argument # instead, to avoid errorring out). @@ -93,8 +121,7 @@ cxx_works() try_cxx_argument() { info Checking whether "$2" is accepted for compilation... - if eval $CXX $CXXFLAGS $2 $CXXFLAGS_EXTRA $LDFLAGS $LDFLAGS_EXTRA '"$configtmpdir"/testfile.cc' \ - -c -o '"$configtmpdir"/testfile.o' > /dev/null 2>&1; then + if try_compile_source testfile.cc testfile.o "$2"; then rm "$configtmpdir"/testfile.o sub_info Yes. eval "$1=\"\${$1} \$2\"" @@ -127,14 +154,12 @@ try_ld_argument() info Checking whether "$2" is accepted as a link-time option... if [ ! -e "$configtmpdir"/testfile.o ]; then # If the object file doesn't exist yet, need to compile - if ! eval $CXX $CXXFLAGS $CXXFLAGS_EXTRA '"$configtmpdir"/testfile.cc' \ - -c -o '"$configtmpdir"/testfile.o' > /dev/null 2>&1; then + if ! try_compile_source testfile.cc testfile.o; then sub_info "No (compilation failed)." return fi fi - if eval $CXX $LDFLAGS $LDFLAGS_EXTRA $2 '"$configtmpdir"/testfile.o' \ - -o '"$configtmpdir"/testfile' > /dev/null 2>&1; then + if try_link_object testfile.o testfile "$2"; then sub_info Yes. rm "$configtmpdir"/testfile eval "$1=\"\${$1} \$2\"" @@ -145,27 +170,71 @@ try_ld_argument() } # Test if an argument is supported during both compiling and linking. -# $1 - the name of the compiler-flags variable to potentially add the argument to -# $2 - the name of the link-flags variable to potentially add the argument to +# $1 - the name of the compiler-flags variable to potentially add the argument to (or specify the +# dash ("-") to skip adding to the variable) +# $2 - the name of the link-flags variable to potentially add the argument to (or specify the +# dash ("-") to skip adding to the variable) # $3 - the argument to test/add # CXX - the name of the compiler # CXXFLAGS, CXXFLAGS_EXTRA, LDFLAGS, LDFLAGS_EXTRA - any predetermined flags try_both_argument() { info Checking whether "$3" is accepted for compiling and linking... - if eval $CXX $CXXFLAGS $CXXFLAGS_EXTRA $LDFLAGS $LDFLAGS_EXTRA $3 "$configtmpdir"/testfile.cc \ - -o "$configtmpdir"/testfile > /dev/null 2>&1; then + if try_compile_source testfile.cc testfile.o "$3" && try_link_object testfile.o testfile "$3"; then sub_info Yes. + rm "$configtmpdir"/testfile.o rm "$configtmpdir"/testfile - eval "$1=\"\${$1} \$3\"" - eval "$1=\${$1# }" - eval "$2=\"\${$2} \$3\"" - eval "$2=\${$2# }" + if [ "$1" != "-" ]; then + eval "$1=\"\${$1} \$3\"" + eval "$1=\${$1# }" + fi + if [ "$2" != "-" ]; then + eval "$2=\"\${$2} \$3\"" + eval "$2=\${$2# }" + fi else sub_info No. fi } +# Test if the specified header is usable. +# $1 - the name of the variable to set to 1 or 0 based on header availability +# $2 - the header path +check_header() +{ + info Checking whether "$2" header is available... + echo "#include <$2>" > "$configtmpdir/testheader.cc" + if try_compile_source testheader.cc testheader.o; then + sub_info "Yes." + rm "$configtmpdir"/testheader.o + eval "$1=1" + else + sub_info "No." + eval "$1=0" + fi +} + +# Write the test code for testing exception handling with -fno-rtti +write_no_rtti_check() +{ + cat << _EOF > "$configtmpdir"/no-rtti-check.cc +#include +#include + +int main() +{ + try { + std::stoull("invalid for stoull"); + } + catch (std::invalid_argument &exc) { + return 0; + } + + return -1; +} +_EOF +} + # Quote a string to preserve its original form after shell evaluation (except that an empty @@ -399,7 +468,7 @@ if [ "$PLATFORM" = "Linux" ]; then : "${BUILD_SHUTDOWN:="yes"}" : "${SUPPORT_CGROUPS:="1"}" : "${SUPPORT_CAPABILITIES:="AUTO"}" - : "${SUPPORT_IOPRIO:="0"}" # the required header may not be available + : "${SUPPORT_IOPRIO:="AUTO"}" : "${SUPPORT_OOM_ADJ:="1"}" : "${SYSCONTROLSOCKET:="/run/dinitctl"}" else @@ -501,20 +570,37 @@ if [ "$AUTO_CXXFLAGS" = "true" ]; then do try_optional_cxx_argument CXXFLAGS $argument done - if [ "$PLATFORM" != "Darwin" ]; then - try_optional_cxx_argument CXXFLAGS -fno-rtti + + # -fno-rtti is a special case. Clang+libcxxrt is known for generating broken code + # when disabling run-time type information with code which uses exceptions. + info Checking whether -fno-rtti is accepted for compilation... + if [ -n "${CXX_FOR_BUILD:-}" ]; then + # May not be able to execute the check when cross-compiling. + sub_info Cross-compilation detected. Disabling -fno-rtti to make sure exceptions will always work. + else + write_no_rtti_check + if try_compile_source no-rtti-check.cc no-rtti-check.o -fno-rtti; then + sub_info "Yes." + info Checking whether -fno-rtti breaks exceptions... + if try_link_object no-rtti-check.o no-rtti-check && "$configtmpdir"/no-rtti-check; then + sub_info "No." + CXXFLAGS="$CXXFLAGS -fno-rtti" + else + sub_info "Yes. Disabling -fno-rtti" + fi + else + sub_info "No." + fi fi fi if [ "$AUTO_LDFLAGS" = true ] && [ "$AUTO_CXXFLAGS" = true ]; then - DUMMY_LDFLAGS="" # -flto must work for both compiling and linking, but we don't want to add it to LDFLAGS as, # if LTO is used, CXXFLAGS will always be used alongside LDFLAGS. - if try_both_argument CXXFLAGS DUMMY_LDFLAGS -flto; then + if try_both_argument CXXFLAGS - -flto; then HAS_LTO="true" else HAS_LTO="false" fi - unset DUMMY_LDFLAGS fi if [ "$AUTO_LDFLAGS_BASE" = true ] && [ "$PLATFORM" = FreeBSD ]; then try_ld_argument LDFLAGS_BASE -lrt @@ -537,6 +623,10 @@ else LDFLAGS_LIBCAP="" fi +if [ "$SUPPORT_IOPRIO" = "AUTO" ]; then + check_header SUPPORT_IOPRIO "linux/ioprio.h" +fi + # "XXX_MK" variables are for values already quoted for use in makefiles. The following will now be # set: TEST_LDFLAGS_BASE_MK, LDFLAGS_MK, TEST_LDFLAGS_MK, TEST_CXXFLAGS_MK. For other build # variables, we'll just do the quoting when we write the value to mconfig. @@ -588,10 +678,9 @@ fi # Check whether sanitizers can be used for tests if [ "$AUTO_TEST_LDFLAGS" = "true" ] && [ "$AUTO_TEST_CXXFLAGS" = "true" ]; then - DUMMY_LDFLAGS="" if [ "$HAS_LTO" = true ]; then # Avoid doubling-up sanitizer options - LDFLAGS_VAR=DUMMY_LDFLAGS + LDFLAGS_VAR=- else LDFLAGS_VAR=TEST_LDFLAGS_MK fi @@ -599,7 +688,6 @@ if [ "$AUTO_TEST_LDFLAGS" = "true" ] && [ "$AUTO_TEST_CXXFLAGS" = "true" ]; then CXXFLAGS="$established_TEST_CXXFLAGS" CXXFLAGS_EXTRA="$TEST_CXXFLAGS_EXTRA" \ LDFLAGS="$established_TEST_LDFLAGS" LDFLAGS_EXTRA="$TEST_LDFLAGS_EXTRA" \ try_both_argument TEST_CXXFLAGS_MK $LDFLAGS_VAR -fsanitize=address,undefined - unset DUMMY_LDFLAGS fi ## Create mconfig