使用 GAS AT&T 指令为引导扇区计算填充长度? [英] Calculating padding length with GAS AT&T directives for a boot sector?

查看:32
本文介绍了使用 GAS AT&T 指令为引导扇区计算填充长度?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

所以我想在引导扇区中添加填充.假设,目前只有一个无限循环:jmp ..该扇区的长度需要为 512 字节.此外,还需要在末尾添加魔术数字 0xaa55.

So I want to add padding in the bootsector. Let's say, there is currently just an endless loop in there: jmp .. The sector needs to be 512 bytes long. Also, the magic num 0xaa55 is needed which is added at the end.

jmp .
.skip 508, 0
.word 0xaa55

但是如果我想打印一些东西但不想计算所有字节以将其填充到正确的大小怎么办?
在 Intel/NASM 语法中,它是:

But what if I want to print something but don't want to count all the bytes to pad it into the right size?
In Intel/NASM syntax would it be:

; print something
times 510-($-$$) db 0
dw 0xaa55

但是在 AT&T 语法中?好吧,循环 (.rept) 在这里不起作用,因为 . 没有给出这里需要的绝对值.我们对 .skip/.space 也有同样的问题,它们也需要一个绝对值.

But in AT&T syntax? Well a loop (.rept) doesn't work here because . doesn't give an absolute value which is needed here. We have the same problem with .skip/.space, they need an absolute value too.

有没有一种方法可以使用某种循环/.align/.skip/etc 来添加填充?

Is there a method to add padding using some sort of loop/.align/.skip/etc?

我使用 as 来构建和链接 ld -Ttext 0x7c00 --oformat binary 直到 yasm 对 AT&T 语法足够稳定.

I use as to build and for linking ld -Ttext 0x7c00 --oformat binary until yasm is stable enough for AT&T syntax.

推荐答案

使用 AT&T 语法,您可以在引导加载程序的开头放置一个标签,然后使用以下内容:

With AT&T syntax you can put a label at the start of your bootloader and then use something like this:

.global _start
.text
.code16
_start:
    jmp .

.space 510-(.-_start)
.word 0xaa55

Period . 是相对于当前部分开头的当前位置计数器.句点 ._start 之间的差异是一个绝对值,所以应该在这个表达式中工作.

Period . is the current location counter relative to the beginning of the current section. The difference between period . and _start is an absolute value so should work in this expression.

您可以使用 GCC(将调用 LD)使用如下命令将其组装到引导加载程序中:

You can use GCC (that will invoke LD) to assemble this to a bootloader with a command like:

gcc -Wl,--oformat=binary -Wl,-Ttext=0x7c00 -Wl,--build-id=none 
    -nostartfiles -nostdlib -m32 -o boot.bin boot.s

选项 -Wl,--oformat=binary 将此选项传递给链接器,链接器将强制它输出到平面二进制文件.-Wl,-Ttext=0x7c00 将此选项传递给链接器,链接器将有效地将原点设置为 0x07c00.-Wl,--build-id=none 告诉链接器不要使用 GCC 可能生成的构建 ID.0x7c00 是代码预期加载的偏移量.由于我们不能使用标准库或 C 运行时,我们用 -nostartfiles -nostdlib

The option -Wl,--oformat=binary passes this option to the linker which will force it to output to a flat binary file. -Wl,-Ttext=0x7c00 will pass this option to the linker that will effectively set the origin point to 0x07c00. -Wl,--build-id=none tell the linker not to use the build id that GCC may generate. 0x7c00 is the offset the code is expected to be loaded at. Since we can't use a standard library or C runtime we exclude them with -nostartfiles -nostdlib

如果您打算将多个文件链接在一起,您将无法使用此方法.在这种情况下,您需要将引导签名从代码中删除,并让链接器使用特制的链接器脚本来处理它.如果您将引导加载程序包含在单个程序集文件中,则上述方法将起作用.

You won't be able to use this method if you intend to link multiple files together. In that case you will need to leave the boot signature out of the code and let the linker take care of it with a specially crafted linker script. The method above will work if you contain your bootloader to a single assembly file.

我有一些关于编写引导加载程序代码的一般引导加载程序提示.人们通常遇到的一个大问题是没有设置段寄存器.如果您使用 0x7c00 的原点,那么您至少需要确保 DS register us 设置为 0.如果您编写的代码使用引用内部标签的内存操作数,这将很重要你的代码.

I have some general bootloader tips for writing bootloader code. One big issue people usually have is not setting the segment registers up. If you use an origin point of 0x7c00 then you need to make sure at a minimum that the DS register us set to 0. That will be important if you write code that uses memory operands that reference a label within your code.

使用 GNU 汇编器进行汇编时,请确保设置了所需的正确指令编码..code16 将使汇编器假定目标处理器在 16 位模式下运行..code32 为 32 位编码,.code64 假设为 64 位编码.as 的默认值通常永远不会是 .code16.

When assembling with GNU assembler ensure that you set the proper instruction encoding you want. .code16 will make the assembler assume the target processor is running in 16-bit mode. .code32 for 32-bit encoding, .code64 assumes 64-bit encoding. The default for as is generally never .code16.

