Ada cross-compiler for bare metal SPARC V9 target

Hello, I’m doing some basic bare metal programming on SPARC V9 and currently using freestanding C cross-compiler to build my stage-1 and stage-2 bootloaders for this target.

I would like to experiment with Ada and was looking at GitHub - AdaCore/bb-runtimes: Source repository for the GNAT Bare Metal BSPs · GitHub however I do not actually need a run time with exceptions, tasking, etc. It looks like Ada supports ZFP (Zero Footprint Profile) but I’m not sure if this is equivalent to a freestanding C environment or if this is much more, which requires manual porting to SPARC V9.

I can build a freestanding C cross-compiler, however I’m looking for pointers on how to achieve something similar for Ada. My previous attempt with GCC --target=sparc64-unknown-elf --enable-languages=c,ada resulted in build failures for some of the Ada components. Maybe I also need to exclude certain Ada standard libraries or port a new ZFP run time?

Currently I build my bootloaders as SPARC V9 ELF64 binaries with -ffreestanding -nostdlib -nostartfiles from C code and I would like to try something similar with Ada instead.

Can anyone suggest how to build a minimal Ada cross-compiler for this target? Do I need ZFP and if yes, how should I go about configuring it?

PS. If it helps, details on bootloaders and how I build Binutils and GCC can be found here:

I’m hoping to document my Ada efforts in Part 3 and may be take it further and develop and small Ada run time for SPARC V9.

Thanks.

I could write an entire article about cross compiling gnat but this should be a good starting point!

It’s nearly done but I still have to learn what some of the configuration flags mean and do. I also had build failures trying to get gcc14 to work but gcc15 just worked in my case. Maybe try other versions?

There is also the osdev.wiki with a guide on a gnat cross compiler and a guide for a bare metal runtime. But I’m not so sure how up to date everything is
GNAT Cross-Compiler - OSDev.wiki , Ada Bare Bones - OSDev.wiki , Ada Runtime Library - OSDev.wiki

You will have to build the runtime yourself tho…
Just yesterday night I found out about this runtime that you can use as a base. GitHub - Fabien-Chouteau/bare_runtime: Minimal Ada/SPARK run-time for embedded or other restricted targets · GitHub
But I haven’t adapted it to my use case yet. (And thus no documentation so far)

OK thanks for the info. It looks like building Ada cross-compiler should be done with the same compiler version, which is different to how I normally build C cross-compilers. My build Ada compiler was gcc-12.2.0 and I was building Ada gcc-15.2.0 which resulted in strange failures. Now building a native gcc-15.2.0 with which I built the final cross-compiler resolved those failures.

So the next step for me is to understand how a minimal Ada run time can be integrated. I’m not yet able to build even the simplest Ada binaries:

$ sparc64-unknown-elf-gcc -c -gnat12 test.adb
fatal error, run-time library not installed correctly
cannot locate file system.ads
compilation abandoned

to add the runtime you need to add --RTS=/path/to/runtime to your compile, bind and link steps.
To compile the runtime you need the -gnatp flag.
Best would be if you just copy the .gpr file and build everything using gprbuild (Adas build system).

Here is also a small tip by me. If you have some undefined references like __gnat_somthing missing you can just add it to a stub.c file, give it an empty definition and link it at the end. Your runtime is probably broken now but it’s not guaranteed highly probably still working.

Minimal runtime is available GitHub - Fabien-Chouteau/bare_runtime: Minimal Ada/SPARK run-time for embedded or other restricted targets · GitHub

As far as I remember it is platform independent and there is chance that it is compiler independed, so you can try to use it with your compiler.

Try to download it, cd into runtime directory, run gprbuild -P bare_runtime.gpr. If it completes well - you have ready to use minimal runtime. To use it specify path to runtime directory in the project file

   for Runtime ("Ada") use "<path/to/runtime>";

Thanks, but it looks like I’m going to have issues with bare_runtime, as something seems to be missing there:

gprbuild --config=test.cgpr -Pbare_runtime.gpr

a-elchha.adb:43:06: error: file “bare_runtime_config.ads” not found
gprbuild: *** compilation phase failed

It’s because it is an Alire based project and bare_runtime_config.ads is generated by Alire, so you would first need to install Alire (if you don’t have it) and then run the command alr build in the project’s directory. It’ll generate the file.

Note that the top level alire.toml file also has some project specific instructions to be aware of.

Thanks for the tip. I looked at the included alire.toml file but was hoping gprbuild alone was going to be sufficient. I’ll get the alr tool installed and try again.

