x86_64 ELF Topics
This appendix describes topics relevant to GNAT for bareboard x86-64 ELF and also presents a tutorial on building, running, and debugging an Ada application in GNATemulator.
Supported Processors and Run-Time Libraries
GNAT for bareboard x86-64 ELF targets the Intel 64 architecture (also known as AMD64 or x86-64).
Out of the box GNAT provides run-time support for the following Intel processor families:
Core and Xeon families: processors based on the Sandy Bridge microarchitecture and newer.
Atom families: processors based on the Goldmont microarchitecture and newer.
For these processors the following generic run-times are provided:
light-x86_64
light-tasking-x86_64
embedded-x86_64
All run-time libraries include the necessary low-level hardware startup and initialization functionality required to run applications on systems containing the supported processor families. The run-times only support running on a single core.
Required CPU Features
While the Light run-time has the ability to run on any x86-64 processor, the Light-Tasking and Embedded run-times require the following CPU features that are only present in the supported processor families:
Invariant Time-Stamp Counter
XSAVEOPT instruction
The run-time will check for these features on boot and will raise a run-time error if the processor does not support them.
Running a x86_64-elf Application
The x86_64-elf run-times are designed to be booted from a variety of different bootloaders via bootloader-specific linker scripts. Shipped with GNAT Pro is support for:
GNATemulator
Multiboot 1 and Multiboot 2 complaint bootloaders (like GRUB)
LynxSecure (as a LynxSecure Application)
By default, applications are built for GNATemulator. To build your application
for one of the other supported bootloaders, set the LOADER
project variable
to MULTIBOOT
, MULTIBOOT2
or LSA
. The LOADER
variable can be
specified as a switch to GPRBuild:
gprbuild -P proj.gpr -XLOADER=MULTIBOOT
or in GNAT Studio by adding the following to your project file:
type LOADER_Type is ("MULTIBOOT");
LOADER : LOADER_Type := external ("LOADER", "MULTIBOOT");
GNATemulator
By default, applications are built to run in GNATemulator. To run your program
you can either press the Run with Emulator
toolbar button in GNAT Studio
or run directly from the command line with:
x86_64-elf-gnatemu my_program
Multiboot
The MULTIBOOT
and MULTIBOOT2
loaders allow the resulting ELF binary to
be booted directy using a multiboot complaint bootloader like GRUB.
LynxSecure
The LSA
loader produces an application that can be loaded as a LynxSecure
Application (LSA). To add the application to a LynxSecure SRP, you first need
to convert the ELF binary to a raw binary image:
x86_64-elf-objcopy -O binary <program> <program>.bin
Once converted, the application can be included in an SRP with the following switch:
--subject-fvbmada=ram=128m,cpus=1,realtime=true,SERIAL0,lsapath=<program>.bin
Serial I/O
The run-times are configured to use serial port COM1 with the system default serial configuration.
Clock Speed
The Ravenscar run-times use the invariant Time-Stamp Counter (TSC) and Local
APIC Timer as their clock sources and will attempt to determine the frequencies
of these clock sources on boot, based on the information provided to it by the
processor. On some processors this information may be unavailable, and the
run-time will raise a program error indicating this problem. If this occurs,
the TCS and Local APIC Timer frequency will need to be manually set in
System.BB.Parameters
. You may also need to manually set this frequency
if the run-time determines the wrong TCS or Local APIC Timer frequencies.
Interrupts
x86-64 uses a split interrupt controller design. Each CPU has its own Local Advanced Programmable Interrupt Controller (APIC) that can receive external interrupts from PCI devices via the Message Signaled Interrupts (MSI) feature or from I/O APICs.
The Ravenscar run-times only support attaching protected procedure handlers to Local APIC interrupt vectors via the Attach_Handler aspect. Interrupt vectors 33 to 254 are available for user applications, with Interrupt 32 reserved for the Spurious Interrupt Vector and Interrupt 255 reserved for the APIC Timer as used by the run-time. See Ada.Interrupts.Names for the list of vectors.
Note that interrupt priorities are handled differently by the Intel Local APIC compared to interrupt controllers used by other processors. The Local APIC encodes the interrupt priority of a vector in the top 4 bits of the 8-bit vector number. Consequently, when attaching a protected procedure to an interrupt vector you need to ensure that the Interrupt Priority of the protected object corresponds to the priority class of the interrupt vector. Please refer to Ada.Interrupts.Names for a mapping of vector numbers to Interrupt Priorities, as the run-time will enforce these mappings.
To attach a handler to an I/O APIC IRQ, route the IRQ to a Local APIC Vector using the Route procedure provided in Interfaces.X86_64.IO_APIC and attach the protected handler to the Vector. Then unmask the IRQ using the Mask procedure. For example, in the following code we connect a protected handler called Signal to IRQ4 by routing it to Local APIC Vector 33 and attaching the handler to that vector:
1with Ada.Interrupts.Names; use Ada.Interrupts.Names;
2with Interfaces.X86_64.IOAPIC; use Interfaces.X86_64.IOAPIC;
3
4package My_Pack is
5 protected Handler_Object
6 with Interrupt_Priority => Vector_32_47_Interrupt_Priority
7 is
8 procedure Initialize;
9 procedure Signal with Attach_Handler => Vector_33;
10 end Handler_Object;
11end My_Pack;
12
13package body My_Pack is
14
15 protected body Handler_Object is
16 procedure Initialize is
17 begin
18 Route (Interrupt => IRQ4,
19 To_Vector => Vector_33);
20 Unmask (IRQ4);
21 end Enable_External_Interrupt;
22
23 procedure Signal is
24 begin
25 Ada.Text_IO.Put_Line ("Interrupt handled");
26 end Signal;
27
28 end Handler_Object;
29end My_Pack;
Memory Layout
The GNAT for bareboard x86-64 run-times are designed to be loaded by a bootloader into RAM. By default the run-times are configured to use 128MB. If your system has more or less memory, modify memory-map.ld to suite your requirements or provide your own linker script by setting the environment variable LOADER to ‘USER’ and specifying the linker script via the ‘-T’ linker switch. The run-times will not automatically detect the amount of RAM in the system.
Getting Started
Developing with GNAT for bareboard is similar to native GNAT development, with two important differences. First, when building for a bareboard target, you need to specify both the target processor and the run-time library. These can be defined in a project file (either directly or via the project properties dialog in GNAT Studio) or on the command line. For details on how to specify the target and run-time library for your project, see Introduction to GNAT for Cross Platforms. For the x86-64 the target x86_64-elf needs to be specified.
For example, the following project file fragment shows both the target and the run-time library specified via their respective attributes:
project Demo is
...
for Runtime ("Ada") use "light-x86_64";
for Target use "x86_64-elf";
...
end Demo;
The second difference is how programs are run and debugged. This is dependent on your setup and the tools you use. In many cases, GNAT Studio can be used to debug your program. See Debugging for details.
Getting Started with GNATemulator
This guide describes how to set up, build, run, and debug an Ada application on GNATemulator. Setup for both Windows and Linux hosts is covered. While this section focuses on GNATemulator, most of the steps contained within this section are independent of the application and the target hardware itself.
To begin this setup, it is assumed that GNAT for bareboard x86-64 has been installed and that the install bin directory is in the PATH environment variable.
Creating a new Project
Launch GNAT Studio:
$ gnatstudio
If this is your first time running GNAT Studio, note that GNAT Studio will create a configuration directory and will alert you via a pop-up dialog box. Press ‘OK’ to close that box and continue. A ‘Tip of the Day’ dialog will appear. Press Close to dismiss it.
When on the Welcome page, click on Create new project
and select
Simple Ada project
, and then click on Next
. Change Location and
Settings appropriately and press Apply
.
The project view is now displayed. Next, open the project properties by
right-clicking on the project name in Project tab, and selecting
Project->Properties. With the project properties displayed open the
Toolchains tab, and make sure that the x86_64-elf toolchain is selected
and the Ada run-time is light-x86_64. Then click on Save
.
You can now add your source code.
Building
Within GNAT Studio, build the project using either the Build Main
button on
the toolbar, or the Build/Project->Build All
menu entry. In
the latter case, if a pop-up window appears, press the Execute
button in order to build with the default settings.
As GNAT Studio builds the project, it will display the Messages
view at the bottom
of the GNAT Studio window to show information about the build. On a successful build,
the Memory Usage
view will graphically display the memory usage of the
program, unless the preference is disabled. (See the preference
Build Targets/Project/Build Main page, among others, containing the Display
memory usage
check-box.)
Running the Program
Within GNAT Studio, run your program on the emulator using the Run with Emulator
toolbar button or the Build/Emulator->Run with Emulator menu entry.
As the program executes, it will display its output, if any, in the x86_64-elf-gnatemu view.
Debugging the Program
In GNAT Studio, click on the Debug with Emulator
button on the toolbar.
This will start a GNAT Studio debug session and launch the necessary tools
required to debug the program on the emulator from GNAT Studio. The emulator
will be automatically launched. Debugging a program on the emulator through
GNAT Studio is similar to debugging a native program:
The program can continue to the next breakpoint by pressing the
Continue
button on the toolbar or by using theDebug/Continue
menu item.The program can be stepped through statements using the corresponding menu items in
Debug
menu and buttons on the toolbar.The program can be interrupted by pressing
<ctrl>-\
, or through theDebug/Interrupt
menu.Breakpoints can be set via the
Breakpoints
view, by clicking on the line numbers in the source editor while the program is interrupted, or by right-clicking on the desired line and selectingSet breakpoint on line ...
from theDebug
menu.Variables can be queried from the
Variables
view or by hovering the mouse over a variable with the program interrupted.
GNAT Studio also provides a Debugger Console
view that allows shell access
to the underlying GDB client that is debugging the program. Arbitrary GDB
commands, including the commands corresponding to the above, can be entered
into the console if desired.
The debugging session can be exited via the Debug/Terminate
menu or
by issuing the quit
command in the Debugger Console
.