将常量字符串值传递给寄存器

Passing Constant String Value to Register

我正在为 xv6 OS 重写引导扇区作为一项任务,并试图执行一个无法正确输出到 QEMU 的简单框架。

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

.code16
.globl start

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

movb [=10=]x0e, %ah
movb hello, %al
movb [=10=], %bh
movb , %bl
int [=10=]x10

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    [=11=]xe,%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

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

您需要做的是:

  • Assemble 带有 AS (GNU assembler) 的引导扇区到 ELF 对象。我建议输出为 ELF32 而不是 ELF64。
  • 使用 LD(GNU 链接器)将 ELF 对象link 直接转换为 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 assemble 到目标文件,用LD link 到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    [=16=]x0,%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    [=16=]x47000000,%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   [=16=]x0,(%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    [=16=]xe,%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    [=17=]xe,%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 以防止 linker 发出无法找到入口点的警告。该警告不是致命的,可以忽略。另一种方法是通过添加额外选项 --entry=start 来告诉 LD 入口点是 start。该命令可能如下所示:

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