【问题标题】:Passing Constant String Value to Register将常量字符串值传递给寄存器
【发布时间】:2020-01-11 20:13:28
【问题描述】:

我正在重写 xv6 操作系统的引导扇区作为分配,并尝试执行一个无法正确输出到 QEMU 的简单帧。

这是使用 QEMU 模拟器(系统 i386)和适用于 Windows 的 Linux 子系统(使用 Ubuntu 18.04.1 LTS)。将文字传递给%al时,系统确实正确显示了一个字符,并进入了后续的死循环。

.code16
.globl start

start:
cli
xorw %ax, %ax
movw %ax, %ds
movw %ax, %es
movw %ax, %ss

movb $0x0e, %ah
movb hello, %al
movb $0, %bh
movb $7, %bl
int $0x10

stop:
jmp stop

hello:
.string "Hello world."

.org 0x1fe
.word 0xAA55

我希望输出为H,但打印出来的只是S;在大多数其他情况下,它根本不输出任何内容,除非使用文字。


编辑:这是构建后使用objdump 对二进制文件进行的反汇编:

bootsector.img:     file format elf64-x86-64


Disassembly of section .text:

0000000000000000 <start>:
   0:    fa                       cli
   1:    31 c0                    xor    %eax,%eax
   3:    8e d8                    mov    %eax,%ds
   5:    8e c0                    mov    %eax,%es
   7:    8e d0                    mov    %eax,%ss
   9:    b4 0e                    mov    $0xe,%ah
   b:    a0 00 00 b7 00 b3 03     movabs 0x10cd03b300b70000,%al
  12:    cd 10 

0000000000000014 <stop>:
  14:    eb fe                    jmp    14 <stop>

0000000000000016 <hello>:
  16:    48                       rex.W
  17:    65 6c                    gs insb (%dx),%es:(%rdi)
  19:    6c                       insb   (%dx),%es:(%rdi)
  1a:    6f                       outsl  %ds:(%rsi),(%dx)
  1b:    20 77 6f                 and    %dh,0x6f(%rdi)
  1e:    72 6c                    jb     8c <hello+0x76>
  20:    64 2e 00 00              fs add %al,%cs:(%rax)
    ...
 1fc:    00 00                    add    %al,(%rax)
 1fe:    55                       push   %rbp
 1ff:    aa                       stos   %al,%es:(%rdi)

我用来构建和执行代码的步骤:

$ as bootsector.S -o bootsector.img
$ objcopy -O binary bootsector.img
$ qemu-system-i386 bootsector.img -curses

【问题讨论】:

  • 您忽略了设置任何段寄存器(它们需要匹配您传递给链接器的任何地址),movb %7, %bl 应该是 movb $7, %bl
  • 我已经更正了我重写代码时的错字,但在我测试时它不存在。我已经更新了我的代码来设置段寄存器,但它仍然失败。我想重申,将文字传递给 %al 时它确实有效。
  • 好点,我想我只是假设那里有一个int $10? @OP:xorw %ax, %ds 不是法律指令,我猜你的意思是mov %ax, %ds?但我们仍然需要看看你是如何链接的。您应该展示所有代码,包括如何组装和创建二进制文件。此外,该程序足够短,您可以包含前几条指令的十六进制转储/反汇编。
  • 我在重写代码时犯了很多错误,对不起。我完全更正了帖子中的代码,并包含了反汇编。
  • @user3658679:你是如何创建bootsector.img的?发布命令行步骤,否则请查看例如stackoverflow.com/q/32508919/786653

标签: assembly x86 bootloader gnu-assembler xv6


【解决方案1】:

问题是您正在将代码组装到目标文件中,并且您正在将对象直接转换为二进制文件。您需要添加一个链接过程以从 ELF 对象生成可执行文件并将其转换为二进制文件。链接步骤还指定了 0x7c00 的原点。

你需要做的是:

  • 使用 AS(GNU 汇编器)将引导扇区组装到 ELF 对象。我建议输出为 ELF32 而不是 ELF64。
  • 使用 LD(GNU 链接器)将 ELF 对象链接到 ELF 可执行文件或直接链接到二进制文件。使用 LD 指定 0x7c00 的原点。
  • 将引导扇区放入映像文件中。

