DEV Community

Pierre Gradot
Pierre Gradot

Posted on

Just in case: Debian Bookworm comes with a buggy GCC

Last month, I embarked on a new project. I set up a new computer with the latest Debian version, installed my favorites tools, and was all set to code. My first task was to migrate all the repositories from C++14 to C++20. While it might seem as straightforward as updating all the CMakeLists.txt to replace set (CMAKE_CXX_STANDARD 14) with set (CMAKE_CXX_STANDARD 20) reality proved otherwise (and I knew it would).

Among the funny things I encountered (such as std::experimental::basic_string_view::to_string() being missing in std::basic_string_view), the most cryptic error was something like that:

/usr/include/c++/12/bits/char_traits.h:431:56: warning: ‘void* __builtin_memcpy(void*, const void*, long unsigned int)’ accessing 9223372036854775810 or more bytes at offsets -4611686018427387902 and [-4611686018427387903, 4611686018427387904] may overlap up to 9223372036854775813 bytes at offset -3 [-Wrestrict]

  431 |         return static_cast<char_type*>(__builtin_memcpy(__s1, __s2, __n));

Enter fullscreen mode Exit fullscreen mode

This is a known bug in GCC and it affects only the 12.x branch. It's (Bug 105329 - [12/13/14 Regression] Bogus restrict warning when assigning 1-char string literal to std::string since r12-3347-g8af8abfbbace49e6).

Encountering this bug might not be an everyday occurrence, but it's quite easy to trigger it. Unfortunately, as you have guessed, Debian Bookworm ships with this problematic version of GCC. Bookworm is the latest stable version of Debian, released in June this year. Previous stables version, Buster and Bulleye, came with GCC 8 and GCC 10 respectively. Since Debian releases a new stable version every two years, if you are using Debian like me, we may have to deal with this bug for an extended period.

In this article, I will explain how you can trigger this bug and how you can avoid it. It's also an opportunity to revisit various techniques for circumventing spurious warnings.

Conditions to Trigger the Bug

Beyond from the source code itself, specific build conditions are necessary to trigger the bug.

  1. You must use GCC 12, obviously. GCC 11 wasn't affected and the bug is fixed in GCC 13.
  2. Optimizations must be enabled. You must use either -O2 or -O3. -O0, -O1 and -Os don't seem to concerned. If you set CMAKE_BUILD_TYPE to Release, CMake will add -O3. Your release builds will then be susceptible, unlike debug builds.
  3. You must use C++20 and C++23. C++17 is unaffected.
  4. You must pass the -Wrestrict flag since it causes the warning. It is included in -Wall.

