Запуск своего кода во время загрузки позволяет полностью контролировать работу компьютера, давая полную свободу действий. Рассмотрим процесс создания и запуска "голого" кода.
При создании загрузочного кода необходимо учитывать следующие особенности:
- 16 битный «реальный режим»
- mbr-код только 446 байт
- нет системных вызовов и функций ОС, нет библиотек (только статические)
- есть(эмулируются) прерывания bios
1. Hello world
В интернете существует множество примеров кода для вывода строки. Так как в дальнейшем этот код станет основной системного загрузчика и других программ, то будем сразу использовать язык программирования си.
helloboot.c:
void putch(char ch){ // функция вывода символа на экран в текстовом режиме
asm( // ассемблерная вставка, для вызова прерываний bios
" mov $0x0e, %ah ""\n"
" mov %[ch], %%al ""\n"
" int $0x10 ""\n"
:
: [ch]"g"(ch)
);
}
void __attribute__((noreturn)) main() { // главная функция, которая не завершается
char *hello="hello world";
char *p = hello;
while(*p!=0) putch(*p++); // вывод по символам
while (1); // бесконечное ожидание
}
2. Сборка
Скомпилируем объектный модуль:
> gcc -m16 -ffreestanding -fno-builtin -Os -c -o helloboot.o helloboot.c
-m16 -- для создания 16 битного кода
-ffreestanding -- код произвольной структуры
-fno-builtin -- не использовать встроенные функции
-Os -- оптимизировать размер
Скомпонуем бинарный образ без заголовков и секции:
> ld --oformat binary -Ttext 0x7c00 -e main -o helloboot.bin helloboot.o
--oformat binary -- создать образ состоящий только из кода и данных, размещенных последовательно
-Ttext 0x7c00 -- виртуальный адрес начала размещения кода, куда загрузчик передает управление
-e main -- точка входа в программу
3. Проверка кода
Используя утилиты binutills, можно убедиться в правильности создания образа.
>objdump -b binary -m i8086 -x -D -s helloboot.bin
helloboot.bin: file format binary
helloboot.bin
architecture: UNKNOWN!, flags 0x00000000:
start address 0x00000000
Sections:
Idx Name Size VMA LMA File off Algn
0 .data 00000079 00000000 00000000 00000000 2**0
CONTENTS, ALLOC, LOAD, DATA
SYMBOL TABLE:
no symbols
Contents of section .data:
0000 66556689 e56683ec 0467668b 45086788 fUf..f...gf.E.g.
0010 45fcb40e 678a45fc cd109066 c966c366 E...g.E....f.f.f
0020 556689e5 6683ec10 6766c745 f86d7c00 Uf..f...gf.E.m|.
0030 0067668b 45f86766 8945fceb 2267668b .gf.E.gf.E.."gf.
0040 45fc6766 8d500167 668955fc 678a0066 E.gf.P.gf.U.g..f
0050 0fbec066 5066e8a5 ffffff66 83c40467 ...fPf.....f...g
0060 668b45fc 678a0084 c075d2eb fe68656c f.E.g....u...hel
0070 6c6f2077 6f726c64 00 lo world.
Disassembly of section .data:
00000000 <.data>:
0: 66 55 push %ebp
2: 66 89 e5 mov %esp,%ebp
5: 66 83 ec 04 sub $0x4,%esp
9: 67 66 8b 45 08 mov 0x8(%ebp),%eax
e: 67 88 45 fc mov %al,-0x4(%ebp)
12: b4 0e mov $0xe,%ah
14: 67 8a 45 fc mov -0x4(%ebp),%al
18: cd 10 int $0x10
1a: 90 nop
1b: 66 c9 leavel
1d: 66 c3 retl
1f: 66 55 push %ebp
21: 66 89 e5 mov %esp,%ebp
24: 66 83 ec 10 sub $0x10,%esp
28: 67 66 c7 45 f8 6d 7c movl $0x7c6d,-0x8(%ebp)
2f: 00 00
31: 67 66 8b 45 f8 mov -0x8(%ebp),%eax
36: 67 66 89 45 fc mov %eax,-0x4(%ebp)
3b: eb 22 jmp 0x5f
3d: 67 66 8b 45 fc mov -0x4(%ebp),%eax
42: 67 66 8d 50 01 lea 0x1(%eax),%edx
47: 67 66 89 55 fc mov %edx,-0x4(%ebp)
4c: 67 8a 00 mov (%eax),%al
4f: 66 0f be c0 movsbl %al,%eax
53: 66 50 push %eax
55: 66 e8 a5 ff ff ff calll 0x0
5b: 66 83 c4 04 add $0x4,%esp
5f: 67 66 8b 45 fc mov -0x4(%ebp),%eax
64: 67 8a 00 mov (%eax),%al
67: 84 c0 test %al,%al
69: 75 d2 jne 0x3d
6b: eb fe jmp 0x6b
6d: 68 65 6c push $0x6c65
70: 6c insb (%dx),%es:(%di)
71: 6f outsw %ds:(%si),(%dx)
72: 20 77 6f and %dh,0x6f(%bx)
75: 72 6c jb 0xe3
77: 64 fs
...
Так же перед использованием загрузочного образа, необходимо убедиться, что его размер не превышает 446 байт:
>size --target binary helloboot.bin
text data bss dec hex filename
0 65 0 65 41 helloboot.bin
4. Запуск кода
Тестирование и отладку загружаемого кода, удобнее делать используя виртуальную машину.
Если разработка уже идет из виртуальной машины, то необходимо подключить новый виртуальный диск на несколько мегабайт, и создать разметку mbr с помощью fdisk или parted.
!!!Иначе, лучше тогда создать новый виртуальный фиксированного размера в формате vhd, с файлом которого можно работать как с содержимым обычного диска.
запишем загрузочный образ на новый диск.
>dd if=helloboot.bin of=/dev/sdb
Перезагрузимся и выберем новый диск для загрузки:

Видно, что код выполнился и вывел на экран строку.
!!! При использовании виртуальной машины qemu, существует возможность отлаживать выполняемый код через gdb.