Ada Bare Bones: Difference between revisions

3,903 bytes removed ,  29 days ago
m
Bot: Replace deprecated source tag with syntaxhighlight
[unchecked revision][unchecked revision]
m (s/dword/uint32_t/g)
m (Bot: Replace deprecated source tag with syntaxhighlight)
 
(22 intermediate revisions by 6 users not shown)
Line 1:
''The aim of this tutorial is to produce a small Multiboot kernel written in the Ada programming language.''
{{In Progress}}
{{Rating|2}}{{Template:Kernel designs}}
 
==Preface==
''In this tutorial we will compile a simple Ada kernel and boot it.''
 
One of the first things people ask on the Ada IRC channel on Freenode is ''"Can Ada be used for OS development?"'' to which the answer is a resounding '''yes'''.
<big><b>WAIT! Have you read [[Getting Started]], [[Beginner Mistakes]], and some of the related [[:Category:OS theory|OS theory]]?</b></big>
 
=== Prerequisites ===
==Preface==
 
The following section details the prerequisite software necessary for successfully building the kernel.
This tutorial is based on my multiboot kernel which I developed some time ago (originating from 2000!!) and placed on my site [http://www.archeia.com/an-ada95-hello-world-style-kernel.html] and will also be the basis for my own kernel [https://github.com/Lucretia/tamp TAMP].
 
==== Cross-Compiler ====
One of the first things people ask on the Ada IRC channel on Freenode is "Can Ada be used for OS development?" to which the answer is a resounding '''yes'''. But there are 2 problems:
 
{{Main|GNAT Cross-Compiler}}
# The people asking this question are new to Ada, and
# GNAT is not the easiest compiler to build.
 
This tutorial requires the use of a [[GCC_Cross-Compiler|cross-compiler]] with Ada support targeting the <tt>i686-elf</tt> architecture. Theoretically, any Ada compiler capable of producing bare-metal ELF binaries for this architecture is suitable. The recommended compiler is [https://www.gnu.org/software/gnat/ GNAT], which is part of the GNU Compiler Collection maintained by the Free Software Foundation. Historically it was difficult to create cross-compiler builds for GNAT, but recent versions of GCC have made this much simpler. For a tutorial on how to set up a GNAT cross-compiler, refer to [[GNAT Cross-Compiler|this article]].
Therefore these users don't understand what it takes to get the compiler into a useable state.
 
==== GPRbuild ====
As you may have seen from other bare bones tutorials on this site, they state that you must have a compiler built which can handle [[ELF]] files, the usual way is by building GCC which targets i386-elf or some other similar architecture. The problem here is that GNAT will not build for these targets out of the box without messing with it's makefile. You have to do this as the makefile builds the RTS and then the gnat tools (gnatmake, gnatbind, et al) which must all be built to have a working compiler - even though we will be replacing the RTS with our own cut down version.
 
This tutorial will require the use of the [https://docs.adacore.com/gprbuild-docs/html/gprbuild_ug.html GPRBuild] utility to build GNAT project files. GPRbuild is a generic build tool designed for the construction of multi-language systems. It is included with AdaCore's distribution of their proprietary GNAT compiler: [https://www.adacore.com/download] or can be built from source files obtained from AdaCore's [https://github.com/AdaCore/gprbuild github profile]. Older versions of GNAT distributed by the Free Software Foundation were able to build GNAT project files using the <tt>gnatmake</tt> utility, however newer versions do not support all the required features.
For this tutorial, we will use the system GNAT compiler to build for a PC i386. GNAT is part of GCC. Later I will show how to build an arm-elf compiler and tools. My OS is Debian testing 64-bit with GNAT 4.6.
 
The cross-compiler mentioned above will need to be properly integrated with GPRbuild and its associated tools. This can be accomplished by following the guide detailed [[GNAT_Cross-Compiler#GPRbuild_Integration|here]].
==GNAT and the Ada runtime system (RTS)==
 
== Ada run-time library ==
For this kernel we will be configuring a zero footprint RTS profile. This basically means, we have a compiler, tools and not much else.
{{Main|Ada Runtime Library}}
 
The Ada run-time library is responsible for the implementation of the standard library as defined in the [http://ada-auth.org/standards/12rm/html/RM-TOC.html Ada Language Reference Manual]. Not all features outlined in the reference manual need to be implemented for every platform. Since the kernel will be running in a freestanding environment without the support of an underlying operating system, it will be built with what is known as a ''a zero-footprint run-time system'' (commonly abbreviated as 'ZFP'). A zero-footprint RTS will typically provide only the bare-minimum functionality for the target architecture.
===Directory structure===
 
The following steps will detail how to create an initial zero-footprint runtime library suitable for building the kernel.
We need a place to structure this kernel,
 
<source lang="bash">
The first step is to set up the directory structure to hold the runtime library. GNAT expects the runtime library to conform to the a set directory structure. It requires the presence of two directories: <tt>adalib</tt> and <tt>adainclude</tt>. <tt>adainclude</tt> contains the runtime package specifications and source files, which are used analogously to include files in C. <tt>adalib</tt> contains the compiled binary artifacts of the runtime library.
 
The following code demonstrates setting up a directory structure for the RTS:
 
<syntaxhighlight lang="bash">
mkdir -p bare_bones/src/pc
cd bare_bones
Line 35 ⟶ 39:
mkdir -p rts/src
mkdir -p obj
</syntaxhighlight>
</source>
 
The best way to begin creating a new run-time library is to base it upon the existing source files obtained from the system GNAT installation, modifying them where necessary.
===RTS files to copy===
 
YouThe willfollowing needcode todemonstrates copycopying the followingrequired files from yourthe compilerhost system's RTSGNAT directoryinstallation's run-time library into <tt>rts/src</tt> and then createcreating symbolic links from them to <tt>rts/boards/<${arch>}/adainclude</tt>. whereWhere your ''<tt>${arch''}</tt> is <tt>i386</tt>, or arm<tt>armv6</tt>, etc. The source location will need to be modified to reflect the location of the system compiler.
 
<syntaxhighlight lang="bash">
'''N.B:''' You need to modify the location where these files are copied from, I've just used the location from my machine, which is most likely different to yours.
system_compiler_dir="/usr/lib/gcc/x86_64-linux-gnu/8"
rts_dir="${PWD}/rts"
 
<source lang="bash">
for f in "ada.ads" "a-unccon.ads" "a-uncdea.ads" "gnat.ads" "g-souinf.ads" \
"interfac.ads" "s-atacco.adb" "s-atacco.ads" "s-maccod.ads" "s-stoele.adb" \
"s-stoele.ads"
do
cp /usr/lib/gcc/x86_64-linux-gnu/4.6"${system_compiler_dir}/adainclude/$f" rts"${rts_dir}/src/"
ln -s `pwd`/rts"${rts_dir}/src/$f" `pwd`"${rts_dir}/rts/boards/i386/adainclude/$f"
done
</syntaxhighlight>
</source>
 
==Files==
Line 57 ⟶ 62:
===gnat.adc===
 
During compilation the GNAT compiler will search for a file named <tt>gnat.adc</tt> within the current working directory. This file is expected to contain one or more configuration pragmas that will be applied to the current compilation. These pragma directives instruct the compiler what features of the runtime should be restricted. This is of particular importance to kernel development since many of Ada's features are not supported by a freestanding, bare-metal environment.
This file in the root directory of the build tells GNAT there are some configuration pragmas to apply to the build. These pragmas can also be placed at the start of your custom sytem.ads (see below), but we'll place them here for now.
 
These pragma directives can alternatively be placed at the start of the runtime library's <tt>system.ads</tt> file (see below), however convention dictates using the <tt>gnat.adc</tt> file for this purpose.
'''Note:''' ''Do not use '''pragma No_Run_Time''' as it is obsolete and has been for a number of years now!''
 
Additionally, it is possible to apply additional global configuration files by appending the following line to the <tt>Builder</tt> package in a GNAT project file:
What these do is to tell GNAT how much of the RTS we can use in our kernel, which is not a lot really.
 
<sourcesyntaxhighlight lang="ada">
package Builder is
-- ...
for Global_Configuration_Pragmas use "kernel.adc";
end Builder;
</syntaxhighlight>
 
It is also possible to instruct the compiler to apply additional files containing configuration pragmas to the current compilation using the switch <tt>-gnatec=path</tt> on the command line. These configuration pragmas are applied in addition to those found in <tt>gnat.adc</tt>, if it is present.
More information on configuration files can be found in the GNAT documentation: [https://gcc.gnu.org/onlinedocs/gnat_ugn/Handling-of-Configuration-Pragmas.html]
 
The GNAT Reference Manual and the Ada Reference Manual provide information on the various configuration pragmas.
See below for a list of restriction pragmas useful for a bare bones kernel and runtime:
 
<syntaxhighlight lang="ada">
pragma Discard_Names;
pragma Restrictions (No_Enumeration_Maps);
Line 77 ⟶ 95:
pragma Restrictions (No_Implicit_Dynamic_Code);
pragma Restrictions (No_Secondary_Stack);
</syntaxhighlight>
</source>
 
The final version of [https://github.com/Lucretia/bare_bones/blob/master/gnat.adc gnat.adc] can be downloaded from GitHub.
 
By passing the '''-r''' flag to the binder (inside the bare_bones.gpr file), the binder will list further restrictions you can apply to enforce further checks.
 
<source lang="ada">
package Binder is
for Default_Switches ("Ada") use ("-r");
end Binder;
</source>
 
'''Note:''' ''Do not use <tt>pragma No_Run_Time</tt>. It is obsolete.''
Between the GNAT Reference Manual and the Ada 2005 Reference Manual, you can find out what the various pragmas are and what they do.
 
Below is an explanation of these configuration pragmas:
====Discard_Names====
 
{| {{wikitable}}
In Ada, the compiler generates strings for various data types, e.g. enumerations, these strings can then be used in I/O.
! Restriction
! Purpose
|-
| Discard_Names || The Ada compiler automatically generates strings containing the names of enumerated data types, among others. These strings can be used in I/O.
 
<sourcesyntaxhighlight lang="ada">
type Fruit is (Orange, Banana, Apple);
-- Ada defines the following strings, "Orange", "Banana" and "Apple" in an array.
Line 102 ⟶ 114:
Put (Fruit'Image (Orange));
-- Prints "Orange" to the console.
</syntaxhighlight>
</source>
 
This pragmadirective tellsinstructs the compiler not to generate these tablesstrings.
|-
| Normalize_Scalars || Forces all scalars to be initialised. Refer to [http://gcc.gnu.org/onlinedocs/gcc-4.7.0/gnat_rm/Pragma-Normalize_005fScalars.html#Pragma-Normalize_005fScalars GNAT RM:Normalize_Scalars] for more information.
|-
| No_Exception_Propagation || This directive forces the compiler to disallow any attempt to raise an exception over a subprogram boundary. Refer to [http://gcc.gnu.org/onlinedocs/gcc-4.7.0/gnat_rm/No_005fException_005fPropagation.html#No_005fException_005fPropagation GNAT RM:No_Exception_Propagation] for more information.
 
Note: The [http://docs.adacore.com/gnat-hie-docs/html/gnathie_ug_3.html#SEC8 GNAT High Integrity Edition] documentation states the following:
====Normalize_Scalars====
 
Forces all scalars to be initialised, see the latest [http://gcc.gnu.org/onlinedocs/gcc-4.7.0/gnat_rm/Pragma-Normalize_005fScalars.html#Pragma-Normalize_005fScalars GNAT RM:Normalize_Scalars] for more information.
 
====No_Exception_Propagation====
 
This forces the compiler to disallow any attempt to raise an exception over a subprogram boundary. All exceptions are caught with the Last_Chance_Handler subprogram. See [http://gcc.gnu.org/onlinedocs/gcc-4.7.0/gnat_rm/No_005fException_005fPropagation.html#No_005fException_005fPropagation GNAT RM:No_Exception_Propagation] for more information.
 
The [http://docs.adacore.com/gnat-hie-docs/html/gnathie_ug_3.html#SEC8 GNAT High Integrity Edition] documentation states the folowing:
 
<blockquote>
Exception declarations and raise statements are still permitted under this restriction. A raise statement is compiled into a call of <tt>__gnat_last_chance_handler</tt>.
</blockquote>
 
All exceptions that are not handled with an explicit exception handler within its subprogram will be caught with the <tt>Last_Chance_Handler</tt> subprogram. This will cause a warning to be issued at compile time.
I have been unable, thus far, to raise my own exceptions, although I can declare one at library level. On placing a declaration inside console.ads:
|-
| No_Exception_Registration || Ensures no stream operations are performed on types declared in Ada.Exceptions. See [http://gcc.gnu.org/onlinedocs/gcc-4.7.0/gnat_rm/No_005fException_005fRegistration.html#No_005fException_005fRegistration GNAT RM:No_Exception_Registration] for more information.
|-
| No_Finalization
| Restricts the use of controlled types. Refer to [http://gcc.gnu.org/onlinedocs/gcc-4.7.0/gnat_rm/No_005fFinalization.html#No_005fFinalization GNAT RM:No_Finalization] for more information.
|-
| No_Tasking
| This directive restricts all features related to tasking, including the use of protected objects. Refer to [http://gcc.gnu.org/onlinedocs/gcc-4.7.0/gnat_rm/No_005fTasking.html#No_005fTasking GNAT RM:No_Tasking] for more information.
|-
| No_Protected_Types
| This reinforces the above restriction. Refer to [http://gcc.gnu.org/onlinedocs/gcc-4.7.0/gnat_rm/No_005fProtected_005fTypes.html#No_005fProtected_005fTypes GNAT RM:No_Protected_Types] for more information.
|-
| No_Delay
| Restricts the use of <tt>delay</tt> statements or the calendar package. Refer to [http://gcc.gnu.org/onlinedocs/gcc-4.7.0/gnat_rm/No_005fDelay.html#No_005fDelay GNAT RM:No_Delay] for more information.
|-
| No_Recursion
| Restricts the use of recursion. Refer to [http://gcc.gnu.org/onlinedocs/gcc-4.7.0/gnat_rm/No_005fRecursion.html#No_005fRecursion GNAT RM:No_Recursion] for more information.
|-
| No_Allocators
| Restricts the use of dynamic memory. This is essential for a bare-metal application, since there is no underlying facility for allocation of dynamic memory. Refer to [http://gcc.gnu.org/onlinedocs/gcc-4.7.0/gnat_rm/No_005fAllocators.html#No_005fAllocators GNAT RM:No_Allocators] for more information.
|-
| No_Dispatch
| Disallows calling a subprogram using Ada's object-orientated mechanism. Refer to [http://gcc.gnu.org/onlinedocs/gcc-4.7.0/gnat_rm/No_005fDispatch.html#No_005fDispatch GNAT RM:No_Dispatch] for more information.
|-
| No_Implicit_Dynamic_Code
| Disallows nested subprograms or any other features that generate trampolines on the stack. Refer to [http://gcc.gnu.org/onlinedocs/gcc-4.7.0/gnat_rm/No_005fImplicit_005fDynamic_005fCode.html#No_005fImplicit_005fDynamic_005fCode GNAT RM:No_Implicit_Dynamic_Code] for more information.
|-
| No_Secondary_Stack
| Ada uses a ''secondary stack'' to return unconstrained types, such as arbitrary length strings or variant records. This directive instructs the compiler that there is no secondary stack present, and to restrict the use of code that requires one. Refer to [http://gcc.gnu.org/onlinedocs/gcc-4.7.0/gnat_rm/No_005fSecondary_005fStack.html#No_005fSecondary_005fStack GNAT RM:No_Secondary_Stack] for more information.
|}
 
Passing the <tt>-r</tt> flag to the binder instructs it to emit a list of further restrictions that are possible to apply to the project.
<source lang="ada">
<syntaxhighlight lang="ada">
TE : exception; -- A badly named exception.
package Binder is
</source>
for Default_Switches ("Ada") use ("-r");
 
end Binder;
and raising inside bare_bones.adb:
</syntaxhighlight>
 
<source lang="ada">
raise Console.TE;
exception
when Console.TE =>
Put ("TE caught", 1, 2);
</source>
 
and upon compiling, I get the following:
 
<pre>
bare_bones.adb:17:04: construct not allowed in configurable run-time mode
bare_bones.adb:17:04: file a-except.ads not found
bare_bones.adb:17:04: entity "Ada.Exceptions.Raise_Exception" not available
gnatmake: "/home/laguest/src/mine/bare_bones/src/bare_bones.adb" compilation error
make: *** [disk/boot/bare_bones] Error 4
</pre>
 
so the compiler is looking for the Ada.Exceptions package which defines the normal language standard exception handling, not the cut down version we are using here. Do the same with one of the language defined exceptions works fine. ''I will file a bug at FSF for this as I believe it to be incorrect''.
 
I have had it confirmed via a [http://gcc.gnu.org/bugzilla/show_bug.cgi?id=53684 bug I filed] that the programmer is not allowed to declare their own exceptions and catch them. I have experimented by creating a version of the Ada.Exceptions (a-except.ad[sb]) package:
 
<source lang="ada">
with System;
 
package Ada.Exceptions is
pragma Preelaborate (Exceptions);
 
type Exception_Id is private;
pragma Preelaborable_Initialization (Exception_Id);
 
Null_Id : constant Exception_Id;
 
procedure Raise_Exception
(E : Exception_Id;
Message : String := "");
pragma No_Return (Raise_Exception);
private
type Exception_Id is new Natural;
 
Null_Id : constant Exception_Id := Exception_Id'First;
end Ada.Exceptions;
 
with GNAT.Source_Info;
with Last_Chance_Handler;
 
package body Ada.Exceptions is
procedure Raise_Exception
(E : Exception_Id;
Message : String := "") is
pragma Unreferenced (E);
pragma Unreferenced (Message);
File : String := GNAT.Source_Info.File;
Line : Positive := GNAT.Source_Info.Line;
Source_Location : String := GNAT.Source_Info.Source_Location;
Enclosing_Entity : String := GNAT.Source_Info.Enclosing_Entity;
pragma Unreferenced (File, Line, Source_Location, Enclosing_Entity);
begin
Last_Chance_Handler (System.Null_Address, 0);
 
loop
null;
end loop;
end Raise_Exception;
end Ada.Exceptions;
</source>
 
This seemed to work until I tried to catch the exception inside bare_bones.adb at which point the compiler threw up a number of warnings, resulting in a compile failure:
 
<pre>
bare_bones.adb:23:17: warning: pragma Restrictions (No_Exception_Propagation) in effect
bare_bones.adb:23:17: warning: "Constraint_Error" may call Last_Chance_Handler
bare_bones.adb:30:07: warning: pragma Restrictions (No_Exception_Propagation) in effect
bare_bones.adb:30:07: warning: Last_Chance_Handler will be called on exception
bare_bones.adb:33:17: warning: pragma Restrictions (No_Exception_Propagation) in effect
bare_bones.adb:33:17: warning: "Constraint_Error" may call Last_Chance_Handler
bare_bones.adb:38:20: warning: pragma Restrictions (No_Exception_Propagation) in effect
bare_bones.adb:38:20: warning: "Constraint_Error" may call Last_Chance_Handler
bare_bones.adb:44:20: warning: pragma Restrictions (No_Exception_Propagation) in effect
bare_bones.adb:44:20: warning: "Constraint_Error" may call Last_Chance_Handler
bare_bones.adb:50:20: warning: pragma Restrictions (No_Exception_Propagation) in effect
bare_bones.adb:50:20: warning: "Constraint_Error" may call Last_Chance_Handler
bare_bones.adb:56:20: warning: pragma Restrictions (No_Exception_Propagation) in effect
bare_bones.adb:56:20: warning: "Constraint_Error" may call Last_Chance_Handler
bare_bones.adb:61:42: warning: pragma Restrictions (No_Exception_Propagation) in effect
bare_bones.adb:61:42: warning: "Constraint_Error" may call Last_Chance_Handler
bare_bones.adb:86:21: warning: pragma Restrictions (No_Exception_Propagation) in effect
bare_bones.adb:86:21: warning: "Constraint_Error" may call Last_Chance_Handler
bare_bones.adb:86:30: warning: pragma Restrictions (No_Exception_Propagation) in effect
bare_bones.adb:86:30: warning: "Constraint_Error" may call Last_Chance_Handler
bare_bones.adb:86:39: warning: pragma Restrictions (No_Exception_Propagation) in effect
bare_bones.adb:86:39: warning: "Constraint_Error" may call Last_Chance_Handler
bare_bones.adb:89:26: warning: pragma Restrictions (No_Exception_Propagation) in effect
bare_bones.adb:89:26: warning: "Constraint_Error" may call Last_Chance_Handler
bare_bones.adb:97:20: warning: pragma Restrictions (No_Exception_Propagation) in effect
bare_bones.adb:97:20: warning: "Constraint_Error" may call Last_Chance_Handler
bare_bones.adb:104:20: warning: pragma Restrictions (No_Exception_Propagation) in effect
bare_bones.adb:104:20: warning: "Constraint_Error" may call Last_Chance_Handler
bare_bones.adb:110:20: warning: pragma Restrictions (No_Exception_Propagation) in effect
bare_bones.adb:110:20: warning: "Constraint_Error" may call Last_Chance_Handler
bare_bones.adb:124:20: warning: pragma Restrictions (No_Exception_Propagation) in effect
bare_bones.adb:124:20: warning: "Constraint_Error" may call Last_Chance_Handler
bare_bones.adb:130:20: warning: pragma Restrictions (No_Exception_Propagation) in effect
bare_bones.adb:130:20: warning: "Constraint_Error" may call Last_Chance_Handler
bare_bones.adb:136:20: warning: pragma Restrictions (No_Exception_Propagation) in effect
bare_bones.adb:136:20: warning: "Constraint_Error" may call Last_Chance_Handler
bare_bones.adb:142:20: warning: pragma Restrictions (No_Exception_Propagation) in effect
bare_bones.adb:142:20: warning: "Constraint_Error" may call Last_Chance_Handler
bare_bones.adb:148:20: warning: pragma Restrictions (No_Exception_Propagation) in effect
bare_bones.adb:148:20: warning: "Constraint_Error" may call Last_Chance_Handler
bare_bones.adb:151:04: warning: pragma Restrictions (No_Exception_Propagation) in effect
bare_bones.adb:151:04: warning: Last_Chance_Handler will be called on exception
bare_bones.adb:160:04: warning: pragma Restrictions (No_Exception_Propagation) in effect
bare_bones.adb:160:04: warning: this handler can never be entered, and has been removed
bare_bones.adb:160:09: violation of restriction "No_Exception_Propagation" at ../gnat.adc:10
gnatmake: "/home/laguest/src/mine/bare_bones/src/bare_bones.adb" compilation error
make: *** [disk/boot/bare_bones] Error 4
</pre>
 
So the net effect of this is to not bother with this package and only use the language defined exceptions for bare metal programming.
 
====No_Exception_Registration====
 
Ensures no stream operations are performed on types declared in Ada.Exceptions, see [http://gcc.gnu.org/onlinedocs/gcc-4.7.0/gnat_rm/No_005fException_005fRegistration.html#No_005fException_005fRegistration GNAT RM:No_Exception_Registration] for more information.
 
====No_Finalization====
 
Controlled types cannot be used, see [http://gcc.gnu.org/onlinedocs/gcc-4.7.0/gnat_rm/No_005fFinalization.html#No_005fFinalization GNAT RM:No_Finalization] for more information.
 
====No_Tasking====
 
Turns off tasking, so you cannot define tasks or protected objects or do anything related to tasking, see [http://gcc.gnu.org/onlinedocs/gcc-4.7.0/gnat_rm/No_005fTasking.html#No_005fTasking GNAT RM:No_Tasking] for more information.
 
====No_Protected_Types====
 
This is pretty much here for reinforcement of the above restriction. See [http://gcc.gnu.org/onlinedocs/gcc-4.7.0/gnat_rm/No_005fProtected_005fTypes.html#No_005fProtected_005fTypes GNAT RM:No_Protected_Types] for more information.
 
====No_Delay====
 
You cannot use delay statements or the calendar package, see [http://gcc.gnu.org/onlinedocs/gcc-4.7.0/gnat_rm/No_005fDelay.html#No_005fDelay GNAT RM:No_Delay] for more information.
 
====No_Recursion====
 
Should be self evident, see [http://gcc.gnu.org/onlinedocs/gcc-4.7.0/gnat_rm/No_005fRecursion.html#No_005fRecursion GNAT RM:No_Recursion] for more information.
 
====No_Allocators====
 
You cannot use dynamic memory, see [http://gcc.gnu.org/onlinedocs/gcc-4.7.0/gnat_rm/No_005fAllocators.html#No_005fAllocators GNAT RM:No_Allocators] for more information.
 
====No_Dispatch====
 
You cannot call a subprogram using Ada's object-orientated mechanism, see [http://gcc.gnu.org/onlinedocs/gcc-4.7.0/gnat_rm/No_005fDispatch.html#No_005fDispatch GNAT RM:No_Dispatch] for more information.
 
====No_Implicit_Dynamic_Code====
 
You cannot use nested subprograms or any other features that generate trampolines on the stack, see [http://gcc.gnu.org/onlinedocs/gcc-4.7.0/gnat_rm/No_005fImplicit_005fDynamic_005fCode.html#No_005fImplicit_005fDynamic_005fCode GNAT RM:No_Implicit_Dynamic_Code] for more information.
 
====No_Secondary_Stack====
 
Without a secondary stack, you cannot return unconstrained types, such as arbitrary length strings, or variant records, see [http://gcc.gnu.org/onlinedocs/gcc-4.7.0/gnat_rm/No_005fSecondary_005fStack.html#No_005fSecondary_005fStack GNAT RM:No_Secondary_Stack] for more information.
 
What this also means is you cannot use the runtime features 'Image and 'Val on any types, this would be useful for sending debugging info to the console, i.e. means you don't have to write your own code for converting strings to/from numbers.
 
I believe that it would be a good idea to have a small secondary stack defined in the assembler startup code, but define your own System.Secondary_Stack (s-secsta.ad[sb]) package which provides the correct API. Inside this package in it's executable part, you could then import the secondary stack from the assembly code, this would then be executed on elaboration of the package at the start of the kernel's execution.
 
===system.ads===
Line 312 ⟶ 187:
Also, add the following line in the private part of the package:
 
<sourcesyntaxhighlight lang="ada">
private
 
Run_Time_Name : constant String := "Bare Bones Run Time";
</syntaxhighlight>
</source>
 
According to targparm.ads, it must be the first thing after the private keyword. It should also show up in error messages in parentheses, but I've not managed to get it to show up thus far. This is useful as it should show you which RTS you are currently using, just in case you configure your build incorrectly.
Line 322 ⟶ 197:
===Last chance handler===
 
The Ada runtime requires the presence of a <tt>Last_Chance_Handler</tt> subprogram. This subprogram is used as a handler for any exceptions that are not explicitly handled within their subprogram. These calls to the <tt>Last_Chance_Handler</tt> procedure in the case of unhandled exceptions will be automatically generated by the compiler.
When you start to write and compile Ada using this custom environment, the compiler will automatically place calls from the runtime into your final binary (this is what the compiler normally does, but we've restricted it a lot). One of these calls is to '''Last_Chance_Handler''' so create 2 new files and place into ''rts/boards/'''<arch>'''/adainclude'', as follows.
 
In order to facilitate this the last chance handler procedure must be defined somewhere within the program. Typically this is defined within the runtime. The last chance handler procedure may have any name, however the compiler will search for a procedure with external linkage with the name <tt>__gnat_last_chance_handler</tt>.
 
Create the following files in the <tt>rts/boards/${arch}/adainclude</tt>:
 
====last_chance_handler.ads====
 
<sourcesyntaxhighlight lang="ada">
with System;
 
Line 332 ⟶ 211:
(Source_Location : System.Address; Line : Integer);
pragma Export (C, Last_Chance_Handler, "__gnat_last_chance_handler");
</syntaxhighlight>
</source>
 
====last_chance_handler.adb====
 
<sourcesyntaxhighlight lang="ada">
procedure Last_Chance_Handler
(Source_Location : System.Address; Line : Integer) is
pragma Unreferenced (Source_Location, Line);
begin
-- TODO: Add in board-specific code to dump theexception infoinformation to serial/screen which.
-- is obviously board specific.
loop
null;
end loop;
end Last_Chance_Handler;
</syntaxhighlight>
</source>
 
The contents of the <tt>Last_Chance_Handler</tt> procedure will need to be tailored to the specific platform of the kernel. Typically this procedure will dump information regarding the exception to output media such as a [[Serial_Ports|serial port]].
As you can see, the meat of the handler is actualy a null loop at the moment, this is something you need to complete for your OS kernel and also, per platform.
 
===Compiling the runtime===
 
Create a file called <tt>gnat.gpr</tt> in the root directory and copywith the following into itcontents:
 
<sourcesyntaxhighlight lang="ada">
library project gnat is
type Arch_Name is ("i386", "arm");
Line 396 ⟶ 274:
for Library_Dir use "rts/boards/" & Arch & "/adalib";
end gnat;
</syntaxhighlight>
</source>
 
Now compile with the following command:
 
<sourcesyntaxhighlight lang="bash">
gnatmakegprbuild -XBoard=pc -Pgnat.gpr
</syntaxhighlight>
</source>
 
Inside <tt>rts/boards/i386/adainclude/ you should have</tt> the RTS sources should be symbolically linked along with the custom <tt>last_chance_hander</tt> and system files. Inside <tt>rts/boards/i386/adalib/</tt> youthere should havebe the <tt>libgnat-4.6.a</tt> andfile as well alsoas <tt>*.ali</tt> matching the source files, which are required by GNAT.
 
'''N.BNote:''' Please note that the above lib might need it's name changingchanged as some OSes might have built libgnat with a version number different to the one I'mused usinghere. YoursThe mightsystem libnat version may be <tt>libgnat-4.4.a</tt> and GNAT will expect to find that, so change the name in the GPR file accordingly.''
 
===startup.s===
 
This is the main system bootstrapping code. This version is PC specific, so place this in the <tt>src/pc</tt> directory.
 
''Note: This source file is written using GAS syntax.''
====GAS====
 
<sourcesyntaxhighlight lang="asm">
.global startup # Make the startup entry point symbol visible to the linker
 
Line 465 ⟶ 343:
# for some reason, (such as in the case of an NMI).
jmp hang
</syntaxhighlight>
</source>
 
===multiboot.ads===
Line 479 ⟶ 357:
====console.ads====
 
<sourcesyntaxhighlight lang="ada">
with System;
 
Line 598 ⟶ 476:
procedure Clear (Background : in Background_Colour := Black);
end Console;
</syntaxhighlight>
</source>
 
====console.adb====
 
<sourcesyntaxhighlight lang="ada">
package body Console is
procedure Put
Line 641 ⟶ 519:
end Clear;
end Console;
</syntaxhighlight>
</source>
 
===bare_bones.adb===
Line 647 ⟶ 525:
This is platform independent and therefore goes into the src directory.
 
<sourcesyntaxhighlight lang="ada">
with Console; use Console;
 
Line 659 ⟶ 537:
end Bare_Bones;
pragma No_Return (Bare_Bones);
</syntaxhighlight>
</source>
 
===linker.ld===
Line 707 ⟶ 585:
Place this file in the root directory.
 
<sourcesyntaxhighlight lang="make">
ARCH = i386
RTS_DIR = `pwd`/rts/boards/$(ARCH)
 
ifeq ($(ARCH),i386)
GPRBUILD = gprbuild
GNATMAKE = gnatmake
AS = as
ASFLAGS = --32 -march=i386
Line 726 ⟶ 604:
 
bare_bones: $(OBJS) src/bare_bones.adb
$(GNATMAKEGPRBUILD) --RTS=$(RTS_DIR) -XBoard=$(BOARD) -Pbare_bones.gpr
 
obj/startup.o: src/$(BOARD)/startup.s
Line 735 ⟶ 613:
clean:
-rm obj/* *~ bare_bones
</syntaxhighlight>
</source>
 
===bare_bones.gpr===
Line 741 ⟶ 619:
Place this file in the root directory.
 
<sourcesyntaxhighlight lang="ada">
project Bare_Bones is
type Arch_Name is ("i386", "arm");
Line 796 ⟶ 674:
end Linker;
end Bare_Bones;
</syntaxhighlight>
</source>
 
==Targetting GNAT to bare metal==
 
Then GNAT is built, it automatically builds:
 
# The compiler
# The runtime
# The tools (gnatmake, gnatlink, gnatls, etc.).
 
Unfortunately, the toolchain cannot be built for bare machine targets (i.e. <arch>-elf|coff|pe) straight out of the box like a C compiler can be.
 
To enable this, a number of files need to be modified. I will show what needs to be done to support arm-elf, ready for the next section on the Raspberry Pi. This work is based on work done previously [http://www.zsk.p.lodz.pl/~morawski/Dyplomy/Praca%20dyplomowa%20p.%20Horna.pdf].
 
===gcc/ada/adaint.c===
 
===gcc/ada/adaint.h===
 
===gcc/ada/gcc-interface/Makefile.in===
 
===gcc/ada/gsocket.h===
 
===gcc/ada/mlib-tgt-specific-bare.adb===
 
Can we get rid of this?
 
===gcc/ada/s-oscons-tmplt.c===
 
===gcc/ada/system-bare-armel.ads===
 
===gnattools/configure[.ac]===
 
==Raspberry Pi==
Line 850 ⟶ 698:
 
 
<sourcesyntaxhighlight lang="bash">
git clone git://github.com/gonzoua/u-boot-pi.git
 
cd u-boot-pi
make rpi_b_config
</syntaxhighlight>
</source>
 
==Testing==
Line 861 ⟶ 709:
Make sure you have built the RTS above before this next stage otherwise you won't be able to compile the kernel.
 
<sourcesyntaxhighlight lang="bash">
make qemu
</syntaxhighlight>
</source>
 
On the QEMU window, it should clear the screen, the the cursor won't move so it will be in the middle of the screen, in the top-left corner will be the message "Hello, bare bones in Ada."
Line 873 ⟶ 721:
In fact there have ben a lot of changes since I started this project and it is a better idea to grab the source from GitHub as it will be more up to date. I will leave the documents above so you can see how it's evolved and also how it works, maybe a bit clearer that it is now.
 
==FutureNext Steps==
 
A useful next step for further developing the bare bones kernel outlined in this article is implementing capability for using the <tt>'Image</tt> attributes on scalar types. This facilitates the printing of integers in string form, which is extremely useful for kernel debugging. A simple guide on how to accomplish this is detailed [[Ada_Runtime_Library#Image_attributes|here]].
# Implement a secondary stack so we can use 'Image attributes.
# Implement a 64-bit version (non EFI).
# Implement a (U)EFI version.
 
[[Category:Bare bones tutorials]]
[[Category:Ada]]
stage