4

I am building my Ada/SPARK project using GNAT and I am using a linker script. Here is an excerpt:

SECTIONS
{
    .code :
    {
        . = ALIGN(0x4);
        *(.text.section1)
        _end_of_section1 = .;
        *(.text.section2)
        ...
    }
}

The symbol _end_of_section1 is the address between the two sections. I'd like to be able to access this in my Ada code. I know it's possible in C using extern char _end_of_section1[];. Is it possible to do something like this in Ada? If not, is there some other way to get this address in the code?

Nola
  • 436
  • 4
  • 12

1 Answers1

5

You can import a linker symbol by using the Importand Link_Name aspects (see also RM B.1):

main.adb (updated on 25-jan)

with System.Storage_Elements;
with System.Address_Image;
with Ada.Text_IO; use Ada.Text_IO;

procedure Main is   
      
   package SSE renames System.Storage_Elements;

   package Storage_Element_IO is
     new Ada.Text_IO.Modular_IO (SSE.Storage_Element);
   use Storage_Element_IO;
       
   Start_Symbol : aliased SSE.Storage_Element
     with Import, Link_Name => "_start";
   
   Start_Symbol_Addr : constant System.Address := Start_Symbol'Address;

begin
   Put ("Address : ");
   Put (System.Address_Image (Start_Symbol_Addr));   
   New_Line;
   
   Put ("Value   : ");
   Put (Start_Symbol, Base => 16);
   New_Line;
   
end Main;

output

$ ./obj/main 
Address : 0000000000403300
Value   : 16#F3#

output (objdump)

$ objdump -d -M intel ./obj/main | grep -A5 "<_start>"
0000000000403300 <_start>:
  403300:   f3 0f 1e fa             endbr64 
  403304:   31 ed                   xor    ebp,ebp
  403306:   49 89 d1                mov    r9,rdx
  403309:   5e                      pop    rsi
  40330a:   48 89 e2                mov    rdx,rsp
  ...
DeeDee
  • 5,654
  • 7
  • 14
  • 2
    I’ve always used `External_Name` rather than `Link_Name`. The RM is a little vague on this! – Simon Wright Jan 24 '22 at 14:18
  • Thanks for the quick response! – Nola Jan 24 '22 at 19:30
  • 1
    If I want to use Text_Section_First_Byte as a System.Address in my code, what is the best way to do this? Can I just declare it as a System.Address instead of a SSE.Storage_Element? – Nola Jan 25 '22 at 17:37
  • 1
    @Nola You can obtain the address of the storage element by using the `'Address` attribute (see the updated example and [RM 13.3 (11)](http://www.ada-auth.org/standards/12rm/html/RM-13-3.html#p11)). I changed the linker symbol from `.text` to `_start` for this example to work as the `.text` symbol is resolved to the `.text` section in the object file of `Main`, not to the `.text` of the final executable. – DeeDee Jan 25 '22 at 19:48
  • 1
    @Nola Note that I also added the `aliased` keyword to the declaration of the imported symbol. This is not necessary if you use GNAT (see [RM 13.2 (19)](http://www.ada-auth.org/standards/12rm/html/RM-13-3.html#p19) and the [implementation notes for GNAT](https://gcc.gnu.org/onlinedocs/gnat_rm/RM-13-3-14-19-Address-Clauses.html#RM-13-3-14-19-Address-Clauses)), but I would recommend to do it anyway for good practice and portability. – DeeDee Jan 25 '22 at 20:06
  • 1
    I think I understand now. The Storage_Element we declare doesn't actually store an address in it. It is treated as a variable which is located at the address we specified with Link_Name. This is why we need to use 'Address to get its address. Thanks again for the great explanation and example! – Nola Jan 25 '22 at 21:15
  • 1
    @Nola Yes, indeed. However, needless to say, the variable may not actually be writable. For example if it’s located at some read-only or write-protected memory (as is typically the case for memory that contains executable code on modern OS’es). In that regard, it may even be a good idea to also add the `constant` keyword to the declaration of the imported symbol. – DeeDee Jan 25 '22 at 21:46
  • Makes sense. I definitely don't want the variable to be writable in my case since it is just being use to get a position in memory. – Nola Jan 28 '22 at 15:02