Assembly 使用气体AT&;计算填充长度;引导扇区的T指令?

Assembly 使用气体AT&;计算填充长度;引导扇区的T指令?,assembly,x86-16,bootloader,gnu-assembler,att,Assembly,X86 16,Bootloader,Gnu Assembler,Att,所以我想在引导扇区中添加填充。比方说,目前只有一个无止境的循环:jmp.。扇区需要512字节长。此外,还需要在末尾添加magic num0xaa55 jmp . .skip 508, 0 .word 0xaa55 但是,如果我想打印一些内容,但不想计算所有字节以将其填充到正确的大小,该怎么办? 在Intel/NASM语法中,它是: ; print something times 510-($-$$) db 0 dw 0xaa55 但是在AT&T语法中呢?循环(.rept)在这里不起作用,因为

所以我想在引导扇区中添加填充。比方说,目前只有一个无止境的循环:
jmp.
。扇区需要512字节长。此外,还需要在末尾添加magic num
0xaa55

jmp .
.skip 508, 0
.word 0xaa55
但是,如果我想打印一些内容,但不想计算所有字节以将其填充到正确的大小,该怎么办?
在Intel/NASM语法中,它是:

; print something
times 510-($-$$) db 0
dw 0xaa55
但是在AT&T语法中呢?循环(
.rept
)在这里不起作用,因为
没有给出此处所需的绝对值。我们对
.skip
/
.space
也有同样的问题,它们也需要一个绝对值

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

编辑:
我使用
as
来构建和链接
ld-Ttext 0x7c00--oformat binary
,直到
yasm
对于AT&T语法来说足够稳定。

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

.global _start
.text
.code16
_start:
    jmp .

.space 510-(.-_start)
.word 0xaa55
Period
是相对于当前节开头的当前位置计数器。句点
\u start
之间的差值是一个绝对值,因此应在此表达式中起作用

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

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可能生成的build id。0x7c00是预期加载代码的偏移量。因为我们不能使用标准库或C运行时,所以我们用
-nostartfiles-nostlib

如果要将多个文件链接在一起,则无法使用此方法。在这种情况下,您需要将引导签名保留在代码之外,并让链接器使用精心编制的链接器脚本来处理它。如果将引导加载程序包含到单个程序集文件中,则上述方法将起作用


我有一些编写引导加载程序代码的通用方法。人们通常面临的一个大问题是不设置段寄存器。如果使用原点0x7c00,则需要至少确保DS register us设置为0。如果您编写的代码使用引用代码中标签的内存操作数,那么这一点很重要

使用GNU汇编程序进行汇编时,请确保设置了所需的正确指令编码
.code16
将使汇编程序假定目标处理器以16位模式运行
.code32
对于32位编码,
.code64
采用64位编码。
as
的默认值通常是从不
.code16


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

文件
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

# 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
,具有直接向屏幕显示字符串的简单功能:

.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
的文件,我们可以执行以下操作:

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个字节。我们不再在汇编代码中进行任何填充,也不再在那里发出引导签名


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

您只需使用:

.org
指令将位置计数器(
)前进到给定的值,用零填充任何跳过的位置(默认情况下)


请注意,位置计数器相对于正在生成的对象文件中当前节的开头。这使得上面的
.org
指令与执行
.space 510-(.-.text)
相同,其中
.text
是当前对象中
.text
部分的开始,而不是最终链接输出。这意味着它只有在从单个程序集文件创建引导加载程序时才真正起作用

执行此操作的干净方法当然是使用链接器脚本将位和段放置到适当的位置,但这也应该做到。@fuz您是正确的,但只要有一个文件,此方法仍然有效。如果他正在为引导加载程序将对象链接在一起,他还有其他问题。我有另一个答案,关于使用gnu汇编器作为引导加载程序,它显示了这样一个链接器脚本(用于LD)。如果OP正在做类似的事情。我可能对此做出了错误的假设,但OP没有说明他是如何组装这个东西的。只是吹毛求疵,你的“英特尔语法”示例就是NASM或MASM语法。GAS
.intel\u语法
仍然使用
而不是
$
,使用
.word
而不是
dw
。迪尔
    .code16
    .text
    jmp     .
    .org    510
    .word   0xaa55