AdaCore / bb-runtimes

Source repository for the GNAT Bare Metal BSPs
Other
65 stars 51 forks source link

Interfaces.C.Strings tries to call in libc’s strlen #64

Closed simonjwright closed 1 year ago

simonjwright commented 1 year ago

There is a new strlen-related optimisation in GCC 12 which results in calls to strlen() in libc. You wouldn't notice this in a native compilation, but you will see it in the light runtimes, which link with -nolibc.

(Incidentally, as well as -nolibc, the embedded runtimes say -Wl,--start-group,-lgnarl,-lgnat,-lc,-lgcc,--end-group - so why -nolibc?)

Building this:

with Interfaces.C.Strings;
procedure Kev is
    Cp : Interfaces.C.Strings.chars_ptr
     := Interfaces.C.Strings.New_String ("foo");
   Ca : Interfaces.C.char_array
     := Interfaces.C.Strings.Value (Cp);
begin
   Str := Interfaces.C.Strings.New_String ("foo");
end Kev;

with

   for Target use "arm-eabi";
   for Runtime ("ada") use "light-cortex-m4f";

results in

/Users/simon/.config/alire/cache/dependencies/gnat_arm_elf_12.2.1_f4bfd822/bin/../lib/gcc/arm-eabi/12.2.0/../../../../arm-eabi/bin/ld: /users/simon/.config/alire/cache/dependencies/gnat_arm_elf_12.2.1_f4bfd822/arm-eabi/lib/gnat/light-cortex-m4f/adalib/libgnat.a(i-cstrin.o): in function `interfaces__c__strings__value':
i-cstrin.adb:(.text.interfaces__c__strings__value+0xe): undefined reference to `strlen'

On investigation, if optimising at -O2, when the compiler sees this

      Result : char_array (0 .. Strlen (Item));

where you would have expected the Strlen to be the one at line 201 what actually gets called is just strlen which is of course provided as part of libc.

Also, in the Strlen at line 201, the loop is translated as a call to strlen.

This is fascinating: it seems as though the compiler recognises that the code generated in the local function Strlen is the same as ... what __builtin_strlen would have produced? and generates the call to strlen??

In fact, at -O2, i-cstrin.o references memcpy, memset, and strlen, whereas at lower levels it only references memcpy.

Other objects reference memcpy and memset, but that’s not an issue because the RTS provides them (s-memcop, s-memset).

Could this be fixed by providing s-strlen in the RTS?

with System.Memory_Types;
package System.Strlen is

   function Strlen (Item : Address)
                   return Memory_Types.size_t
   with
     Export,
     Convention => C,
     External_Name => "strlen";

end System.Strlen;
package body System.Strlen is

   function Strlen (Item : Address)
                   return Memory_Types.size_t
   is
      use Memory_Types;
      Item_Addr : constant IA := To_IA (Item);
      Result : IA := 0;
   begin
      loop
         exit when To_Byte_Ptr (Item_Addr + Result).all = 0;
         Result := Result + 1;
      end loop;
      return size_t (Result);
   end Strlen;

end System.Strlen;
Fabien-Chouteau commented 1 year ago

Thanks for the report @simonjwright

My apologies, this is already taken care of in the development branch, see: https://github.com/AdaCore/bb-runtimes/commit/5a37fb3f216a731e73ed353dcbd4f1c9d3e697ec

I will see if can do new FSF builds with that option.