Как преобразовать опцию ld -Ttext в скрипт компоновщика? - PullRequest
1 голос
/ 10 марта 2019

Я нашел это руководство по созданию операционной системы, и я пытаюсь преобразовать компоновочную часть в файле make в скрипт компоновщика.

Вот учебник: https://github.com/ghaiklor/ghaiklor-os-gcc

Вот файл make:

SOURCES = $(shell find cpu drivers include kernel libc -name '*.c')
HEADERS = $(shell find cpu drivers include kernel libc -name '*.h')
OBJ = ${SOURCES:.c=.o cpu/interrupt.o}

ASM = nasm 
CC = gcc  
LD = ld -m elf_i386 
CFLAGS = -g -ffreestanding -Wall -Wextra -fno-exceptions -m32 -std=c11 -fno-pie

ifeq ($(shell uname -s),Darwin)     
    CC = i386-elf-gcc   
    LD = i386-elf-ld 
endif

all: os-image.bin

run: all    
    qemu-system-i386 os-image.bin

clean:  
    rm -rf *.dis *.o *.elf  
    rm -rf *.bin os-image.bin boot/*.bin kernel/*.bin   
    rm -rf boot/*.o cpu/*.o drivers/*.o kernel/*.o libc/*.o

os-image.bin: boot/boot.bin kernel/kernel.bin   
    cat $^ > os-image.bin

boot/boot.bin: boot/boot.asm    
    ${ASM} $< -f bin -o $@

kernel/kernel.bin: boot/kernel_entry.o ${OBJ}   
     ${LD} -o $@ -Ttext 0x1000 $^ --oformat binary

# ${LD} -o $@ -Tlinker.ld
# ${LD} -o $@ -Ttext 0x1000 $^ --oformat binary

%.o: %.c ${HEADERS}     ${CC} ${CFLAGS} -c $< -o $@

%.o: %.asm  ${ASM} $< -f elf -o $@

%.bin: %.asm    ${ASM} $< -f bin -o $@

Вот моя попытка создать скрипт компоновщика для фазы компоновки этого файла make:

ENTRY(_start)
OUTPUT_FORMAT(binary)
INPUT(boot/kernel_entry.o cpu/idt.o cpu/interrupt.o cpu/isr.o cpu/ports.o cpu/timer.o drivers/keyboard.o drivers/screen.o libc/mem.o libc/string.o libc/common.o kernel/kernel.o)
OUTPUT(kernel/kernel.bin)
SECTIONS
{
    . = 0x1000;
    .text : { *(.text) }
    end = .; _end = .; __end = .;
}

Для boot / kernel_entry.o нет .data или .bss, поэтому я не включил их в скрипт компоновщика. Я знаю, что -Ttext 0x1000 - это то место, где предполагается загрузка секции .text, и поэтому я установил счетчик для запуска по адресу 0x1000. Когда я запускаю систему с новой командой линковки в make-файле ${LD} -o $@ -Tlinker.ld, система работает не так, как обычно, поэтому я делаю что-то не так. Я попытался добавить простые разделы .data и .bss и все другие виды вещей, но все еще не могу заставить его работать правильно со скриптом связывания. Любая помощь будет великолепна.

Спасибо.

1 Ответ

2 голосов
/ 10 марта 2019

Учебник, на который вы ссылались, был посвящен 64-битному коду. Ваши Makefile и последующие комментарии предполагают, что вы пытаетесь изменить его для сборки / компиляции / запуска в качестве 32-битного ядра. Я разместил копию пересмотренного проекта, который обсуждается ниже, на моем wesbite . Сжатый архив можно скачать с здесь .

Урок, который у вас есть, довольно тупой, когда дело доходит до загрузки ядра в память. Это требует, чтобы вы знали, сколько секторов нужно прочитать, и жестко запрограммировали значение. Неправильное поведение может вызвать необычное поведение. Вместо жесткого кодирования значения вы можете заставить NASM включить kernel.bin внутри boot.bin, чтобы загрузчик мог вычислить количество секторов, которые нужно прочитать во время сборки. Не все эмуляторы и реальные машины поддерживают многодорожечное чтение, поэтому я бы изменил загрузчик для чтения одного сектора за раз, используя адресацию LBA. Чтобы узнать больше о вычислениях преобразования CHS в LBA, вы можете посмотреть мой ответ Stackoveflow на эту тему. Измените boot/boot.asm на:

STAGE2_ABS_ADDR  equ 0x01000
STAGE2_RUN_SEG   equ 0x0000
STAGE2_RUN_OFS   equ STAGE2_ABS_ADDR
                                ; Run stage2 with segment of 0x0000 and offset of 0x1000

STAGE2_LOAD_SEG  equ STAGE2_ABS_ADDR>>4
                                ; Segment to start reading Stage2 into
                                ;     right after bootloader

STAGE2_LBA_START equ 1          ; Logical Block Address(LBA) Stage2 starts on
                                ;     LBA 1 = sector after boot sector
STAGE2_LBA_END   equ STAGE2_LBA_START + NUM_STAGE2_SECTORS
                                ; Logical Block Address(LBA) Stage2 ends at
DISK_RETRIES     equ 3          ; Number of times to retry on disk error

bits 16
ORG 0x7c00

; Include a BPB (1.44MB floppy with FAT12) to be more comaptible with USB floppy media
; %include "bpb.inc"

boot_start:
    xor ax, ax                  ; DS=SS=ES=0 for stage2 loading
    mov ds, ax
    mov ss, ax                  ; Stack at 0x0000:0x0000
                                ;     (grows down fromtopof1st 64KiB segment)
    mov sp, 0x0000
    cld                         ; Set string instructions to use forward movement

    ; Read Stage2 1 sector at a time until stage2 is completely loaded
load_stage2:
    mov [bootDevice], dl        ; Save boot drive
    mov bx, MSG_LOAD_KERNEL
    call print_string

    mov di, STAGE2_LOAD_SEG     ; DI = Current segment to read into
    mov si, STAGE2_LBA_START    ; SI = LBA that stage2 starts at
    jmp .chk_for_last_lba       ; Check to see if we are last sector in stage2

.read_sector_loop:
    mov bp, DISK_RETRIES        ; Set disk retry count

    call lba_to_chs             ; Convert current LBA to CHS
    mov es, di                  ; Set ES to current segment number to read into
    xor bx, bx                  ; Offset zero in segment

.retry:
    mov ax, 0x0201              ; Call function 0x02 of int 13h (read sectors)
                                ;     AL = 1 = Sectors to read
    int 0x13                    ; BIOS Disk interrupt call
    jc .disk_error              ; If CF set then disk error

.success:
    add di, 512>>4              ; Advance to next 512 byte segment (0x20*16=512)
    inc si                      ; Next LBA

.chk_for_last_lba:
    cmp si, STAGE2_LBA_END      ; Have we reached the last stage2 sector?
    jl .read_sector_loop        ;     If we haven't then read next sector

.stage2_loaded:
    call switch_to_pm

.disk_error:
    xor ah, ah                  ; Int13h/AH=0 is drive reset
    int 0x13
    dec bp                      ; Decrease retry count
    jge .retry                  ; If retry count not exceeded then try again

error_end:
    ; Unrecoverable error; print drive error; enter infinite loop
    mov bx, diskErrorMsg        ; Display disk error message
    call print_string
    cli
.error_loop:
    hlt
    jmp .error_loop

;    Function: lba_to_chs
; Description: Translate Logical block address to CHS (Cylinder, Head, Sector).
;              Works for all valid FAT12 compatible disk geometries.
;
;   Resources: http://www.ctyme.com/intr/rb-0607.htm
;              https://en.wikipedia.org/wiki/Logical_block_addressing#CHS_conversion
;              https://stackoverflow.com/q/45434899/3857942
;              Sector    = (LBA mod SPT) + 1
;              Head      = (LBA / SPT) mod HEADS
;              Cylinder  = (LBA / SPT) / HEADS
;
;      Inputs: SI = LBA
;     Outputs: DL = Boot Drive Number
;              DH = Head
;              CH = Cylinder (lower 8 bits of 10-bit cylinder)
;              CL = Sector/Cylinder
;                   Upper 2 bits of 10-bit Cylinders in upper 2 bits of CL
;                   Sector in lower 6 bits of CL
;
;       Notes: Output registers match expectation of Int 13h/AH=2 inputs
;
lba_to_chs:
    push ax                     ; Preserve AX
    mov ax, si                  ; Copy LBA to AX
    xor dx, dx                  ; Upper 16-bit of 32-bit value set to 0 for DIV
    div word [sectorsPerTrack]  ; 32-bit by 16-bit DIV : LBA / SPT
    mov cl, dl                  ; CL = S = LBA mod SPT
    inc cl                      ; CL = S = (LBA mod SPT) + 1
    xor dx, dx                  ; Upper 16-bit of 32-bit value set to 0 for DIV
    div word [numHeads]         ; 32-bit by 16-bit DIV : (LBA / SPT) / HEADS
    mov dh, dl                  ; DH = H = (LBA / SPT) mod HEADS
    mov dl, [bootDevice]        ; boot device, not necessary to set but convenient
    mov ch, al                  ; CH = C(lower 8 bits) = (LBA / SPT) / HEADS
    shl ah, 6                   ; Store upper 2 bits of 10-bit Cylinder into
    or  cl, ah                  ;     upper 2 bits of Sector (CL)
    pop ax                      ; Restore scratch registers
    ret

%include "boot/print/print_string.asm"
%include "boot/pm/switch_to_pm.asm"
%include "boot/pm/gdt.asm"

bits 32

begin_pm:
jmp 0x1000

; Uncomment these lines if not using a BPB (via bpb.inc)
%ifndef WITH_BPB
numHeads:        dw 2         ; 1.44MB Floppy has 2 heads & 18 sector per track
sectorsPerTrack: dw 18
%endif

bootDevice:      db 0x00
diskErrorMsg:    db "Unrecoverable disk error!", 0
MSG_PROT_MODE db "Landed in 32-bit Protected Mode", 0
MSG_LOAD_KERNEL db "Loading kernel into memory", 0



; Pad boot sector to 510 bytes and add 2 byte boot signature for 512 total bytes
TIMES 510-($-$$) db  0
dw 0xaa55

; Beginning of stage2. This is at 0x1000 and will allow your stage2 to be 32.5KiB
; before running into problems. DL will be set to the drive number originally
; passed to us by the BIOS.

NUM_STAGE2_SECTORS equ (stage2_end-stage2_start+511) / 512
                                ; Number of 512 byte sectors stage2 uses.

stage2_start:
    ; Insert stage2 binary here. It is done this way since we
    ; can determine the size(and number of sectors) to load since
    ;     Size = stage2_end-stage2_start
    incbin "kernel/kernel.bin"

; End of stage2. Make sure this label is LAST in this file!
stage2_end:

Ваш Makefile может использовать некоторую очистку. Я заметил, что вы добавили interrupts.o к OBJ вручную (так как это файл ASM). Я бы порекомендовал собрать все файлы, связанные с ядром .asm, и добавить их в список OBJ. Я рекомендую изменить это на это:

SOURCES  = $(shell find cpu drivers include kernel libc -name '*.c')
KERN_ASM = $(shell find cpu drivers include kernel libc -name '*.asm')
HEADERS  = $(shell find cpu drivers include kernel libc -name '*.h')
OBJ      = ${SOURCES:.c=.o} ${KERN_ASM:.asm=.o}

ASM = nasm
CC = gcc
LD = ld -m elf_i386
OBJCOPY = objcopy

CFLAGS = -g -ffreestanding -Wall -Wextra -fno-exceptions -m32 -std=c11 -fno-pic \
         -fno-asynchronous-unwind-tables

ifeq ($(shell uname -s),Darwin)
        CC = i386-elf-gcc
        LD = i386-elf-ld
        OBJCOPY = i386-elf-objcopy
endif

all: os-image.bin

run: all
        qemu-system-i386 os-image.bin

clean:
        rm -rf *.dis *.o *.elf
        rm -rf *.bin os-image.bin boot/*.bin kernel/*.bin
        rm -rf boot/*.o cpu/*.o drivers/*.o kernel/*.o libc/*.o

# Make a 1.44MiB disk image. Can work for HDA and FDA booting
os-image.bin: kernel/kernel.bin boot/boot.bin
        dd if=/dev/zero of=$@ bs=1024 count=1440
        dd if=$(word 2,$^) of=$@ conv=notrunc

boot/boot.bin: boot/boot.asm
        ${ASM} $< -f bin -o $@

kernel/kernel.bin: kernel/kernel.elf
        ${OBJCOPY} -O binary $^ $@

kernel/kernel.elf: ${OBJ}
        ${LD} -o $@ -Tlinker.ld $^

%.o: %.c ${HEADERS}
        ${CC} ${CFLAGS} -c $< -o $@

%.o: %.asm
        ${ASM} $< -f elf -o $@

%.bin: %.asm
        ${ASM} $< -f bin -o $@

Этот файл make использует DD для создания образа дискеты 1,44 МБ, который можно использовать в качестве образа дискеты или жесткого диска. Вы заметите, что я удалил kernel_entry.asm из списка явных зависимостей. Чтобы этот новый Makefile работал Вы должны ПЕРЕМЕСТИТЬ boot/kernel_entry.asm до kernel/kernel_entry.asm. Убедитесь, что вы УДАЛЕНЫ boot / kernel_entry.asm .

Измените kernel/kernel_entry.asm для использования секции .text.entry и обнулите BSS. Это может выглядеть так:

global _start

bits 32
extern kernel_main
extern __bss_start
extern __bss_sizel

section .text.entry

_start:
  ; Zero out the BSS memory area a DWORD at a time
  ; since the memory isn't guaranteed to already be zero
  xor eax, eax
  mov ecx, __bss_sizel
  mov edi, __bss_start
  rep stosd

  ; Call C entry point of kernel
  call kernel_main
  jmp $

Сценарий компоновщика linker.ld, который работает с этими изменениями, выглядит следующим образом:

OUTPUT_FORMAT(elf32-i386)

SECTIONS {
    . = 0x1000;

    .text : SUBALIGN(4)
    {
        *(.text.entry)       /* Ensure .text.entry appears first */
        *(.text*)
        *(.rodata*)
        *(.data)
    }

    .bss : SUBALIGN(4) {
        __bss_start = .;
        *(COMMON)            /* all COMMON sections from all files */
        *(.bss)              /* all BSS sections from all files */
    }
    . = ALIGN(4);
    __bss_end = .;
    __bss_sizeb = __bss_end - __bss_start;       /* BSS size in bytes */
    __bss_sizel = (__bss_end - __bss_start) / 4; /* BSS size in longs/DWORDs */

    /DISCARD/ : {            /* Remove Unneeded sections */
        *(.eh_frame);
        *(.comment);
    }

    end = .; _end = .; __end = .;
}

Он обрабатывает все обычные сегменты, которые вы обычно видите в файле ELF для базовой ОС. Он также использует специальный раздел .entry.text, чтобы гарантировать, что код в kernel/kernel_entry.asm будет первым.

...