0xStubs

System Administration, Programming and Reconfigurable Computing

Using the C++23 std Module with Clang 18

A feature introduced in C++20 is modules. With C++23, the standard library is now supposed to provide an std module that can be used instead of all the #includes that we are used to. This allows code like the following:

import std;

int main() {
  std::print("Hello world!\n");
}

While (partial) support for modules is included in GCC since version 11 and Clang since version 8, support for the std module is currently still lacking in libstdc++ and only included in libc++ since version 17 (see https://en.cppreference.com/w/cpp/compiler_support). But since I have Clang 18 and libc++ installed, I wanted to give it a try.

Simply trying to compile above code, unfortunately leads to an error:

% clang++ -std=c++23 -stdlib=libc++ -o test test.cpp 
test.cpp:1:8: fatal error: module 'std' not found
    1 | import std;
      | ~~~~~~~^~~
1 error generated.

One might be tempted to try Clang’s -fmodules flag but in fact this does not refer to modules as defined by the C++ standard and using it actually causes more issues:

% clang++ -std=c++23 -stdlib=libc++ -fmodules -o test test.cpp
test.cpp:1:1: error: import of module 'std' imported non C++20 importable modules
    1 | import std;
      | ^
test.cpp:4:8: error: no member named 'print' in namespace 'std'
    4 |   std::print("Hello world!\n");
      |   ~~~~~^
2 errors generated.

Most people experimenting with modules use CMake as build system and this is what is suggested by the libc++ documentation. However, the page also generally describes that we need to generate a BMI (Built Module Interface) file (.pcm) from the module sources (.cppm). A script posted in the LLVM discourse was very helpful in determining the necessary steps. First, the module header needs to be precompiled:

clang++ -std=c++23 -stdlib=libc++ \
    -Wno-reserved-identifier -Wno-reserved-module-identifier \
    --precompile -o std.pcm /usr/share/libc++/v1/std.cppm

The location of the cppm file may of course differ on different systems. Also, shipping the module source currently must be explicitly activated when building libc++. After precompiling the module, we can compile our program as follows:

clang++ -std=c++23 -stdlib=libc++ \
    -fmodule-file=std=std.pcm -o test std.pcm test.cpp

Note that we need to specify where the std module can be found via -fmodule-file and also need to specify it as an input file. All of this can of course be automated in a Makefile:

CXX = clang++
CXXFLAGS += -Weverything -Wno-c++98-compat -Wno-pre-c++20-compat
CXXFLAGS += -std=c++23 -stdlib=libc++

test: std.pcm test.cpp
	$(CXX) $(CXXFLAGS) -fmodule-file=std=std.pcm -o $@ $^

std.pcm: /usr/share/libc++/v1/std.cppm
	$(CXX) $(CXXFLAGS) -Wno-reserved-identifier -Wno-reserved-module-identifier --precompile -o $@ $^

openSUSE Tumbleweed on the StarFive VisionFive 2

In March, I wrote about my initial contact with the StarFive VisionFive 2 board. Since then, StarFive released a couple of new releases of their Debian-based operating system. However, these releases are still based on a Debian unstable snapshot from 2022 and a Linux 5.15.0 kernel that contains several severe vulnerabilities. Updating the image is not possible without breaking their custom fixes to mesa.

By now there are also dozens of distributions available for the board. Many of them rely on StarFive’s outdated kernel sources. Others merge those kernel sources with more recent upstream patches or selectively apply StarFive patches on up-to-date mainline kernel sources. However, looking at the current status of the upstreaming efforts for the JH7110 SoC, it shows that most parts of the SoC are supported in mainline Linux 6.6. The most important part missing is certainly the HDMI output.

As I am using the board purely headless, i.e., without any display, HDMI does not matter for me. With that, openSUSE Tumbleweed looked very interesting to me, currently coming with a stock Linux 6.6-rc3 kernel and stock u-boot 2023.10-rc4 and promising regular, rolling updates. However, it comes with several issues, namely:

  1. It boots up with 8 GiB of memory visible to the kernel – no matter how much memory your board has. If you have less than 8 GiB, segmentation faults will bite you.
  2. Lots of hardware that should work with the supplied kernel does not. For example, PCIe, USB, thermal sensors, SPI, etc…
  3. It feels slow, in particular when I/O on the SD card is involved.
  4. It does not properly shutdown. Instead, in most cases, the board freezes on shutdown and the file systems need a check on next boot.

By now I could fix most of these issues. If you are interested or affected yourself, read through the solutions / workarounds below.

Read More

Using the C++ bool type in HPC codes

When writing C++ code, you are probably inclined to use data types that are closest to what you are trying to express, expecting that this allows the compiler to provide the most efficient implementation possible. So, when processing boolean values, i.e., single-bit information distinguishing between true and false, you will likely want to use the bool type in C++. However, this bool type may behave in unexpected ways, in particular if you are working with codes that have to perform well and correctly in multi-threaded codes.

Read More

“touch -r $< $@” in Makefiles – Please don’t

Recently I had a bad memory module in my main storage system which is also the backup target of many of my systems. To be on the safe side, I validated all backed up files against their origin via checksums. Interestingly, multiple systems showed differing checksums for all DejaVu font files, i.e. the files in the backup did not match those in the system. Clearly, this was not caused by the bad memory module. It looked very much like a systematic issue.

Read More

Tinkering with the StarFive VisionFive 2

With RISC-V getting more and more attention, I was curious and got myself a StarFive VisionFive 2, a single board computer equipped with a SiFive JH7110 SoC which implements the RV64GC ISA. While the performance is not on par with ARM SBCs yet, the overall experience has been quite good so far. In this article, I share some of my experiences with it.

StarFive VisionFive 2 SBC
StarFive VisionFive 2 SBC
Read More

Stuck UART on the ZedBoard

Recently I grabbed my trusted old Avnet / Digilent ZedBoard to test how well the current Xilinx tools work with this now a decade old platform. However, I was facing a weird issue: The serial output by the Zynq PS was only received via USB on my computer when I was sending characters via UART from my computer to the PS as well. As soon as there was a pause in communication, any further serial output by the PS was silently dropped.

Read More

How not to use the HackRF One as a spectrum analyzer

A while ago I wrote about how to use the HackRF One as a spectrum analyzer, and mentioned that apart from using its sweep functionality, it could also be used as a real-time 20 MHz spectrum analyzer using any SDR software. I now had to learn about its limitations and possible pitfalls when using it this way.

I wanted to look at the spectrum put out by an RF transmitter when transmitting an unmodulated carrier, i.e., a simple sine wave. In particular, I wanted to know about any nearby spurs, so looking at it with a high-bandwidth SDR seemed to be a good idea. So I connected the transmitter to the HackRF One, using appropriate attenuation. Looking at the spectrum put out by the transmitter gave me a bit of a shock:

Read More

libc’s random number generators and what to be aware of when seeding them

When you need pseudo-random numbers in C code, commonly used routines are random(), rand() and rand_r(unsigned int*). First and foremost, it is important to know that none of these routines produce high quality random numbers suitable for cryptographic use. But there are cases where you just need some random looking numbers. Let’s focus on these use-cases in this blog post.

For example, let’s assume you have a multi-threaded OpenMP program where you need to generate different pseudo-random numbers in each thread. A natural approach to this would be the following:

#pragma omp parallel
{
    unsigned seed = omp_get_thread_num();
    #pragma omp for
    for (...) {
        int rnd = rand_r(&seed);
        ...
    }
}

Each thread stores its own state of the random number generator and initializes (seeds) it with its thread number. Therefore, each thread should obtain different random numbers, although they will be idential on each run due to the deterministically chosen initial values. So, what’s wrong with this code? Let’s have a look at the very first random numbers generated for each thread on macOS 12:

Read More

Understanding USB Microphone Whine

I recently got my hands on a USB microphone, namely the Samson Meteor Mic, a large diaphragm electret condenser microphone that not only looks cute but also has overall good reviews online. However, when testing the microphone I was rather disappointed by the annoying background noise that is clearly audible when turning up the volume of the microphone. Have a listen:

Example of background noise
Same example as before but amplified

We hear two things here: One is relatively normal microphone hiss. The other is a high-pitched noise which you really do not want to have in your recordings. Let’s have a look at the spectrum of the recorded noise, using Audacity and its spectrum analysis (i.e., an FFT):

Read More

Making Touchpads with Builtin Windows Gestures Less Annoying on Raspberry Pi OS

I use a Bluetooth keyboard with included touchpad to control a Raspberry Pi 4. To be specific, I use the Keysonic KSK-5220BT by RaidSonic. One thing that annoys me for some time now is: Whenever I touch the touchpad at the very edge, the LXDE menu will open and the cursor won’t move.

The reason is that the touchpad supports Windows swipe gestures. For whatever reason these gestures are (at least with this touchpad) not recognized in software but in hardware and are translated as follows:

  • Swipe from right edge > Windows + A
  • Swipe from bottom edge > Windows + B
  • Swipe from left edge > Windows + Tab
  • Swipe from top edge > Windows + Down

Raspberry Pi OS (formerly Raspbian) has a large set of key binding preconfigured in /etc/xdg/openbox/lxde-pi-rc.xml. In particular, every press of the Windows / Super key will open the application menu. So each of these swipe gestures will open the menu and issue an additional key press (A / B / Tab / Down).

A workaround is to edit that file and replace

<keybind key="Super_L">
  <action name="Execute">
    <command>lxpanelctl menu</command>
  </action>
</keybind>

by

<keybind key="W-a">
  <action name="Execute">
    <command>true</command>
  </action>
</keybind>
<keybind key="W-b">
  <action name="Execute">
    <command>true</command>
  </action>
</keybind>
<keybind key="W-Down">
  <action name="Execute">
    <command>true</command>
  </action>
</keybind>
<keybind key="W-Tab">
  <action name="Execute">
    <command>true</command>
  </action>
</keybind>

With this modification, the cursor still won’t move when touching the edge of the touchpad. But at least no action is triggered and no key press is issued. But be aware that an update of the raspberrypi-ui-mods package might overwrite these changes.

Update Mar 11, 2023:

In 2021 Raspberry Pi OS updated to Debian Bullseye as a foundation. Additionally, they changed the default window manager from Openbox to Mutter on all devices with more than 2 GiB of memory. For Mutter, the procedure for disabling the annoying key bindings is slightly different:

  1. Disable Mutters key binding for the Windows / Super key:
    gsettings set org.gnome.mutter.keybindings lxpanel-menu "[]"
  2. Install xbindkeys:
    apt install xbindkeys
  3. Create a file called .xbindkeysrc in your home directory with the following content:
    "true"
       Super_L a
    
    "true"
       Super_L b
    
    "true"
       Super_L Tab
    
    "true"
       Super_L Down
    
  4. Manually start xbindkeys or logout and login again.