This means your build may break simply by:

  • Updating GCC (it happened to GoogleTest).
  • Changing from debug to release mode (but I guess your CI always builds in release mode, right?).
  • Switching for C++17 or below to C++20 or above (and I encourage you to regularly upgrade to the new standard version).
  • Enabling warnings (no, I can't believe you had lived without -Wall so far).

I use the word "break" because most release builds also use -Werror to turn warnings into errors, ensuring they are absolutely noticeable.

The Code Itself

Oh, poor boyz and girlz... The code is so simple.

Here is a CMake CMakeLists.txt:

cmake_minimum_required(VERSION 3.27)
project(buggy)

add_executable(buggy main.cpp)
target_compile_options(buggy PRIVATE -Wall)
target_compile_features(buggy PRIVATE cxx_std_20)
Enter fullscreen mode Exit fullscreen mode

And here a main.cpp that will raise the dreaded warning:

#include <string>

std::string s;

int main() {
    s = "a"; // oopsi...
}
Enter fullscreen mode Exit fullscreen mode

Here is another case that emits the warning:

#include <string>

std::string s;

int main() {
    s = "a" + std::string("b"); // oopsi...
}
Enter fullscreen mode Exit fullscreen mode

The string literal must be single byte and must be the left-hand-side operand. The following lines are fine:

s = "ab";
s = "ab" + std::string("c");
s = std::string("a") + "b";
Enter fullscreen mode Exit fullscreen mode

Circumvention Measures

The discussion for bug 105329 clearly indicates, in comments 26 and 27, than the 12 branch won't be fixed. The solution is hence to move to GCC 13. Alas! Debian will probably not change the major version of GCC in Bookworm. The next release, Trixie, is currently in testing mode and comes with GCC 13, so perhaps one day it will be available in the backports.

Fortunately, there are easy workarounds.

Disable -Wrestrict globally

As a temporary solution, you may add -Wno-restrict to your build:

target_compile_options(buggy PRIVATE -Wall -Wno-restrict)
Enter fullscreen mode Exit fullscreen mode

This is extreme and I highly discourage pushing such a change to a develop or a master/main branch.

Disable -Wrestrict for some files

Another solution I also consider temporary is to disable the warning but only for a particular set of files.

You can use CMake for this, especially if you can't change these sources files:

set_source_files_properties(
        main.cpp
        PROPERTIES
        COMPILE_FLAGS -Wno-restrict)
Enter fullscreen mode Exit fullscreen mode

Alternatively, you can use a pragma at the beginning of each file:

#pragma GCC diagnostic ignored "-Wrestrict"
#include <string>

std::string s;

int main() {
    s = "a";
}
Enter fullscreen mode Exit fullscreen mode

Disable -Wrestrict locally

Disabling the warning only for a couple of lines is a more acceptable solution:

#include <string>

std::string s;

int main() {
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wrestrict"
    s = "a";
#pragma GCC diagnostic pop
}
Enter fullscreen mode Exit fullscreen mode

I had to use this technique around a #include <boost/test/data/test_case.hpp> because BOOST_DATA_TEST_CASE triggered the bug. This solution is very verbose and reduce your code's readability. Think twice before choosing it.

Note about Clang

Clang does support #pragma GCC but it doesn't support -Wrestrict... It means that the previous codes will raise a warning or error:

$ clang++ main.cpp -Wall -Werror
main.cpp:7:32: error: unknown warning group '-Wrestrict', ignored [-Werror,-Wunknown-warning-option]
#pragma GCC diagnostic ignored "-Wrestrict"
Enter fullscreen mode Exit fullscreen mode

If you want your code to compile both with GCC and Clang, you will need some extra preprocessor tricks:

#ifndef __clang__
#pragma GCC diagnostic ignored "-Wrestrict"
#endif
Enter fullscreen mode Exit fullscreen mode

See Clang's builtin-macros.

The preferred solution: change the code

The solution with set_source_files_properties() may be acceptable if you plan to switch to GCC 13 soon. Otherwise, if you plan to use GCC 12 for a long time, just change your code.

Remember the two faulty lines:

 s = "a";
 s = "a" + std::string("b");
Enter fullscreen mode Exit fullscreen mode

Simply change them to:

s = std::string("a");
s = std::string("a") + std::string("b");
Enter fullscreen mode Exit fullscreen mode

This simply makes implicit conversions explicit.

Conclusion

At the end of the day, the warning message is quite scary, but the code can easily be changed to avoid the bug. Apart from changing the code to avoid this particular issue with GCC 12, we also discussed several techniques you may use to mitigate spurious warnings in your code.

This is not the first time I run into a compiler's bug. It's always so much fun (sarcasm indented) 😆

Top comments (4)

Collapse
 
steinerrw profile image
Rick Steiner

I have similar issues with gcc since upgrading to bookworm...
I have posted issue on several forums and am reaching out to see if others have same issues...

After installing all boards/libraries etc with new RPi OS upgrade everything works really smooth. The arduino IDE 1.8.19 runs well...
I can compile and upload for all my different boards except the UNO WIFI Rev2. I have removed board def, reinstalled, rebooted, installed prior versions and nothing seems to get over this error...
It was working before the upgrade.

Adding additional verbose error messages...

Arduino: 1.8.19 (Linux), Board: "Arduino Uno WiFi Rev2, None (ATMEGA4809)"

arduino-builder -dump-prefs -logger=machine -hardware /usr/share/arduino/hardware -hardware /home/rick/.arduino15/packages -hardware /home/rick/Arduino/hardware -tools /usr/share/arduino/hardware/tools/avr -tools /home/rick/.arduino15/packages -libraries /home/rick/Arduino/libraries -fqbn=arduino:megaavr:uno2018:mode=off -vid-pid=03EB_2145 -ide-version=10819 -build-path /tmp/arduino_build_168707 -warnings=none -build-cache /tmp/arduino_cache_356267 -prefs=build.warn_data_percentage=75 -prefs=runtime.tools.arduinoOTA.path=/home/rick/.arduino15/packages/arduino/tools/arduinoOTA/1.3.0 -prefs=runtime.tools.arduinoOTA-1.3.0.path=/home/rick/.arduino15/packages/arduino/tools/arduinoOTA/1.3.0 -prefs=runtime.tools.avr-gcc.path=/home/rick/.arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino5 -prefs=runtime.tools.avr-gcc-7.3.0-atmel3.6.1-arduino5.path=/home/rick/.arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino5 -prefs=runtime.tools.avrdude.path=/home/rick/.arduino15/packages/arduino/tools/avrdude/6.3.0-arduino17 -prefs=runtime.tools.avrdude-6.3.0-arduino17.path=/home/rick/.arduino15/packages/arduino/tools/avrdude/6.3.0-arduino17 -verbose /home/rick/Arduino/nRF2401-Transmitter/nRF2401-Transmitter.ino
arduino-builder -compile -logger=machine -hardware /usr/share/arduino/hardware -hardware /home/rick/.arduino15/packages -hardware /home/rick/Arduino/hardware -tools /usr/share/arduino/hardware/tools/avr -tools /home/rick/.arduino15/packages -libraries /home/rick/Arduino/libraries -fqbn=arduino:megaavr:uno2018:mode=off -vid-pid=03EB_2145 -ide-version=10819 -build-path /tmp/arduino_build_168707 -warnings=none -build-cache /tmp/arduino_cache_356267 -prefs=build.warn_data_percentage=75 -prefs=runtime.tools.arduinoOTA.path=/home/rick/.arduino15/packages/arduino/tools/arduinoOTA/1.3.0 -prefs=runtime.tools.arduinoOTA-1.3.0.path=/home/rick/.arduino15/packages/arduino/tools/arduinoOTA/1.3.0 -prefs=runtime.tools.avr-gcc.path=/home/rick/.arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino5 -prefs=runtime.tools.avr-gcc-7.3.0-atmel3.6.1-arduino5.path=/home/rick/.arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino5 -prefs=runtime.tools.avrdude.path=/home/rick/.arduino15/packages/arduino/tools/avrdude/6.3.0-arduino17 -prefs=runtime.tools.avrdude-6.3.0-arduino17.path=/home/rick/.arduino15/packages/arduino/tools/avrdude/6.3.0-arduino17 -verbose /home/rick/Arduino/nRF2401-Transmitter/nRF2401-Transmitter.ino
Using board 'uno2018' from platform in folder: /home/rick/.arduino15/packages/arduino/hardware/megaavr/1.8.8
Using core 'arduino' from platform in folder: /home/rick/.arduino15/packages/arduino/hardware/megaavr/1.8.8
Warning: platform.txt from core 'Arduino megaAVR Boards' contains deprecated compiler.path={runtime.tools.avr-gcc.path}/bin/, automatically converted to compiler.path=/usr/bin/. Consider upgrading this core.
Detecting libraries used...
"/usr/bin/avr-g++" -c -g -Os -w -std=gnu++11 -fpermissive -fno-exceptions -ffunction-sections -fdata-sections -fno-threadsafe-statics -Wno-error=narrowing -flto -w -x c++ -E -CC -mmcu=atmega4809 -DF_CPU=16000000L -DARDUINO=10819 -DARDUINO_AVR_UNO_WIFI_REV2 -DARDUINO_ARCH_MEGAAVR -DMILLIS_USE_TIMERB3 "-I/home/rick/.arduino15/packages/arduino/hardware/megaavr/1.8.8/cores/arduino/api/deprecated" "-I/home/rick/.arduino15/packages/arduino/hardware/megaavr/1.8.8/cores/arduino" "-I/home/rick/.arduino15/packages/arduino/hardware/megaavr/1.8.8/variants/uno2018" "/tmp/arduino_build_168707/sketch/nRF2401-Transmitter.ino.cpp" -o "/dev/null"
Generating function prototypes...
"/usr/bin/avr-g++" -c -g -Os -w -std=gnu++11 -fpermissive -fno-exceptions -ffunction-sections -fdata-sections -fno-threadsafe-statics -Wno-error=narrowing -flto -w -x c++ -E -CC -mmcu=atmega4809 -DF_CPU=16000000L -DARDUINO=10819 -DARDUINO_AVR_UNO_WIFI_REV2 -DARDUINO_ARCH_MEGAAVR -DMILLIS_USE_TIMERB3 "-I/home/rick/.arduino15/packages/arduino/hardware/megaavr/1.8.8/cores/arduino/api/deprecated" "-I/home/rick/.arduino15/packages/arduino/hardware/megaavr/1.8.8/cores/arduino" "-I/home/rick/.arduino15/packages/arduino/hardware/megaavr/1.8.8/variants/uno2018" "/tmp/arduino_build_168707/sketch/nRF2401-Transmitter.ino.cpp" -o "/tmp/arduino_build_168707/preproc/ctags_target_for_gcc_minus_e.cpp"
avr-g++: error: device-specs/specs-atmega4809: No such file or directory
exit status 1
Error compiling for board Arduino Uno WiFi Rev2.

Collapse
 
pgradot profile image
Pierre Gradot

The issue doesn't seem similar at all. A file is missing in your Arduino toolchain.

You can determine the previous version and current version of the toolchain, and look at the changelog to see if the support as been deprecated.

You can also try to locate the file by hand. Maybe it's just a wrong path. Try to cd home/rick/.arduino15 and search for the file with find -name *specs-atmega4809*.

Collapse
 
steinerrw profile image
Rick Steiner

Pierre;
I was able to search and found the file in question...

/home/rick/.arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino5/lib/gcc/avr/7.3.0/device-specs

I am not overly proficient with this...so forgive me if I ask simple questions...Is that the right path for the compiler to find it, if not do you know what the path should be.

Is there something in the file itself which needs to be changed?

Thanks
RIck

Thread Thread
 
pgradot profile image
Pierre Gradot

I have never used Arduino toolchains, so I can't really tell you. What I would do in your situation:

  • find a target that works
  • understand where its files are located
  • compare with the targets that don't work
  • see if there are differences with the path and in the project configuration