Создание простого многозадачного ядра, загруженного с помощью grub2

Я пытаюсь следовать инструкциям здесь, чтобы построить простое ядро ОС:http://mikeos.sourceforge.net/write-your-own-os.html

за исключением того, что вместо загрузки с дискеты я хочу создать ISO-образ на основе grub и загрузить компакт-диск с несколькими загрузками в эмуляторе. Я добавил следующее к источнику, указанному на этой странице, для заголовка multiboot:

MBALIGN     equ  1<<0                   ; align loaded modules on page boundaries
MEMINFO     equ  1<<1                   ; provide memory map
FLAGS       equ  MBALIGN | MEMINFO      ; this is the Multiboot 'flag' field
MAGIC       equ  0x1BADB002             ; 'magic number' lets bootloader find the header
CHECKSUM    equ -(MAGIC + FLAGS)        ; checksum of above, to prove we are multiboot
section .multiboot
align 4
    dd MAGIC
    dd FLAGS
    dd CHECKSUM

и я делаю следующее, чтобы создать образ:

nasm -felf32 -o init.bin  init.s
cp init.bin target/boot/init.bin
grub2-mkrescue -o init.iso target/

затем я бегу qemu для загрузки:

qemu-system-x86_64 -cdrom ./init.iso 

после выбора "myos" из меню загрузки, я получаю сообщение об ошибке

error: invalid arch-dependent ELF magic

что это значит, и как это можно исправить? Я попытался возиться с форматом elf, но только -felf32 кажется, работает...

1 ответов


GRUB поддерживает ELF32 и плоские файлы. Ваш заголовок, хотя неявно говорит, что вы предоставляете эльф бинарный.

использование плоского двоичного файла с Multiboot

если вы хотите сообщить загрузчику Multiboot (GRUB), что вы используете плоский двоичный файл, вы должны установить 16 бит до 1:

MULTIBOOT_AOUT_KLUDGE    equ  1 << 16
                              ;FLAGS[16] indicates to GRUB we are not
                              ;an ELF executable and the fields
                              ;header address,load address,load end address,
                              ;bss end address, and entry address will be
                              ;available in our Multiboot header

это не так просто, как просто указав этот флаг. Необходимо предоставить полный заголовок Multiboot, который предоставляет Multiboot загрузчик информации, чтобы загрузить наш двоичный файл в память. При использовании эльф формат эта информация находится в эльф заголовок, который предшествует нашему коду, поэтому не должен быть явно предоставлен. Заголовок Multiboot определяется в документация GRUB в деталях.

при использовании NASM С -f bin важно отметить, что нам нужно указать исходную точку для нашего кода. Многозарядные погрузчики загружают наше ядро по физическому адресу 0x100000. Мы должны указать в нашем файле ассемблера, что наша исходная точка 0x100000 так что правильные смещения и т. д. будет генерироваться в нашем конечном плоском двоичном изображении.

это пример, разделенный и измененный из одного из моих собственных проектов, который предоставляет простой заголовок. Вызов _Main настроен как вызов C в Примере, но вам не нужно делать это таким образом. Обычно я вызываю функцию, которая принимает пару параметров в стеке (используя вызов C Конвенция.)

[BITS 32]
[global _start]
[ORG 0x100000]                ;If using '-f bin' we need to specify the
                              ;origin point for our code with ORG directive
                              ;multiboot loaders load us at physical 
                              ;address 0x100000

MULTIBOOT_AOUT_KLUDGE    equ  1 << 16
                              ;FLAGS[16] indicates to GRUB we are not
                              ;an ELF executable and the fields
                              ;header address, load address, load end address;
                              ;bss end address and entry address will be available
                              ;in Multiboot header
MULTIBOOT_ALIGN          equ  1<<0   ; align loaded modules on page boundaries
MULTIBOOT_MEMINFO        equ  1<<1   ; provide memory map