There’s a lot of people here with Alire experience, so definitely ask if you run into any problems. I’m less experienced with it myself though.

If you have trouble installing it, there is also a resource for some pre made installation packages and instruction (by platform) reference at: https://www.getada.dev/

How do I force alr to use my external toolchain. I’ve done ~/bin/alr toolchain --select

$ ~/bin/alr toolchain
CRATE VERSION STATUS NOTES
gprbuild 18.0.0 Default Detected at /bin/gprbuild
gprbuild 2023.0.0 Available Provided by system package: gprbuild
gnat_native 14.2.1 Available
gnat_external 15.2.0 Default Detected at /home/user/gcc-15.2.0_native/bin/gnat

But it keeps insisting on using older system compiler

$ ~/bin/alr build -- -v
ⓘ Building bare_runtime=14.0.0-dev/bare_runtime.gpr…
Changing to object directory of “Bare_Runtime”: “/home/user/tmp4/bare_runtime-main/obj-x86_64-linux-gnu/”
/usr/bin/gcc-12 -c -x ada -gnatA --RTS=/home/user/tmp4/bare_runtime-main/ -gnatg -nostdinc -fno-delete-null-pointer-checks -gnaty-d -gnatp -gnatn2 -gnaty-z -fcallgraph-info=su,da -ffunction-sections -fdata-sections -O2 -gnatec=/tmp/GNAT-TEMP-000003.TMP -gnatem=/tmp/GNAT-TEMP-000004.TMP /home/user/tmp4/bare_runtime-main/src/unchdeal.ads

Use of Alire might be tricky at this stage. There is no SPARC compiler in it, I suggest to build runtime with raw gprbuild first.

Even at this stage it might be necessary to do updates in its knowledge base.

PS. Try to set dependency from just gnat in alire.toml. Don’t forget to set target in project file properly.

I’ve used Ada with gnatmake and gprbuild for some of my project, both have good documentation. A few years ago I looked at alr but did not like it at all, as for my use cases, I’m not interested in package managers or downloading other people’s crates. I just want to get on with my own projects and gprbuild is sufficient.

It seems some Ada repos like bare_runtime have dependencies on alr package manager to automagically generate some config files, without which I get build errors. I don’t like this trend and much prefer static project files. I’ll keep trying but if anyone knows how to build this without using alr then let me know. I created a test.cgpr config file to point gprbuild to my SPARC V9 cross-compiler. The build starts OK, but as mentioned previously, it later fails due to missing bare_runtime_config.ads file. In the previous failed attempts to use alr this file is output in the config/ directory and contains some nonsense about crate name and version. I may just try and hack the code to completely remove references to these config files and see how far this gets me.

You override the compiler selection in your gpr file by defining which driver to use

package Compiler is
   for Driver ("Ada") use "/path/to/compiler";
end Compiler;

Generate this file by Alire, or remove corresponding with clause from the project file and fix possible issues.

It would be useful to have a “dealire” script that would create a working gpr-file for the crates.

It wouldn’t be just a script, I usually build my own cross-compilers + tools and document the entire process. It is a good learning experience.

If it helps, I don’t think you need to fully build it with alire. I was just saying you could use it to generate that file. See if that file was generated (I think in a config subfolder) and if so you can go back to building like you normally do with gnatmake/gprbuild

OK thanks for the tips. I managed to build bare_runtime with my cross-compiler after making a few small source code changes to it. I’m a bit confused on the workflow for bare metal Ada:

I built Ada cross-compiler with --disable-libada however this resulted in no gnatlink utility:

$ ls -1 ~/gcc-15.2.0_sparc64-unknown-elf/bin/*gnat*
/home/user/gcc-15.2.0_sparc64-unknown-elf/bin/sparc64-unknown-elf-gnatbind

So I have cross binutils + gcc and gnatbind. Do I need gnatlink or for bare metal targets linking should be done manually with the cross linker?

I looked at this page Porting the Ada Runtime to a new ARM board | AdaCore howerver it does not mention gnatlink.

As far as I could tell gprbuild doesn’t use gnatlink. It uses gcc. (That’s also what I use)

Edit: I should add that you can define which gcc gpr should use during linking in the gprfile using Driver. Eg:

package Linker is
   Driver use "/path/to/gcc";
end Linker 

OK thanks for the info. I used a combination of gnatmake and gprbuild with different projects and gnatlink may be specific to gnatmake workflow. I’ll stick with gprbuild as it is more flexible.