Настройка голого металлического набора инструментов x86 Ada - PullRequest
7 голосов
/ 23 июня 2019

Пожалуйста, прости довольно широкий вопрос.Мне интересно, как создать набор инструментов Ada, ориентированный на голый металл x86.Я видел учебник Lucretia по Ada Bare Bones на osdev.org , в котором содержится некоторая полезная информация о создании подходящей среды выполнения для разработки на голом железе.Этот аспект довольно прост, но я немного не уверен в том, как создать кросс-компилятор для платформы, или если это даже необходимо.

Правильно ли я полагаю, что создание «автономного» двоичного файла выполняется путем компиляции с правильным типом RTS?Если бы я должен был создать / использовать правильную отдельно стоящую RTS, было бы целесообразно использовать либо готовый AdaCore, либо FSF GNAT для x86?Любая помощь в понимании этого будет принята с благодарностью.

1 Ответ

9 голосов
/ 25 июня 2019

Прежде всего, обратите внимание, что я отмечаю, что являюсь экспертом в программировании на голом металле, но, поскольку это интересно, я попробую. При этом я не думаю, что вам нужен кросс-компилятор. Компилятор нативной платформы (например, GNAT CE 2019 для Linux x86-64) просто подойдет.

Чтобы проиллюстрировать это, вы можете воссоздать пример multiboot / hello_world , найденный здесь на GitHub в Аде. Вот шаги, которые я предпринял на своем компьютере Debian с установленным GNAT CE 2019, чтобы это заработало.

Прежде всего я установил несколько необходимых пакетов (QEMU, NASM и GNU xorriso) и клонировал указанный выше репозиторий:

$ sudo apt-get install qemu nasm xorriso
$ git clone https://github.com/cirosantilli/x86-bare-metal-examples.git

Затем в репозитории я переключился на каталог multiboot/hello-world, собрал пример как есть и выполнил полученный образ в QEMU, чтобы проверить, все ли было правильно настроено:

multiboot/hello-world $ make
multiboot/hello-world $ make run

В результате появилось окно QEMU с надписью hello world в верхнем левом углу. Я продолжил, закрыв QEMU и запустив make clean для очистки.

Затем я удалил main.c и заменил его переводом Ada main.adb :

with System.Storage_Elements;