正如我上面提到的,使用多个目标文件来创建您的引导加载程序会带来汇编指令无法克服的挑战.为此,您可以创建一个特殊的链接器脚本,将 Origin 点设置为 0x7c00 并让链接器将引导签名放在输出文件中.使用这种方法你不需要做任何填充,链接器会为你做.下面显示了一个基本的链接器脚本,它处理诸如 .text.data.rodata 等传统部分.您可能永远不会使用某些部分,但我添加了它们作为示例:

As I mentioned above using multiple object files to create your bootloader presents challenges that can't be overcome with assembly directives. In order to do this you can create a special linker script that sets the Origin point to 0x7c00 and lets the linker place the boot signature in the output file. Using this method you don't need to do any padding, the linker will do it for you. A basic linker script that deals with traditional sections like .text, .data, .rodata is shown below. You may never use some of the section, but I added them as an example:

文件 bootloader.ld

OUTPUT_FORMAT("elf32-i386");
ENTRY(_start);
SECTIONS
{
    . = 0x7C00;
    /* Code section, .text.bootentry code before other code */
    .text : SUBALIGN(0) {
        *(.text.bootentry);
        *(.text)
    }

    /* Read only data section with no alignment */
    .rodata : SUBALIGN(0) {
        *(.rodata)
    }

    /* Data section with no alignment */
    .data : SUBALIGN(0) {
        *(.data)
    }

    /* Boot signature at 510th byte from 0x7c00 */
    .sig : AT(0x7DFE) {
        SHORT(0xaa55);
    }

    /DISCARD/ : {
        *(.eh_frame);
        *(.comment);
        *(.note*);
    }
}

文件 boot.s 包含引导加载程序的主要入口点:

File boot.s containing main entry point of bootloader:

# Section .text.bootentry is always placed before all other code and data
# in the linker script. If using multiple object files only specify
# one .text.bootentry as that will be the code that will start executing
# at 0x7c00

.section .text.bootentry
.code16
.global _start
_start:
    # Initialize the segments especially DS and set the stack to grow down from
    # start of bootloader at _start. SS:SP=0x0000:0x7c00
    xor %ax, %ax
    mov %ax, %ds
    mov %ax, %ss
    mov $_start, %sp
    cld                   # Set direction flag forward for string instructions

    mov  $0x20, %al       # 1st param: Attribute black on green
    xor  %cx, %cx         # 2nd param: Screen cell index to write to. (0, 0) = upper left
    mov  $boot_msg, %dx   # 3rd param: String pointer
    call print_str

    # Infinite loop to end bootloader
    cli
.endloop:
    hlt
    jmp .endloop

.section .rodata
boot_msg: .asciz "My bootloader is running"

文件 aux.s 带有一个简单的函数,可以直接在屏幕上显示字符串:

File aux.s with a simple function to display a string directly to screen:

.global print_str         # Make this available to other modules
.section .text
.code16

# print_str (uint8_t attribute, char *str, uint16_t cellindex)
#
# Print a NUL terminated string directly to video memory at specified screen cell
# using a specified attribute (foreground/background)
#
# Calling convention:
#     Watcom
# Inputs:
#     AL = Attribute of characters to print
#     CX = Pointer to NUL terminated string to print
#     DX = Screen cell index to start printing at (cells are 2 bytes wide)
# Clobbers:
#     AX, ES
# Returns:
#    Nothing

print_str:
    push %di
    push %si

    mov  $0xb800, %di     # Segment b800 = text video memory
    mov  %di, %es
    mov  %cx, %di         # DI = screen cell index (0 = upper left corner)
    mov  %dx, %si         # SI = pointer to string (2nd parameter)
    mov  %al, %ah         # AH = attribute (3rd parameter)
    jmp  .testchar

# Print each character until NUL terminator found
.nextchar:
    stosw                 # Store current attrib(AH) and char(AL) to screen
                          # Advances DI by 2. Each text mode cell is 2 bytes
.testchar:
    lodsb                 # Load current char from string into AL(advances SI by 1)
    test %al, %al
    jne  .nextchar        # If we haven't reach NUL terminator display character
                          #     and advance to the next one

    pop %si
    pop %di
    ret

要将此引导加载程序构建到名为 boot.bin 的文件中,我们可以执行以下操作:

To build this bootloader to a file called boot.bin we could do something like:

as --32 aux.s -o aux.o
as --32 boot.s -o boot.o
ld -melf_i386 --oformat=binary -Tlink.ld -nostartfiles -nostdlib 
    aux.o boot.o -o boot.bin

特殊的.text.bootentry 被链接描述文件放置为第一个代码.此部分应仅在一个目标文件中定义,因为它将是出现在引导加载程序开头 0x7c00 处的代码.链接描述文件将 VMA(原点)调整为 0x7dfe 并写入引导签名(0xaa55).0x7dfe 比前 512 个字节的末尾低 2 个字节.我们不再在汇编代码中进行任何填充,也不在那里发出引导签名.

The special .text.bootentry is placed as the first code by the linker script. This section should only be defined in one object file as it will be the code that appears right at the beginning of the bootloader at 0x7c00. The linker script adjusts the VMA (origin) to 0x7dfe and writes the boot signature(0xaa55). 0x7dfe is 2 bytes below the end of the first 512 bytes. We no longer do any padding in the assembly code nor do we emit the boot signature there.

运行此示例引导加载程序时,应在显示屏的左上角打印一个字符串,绿色背景为黑色.

When run this sample bootloader should print a string to the upper left of the display with black on a green background.

这篇关于使用 GAS AT&T 指令为引导扇区计算填充长度?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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