Страницы

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

воскресенье, 1 декабря 2019 г.

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

#c #ассемблер #gcc #boot


Разбираюсь с программированием загрузочного сектора. Делаю согласно этой статье.
Всё получалось до момента программирования вывода "Hello, World!" на языке C. Вот код,
который приводится в статье (собственно, я его просто копирую):

__asm__(".code16\n");
__asm__("jmpl $0x0000, $main\n");

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\n");
__asm__("jmpl $0x0000, $main\n");
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\n");
__asm__("jmpl $0x0000, $main\n");

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. Осталось понять, как правильно отслеживать это смещение...


Ответы

Ответ 1



В случае написание загрузочного сектора - проверяйте дизассемблером что у вас вышло. Для линукса незнаю как, для винды 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.

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

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