procedure Main is

   --  Suppress some checks to prevent undefined references during linking to
   --
   --    __gnat_rcheck_CE_Range_Check
   --    __gnat_rcheck_CE_Overflow_Check
   --
   --  These are Ada Runtime functions (see also GNAT's a-except.adb).

   pragma Suppress (Index_Check);
   pragma Suppress (Overflow_Check);


   --  See also:
   --    https://en.wikipedia.org/wiki/VGA-compatible_text_mode
   --    https://en.wikipedia.org/wiki/Color_Graphics_Adapter#Color_palette

   type Color is (BLACK, BRIGHT);

   for Color'Size use 4;
   for Color use (BLACK => 0, BRIGHT => 7);


   type Text_Buffer_Char is
      record
         Ch : Character;
         Fg : Color;
         Bg : Color;
      end record;   

   for Text_Buffer_Char use
      record
         Ch at 0 range 0 .. 7;
         Fg at 1 range 0 .. 3;
         Bg at 1 range 4 .. 7;
      end record;


   type Text_Buffer is
     array (Natural range <>) of Text_Buffer_Char;


   COLS : constant := 80;
   ROWS : constant := 24;   

   subtype Col is Natural range 0 .. COLS - 1;
   subtype Row is Natural range 0 .. ROWS - 1;


   Output : Text_Buffer (0 .. (COLS * ROWS) - 1);
   for Output'Address use System.Storage_Elements.To_Address (16#B8000#);


   --------------
   -- Put_Char --
   --------------

   procedure Put_Char (X : Col; Y : Row; Fg, Bg : Color; Ch : Character) is
   begin
      Output (Y * COLS + X) := (Ch, Fg, Bg);
   end Put_Char;

   ----------------
   -- Put_String --
   ----------------

   procedure Put_String (X : Col; Y : Row; Fg, Bg : Color; S : String) is
      C : Natural := 0;
   begin
      for I in S'Range loop
         Put_Char (X + C, Y, Fg, Bg, S (I));
         C := C + 1;
      end loop;
   end Put_String;

   -----------
   -- Clear --
   -----------

   procedure Clear (Bg : Color) is
   begin
      for X in Col'Range loop
         for Y in Row'Range loop
            Put_Char (X, Y, Bg, Bg, ' ');
         end loop;
      end loop;
   end Clear;


begin

   Clear (BLACK);
   Put_String (0, 0, BRIGHT, BLACK, "Ada says: Hello world!");

   --  Loop forever.
   while (True) loop
      null;
   end loop;

end Main;

Поскольку мы запускаем Ada, мне пришлось изменить entry.asm и заменить следующие строки, чтобы убедиться, что точка входа программы Ada вместо программы C была вызвана. Точка входа программы Ada, испускаемой GNAT, - _ada_main (см. Вывод objdump -t main.o после компиляции):

-- extern main
++ extern _ada_main

[...]

-- call main
++ call _ada_main

В Makefile я заменил следующие строки, чтобы правильно скомпилировать и связать программу Ada. Обратите внимание, что я компилирую в i386 (с использованием переключателя -m32) и запрашиваю компоновщик выдать исполняемый файл elf_i386, поскольку процессор не выполнит 64-битные инструкции сразу после запуска:

-- ld -m elf_i386 -nostdlib -T linker.ld -o '$@' $^
++ ld -m elf_i386 -T linker.ld -o '$@' $^

[...]

-- main.o: main.c
-- <TAB>gcc -c -m32 -std=c99 -ffreestanding -fno-builtin -Os -o '$@' -Wall -Wextra '$<'
++ main.o: main.adb
++ <TAB>gcc -c -m32 -Os -o '$@' -Wall -Wextra '$<'

[...]

-- rm -f *.elf *.o iso/boot/*.elf *.img
++ rm -f *.ali *.elf *.o iso/boot/*.elf *.img

ПРИМЕЧАНИЕ. Обратите внимание на вкладки (обозначенные <TAB>) перед gcc. make требователен к этой теме!

Затем я снова вызвал make, а затем make run, чтобы увидеть всплывающее окно QEMU, но теперь с текстом:

Ada says: Hello world!

Эта Ада-программа выполняется голым металлом (в реальном режиме IA-32)! Затем я пошел еще дальше, преобразовав main.img в диск VirtualBox (VDI), используя

VBoxManage convertfromraw main.img main.vdi --variant Fixed

, а затем создал простую виртуальную машину (типа "other" и версии "other / unknown") с main.vdi в качестве диска. Я загрузил виртуальную машину и (еще раз) увидел текст "Ада говорит: Привет, мир!" всплывающее окно.

Следовательно, учитывая приведенный выше результат, я думаю, что компилятор не является основной проблемой при программировании x86-голого металла. Я скорее думаю, что основные проблемы:

  • Получение надлежащего времени выполнения Ada (например, нулевой отпечаток; ZFP), которое не связано ни с какими библиотеками ОС (например, стандартная библиотека C; libc). Я не знаю ничего, но некоторые могут существовать вне коробки. Я не уверен, что файл OSDev.org завершен до уровня среды выполнения ZFP. Для простых программ, таких как приведенная выше, вы можете опустить среду выполнения (как я делал в этом примере), если вы хотите подавить проверки (см. Комментарий в исходном коде).

  • Подготовка и запуск процессора x86 (см. здесь , чтобы получить хорошее представление об этом). Приведенный выше пример остается в 32-битном реальном режиме (если я утверждаю, что правильно), но вы можете перейти к защищенному режиму, 64-битным инструкциям и т. Д., Чтобы использовать все его возможности.

...