Some advice from my own cross-compilation adventures:
A “-elf” toolchain should be able to build for any modern Linux kernel. Certain builds may benefit from a “-linux(-musl|-gnu)” on the end of the triplet, but the current ABIs are defined for elf, so I don’t see why -elf wouldn’t work. Only gotcha I can think of off the top of my head is the specific suffix may influence the OS ABI (i.e. 0 - UNIX_SV, 3 - Linux, 6 - SunOS, etc., byte at offset 0x7 in elf). That said, you can use elfedit to rectify this.
The without-headers and with-newlib options when building GCC are very important. The former will prevent looking for system C library headers, that way you don’t inadvertently pull in a constant or something from your host system (or another cross compiler). The latter will prevent glibc-specific behavior when building libgcc among other components. For some reason that I don’t understand the validity of, there is a circular dependency between libgcc and glibc, so libgcc is built in terms of glibc, and thus any compilation is then tainted with glibc, despite what should be the independent nature of the compiler from the environment that consumes it. In any case, ensuring you use with-newlib prevents attempts to use these glibc-specific cases.
That said, if you’re specifically targeting glibc, and you want the purity of a glibc-oriented compilation environment, your challenge is twofold. First, you have to build the cross-compiler using the ‘with-newlib’ option to build it without glibc-based assumptions. Of course there are countless other options you need to include, you’ll find those in the guidance linked in this thread, but essentially you’re cutting out unessential libraries as well as features that can’t really be used in a cross-compilation environment.
Before you start building glibc, make sure you grab a copy of the kernel headers for the kernel you’re looking to target. You can either pass a command-line argument to the glibc configure script or just place the includes (via make INSTALL_HDR_PATH=/path/to/riscv/includes headers_install in the kernel source)
This first compiler can then be used to cross-compile glibc targeting riscv64-unknown-linux-gnu (–host=riscv64-unknown-linux-gnu). You’ll want to ensure this gets installed in your cross-compiler’s prefix, typically /usr/local/. Once you’ve got a working copy of glibc stood up, then you’d need to build GCC again but this time omit ‘with-newlib’ and I believe you would issue --with-headers= and that will build a second pass of GCC but this time using the glibc-specific extensions in libgcc. I’m not certain but I think at this point you could build this version with shared library and threading support enabled since it can make use of those bits of glibc. Not certain though, mileage may vary, I’ve never bothered with glibc purity in my cross compiler like this.
That should then result in a cross-compilation environment capable of building C for riscv64 Linux. I didn’t touch on C++, but I believe you’d just include c++ in your ‘enable-languages’ argument and avoid disabling libstdc++, although I have never tried to build GCC’s libc++ against anything other than glibc.
There really shouldn’t be much different between building these toolchains on Linux vs macOS, or any other reasonably POSIX-y OS. I do most of my day to day on FreeBSD these days, and have fully functioning cross C environments for riscv64, aarch64, x86_64, and m68k. Happy to provide any other info, been tinkering with cross development for a while now.