Страницы

Поиск по вопросам

пятница, 5 октября 2018 г.

Программирование загрузочного сектора C

Разбираюсь с программированием загрузочного сектора. Делаю согласно этой статье. Всё получалось до момента программирования вывода "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 : 7c00: 66 ea 3a 7c 00 00 ljmpw $0x0,$0x7c3a ...
00007c08 : 7c08: 66 55 push %bp 7c0a: 66 89 e5 mov %sp,%bp 7c0d: eb 19 jmp 7c28 7c0f: 67 66 8b 45 08 mov 0x8(%di),%ax 7c14: 67 66 0f b6 00 movzbw (%bx,%si),%ax 7c19: 66 0f be c0 movsbw %al,%ax 7c1d: 80 cc 0e or $0xe,%ah 7c20: cd 10 int $0x10 7c22: 67 66 83 45 08 01 addw $0x1,0x8(%di) 7c28: 67 66 8b 45 08 mov 0x8(%di),%ax 7c2d: 67 66 0f b6 00 movzbw (%bx,%si),%ax 7c32: 84 c0 test %al,%al 7c34: 75 d9 jne 7c0f 7c36: 90 nop 7c37: 66 5d pop %bp 7c39: c3 ret
00007c3a

: 7c3a: 66 55 push %bp 7c3c: 66 89 e5 mov %sp,%bp 7c3f: 66 68 4f 7c pushw $0x7c4f 7c43: 00 00 add %al,(%eax) 7c45: e8 c0 ff 66 83 call 83677c0a 7c4a: c4 04 90 les (%eax,%edx,4),%eax 7c4d: c9 leave 7c4e: c3 ret
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.

Комментариев нет:

Отправить комментарий