MULTIBOOT_HEADER_MAGIC   equ  0x1BADB002
                              ;magic number GRUB searches for in the first 8k
                              ;of the kernel file GRUB is told to load

MULTIBOOT_HEADER_FLAGS   equ  MULTIBOOT_AOUT_KLUDGE|MULTIBOOT_ALIGN|MULTIBOOT_MEMINFO
CHECKSUM                 equ  -(MULTIBOOT_HEADER_MAGIC + MULTIBOOT_HEADER_FLAGS)

KERNEL_STACK             equ  0x00200000  ; Stack starts at the 2mb address & grows down

_start:
        xor    eax, eax                ;Clear eax and ebx in the event
        xor    ebx, ebx                ;we are not loaded by GRUB.
        jmp    multiboot_entry         ;Jump over the multiboot header
        align  4                       ;Multiboot header must be 32
                                       ;bits aligned to avoid error 13
multiboot_header:
        dd   MULTIBOOT_HEADER_MAGIC    ;magic number
        dd   MULTIBOOT_HEADER_FLAGS    ;flags
        dd   CHECKSUM                  ;checksum
        dd   multiboot_header          ;header address
        dd   _start                    ;load address of code entry point
                                       ;in our case _start
        dd   00                        ;load end address : not necessary
        dd   00                        ;bss end address : not necessary
        dd   multiboot_entry           ;entry address GRUB will start at

multiboot_entry:
        mov    esp, KERNEL_STACK       ;Setup the stack
        push   0                       ;Reset EFLAGS
        popf

        push   eax                     ;2nd argument is magic number
        push   ebx                     ;1st argument multiboot info pointer
        call   _Main                   ;Call _Main 
        add    esp, 8                  ;Cleanup 8 bytes pushed as arguments

        cli
endloop:
        hlt
        jmp   endloop

_Main:  
        ret                            ; Do nothing

Мультизагрузчик (жратва) обычно загружается в первый 8k вашего файла (будь то эльф или плоский двоичный), ищет многозадачный заголовок на 32-битной границе. Если 16 бит флага заголовка Multiboot ясно, он предполагает, что вы предоставляете эльф изображения. Затем он анализирует!--25-->эльф заголовок для получения информации, необходимой для загрузки файла ядра в память. Если 16 бит устанавливается, затем требуется полный многозадачный заголовок, чтобы загрузчик имел информацию для чтения вашего ядра в память, выполнения инициализации,а затем вызова в ваше ядро.

затем вы соберете свой init.s на плоский двоичный файл с чем-то вроде:

nasm -f bin -o init.bin init.s

использование ELF с Multiboot

чтобы связать комментарии Шута с вашим оригинальным вопросом, вы должны были загрузиться с эльф и это сработало, но не из-за одной маленькой детали. В вашем примере вы использовали это, чтобы сделать init.бин:

nasm -f elf32 -o init.bin  init.s

при использовании -f elf32, NASM генерирует объектные файлы (они не являются исполняемыми), которые должны быть связаны (с LD например) для создания окончательного эльф(ELF32) исполняемый. Вероятно, это сработало бы, если бы вы выполнили процессы сборки и связи с чем-то вроде:

nasm -f elf32 init.s -o init.o 
ld -Ttext=0x100000 -melf_i386 -o init.bin init.o

пожалуйста обратите внимание, что при использовании -f elf32 вы должны удалить ORG С init.s. The ORG С init.s и используйте эти команды, он должен генерировать плоский двоичный init.бин:

nasm -f elf32 init.s -o init.o
ld -Ttext=0x100000 -melf_i386 -o init.elf init.o
objcopy -O binary init.elf init.bin 

In это NASM сказано генерировать ELF32 объекты. Мы собираемся init.s на эльф объектный файл с именем init.o. Затем мы можем использовать компоновщик (LD), чтобы создать эльф исполняемый файл из init.o под названием init.эльф. Мы используем специальную программу под названием objcopy чтобы снять все эльф заголовки выключены и генерируют плоский двоичный файл исполняемый файл называется init.бин.

