请原谅这个比较宽泛的问题。我想知道如何创建针对裸机x86的Ada工具链。我在osdev.org上看过Lucretia的Ada Bare Bones教程,该教程提供了一些有关为裸机开发构建合适的运行时的有用信息。这方面非常简单,但是我不确定如何为该平台构建交叉编译器,或者是否有必要。
我是否可以通过使用正确类型的RTS进行编译来创建“独立”二进制文件这一假设是否正确?如果我要创建/使用适当的独立式RTS,是否适合使用针对x86的现成的AdaCore或FSF GNAT?任何帮助理解这一点将不胜感激。
Dee*_*Dee 10
首先,请注意,我注意到裸机编程专家,但是由于这很有趣,我将尝试一下。话虽如此,我认为您不需要交叉编译器。本地平台编译器(例如,用于Linux x86-64的GNAT CE 2019)就可以了。
为了说明这一点,您可能想要重新创建在Ada的GitHub上找到的multiboot / hello_world示例。这是我在安装了GNAT CE 2019的Debian机器上采取的步骤,以使此工作正常进行。
首先,我安装了一些必要的软件包(QEMU,NASM和GNU xorriso),并克隆了上述存储库:
$ sudo apt-get install qemu nasm xorriso
$ git clone https://github.com/cirosantilli/x86-bare-metal-examples.git
Run Code Online (Sandbox Code Playgroud)
然后,在存储库中,我切换到目录multiboot/hello-world,按原样构建示例,并在QEMU中执行生成的图像,以检查所有设置是否正确:
multiboot/hello-world $ make
multiboot/hello-world $ make run
Run Code Online (Sandbox Code Playgroud)
结果是弹出一个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;
Run Code Online (Sandbox Code Playgroud)
Because we're running Ada, I had to change entry.asm and replaced the following lines to make sure that that the entry point of the Ada program instead of the C program was invoked. The entry point of the Ada program emitted by GNAT is _ada_main (see output of objdump -t main.o after compilation):
-- extern main
++ extern _ada_main
[...]
-- call main
++ call _ada_main
Run Code Online (Sandbox Code Playgroud)
In the Makefile I replaced the following lines to properly compile and link the Ada program. Note that I compile to i386 (using the -m32 switch) and request the linker to emit an elf_i386 executable as the processor will not execute 64-bit instructions directly after startup:
-- 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
Run Code Online (Sandbox Code Playgroud)
NOTE: Mind the tabs (indicated with <TAB>) before gcc. make is picky on this subject!
I then again subsequently invoked make and then make run to see a QEMU window pop up, but now showing the text:
Ada says: Hello world!
Run Code Online (Sandbox Code Playgroud)
This Ada program executed bare-metal (in IA-32 Real Mode)! I then took the demonstration even further by converting main.img to a VirtualBox disk (VDI) using
VBoxManage convertfromraw main.img main.vdi --variant Fixed
Run Code Online (Sandbox Code Playgroud)
and then created a simple VM (of type "other" and version "other/unknown") with main.vdi as its disk. I booted the VM and (once again) saw the text "Ada says: Hello world!" pop up.
Hence, given the result of above, I think that the compiler is not the main problem when programming x86 bare-metal. I rather think that the main challenges are:
Obtaining a proper Ada Runtime (e.g. zero footprint; ZFP) that does not link to any OS libraries (e.g. C standard library; libc). I don't know any, but some might exist out-of-the box. I'm not sure if the one on OSDev.org is complete to the level of a ZFP runtime. For simple programs as the one above, you can omit the runtime (as I did in this example) if you're willing to suppress checks (see comment in source code).
Getting the x86 processor all up and running (see here for a nice statement on this). The example above remains in 32-bit real mode (if I state correct), but you might want to proceed to protected mode, 64-bit instructions, etc. to benefit of all its power.