您可以使用的命令有:

as boot.s -o boot.o
ld -Ttext=0x7c00 --oformat=binary boot.o -o boot.bin

boot.bin 应该是最终的二进制文件,应该是 512 字节。您通常会将引导扇区放在磁盘映像中,但出于测试目的,您应该能够直接使用 QEMU 运行它:

qemu-system-i386 -fda boot.bin

如果您使用 16 位指令解码转储二进制文件,使用以下命令从 0x7c00 的原点开始:

ndisasm -b16 -o 0x7c00 boot.bin

您应该得到类似于以下内容的输出:

00007C00  FA                cli
00007C01  31C0              xor ax,ax
00007C03  8ED8              mov ds,ax
00007C05  8EC0              mov es,ax
00007C07  8ED0              mov ss,ax
00007C09  B40E              mov ah,0xe
00007C0B  A0167C            mov al,[0x7c16]
00007C0E  B700              mov bh,0x0
00007C10  B307              mov bl,0x7
00007C12  CD10              int 0x10
00007C14  EBFE              jmp short 0x7c14
00007C16  48                dec ax
00007C17  656C              gs insb
00007C19  6C                insb
00007C1A  6F                outsw
00007C1B  20776F            and [bx+0x6f],dh
00007C1E  726C              jc 0x7c8c
00007C20  642E0000          add [cs:bx+si],al
00007C24  0000              add [bx+si],al
00007C26  0000              add [bx+si],al
00007C28  0000              add [bx+si],al

[snip for brevity]

00007DFA  0000              add [bx+si],al
00007DFC  0000              add [bx+si],al
00007DFE  55                push bp
00007DFF  AA                stosb

另一种构建方式

您也可以使用 AS 汇编成目标文件,使用 LD 链接到 ELF,然后使用 OBJCOPY 将 ELF 可执行文件转换为二进制文件。这也允许您使用 ELF 可执行文件使用远程 GDB 等进行符号调试。您还可以使用 OBJDUMP 而不是 NDISASM 来查看生成的代码。

命令的顺序是:

as boot.s -o boot.o
ld -Ttext=0x7c00 boot.o -o boot.elf
objcopy -O binary boot.elf boot.bin

现在您可以使用 OBJDUMP 转储 boot.elf。请注意,您需要指定要解码为 16 位代码。 OBJDUMP 命令是:

objdump -Dx -Mi8086 boot.elf

输出将与此类似(如果您使用的是 64 位工具链):

boot.elf:     file format elf64-x86-64
boot.elf
architecture: i386:x86-64, flags 0x00000112:
EXEC_P, HAS_SYMS, D_PAGED
start address 0x0000000000007c00

Program Header:
    LOAD off    0x0000000000000000 vaddr 0x0000000000007000 paddr 0x0000000000007000 align 2**12
         filesz 0x0000000000000e00 memsz 0x0000000000000e00 flags r-x
    LOAD off    0x00000000000010e8 vaddr 0x00000000004000e8 paddr 0x00000000004000e8 align 2**12
         filesz 0x0000000000000020 memsz 0x0000000000000020 flags r--
    NOTE off    0x00000000000010e8 vaddr 0x00000000004000e8 paddr 0x00000000004000e8 align 2**3
         filesz 0x0000000000000020 memsz 0x0000000000000020 flags r--

Sections:
Idx Name          Size      VMA               LMA               File off  Algn
  0 .note.gnu.property 00000020  00000000004000e8  00000000004000e8  000010e8  2**3
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  1 .text         00000200  0000000000007c00  0000000000007c00  00000c00  2**0
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
SYMBOL TABLE:
00000000004000e8 l    d  .note.gnu.property     0000000000000000 .note.gnu.property
0000000000007c00 l    d  .text  0000000000000000 .text
0000000000000000 l    df *ABS*  0000000000000000 boot.o
0000000000007c16 l       .text  0000000000000000 hello
0000000000007c14 l       .text  0000000000000000 stop
0000000000007c00 g       .text  0000000000000000 _start
0000000000008000 g       .text  0000000000000000 __bss_start
0000000000008000 g       .text  0000000000000000 _edata
0000000000008000 g       .text  0000000000000000 _end



