Разбираюсь с программированием загрузочного сектора. Делаю согласно этой статье. Всё получалось до момента программирования вывода "Hello, World!" на языке C. Вот код, который приводится в статье (собственно, я его просто копирую):
__asm__(".code16
");
__asm__("jmpl $0x0000, $main
");
void printString(const char* pStr) {
while(*pStr) {
__asm__ __volatile__ (
"int $0x10" : : "a"(0x0e00 | *pStr), "b"(0x0007)
);
++pStr;
}
}
void main() {
printString("Hello, World");
}
Компилирую, линкую и тд опять же согласно той статье:
gcc -c -g -Os -m32 -ffreestanding -Wall -Werror test.c -o test.o
ld -melf_i386 -static -Ttest.ld -nostdlib --nmagic -o test.elf test.o
objcopy -O binary test.elf test.bin
dd if=/dev/zero of=floppy.img bs=512 count=2880
dd if=test.bin of=floppy.img
На всякий случай приведу содержимое test.ld:
ENTRY(main);
SECTIONS
{
. = 0x7C00;
.text : AT(0x7C00)
{
*(.text);
}
.sig : AT(0x7DFE)
{
SHORT(0xaa55);
}
}
Запускаю bochs, но вместо того, чтобы напечатать "Hello, World", у меня выводится только один символ "S".
Перенёс функцию в main:
__asm__(".code16
");
__asm__("jmpl $0x0000, $main
");
void main()
{
char s[] = "Hello, World";
char *str = s;
while (*str)
{
__asm__ __volatile__ (
"int $0x10" : : "a"(0x0e00 | *str), "b"(0x0007)
);
++str;
}
}
После этого он перестал выводить что-либо в принципе. Помогло исключение флага оптимизации -Os. Вывод стал верным. Немного поэкспериментировав, я понял, что эта оптимизация "убивает" цикл.
Однако, тот же самый код, но перенесённый в функцию, всё так же выводит символ "S".
Прошу помощи в решении этой проблемы. А так же, если возможно, разъяснения, почему флаг оптимизации "убивает" цикл. Спасибо!
UPD.
Hex от test.bin:
66 EA 3A 7C 00 00 00 00 66 55 66 89 E5 EB 19 67 66 8B 45 08 67 66 0F B6 00 66 0F BE C0 80 CC 0E CD 10 67 66 83 45 08 01 67 66 8B 45 08 67 66 0F B6 00 84 C0 75 D9 90 66 5D C3 66 55 66 89 E5 66 68 4F 7C 00 00 E8 C0 FF 66 83 C4 04 90 C9 C3 48 65 6C 6C 6F 2C 20 57 6F 72 6C 64 00 14 00 00 00 00 00 00 00 01 7A 52 00 01 7C 08 01 1B 0C 04 04 88 01 00 00 1C 00 00 00 1C 00 00 00 8C FF FF FF 32 00 00 00 00 42 0E 08 85 02 43 0D 05 6C C5 0C 04 04 00 00 1C 00 00 00 3C 00 00 00 9E FF FF FF 15 00 00 00 00 42 0E 08 85 02 43 0D 05 4F C5 0C 04 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 55 AA
Файл был получен из следующего исходника:
__asm__(".code16
");
__asm__("jmpl $0x0000, $main
");
void printString(const char* str)
{
while (*str)
{
__asm__ __volatile__ (
"int $0x10" : : "a"(0x0e00 | *str));
++str;
}
}
void main()
{
printString("Hello, World");
}
UPD.
Результат дизассемблирования файла test.elf:
% objdump -d test.elf
test.elf: file format elf32-i386
Disassembly of section .text:
00007c00
00007c08
00007c3a
UPD.
Вот дизассемблирование hiew как просессор видит программу:
7c00: 66EA3A7C00000000 jmpf 00000:07C3A
7c08: 6655 3push ebp
7c0A: 6689E5 mov ebp,esp
7c0D: EB19 jmps 000000028 --↓1
7c0F: 67668B4508 2mov eax,[ebp][8]
7c14: 67660FB600 movzx eax,b,[eax]
7c19: 660FBEC0 movsx eax,al
7c1D: 80CC0E or ah,00E
7c20: CD10 int 010
7c22: 676683450801 add d,[ebp][8],1
7c28: 67668B4508 1mov eax,[ebp][8]
7c2D: 67660FB600 movzx eax,b,[eax]
7c32: 84C0 test al,al
7c34: 75D9 jnz 00000000F --↑2
7c36: 90 nop
7c37: 665D pop ebp
7c39: C3 retn ;
7c3A: 6655 push ebp
7c3C: 6689E5 mov ebp,esp
7c3F: 66684F7C0000 push 000007C4F ;' |O'
7c45: E8C0FF call 000000008 --↑3
7c48: 6683C404 add esp,4
7c4C: 90 nop
7c4C: 90 nop
7c4D: C9 leave
7c4E: C3 retn ;
7c4F: 48 65 6C 6C 6F-2C 20 57 6F-72 6C 64 Hellow, World
Методом тыка, заметил, что действительно указатель необходимо смещать. Для загрузочной области необходимо поместить код в первый сектор диска со смещением в 0x7C00 байт. Это я указываю в файле test.ld. Я попробовал вывести всё, что у меня идёт после. И в куче разнообразных символов затаился желаемый "Hello, World". При этом изменение кода влечёт изменение в смещении, но я почти уверен, что оно будет всегда больше 0x7C00. Осталось понять, как правильно отслеживать это смещение...
Ответ
В случае написание загрузочного сектора - проверяйте дизассемблером что у вас вышло. Для линукса незнаю как, для винды hiew.exe - хороший дизассемблер. Не забывайте что на старте - процессор находится в 16-битном режиме команд и в 16-битном режиме адресации - это важно 1) при сборке бинарника 2) для понимания что происходит 3)при дизассемблировании.
Анализ даного случая показал, что код "неправильно" собран то ли компилятором то ли линкером. Коды 66 - переход в режим 32-битной команды, и 67 - переход в 32-битную адресацию
7c22: 676683450801 add d,[ebp][8],1
7c28: 67668B4508 1mov eax,[ebp][8]
7c2D: 67660FB600 movzx eax,b,[eax]
эти переходы должны напрягать. Их использовать можно (66), но с умом. 32-битную адресацию (67) лучше избегать вообще, вплоть до переключения в защищенный режим, но первичный загрузчик этого не делает. Как вывод - Если код не работает, хотя он проверен, а картинка не складывается - то проблема с опциями сборки.
Конкретно в даном случае помогло поменять директиву code16 (она в новой версии gcc исчезла) на code16gcc. Указывать code16 или code16gcc зависит от версии GCC.