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

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

问题描述

所以我想在bootsector中添加填充.假设当前只有一个无限循环: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

期间.是相对于当前节开始处的当前位置计数器.周期._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 注册给我们,并将其设置为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天全站免登陆