Запуск своего кода во время загрузки позволяет полностью контролировать работу компьютера, давая полную свободу действий. Рассмотрим процесс создания и запуска "голого" кода.
При создании загрузочного кода необходимо учитывать следующие особенности:
- 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.