如何将ld -Ttext选项转换为链接脚本? [英] How to convert ld -Ttext option to a linker script?

查看:72
本文介绍了如何将ld -Ttext选项转换为链接脚本?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经找到了有关创建操作系统的教程,并且正在尝试将make文件中的链接部分转换为链接脚本.

I've found this tutorial on creating an operating system, and I'm trying to convert the linking part in the make file into a linker script.

这里是教程: https://github.com/ghaiklor/ghaiklor-os-gcc

以下是制作文件:

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文件的链接阶段创建一个链接描述文件:

Here is my attempt at creating a linker script for the linking phase of this make file:

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的地址开始.当我使用makefile $ {LD} -o $ @ -Tlinker.ld 中的新链接命令运行系统时,系统无法正常运行,因此我做错了事.我尝试添加简单的.data和.bss部分以及所有其他类型的内容,但仍然无法通过链接脚本使该内容正常工作.任何帮助都会很棒.

There is no .data or .bss for the boot/kernel_entry.o and this is why I did not include them into the linker script. I know that -Ttext 0x1000 is where .text section is suppose to be loaded and thats why I set the counter to start at the address of 0x1000. When I run the system with the new linking command in the makefile ${LD} -o $@ -Tlinker.ld the system isn't working like normal, so I'm doing something wrong. I've tried adding simple .data and .bss sections and all other sorts of things but still can't manage to get the thing to work correctly with a linking script. Any help would be great.

谢谢.

推荐答案

您链接到的教程是针对64位代码的.您的 Makefile 和后续注释建议您尝试将其修改为汇编/编译/运行为32位内核.我已经将下面讨论的修订项目的副本放置在 wesbite 上.可以从此处下载压缩的tarball.

The tutorial you linked to was for a 64-bit code.Your Makefile and subsequent comments suggest you are trying to modify it to assemble/compile/run as a 32-bit kernel. I have placed a copy of the revised project discussed below on my wesbite. A compressed tarball can be downloaded from here.

在将内核加载到内存中时,您所拥有的教程相当愚蠢.它要求您知道要读取多少扇区并对值进行硬编码.弄错了会导致异常的行为.无需对值进行硬编码,您可以使NASM包含 kernel.bin inside boot.bin ,以便引导加载程序可以计算扇区数在组装时阅读.并非所有的仿真器和真实机器都支持多磁道读取,因此我将修改引导加载程序以使用LBA寻址一次读取一个扇区.要了解有关CHS到LBA转换计算的更多信息,请参阅我的其他

The tutorial you have is rather dumb when it comes to loading the kernel into memory. It requires you to know how many sectors to read and hard code the value. Getting this wrong can cause unusual behaviour. Rather than hard coding the value you can get NASM to include kernel.bin inside boot.bin so that the bootloader can compute the number of sectors to read at assembly time. Not all emulators and real machines support multi-track reads so I'd modify the bootloader to read one sector at a time using LBA addressing. To learn more about CHS to LBA conversion calculations you can see my other Stackoveflow answer on the topic. Modify boot/boot.asm to be:

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 列表中.我建议将其更改为此:

Your Makefile can use some cleaning up. I noticed you added interrupts.o to OBJ manually (since it is an ASM file). I'd recommend collecting all the kernel related .asm files and adding them to the OBJ list. I recommend changing it to this:

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.44MiB软盘映像,该映像可用作软盘或硬盘驱动器映像.您会注意到我已经从显式依赖项列表中删除了 kernel_entry.asm .为了使新的 Makefile 工作,您必须将 boot/kernel_entry.asm 移动到 kernel/kernel_entry.asm .确保您删除boot/kernel_entry.asm .

This make file uses DD to create a 1.44MiB floppy image that can be used as a floppy or hard drive disk image. You will notice I have removed kernel_entry.asm from the explicit dependency list. For this new Makefile to work You must MOVE boot/kernel_entry.asm to kernel/kernel_entry.asm. Ensure you REMOVE boot/kernel_entry.asm.

修改 kernel/kernel_entry.asm 以使用 .text.entry 部分,并将BSS归零.它看起来可能像这样:

Modify kernel/kernel_entry.asm to use section .text.entry and zero out the BSS. It can look like this:

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 如下:

The linker script linker.ld that works with these changes is as follows:

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 = .;
}

它处理您在基本OS的ELF文件中通常看到的所有常规段.它还使用特殊的 .entry.text 部分来确保 kernel/kernel_entry.asm 中的代码排在最前面.

It handles all the normal segments you'd generally see in an ELF file for a basic OS. It also uses a special .entry.text section to ensure the code in kernel/kernel_entry.asm comes first.

这篇关于如何将ld -Ttext选项转换为链接脚本?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