runtime detection whether ARMv7 ELF binaries can be loaded on ARMv6 host - linux

Consider a host application that has been compiled for armv6 (think: Raspbian) and can dlopen() extensions/plugins.
Some of these plugins might be compiled for armv7.
If the host application is running on a recent hardware, there shouldn't be any problem loading and running these plugins.
OTOH, if the application is running on legacy hardware (e.g. RPi2), the dlopen() will probably fail.
Now I would like to determine, whether the host application is capable of loading armv7 binaries, without actually trying to do so.
My use case is: writing a package manager that queries an online resource for available plugins; only compatible packages should be displayed: if the host is running on armv6, only armv6 plugins will be shown, but if it is running on armv7 both armv7 and armv6 binaries are presented.
The package architecture (as can be queried via the online resource) is detected via something like readelf -A <binary> | grep Tag_CPU_arch.
My original attempt used the __ARM_ARCH macro on the host, but that is obviously a compile-time check rather than a runtime check.
On x86, there's __builtin_cpu_supports() (for gcc and friends), but that is not available on ARM.
Parsing /proc/cpuinfo might be an option, but honestly I have no clue what to check for...
Ideas?

Related

How can I install GCC and other developer tools inside QEMU virtual machine that only has BusyBox?

I download Linux kernel source code, successfully compiled it and run it with BusyBox in QEMU.
Because of BusyBox, I can use some frequently-used tools, such as vi,ls,cp,cat, etc.
But when I try to compile a simple "hello world" C/C++ program, I get gcc: not found.
In addition, I can't make a new Linux module by make -C /lib/modules/$(shell uname -r)/build/ M=$(PWD) modules inside QEMU.
I googled a lot, still can't figure it out.
So my question is: how can I install common developer tools like gcc, make, etc. inside my bare-bones QEMU VM that is running my custom Linux kernel (and not a standard distribution)?
I see that you are trying to compile some program (or module) to use it inside your QEMU machine, but you do not have a compiler toolchain installed in the machine itself. You have a couple of options:
Probably the easiest: since you already compiled the kernel that you are using for QEMU externally (in your host machine), you can easily also compile anything else this way. For modules, just pointing make to the same kernel source directory where you built the VM kernel should suffice. Once compiled you can then copy them inside the VM disk/image like you did for busybox.
You can download and compile your own GCC from source (always on the host), and then install it inside the QEMU virtual machine. This is usually done by mounting the VM disk (QEMU image or whatever you are using) somewhere (e.g. /mnt/my-qemu-disk) and then configuring GCC with --prefix=/mnt/my-qemu-disk/usr/local, building and installing it with make install. This and other stuff is explained in this documentation page.
Once you have GCC installed inside the machine, you should be able to use it as you normally do. You can now use it to compile GNU Make inside the VM, or you can just compile outside in the same way.
For complex stuff like building kernel modules you will probably also need to build and install GNU binutils in the machine, again either from the inside with the GCC you just installed or from the outside.

glibc version for aarch64

