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

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

Related

Emscripten Clang produce ELF 64-bit executabel and wasm binary cross compiler targets

I have a prepared a minimal Cmake project containing one cpp file which represent the main and one cpp file which represent the shared library, that prints basically hello world.
https://github.com/courteous/wasmELF.git
The target is to compile this miniaml code with emscripten/clang only and produce
1) one WebAssembly (wasm) binary module version 0x1 (MVP)
2) one ELF 64-bit LSB
without clearing the cmake build directory and rebuilding it again.
Currently i can successfully produce them bought by running the commands
emconfigure cmake ../ -DCMAKE_BUILD_TYPE=WASM
make
and
cmake ../ -DCMAKE_BUILD_TYPE=Linux
make
However the problem is that in order to do that i need to compile the first one with Clang the to remove the build and then to do a second compilation with GCC. I would like Emscripten/Clang to produce them bought instead. I do not want to delete the build directory since the compilation times is taking too long. (Well not in this Project but imagine if the project was much larger)
What i see is that emscripten/clang selects always a target "wasm32-unknown-emscripten"
clang++ -target wasm32-unknown-emscripten
and if i understand that correctly the target should change
I do see that the project is producing LLVM IR bitcode since i have send the flag "flto"
i.e.
file TestSharedClass.cpp.o
TestSharedClass.cpp.o: LLVM IR bitcode
and in the CMakeLists.txt
set(CMAKE_CXX_FLAGS "-flto")
x86_64-unknown-linux-gnu is a supported target by emscripten/Clang
~/Projects/emscripten/emsdk/upstream/bin$ ./llc --version
LLVM (http://llvm.org/):
LLVM version 11.0.0git
Optimized build with assertions.
Default target: x86_64-unknown-linux-gnu
Host CPU: haswell
Registered Targets:
wasm32 - WebAssembly 32-bit
wasm64 - WebAssembly 64-bit
x86 - 32-bit X86: Pentium-Pro and above
x86-64 - 64-bit X86: EM64T and AMD64
In cmake i do have
SET(TARGET x86_64-unknown-linux-gnu)
however when i run
emconfigure cmake ../ -DCMAKE_BUILD_TYPE=Linux
make
i get mainTestFile.js and mainTestFile.wasm instead of ELF 64-bitcode.
what i am doing wrong here. How to tell clang to product once ELF and once wasm from the same code run without having to clear the build directory. This should be possible since clang is producing LLVM IR bitcode. Or do i understand that wrong?
https://github.com/emscripten-core/emscripten/issues/10361
OK that seems to not be possible i.e. the reply from the dev on github states that emcc or emmake can not be used with another target other then wasm32-unknown-emscripten.

ARM cross-compilation, "VFP registered used by x.out, not y.o" issue on Linux but not on Windows

I am cross-compiling a project for an ARM Cortex-M4F target. I have been cross-compiling this project successfully for months on a Windows 7 PC and I have now to be able to cross-compile it also from a Linux environment (running in a virtual machine inside the Windows environment). The build is done with the exact same portable Makefile so the compiler and linker calls are exactly the same, except for the paths.
While everything goes well on Windows, I have a bunch of errors on Linux (Ubuntu 18.04 (Bionic Beaver)) when the linker is called (compilation of all files goes well):
/usr/lib/gcc/arm-none-eabi/6.3.1/../../../arm-none-eabi/bin/ld: error: /usr/lib/gcc/arm-none-eabi/6.3.1/../../../arm-none-eabi/lib/crt0.o: Conflicting CPU architectures 13/1
/usr/lib/gcc/arm-none-eabi/6.3.1/../../../arm-none-eabi/bin/ld: failed to merge target specific data of file /usr/lib/gcc/arm-none-eabi/6.3.1/../../../arm-none-eabi/lib/crt0.o
myProgram.out uses VFP register arguments, /usr/lib/gcc/arm-none-eabi/6.3.1/../../../arm-none-eabi/lib/libc_nano.a(lib_a-nano-vfprintf_i.o) does not [And more like this...]
I've read the question & answers of ARM compilation error, VFP registered used by executable, not object file as well as How to (cross-)compile to both ARM hard- and soft-float (softfp) with a single GCC (cross-)compiler? and although they were really helpful to understand one part of the problem, none of them led me to a solution.
On both OSes I am using arm-none-eabi-gcc V6.3.1:
Windows 7 64-bit
gcc version 6.3.1 20170215 (release) [ARM/embedded-6-branch revision 245512] (GNU Tools for ARM Embedded Processors 6-2017-q1-update)
Ubuntu 18.04
gcc version 6.3.1 20170620 (15:6.3.1+svn253039-1build1)
And both seem to have similar multi-lib:
Windows 7 64-bit
.;
thumb;#mthumb
hard;#mfloat-abi=hard
thumb/v6-m;#mthumb#march=armv6s-m
thumb/v7-m;#mthumb#march=armv7-m
thumb/v7e-m;#mthumb#march=armv7e-m
thumb/v7-ar;#mthumb#march=armv7
thumb/v8-m.base;#mthumb#march=armv8-m.base
thumb/v8-m.main;#mthumb#march=armv8-m.main
thumb/v7e-m/fpv4-sp/softfp;#mthumb#march=armv7e-m#mfpu=fpv4-sp-d16#mfloat-abi=softfp
thumb/v7e-m/fpv4-sp/hard;#mthumb#march=armv7e-m#mfpu=fpv4-sp-d16#mfloat-abi=hard
thumb/v7e-m/fpv5-sp/softfp;#mthumb#march=armv7e-m#mfpu=fpv5-sp-d16#mfloat-abi=softfp
thumb/v7e-m/fpv5-sp/hard;#mthumb#march=armv7e-m#mfpu=fpv5-sp-d16#mfloat-abi=hard
thumb/v7e-m/fpv5/softfp;#mthumb#march=armv7e-m#mfpu=fpv5-d16#mfloat-abi=softfp
thumb/v7e-m/fpv5/hard;#mthumb#march=armv7e-m#mfpu=fpv5-d16#mfloat-abi=hard
thumb/v7-ar/fpv3/softfp;#mthumb#march=armv7#mfpu=vfpv3-d16#mfloat-abi=softfp
thumb/v7-ar/fpv3/hard;#mthumb#march=armv7#mfpu=vfpv3-d16#mfloat-abi=hard
thumb/v8-m.main/fpv5-sp/softfp;#mthumb#march=armv8-m.main#mfpu=fpv5-sp-d16#mfloat-abi=softfp
thumb/v8-m.main/fpv5-sp/hard;#mthumb#march=armv8-m.main#mfpu=fpv5-sp-d16#mfloat-abi=hard
thumb/v8-m.main/fpv5/softfp;#mthumb#march=armv8-m.main#mfpu=fpv5-d16#mfloat-abi=softfp
thumb/v8-m.main/fpv5/hard;#mthumb#march=armv8-m.main#mfpu=fpv5-d16#mfloat-abi=hard
Ubuntu 18.04
.;
thumb;#mthumb
hard;#mfloat-abi=hard
thumb/v6-m;#mthumb#march=armv6s-m
thumb/v7-m;#mthumb#march=armv7-m
thumb/v7e-m;#mthumb#march=armv7e-m
thumb/v7-ar;#mthumb#march=armv7
thumb/v7e-m/fpv4-sp/softfp;#mthumb#march=armv7e-m#mfpu=fpv4-sp-d16#mfloat-abi=softfp
thumb/v7e-m/fpv4-sp/hard;#mthumb#march=armv7e-m#mfpu=fpv4-sp-d16#mfloat-abi=hard
thumb/v7e-m/fpv5/softfp;#mthumb#march=armv7e-m#mfpu=fpv5-d16#mfloat-abi=softfp
thumb/v7e-m/fpv5/hard;#mthumb#march=armv7e-m#mfpu=fpv5-d16#mfloat-abi=hard
thumb/v7-ar/fpv3/softfp;#mthumb#march=armv7#mfpu=vfpv3-d16#mfloat-abi=softfp
thumb/v7-ar/fpv3/hard;#mthumb#march=armv7#mfpu=vfpv3-d16#mfloat-abi=hard
thumb/v7-ar/fpv3/hard/be;#mthumb#march=armv7#mfpu=vfpv3-d16#mfloat-abi=hard#mbig-endian
From what I've read here and there it's very important to specify if we want hard or soft float implementation. I want to use the hardware, so every gcc call (assembly compiler, C compiler, linker) has those two flags:
-mfloat-abi=hard -mfpu=fpv4-sp-d16
Here are the other compiler flags:
-ffunction-sections -fdata-sections -fno-strict-aliasing -fno-builtin --short-enums -MP -MD
And linker flags:
-Xlinker -mthumb -mabi=aapcs -mcpu=cortex-m4 -lrdimon -u _printf_float --specs=nano.specs -lc -lnosys -Wl,--gc-sections
I simply don't see here what could be the cause of those error messages since the implementation is specified every time. The only hypothesis I have is that some libraries (like lib_a-nano that we see in the error) are built for a soft implementation. But let's say this is the issue, how do I use the same library built for the hard implementation?

Can gcc produce binary for Arm without cross compilation

Can we configure gcc running on intel x64 architecture to produce binary for ARM chip by just passing some flags to gcc and not using a cross compiler.
Short: Nope
Compiler:
gcc is not a native crosscompiler, the target architecture has to be specified at the time you compile gcc. (Some exceptions apply, as for example x86 and x86_64 can be supported at the same time)
clang would be a native crosscompiler, and you can generate code for arm by passing -target=arm-linux-gnu, but you still cant produce binaries, as you need a linker and a C-library too. Means you can run clang -target=arm-linux-gnu -c <your file> and compile C/C++ Code (will likely need to point it to your C/C++ include paths) - but you cant build binaries.
Rest of the toolchain:
You need a fitting linker and toolchain too, both are specific to the architecture and OS you want to run at.
Possible solutions:
Get a fitting toolchain, or compile your own. For arm linux you have for ex. CrossToolchains if you are on debian, for barebones you can get a crosscompiler from codesourcery.
Since you were very vague, its not possible to give you a clearer answer

GCC: Difference between buildroot gcc and precompiled gcc (installed with APT)?

I'm trying to make custom binaries for initrd for x86 system. I took generic precompiled Debian 7 gcc (version 4.7.2-5) and compiled kernel with it. Next step was to make helloworld program instead of init script in initrd to check my development progress. Helloworld program was also compiled with that gcc. When I tried to start my custom system, kernel started with no problem, but helloworld program encountered some errors:
kernel: init[24879] general protection ip:7fd7271585e0 sp:7fff1ef55070 error:0 in init[7fd727142000+20000]
(numbers are not mine, I took similar string from google). Helloworld program:
#include <stdio.h>
int main(){
printf("Helloworld\r\n");
sleep(9999999);
return 0;
}
Compilation:
gcc -static -o init test.c
Earlier I also had stuck with same problem on ARM system (took generic compiler, compiled kernel and some binaries with it and tried to run, kernel runs, but binary - not). Solved it with complete buildroot system, and took buildroot compiler in next projects.
So my question is: what difference between gcc compiled as part of buildroot and generic precompiled gcc?
I know that buildroot compiler is made in several steps, with differenet libs and so on, is this main difference, platform independence?
I don't need a solution, I can take buildroot anytime. I want to know source of my problem, to avoid such problems in future. Thanks.
UPD: Replaced sleep with while(1); and got same situation. My kernel output:
init[1]: general protection ip: 8053682 sp: bf978294 error: 0 in init[8048000+81000]
printk: 14300820 message suppressed.
and repeating every second.
UPD2: I added vdso32-int80.so (original name, like in kernel tree), tested - no luck.
I added ld-linux.so (2 files: ld-2.13.so with symbolic link), tested - same error.
Busybox way allows to run binaries without any of this libraries, tested by me on ARM platform.
Thanks for trying to help me, any other ideas?

How do I compile and link a 32-bit Windows executable using mingw-w64

I am using Ubuntu 13.04 and installed mingw-w64 using apt-get install mingw-w64. I can compile and link a working 64-bit version of my program with the following command:
x86_64-w64-mingw32-g++ code.cpp -o app.exe
Which generates a 64-bit app.exe file.
What binary or command line flags do I use to generate a 32-bit version of app.exe?
That depends on which variant of toolchain you're currently using. Both DWARF and SEH variants (which come starting from GCC 4.8.0) are only single-target. You can see it yourself by inspecting the directory structure of their distributions, i.e. they contain only the libraries with either 64- or 32-bit addressing, but not both. On the other hand, plain old SJLJ distributions are indeed dual-target, and in order to build 32-bit target, just supply -m32 flag. If that doesn't work, then just build with i686-w64-mingw32-g++.
BONUS
By the way, the three corresponding dynamic-link libraries (DLLs) implementing each GCC exception model are
libgcc_s_dw2-1.dll (DWARF);
libgcc_s_seh-1.dll (SEH);
libgcc_s_sjlj-1.dll (SJLJ).
Hence, to find out what exception model does your current MinGW-w64 distribution exactly provide, you can either
inspect directory and file structure of MinGW-w64 installation in hope to locate one of those DLLs (typically in bin); or
build some real or test C++ code involving exception handling to force linkage with one of those DLLs and then see on which one of those DLLs does the built target depend (for example, can be seen with Dependency Walker on Windows); or
take brute force approach and compile some test code to assembly (instead of machine code) and look for presence of references like ___gxx_personality_v* (DWARF), ___gxx_personality_seh* (SEH), ___gxx_personality_sj* (SJLJ); see Obtaining current GCC exception model.

Resources