Disassembly of section .note.gnu.property:

00000000004000e8 <.note.gnu.property>:
  4000e8:       04 00                   add    $0x0,%al
  4000ea:       00 00                   add    %al,(%rax)
  4000ec:       10 00                   adc    %al,(%rax)
  4000ee:       00 00                   add    %al,(%rax)
  4000f0:       05 00 00 00 47          add    $0x47000000,%eax
  4000f5:       4e 55                   rex.WRX push %rbp
  4000f7:       00 01                   add    %al,(%rcx)
  4000f9:       00 00                   add    %al,(%rax)
  4000fb:       c0 04 00 00             rolb   $0x0,(%rax,%rax,1)
  4000ff:       00 01                   add    %al,(%rcx)
  400101:       00 00                   add    %al,(%rax)
  400103:       00 00                   add    %al,(%rax)
  400105:       00 00                   add    %al,(%rax)
        ...

Disassembly of section .text:

0000000000007c00 <_start>:
    7c00:       fa                      cli
    7c01:       31 c0                   xor    %eax,%eax
    7c03:       8e d8                   mov    %eax,%ds
    7c05:       8e c0                   mov    %eax,%es
    7c07:       8e d0                   mov    %eax,%ss
    7c09:       b4 0e                   mov    $0xe,%ah
    7c0b:       a0 16 7c b7 00 b3 07    movabs 0x10cd07b300b77c16,%al
    7c12:       cd 10

0000000000007c14 <stop>:
    7c14:       eb fe                   jmp    7c14 <stop>

0000000000007c16 <hello>:
    7c16:       48                      rex.W
    7c17:       65 6c                   gs insb (%dx),%es:(%rdi)
    7c19:       6c                      insb   (%dx),%es:(%rdi)
    7c1a:       6f                      outsl  %ds:(%rsi),(%dx)
    7c1b:       20 77 6f                and    %dh,0x6f(%rdi)
    7c1e:       72 6c                   jb     7c8c <hello+0x76>
    7c20:       64 2e 00 00             fs add %al,%cs:(%rax)
        ...
    7dfc:       00 00                   add    %al,(%rax)
    7dfe:       55                      push   %rbp
    7dff:       aa                      stos   %al,%es:(%rdi)

观察

您可能想知道为什么在您的输出中出现奇怪的movabs 指令:

bootsector.img:     file format elf64-x86-64

Disassembly of section .text:

0000000000000000 <start>:
   0:    fa                       cli
   1:    31 c0                    xor    %eax,%eax
   3:    8e d8                    mov    %eax,%ds
   5:    8e c0                    mov    %eax,%es
   7:    8e d0                    mov    %eax,%ss
   9:    b4 0e                    mov    $0xe,%ah
   b:    a0 00 00 b7 00 b3 03     movabs 0x10cd03b300b70000,%al
  12:    cd 10

OBJDUMP 不知道代码是 16 位的(该信息不会保留在 ELF 目标文件中)。 OBJDUMP 默认为 64 位解码,因为目标文件格式为 elf64-x86-64 (ELF64)。默认情况下,64 位工具链上的 AS 生成 64 位对象。 OBJDUMP 将默认为 ELF64 文件的 64 位解码,这就是导致错误解码的原因。您可以使用-Mi8086 请求通过 OBJDUMP 进行 16 位解码。


其他建议

  • 考虑将start 重命名为_start,以防止链接器发出无法找到入口点的警告。该警告不是致命的,可以忽略。另一种方法是通过添加额外选项--entry=start 来告诉LD 入口点是start。该命令可能如下所示:

    ld -Ttext=0x7c00 --entry=start boot.o -o boot.elf
    

【讨论】:

  • 谢谢,我错过了将 AS 输出链接到 ELF 文件并指定原点的步骤。 :)
猜你喜欢
  • 2014-10-31
  • 1970-01-01
  • 1970-01-01
  • 2020-06-16
  • 1970-01-01
  • 2023-04-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多