I'm cross-compiling an application for aarch64 on my x86 Ubuntu Bionic system, and I have problems with glibc version mismatch. My cross-compile toolchain was using v2.27, while the system that is to run the application has v2.24. I thought that it might be due to my toolchain having a too high version, so I decided to downgrade.
After removing all previous cross-compilation installs, I installed gcc-4.8-aarch64-linux-gnu (as I had successfully cross-compiled the application with this version on a different host system), thinking that it would install an older aarch64 version of glibc to /usr/aarch64-linux-gnu/lib/. However, again, v2.27 was installed (I verified that this directory didn't exist before installing the new cross-compilation toolchain).
So my question is twofold:
What determines which aarch64 version of glibc is installed on my system when installing gcc-4.8-aarch64-linux-gnu? Is it directly tied to my own system's x86 version of glibc?
Is there a correct way to install the aarch64 version of glibc v2.24 (or lower) on my system?
I concur with your hypothesis. After battling similar symptoms for 40 hours straight, I've discovered this confirmation:
https://packages.ubuntu.com/impish/gcc-10-aarch64-linux-gnu
https://packages.debian.org/bullseye/gcc-aarch64-linux-gnu
Note that Ubuntu 21.10 (Impish) and Debian 11 (Bullseye) have packages for a gcc 10 cross compiler. Be wary of the very confusing fact the Ubuntu's default package is actually gcc 11, but Debian 11's default is gcc 10. The similar version numbers of Debian and gcc are a coincidence. Also ignore for now the fact that Ubuntu's package is gcc 10.3.0 and Debian's is gcc 10.2.1.
Focus instead on the recommendations and dependencies of each package. Ultimately the Ubuntu package calls up libc >= 2.34, while the Debian package calls up libc >= 2.28.
Sure enough, when I cross-compile from Impish on x86 for Bullseye on aarch64 (despite having a complete SYSROOT for the target), I get this at runtime:
/lib/aarch64-linux-gnu/libc.so.6: version 'GLIBC_2.34' not found
But your question remains, is there any tie between the host libc and that used by the cross-compiler? The answer is a definite maybe.
See this excellent answer and links for an overview of a cross-compiler. The take-away:
You don't just cross-compile glibc, you need to cross-compile an entire toolchain. Toolchain components are ALWAYS: ld + gcc + libc + gdb.
So the C library is an integral part of the cross-compiler.
What shenanigans then, are going on when you install gcc-aarch64-linux-gnu? It's just a compiler - only one of the four parts of a toolchain.
Well apparently there's some flexibility. Technically, a cross-compiler can be naked. That's typically only useful when you're compiling an operating system, rather than an executable that runs on an operating system. So you can construct special toolchains for special purposes.
But for the standard purpose (cross compiling for Linux on another architecture) you want a typical toolchain. Which is where the package's dependencies and recommendations come in. A gcc is always in want of an ld which is always in want of a libc, and the ménage à trois is intimate. In fact, gcc is built with libc using ld in a complex do-si-do. See this example from a great guide by Preshing on Programming:
It's possible to force separation and link to other libraries, but it's not easy.
For example, the linker you use has a set of default search directories that are baked in. From the fine manual:
The default set of paths searched (without being specified with -L) depends on which emulation mode ld is using, and in some cases also on how it was configured.
And it gets more intwined. By default, gcc will call on a dynamic linker whose location is hard-coded. For a cross-compiler, it might be something like /lib/ld-linux-aarch64.so.1. Not only that, the executable may also end up with the hardcoded path, as its program interpreter.
Again, if you're careful you can tear apart the toolchain and override things. But not only is it tricky to enforce, particularly if you have a complex build, the multitude of combinations of options and paths means there are also often bugs. So your host environment can easily leak into your cross-compiling toolchain.
So in summary, cross-compiling requires a toolchain. While pulling a cross-compiler from a package manager seems like an easy and legitimate thing to do, it comes with a lot of implicit baggage. You can either carefully follow the package dependencies to check what version you're getting, or use one of the many dedicated toolchain environments, such as crosstool-NG.

Linux kernel on ARM Cortex-M: how to build proper executables

I need to build a complete linux development framework for a Cortex-M MCU, specifically a STM32F7 Cortex-M7. First I've to explain some background info so please bear with me.
I've downloaded and built a gcc toolchain with croostool-ng 1.24 specifying an armv7e-m architecture with thumb-only instructions and linux 4.20 as the OS and that I want the output to be FLAT executables (I assumed it will mean bFLT).
Then I proceeded to compile the linux kernel (version 4.20) using configs/stm32_defconf, and then a statically compiled busybox rootfs, all using my new toolchain.
Kernel booted just fine but throw me an error and kernel painc with the following message:
Starting init: /sbin/init exists but couldn't execute it (error -8)
and
request_module: modprobe binfmt-464c cannot be processed, kmod busy with 50 threads
The interesting part is the last message. My busybox excutable turned out to be an .ELF! Cortex-M has no MMU, so it's imposible to build a linux kernel on a MMU-less architecture with .ELF support, that's why an (464c)"LF" binary loader can't be found, there is none.
So at last, my question is:
how could I build bFLT executables to run on MMU-less Linux architectures? My toolchain has elf2flt, but in crosstool-ng I've already specified a MMU-less architecture and FLAT binary and I was expecting direct bFLT output, not a "useless" executable. Is that even possible?
Or better: is there anywhere a documented standard procedure to build a complete, working Linux system based on Cortex-M?
Follow-up:
I gave up on building FLAT binaries and tried FDPIC executables. Another dead end. AFAIK:
Linux has long been supporting ELF FDPIC, but the ABI for ARM is pretty new.
It seems that still at this day and age, GCC has not a standard way to enable FDPIC. On some architectures you can use -mfdpic. Not on arm, don't know why. I even don't know if ARM FDPIC is supported at all by mainline GCC. Info is extremely scarce if inexistent.
It seems crosstool-ng 1.24 is BROKEN at building ARM ELF FDPIC support. Resulting gcc has not -mfdpic, and -fPIC generates ARM executables, not ARM FDPIC.
Any insight will be very appreciated.
you can generate FDPIC ELF files just with a prebuilt arm-linux-gnueabi-gcc compiler.
Specifications of an FDPIC ELF file:
Position independent executable/code (i.e. -fPIE and fPIC)
Should be compiled as a shared executable (ET_DYN ELF) to be position independent
use these flags to compile your programs:
arm-linux-gnueabi-gcc -shared -fPIE -fPIC <YOUR PROGRAM.C> -o <OUTPUT FILE>
I've compiled busybox successfully for STM32H7 with this method.
As I know, unfortunately FDPIC ELFs should be compiled with - shared flag so, they use shared libraries and cannot be compiled as -static ELF.
For more information take a look at this file:
https://github.com/torvalds/linux/blob/master/fs/binfmt_elf_fdpic.c
Track the crosstool-ng BFLAT issue from here:
https://github.com/crosstool-ng/crosstool-ng/issues/1399

Why does qemu performance differ when downloaded from repository and compiled from source?

I am doing some tests on the performance of QEMU (qemu-system-i386) in full emulation mode (with TCG), and avoiding KVM. I have installed the version available in the debian repository (QEMU emulator version 1.1.2 (Debian 1.1.2+dfsg-6a+deb7u6), and downloaded and compiled the sources for Qemu 2.3.0, Qemu 1.0.0, Qemu 1.1.2, and also the source for Debian 1.1.2+dfsg-6a+deb7u6.
I am configuring the project in the following way, in all cases:
./configure --enable-sdl --target-list=i386-softmmu --disable-kvm --enable-tcg-interpreter
The version installed from the repository is quite faster than any of the versions manually compiled. I am always using the same image for the hard drive). I have ensured that kvm is never enabled or loaded:
* Querying qemu (info kvm)
* Looking for loaded drivers (lsmod | grep kvm), and (ls /dev/kvm).
Kvm is not loaded in any of the cases.
I have also tried to change the -O2 by -O3 in the configuration file, and stripped the symbols in the binary (which should not be a difference).
I must be missing something (default configuration for the debian package, compilation options... but I cannot figure out what).
Which could be the reason for this performance difference? (Any idea, experience?)
Thank you very much!
The problem here is your '--enable-tcg-interpreter' argument to configure. This disables the usual JIT-based TCG backend for the host CPU in favour of a slow interpreted backend. You never want the interpreter unless QEMU would otherwise not support your host CPU at all; as you have found, it is markedly slower than the default JIT.

Can GHC link binaries against a libc implementation such as uclibc (used in OpenWrt by default)?

I am using Debian/MIPS+QEMU to build MIPS ports of PortFusion (a TCP tunneling solution). The resulting binaries are linked against GNU libc. Thus, they cannot be just copied over and used on vanilla OpenWrt which ships with uclibc instead of eglibc (which seems binary-compatible with GNU libc).
Is there a way to link Haskell/GHC binaries on Debian/MIPS against uclibc instead of eglibc?
Can OpenWrt's using uclibc be really the reason why PortFusion binaries copied over from Debian fail to run with -ash: binary not found or can this message be due to something entirely else?
See https://github.com/corsis/PortFusion/wiki/MIPS-Builds for details on which haskell-platform, Linux kernel and CPU emulation are used.
The current head of OpenWrt's GIT repository is failing at make when I attempt building custom OpenWrt images that use eglibc instead.
Is there a way to link Haskell/GHC binaries on Debian/MIPS against
uclibc instead of eglibc?
No. You need to to rebuild Haskell/GHC from sources using uclibc-based GCC cross-compiler.
Can OpenWrt's using uclibc be really the reason
Yes. Also, you can try to use ldd on your MIPS pplatform to check what library is missing. I'm sure it will be some of libc-related libraries.

Resources