Writing Linux Modules in Ada - Part 3

Posted on Fri 25 November 2016 in misc View Comments

View on Github Star on Github Download Pt-3

In this part, I will continue porting capabilities to the run-time

Image

The 'Image attribute is used to return the string representation of a value. This value can be integer, boolean, enumeration etc.

The run-time have a separate implementation of the attribute for each supported type. Here is the list of files from my native run-time:

/usr/gnat/lib/gcc/x86_64-pc-linux-gnu/4.9.4/rts-native/adainclude$  lsg img
s-imgbiu.adb
s-imgbiu.ads
s-imgboo.adb
s-imgboo.ads
s-imgcha.adb
s-imgcha.ads
s-imgdec.adb
s-imgdec.ads
s-imgenu.adb
s-imgenu.ads
s-imgint.adb
s-imgint.ads
s-imgllb.adb
s-imgllb.ads
s-imglld.adb
s-imglld.ads
s-imglli.adb
s-imglli.ads
s-imgllu.adb
s-imgllu.ads
s-imgllw.adb
s-imgllw.ads
s-imgrea.adb
s-imgrea.ads
s-imguns.adb
s-imguns.ads
s-imgwch.adb
s-imgwch.ads
s-imgwiu.adb
s-imgwiu.ads

These packages are not preset in our module's run-time, so when attempting to compile something like this:

package body Ada_Foo_Pack is
   procedure Ada_Foo is
      S : String := Integer'Image (42) & Character'Val (0);
   begin
      Print_Kernel (S);
   end Ada_Foo;
begin
   null;
end Ada_Foo_Pack;

Will result in compiler error:

ada_foo_pack.adb:3:22: construct not allowed in configurable run-time mode
ada_foo_pack.adb:3:22: file s-imgint.ads not found
ada_foo_pack.adb:3:22: entity "System.Img_Int.Image_Integer" not available

First Step

The first step would be to copy one of the native implementations as is.

Unfortunately, the compilation results in the following errors:

s-imgint.adb:75:10: violation of restriction "No_Recursion" at /home/artium/Projects/Ada_Kernel_Module_Toolkit/rts/gnat.adc:18
s-imgint.ads:42:07: violation of restriction "No_Default_Initialization" at /home/artium/Projects/Ada_Kernel_Module_Toolkit/rts/gnat.adc:74

No_Default_Initialization

The second violation points to the out parameter P of the following declaration

procedure Image_Integer
  (V : Integer;
   S : in out String;
   P : out Natural);

After some reading documentation and asking for help at the #ada irc channel, I understood that this violation happens because gnat.adc has both the No_Default_Initialization restriction and the Normalize_Scalars pragma.

As sparre from the irc channel explains, "Normalize_Scalars is a default initialization and you can't override the default initialization of an "out" parameter".

As explained in part 1, the file gnat.c was inherited from the bare bones tutorial. I do not know why the No_Default_Initialization restriction was added, but Normalize_Scalars looks pretty useful. Between these two I chose to remove the first one.

No_Recursion

This one is simple, the code have a recursion:

procedure Set_Digits
  (T : Integer;
   S : in out String;
   P : in out Natural)
is
begin
   if T <= -10 then
      Set_Digits (T / 10, S, P);
      P := P + 1;
      S (P) := Character'Val (48 - (T rem 10));
   else
      P := P + 1;
      S (P) := Character'Val (48 - T);
   end if;
end Set_Digits;

The following version I wrote does not use recursion:

procedure Set_Digits
  (T : Integer;
   S : in out String;
   P : in out Natural)
is
   D : Natural := 0; -- will store number of digits
begin

   declare
      T2 : Integer := T;
   begin
      while T2 /= 0 loop
         D := D + 1;
         T2 := T2 / 10;
      end loop;
   end;

   if D = 0 then
      P := P + 1;
      S (P) := '0';
   else
      for I in reverse 0 .. D - 1 loop
         P := P + 1;
         S (P) := Character'Val (48 - (T / 10 ** I) rem 10);
      end loop;
   end if;
end Set_Digits;

The exponent operator I used in the code required copying additional files to the run-time:

s-exnint.ads
s-exnint.adb

It was very tempting to rewrite this package completely, as it is not the most efficient implementation possible and does not use the Ada type system intelligently.

But as I already explained, I am guided by the principle of doing as little changes as possible to the native run-time.

The Unknown Symbol

After the changes made, compiling the module resulted in a warning:

WARNING: "system__img_int__image_integer" [/home/artium/Projects/Ada_Kernel_Module_Toolkit/hello.ko] undefined!

Trying to insert the module resulted in an error:

$sudo insmod hello.ko
insmod: ERROR: could not insert module hello.ko: Unknown symbol in module
$dmesg | tail -1
[132897.268341] hello: Unknown symbol system__img_int__image_integer (err 0)

It turns out that I forgot to add the run-time lib (libgnat.a) to the module's makefile. I am a little bit puzzled about why the previous stuff worked.

After adding "rts/adalib/libgnat.a" to _hello-y variable, the module loaded and produced the desired output:

$ sudo insmod hello.ko 
$ dmesg | tail -1
[76863.379199]  42

Enumeration

To make the Image attribute work with enumeration types I had to copy two additional files from the native run time:

s-imenne.ads
s-imenne.adb

I also had to comment two additional restrictions in the gnat.adc:

-- pragma Discard_Names;
-- pragma Restrictions (No_Enumeration_Maps);

What happens here is that the compiler will generate the strings and store them in the compiled binary.

When compiling calls to Some_Type'Image(Some_Value) the compiler will emit a call to an run-time function that will actually do the location of the correct string.

Notice that Some_Value can also be a variable, that is why the string must be resolved at run time.

Real Types

To add support for Image of real types (fixed and floating point), a bunch of additional files should be copied:

s-exnlli.adb
s-exnlli.ads
s-expllu.adb
s-expllu.ads
s-expuns.adb
s-expuns.ads
s-fatllf.ads
s-flocon.adb
s-flocon.ads
s-imglli.adb
s-imglli.ads
s-imgllu.adb
s-imgllu.ads
s-imgrea.adb
s-imgrea.ads
s-imguns.adb
s-imguns.ads
s-powtab.ads
s-unstyp.ads

I modified one of the file, s-flocon.adb and removed the import of __gnat_init_float. The Code that supposed to initialize the FPU does nothing now. I have no problems with that, so far. After all, the Linux kernel should have already did the initialization before our module is being insert.

Additionally a fixed point restriction should be lifted from gnat.adc:

--  pragma Restrictions (No_Fixed_Point);

Final Words

That is all for now. Next I will do Interfaces package and start binding functions from the Kernel itself.