это намного больше, чем просто использование NASM С -f bin возможность генерировать плоский исполняемый файл init.бин. Тогда зачем беспокоиться? С помощью метода выше вы можете сказать NASM для создания отладочной информации, которая может быть использована gdb (отладчик GNU). Если вы попытаетесь использовать -g(включить отладку) с помощью NASM используя -f bin нет отладку информация генерируется. Отладочную информацию можно создать, изменив последовательность сборки следующим образом:

nasm -g3 -F dwarf -f elf32 init.s -o init.o
ld -Ttext=0x100000 -melf_i386 -o init.elf init.o
objcopy -O binary init.elf init.bin

init.o будет содержать отладочную информацию (в гном формат), который будет связан с LD на init.эльф (который сохраняет отладочную информацию). Плоские двоичные файлы не содержат отладочной информации, потому что они удаляются при использовании objcopy С -O binary. Вы можете использовать init.эльф если включить средство удаленной отладки в QEMU и использовать GDB для отладки. Эта отладочная информация в init.эльф предоставляет отладчику информацию, которая позволяет выполнить один шаг через код, получить доступ к переменным и меткам по имени, см. исходный код ассемблера и т. д.

помимо генерации отладочной информации, есть еще одна причина использовать NASM/LD/OBJCOPY процесс генерации двоичного файла ядра. LD на Изменить. LD позволяет человеку создавать скрипты компоновщика, которые позволяют вам лучше настраивать, как вещи выкладываются в конечном двоичном файле. Это может быть полезно для более сложных ядер, которые могут содержать код из разных сред (с, ассемблер и т. д.). Для маленького игрушечного ядра это может и не понадобиться, но по мере усложнения ядра преимущества использования скрипт линкера станет более очевидным.

удаленная отладка QEMU с GDB

если вы используете метод в предыдущем разделе для создания отладочной информации внутри эльф исполняемый файл (init.эльф) можно запустить QEMU а это:

  • загрузить QEMU окружающая среда и остановка процессора при запуске. Из man-страницы :

    - S Не запускайте CPU при запуске (вы должны ввести " c " на мониторе).

  • сделать QEMU послушать GDB удаленное соединение на localhost: 1234 . Из man-страницы :

    -S сокращение для-gdb tcp:: 1234, т. е. откройте gdbserver на TCP-порту 1234.

тогда вам просто нужно запустить GDB так что:

  • запуск GDB С эльф исполняемый файл (init.эльф) с отладочными символами и информация
  • подключается к localhost:1234, где QEMU слушать
  • настройка макета отладки по вашему выбору
  • устанавливает точку останова в нашем ядре (в этом примере multiboot_entry)

вот пример запуска нашего ядра из образа CD-ROM init.iso и начало GDB для подключения к нему:

qemu-system-x86_64 -cdrom ./init.iso -S -s &    
gdb init.elf \
        -ex 'target remote localhost:1234' \
        -ex 'layout src' \
        -ex 'layout regs' \
        -ex 'break multiboot_entry' \
        -ex 'continue'

вы должны иметь возможность использовать GDB почти так же, как отладка обычной программы. Это предполагает, что вы не будете отлаживать 16-разрядную программу (ядро).

Важные Соображения

как указывает шут, при использовании Мультизагрузочных совместимых загрузчиков, таких как жратва, процессор находится в 32-битном защищенном режиме (не 16-битный реальный режим). В отличие от загрузившись прямо из BIOS, вы не сможете использовать 16-битный код, включая большинство прерываний PC-BIOS. Если вам нужно быть в реальном режиме, вам придется вернуться в реальный режим вручную или создать задачу VM86 (последняя не тривиальна).

это важное соображение, так как некоторые из кода, с которым вы связаны в MikeOS 16-бит.