来自 AT&T 语法的 Intel Assembly ljmp 语法
Intel Assembly ljmp syntax from AT&T syntax
我正在尝试将 xv6 引导代码从 At&t 语法转换为 Intel 语法,但 ljmp 指令有问题。我正在学习Intel电脑的启动过程,我对Intel汇编不是特别强。
原始的 AT&T 语法是 ljmp [=12=]x8, $start32
。
最小示例:
.code16
jmp 0x8:start32 # won't assemble
.code32
start32:
nop
使用 as -32 -msyntax=intel -mnaked-reg foo.s
和 GNU Binutils 2.35.1 生成
Error: junk ':start32' after expression
用于远 jmp 行。
我正在使用 GNU 和 gcc 工具。
程序集也可能存在其他问题,例如 gdtdesc 和 gdt。
移植到 Intel 语法的完整代码是:
# Start the first CPU: switch to 32-bit protectied mode, jump into C.
# The BIOS loads this code from the first sector of the hard disk into
# memory at physical address 0x7c00 and starts executing in real mode
# with cs = 0 and ip = 7c00.
.code16
.global start
start:
# Disable interrupts.
cli
# Zero data segment registers DS, ES, and SS.
xor ax, ax
mov ds, ax
mov es, ax
mov ss, ax
seta20.1:
# Wait for not busy.
in al, 0x64
test al, 0x2
jnz seta20.1
# 0xd1 -> port 0x64
mov al, 0xd1
out 0x64, al
seta20.2:
# Wait for not busy.
in al, 0x64
test al, 0x2
jnz seta20.2
# 0xdf -> port 0x60
mov al, 0xdf
out 0x60, al
# Switch from real to protected mode. Use a bootstrap GDT that makes
# virtual addresses map directly to physical addressses so that the
# effective memory map doesn't change during the transition.
lgdt gdtdesc
# Protection Enable in cr0 register.
mov eax, cr0
or eax, 0x1
mov cr0, eax
# Complete the transtion to 32-bit protected mode by using a long jmp
# to reload cs and eip. The segment descriptors are set up with no
# translation, so that the mapping is still the identity mapping.
# This instruction giving me problems.
ljmp start32, 0x8
.code32
start32:
# Set up the protected-mode data segment registers
mov ax, 0x10
mov ds, ax
mov es, ax
mov ss, ax
# Zero the segments not ready for use.
xor ax, ax
mov fs, ax
mov gs, ax
# Set up the stack pointer and call into C.
mov esp, start
call bootmain
# If bootmain returns spin.. ??
spin:
hlt
jmp spin
# Bootstrap GDT set up null segment, code segment, and data segment respectively.
# Force 4 byte alignment.
.p2align 2
gdt:
.word 0x0000, 0x0000
.byte 0, 0, 0, 0
.word 0xffff, 0x0000
.byte 0, 0x9a, 0xcf, 0
.word 0xffff, 0x0000
.byte 0, 0x92, 0xcf, 0
# sizeof(gdt) - 1 and address of gdt respectively.
gdtdesc:
.word (gdtdesc - gdt - 1)
.long gdt
在您提供的完整翻译代码中,这一行是不正确的:
ljmp start32, 0x8
GNU 汇编程序的 Intel 语法中 FAR JMP 的正确语法是:
ljmp 0x08, start32
首先是选择器值,其次是偏移量。似乎在从 AT&T 语法翻译时,您颠倒了这 2 个值,而顺序应该保持不变。如果值颠倒,您将得到错误 Error: can't handle non absolute segment in 'ljmp'
。在 GNU Assembler 的 Intel 语法中,您还可以将 ljmp
替换为 jmp
,这样 jmp 0x08, start32
也可以工作。
有不同风格的英特尔语法。 jmp 0x8:start32
是 NASM 的 Intel 语法,它不同于 GNU 汇编程序的 Intel 语法,其中 :
和 ,
不同。如果您使用 :
来分隔两个值,您将在 GNU 汇编程序中得到错误 Error: junk ':start32' after expression
。
备注
- 如果
bootmain
中的代码不起作用,则可能是与您在此问题中提供的引导加载程序代码无关的问题。如果您还使用 Intel 语法而不是 AT&T 语法构建所有 C 代码,那么请确保所有内联汇编都已正确转换,因为源和操作数也会被反转。 xv6 可能在许多文件中有内联汇编,包括 xv6-public/x86.h
、xv6-public/spinlock.c
、xv6-public/usertests.c
和 xv6-public/stressfs.c
可以使用jmp 0x08, start32
出于某种原因,jmp 0x8:start32
仅在 .intel_syntax noprefix
之后有效,即使使用应该等效的命令行参数也是如此。这是 Binutils objdump -d -Mintel -mi8086
使用的语法,例如ea 16 00 08 00 jmp 0x8:0x16
所以这可能是一个 GAS 错误,有时不被接受。
我编辑了你的问题,根据你回复 Jester 的评论,用 as
2.35.1(我在 Arch GNU/Linux 上有)创建了一个可重现的小例子。我包含命令行选项:我假设您一定一直在使用这些选项,因为您的文件中没有 .intel_syntax noprefix
指令。
这似乎是问题所在:-msyntax=intel -mnaked-reg
使其他英特尔语法正常工作,如 xor ax,ax
,但 不是 jmp 0x8:start32
work(或其他写法)。只有 .intel_syntax noprefix
1 指令使该语法适用于 far jmp 工作。
# .intel_syntax noprefix # rely on command line options to set this
.code16
xor ax, ax # verify that command-line setting of intel_syntax worked, otherwise this line errors.
ljmp 0x8, start32 # Working before or after a syntax directive, but is basically AT&T syntax
# jmp 0x8:start32 # fails here, works after a directive
jmp 0x8, start32 # Michael Petch's suggested syntax that's still somewhat AT&Tish. works with just cmdline opts.
.att_syntax
ljmp [=10=]x8, $start32 # working everywhere, even with clang
.intel_syntax noprefix
jmp 0x8:start32 # objdump disassembly syntax, but only works after a .intel_syntax noprefix directive
.code32
start32:
nop
我验证了 -msyntax=intel -mnaked-reg
对其他需要其效果的指令有效:movzx ax, al
有效。但是如果没有 -mnaked-reg
,我们会得到“太多的内存引用”,因为“ax”和“al”将被视为符号名称。没有或没有 -msyntax=intel
.
的“操作数大小不匹配”
来自 as -32 -msyntax=intel -mmnemonic=intel -mnaked-reg -o foo.o foo.s -al --listing-lhs-width=2 --listing-rhs-width=140
的 GAS 列表
(我很确定 -mmnemonic=intel
是无关紧要的,并且由 syntax=intel 暗示。)
请注意,您可以看到哪些指令有效,因为它们有机器代码,哪些指令无效(第一个 jmp 0x8:start32
),因为它的左侧栏是空的。第一列通常是地址,但 ????因为组装失败。 (因为我取消注释 jmp 0x8:start32
以表明它第一次失败,第二次工作。)
foo.s: Assembler messages:
foo.s:6: Error: junk `:start32' after expression
GAS LISTING foo.s page 1
1 # .intel_syntax noprefix # rely on command line options to set this
2 .code16
3 ???? 0FB6C0 movzx ax, al # verify that command-line setting of intel_syntax worked, otherwise this line errors.
4
5 ???? EA170008 00 ljmp 0x8, start32 # Working before or after a syntax directive, but is basically AT&T syntax
6 jmp 0x8:start32 # fails here, works after a directive
7 ???? EA170008 00 jmp 0x8, start32 # Michael Petch's suggested syntax that's still somewhat AT&Tish. works with just cmdline opts.
8
9 .att_syntax
10 ???? EA170008 00 ljmp [=11=]x8, $start32 # working everywhere, even with clang
11 .intel_syntax noprefix
12 ???? EA170008 00 jmp 0x8:start32 # objdump disassembly syntax, but only works after a .intel_syntax noprefix directive
13
14 .code32
15 start32:
16 ???? 90 nop
17
(GAS 确实在“words”中列出了左列的字段宽度,这显然意味着 32 位块。这就是为什么段选择器的 00
最高有效字节由 [= 分隔的原因95=].)
在 jmp 0x8:label
之前 加上标签 没有帮助;这不是前向与后向参考的问题。即使 jmp 0x8:23
也无法 assemble.
disassemblers 从工作构建中“推荐”的语法:
objdump -drwC -Mintel -mi8086 foo.o
:
foo.o: file format elf32-i386
Disassembly of section .text:
00000000 <start32-0x17>:
0: 0f b6 c0 movzx ax,al
3: ea 17 00 08 00 jmp 0x8:0x17 4: R_386_16 .text
8: ea 17 00 08 00 jmp 0x8:0x17 9: R_386_16 .text
d: ea 17 00 08 00 jmp 0x8:0x17 e: R_386_16 .text
12: ea 17 00 08 00 jmp 0x8:0x17 13: R_386_16 .text
00000017 <start32>:
17: 90 nop
llvm-objdump --mattr=+16bit-mode --x86-asm-syntax=intel -d foo.o
:
00000000 <.text>:
0: 0f b6 c0 movzx ax, al
3: ea 17 00 08 00 ljmp 8, 23
8: ea 17 00 08 00 ljmp 8, 23
d: ea 17 00 08 00 ljmp 8, 23
12: ea 17 00 08 00 ljmp 8, 23
00000017 <start32>:
17: 90 nop
顺便说一句,我没有得到 clang 11.0 到 assemble 任何带有符号名称的英特尔语法版本。 ljmp 8, 12
assemble 有 clang,但甚至没有 ljmp 8, start32
。只有切换到 AT&T 语法并返回才能让 clang 的内置 assembler (clang -m32 -masm=intel -c
) 发出 16 位模式 far jmp.
.att_syntax
ljmp [=14=]x8, $start32 # working everywhere, even with clang
.intel_syntax noprefix
请记住,这种直接形式的 far JMP 在 64 位模式下不可用;也许这就是为什么 LLVM 的内置 assembler 似乎花费较少的精力。
脚注 1:实际上 .intel_syntax prefix
也有效,但永远不要使用它。没有人想看到 mov %eax, [%eax]
的科学怪人,尤其是 add %edx, %eax
使用 dst, src
命令,但带有 AT&T 修饰的寄存器名称。
我正在尝试将 xv6 引导代码从 At&t 语法转换为 Intel 语法,但 ljmp 指令有问题。我正在学习Intel电脑的启动过程,我对Intel汇编不是特别强。
原始的 AT&T 语法是 ljmp [=12=]x8, $start32
。
最小示例:
.code16
jmp 0x8:start32 # won't assemble
.code32
start32:
nop
使用 as -32 -msyntax=intel -mnaked-reg foo.s
和 GNU Binutils 2.35.1 生成
Error: junk ':start32' after expression
用于远 jmp 行。
我正在使用 GNU 和 gcc 工具。
程序集也可能存在其他问题,例如 gdtdesc 和 gdt。
移植到 Intel 语法的完整代码是:
# Start the first CPU: switch to 32-bit protectied mode, jump into C.
# The BIOS loads this code from the first sector of the hard disk into
# memory at physical address 0x7c00 and starts executing in real mode
# with cs = 0 and ip = 7c00.
.code16
.global start
start:
# Disable interrupts.
cli
# Zero data segment registers DS, ES, and SS.
xor ax, ax
mov ds, ax
mov es, ax
mov ss, ax
seta20.1:
# Wait for not busy.
in al, 0x64
test al, 0x2
jnz seta20.1
# 0xd1 -> port 0x64
mov al, 0xd1
out 0x64, al
seta20.2:
# Wait for not busy.
in al, 0x64
test al, 0x2
jnz seta20.2
# 0xdf -> port 0x60
mov al, 0xdf
out 0x60, al
# Switch from real to protected mode. Use a bootstrap GDT that makes
# virtual addresses map directly to physical addressses so that the
# effective memory map doesn't change during the transition.
lgdt gdtdesc
# Protection Enable in cr0 register.
mov eax, cr0
or eax, 0x1
mov cr0, eax
# Complete the transtion to 32-bit protected mode by using a long jmp
# to reload cs and eip. The segment descriptors are set up with no
# translation, so that the mapping is still the identity mapping.
# This instruction giving me problems.
ljmp start32, 0x8
.code32
start32:
# Set up the protected-mode data segment registers
mov ax, 0x10
mov ds, ax
mov es, ax
mov ss, ax
# Zero the segments not ready for use.
xor ax, ax
mov fs, ax
mov gs, ax
# Set up the stack pointer and call into C.
mov esp, start
call bootmain
# If bootmain returns spin.. ??
spin:
hlt
jmp spin
# Bootstrap GDT set up null segment, code segment, and data segment respectively.
# Force 4 byte alignment.
.p2align 2
gdt:
.word 0x0000, 0x0000
.byte 0, 0, 0, 0
.word 0xffff, 0x0000
.byte 0, 0x9a, 0xcf, 0
.word 0xffff, 0x0000
.byte 0, 0x92, 0xcf, 0
# sizeof(gdt) - 1 and address of gdt respectively.
gdtdesc:
.word (gdtdesc - gdt - 1)
.long gdt
在您提供的完整翻译代码中,这一行是不正确的:
ljmp start32, 0x8
GNU 汇编程序的 Intel 语法中 FAR JMP 的正确语法是:
ljmp 0x08, start32
首先是选择器值,其次是偏移量。似乎在从 AT&T 语法翻译时,您颠倒了这 2 个值,而顺序应该保持不变。如果值颠倒,您将得到错误 Error: can't handle non absolute segment in 'ljmp'
。在 GNU Assembler 的 Intel 语法中,您还可以将 ljmp
替换为 jmp
,这样 jmp 0x08, start32
也可以工作。
有不同风格的英特尔语法。 jmp 0x8:start32
是 NASM 的 Intel 语法,它不同于 GNU 汇编程序的 Intel 语法,其中 :
和 ,
不同。如果您使用 :
来分隔两个值,您将在 GNU 汇编程序中得到错误 Error: junk ':start32' after expression
。
备注
- 如果
bootmain
中的代码不起作用,则可能是与您在此问题中提供的引导加载程序代码无关的问题。如果您还使用 Intel 语法而不是 AT&T 语法构建所有 C 代码,那么请确保所有内联汇编都已正确转换,因为源和操作数也会被反转。 xv6 可能在许多文件中有内联汇编,包括xv6-public/x86.h
、xv6-public/spinlock.c
、xv6-public/usertests.c
和xv6-public/stressfs.c
可以使用jmp 0x08, start32
出于某种原因,jmp 0x8:start32
仅在 .intel_syntax noprefix
之后有效,即使使用应该等效的命令行参数也是如此。这是 Binutils objdump -d -Mintel -mi8086
使用的语法,例如ea 16 00 08 00 jmp 0x8:0x16
所以这可能是一个 GAS 错误,有时不被接受。
我编辑了你的问题,根据你回复 Jester 的评论,用 as
2.35.1(我在 Arch GNU/Linux 上有)创建了一个可重现的小例子。我包含命令行选项:我假设您一定一直在使用这些选项,因为您的文件中没有 .intel_syntax noprefix
指令。
这似乎是问题所在:-msyntax=intel -mnaked-reg
使其他英特尔语法正常工作,如 xor ax,ax
,但 不是 jmp 0x8:start32
work(或其他写法)。只有 .intel_syntax noprefix
1 指令使该语法适用于 far jmp 工作。
# .intel_syntax noprefix # rely on command line options to set this
.code16
xor ax, ax # verify that command-line setting of intel_syntax worked, otherwise this line errors.
ljmp 0x8, start32 # Working before or after a syntax directive, but is basically AT&T syntax
# jmp 0x8:start32 # fails here, works after a directive
jmp 0x8, start32 # Michael Petch's suggested syntax that's still somewhat AT&Tish. works with just cmdline opts.
.att_syntax
ljmp [=10=]x8, $start32 # working everywhere, even with clang
.intel_syntax noprefix
jmp 0x8:start32 # objdump disassembly syntax, but only works after a .intel_syntax noprefix directive
.code32
start32:
nop
我验证了 -msyntax=intel -mnaked-reg
对其他需要其效果的指令有效:movzx ax, al
有效。但是如果没有 -mnaked-reg
,我们会得到“太多的内存引用”,因为“ax”和“al”将被视为符号名称。没有或没有 -msyntax=intel
.
来自 as -32 -msyntax=intel -mmnemonic=intel -mnaked-reg -o foo.o foo.s -al --listing-lhs-width=2 --listing-rhs-width=140
的 GAS 列表
(我很确定 -mmnemonic=intel
是无关紧要的,并且由 syntax=intel 暗示。)
请注意,您可以看到哪些指令有效,因为它们有机器代码,哪些指令无效(第一个 jmp 0x8:start32
),因为它的左侧栏是空的。第一列通常是地址,但 ????因为组装失败。 (因为我取消注释 jmp 0x8:start32
以表明它第一次失败,第二次工作。)
foo.s: Assembler messages:
foo.s:6: Error: junk `:start32' after expression
GAS LISTING foo.s page 1
1 # .intel_syntax noprefix # rely on command line options to set this
2 .code16
3 ???? 0FB6C0 movzx ax, al # verify that command-line setting of intel_syntax worked, otherwise this line errors.
4
5 ???? EA170008 00 ljmp 0x8, start32 # Working before or after a syntax directive, but is basically AT&T syntax
6 jmp 0x8:start32 # fails here, works after a directive
7 ???? EA170008 00 jmp 0x8, start32 # Michael Petch's suggested syntax that's still somewhat AT&Tish. works with just cmdline opts.
8
9 .att_syntax
10 ???? EA170008 00 ljmp [=11=]x8, $start32 # working everywhere, even with clang
11 .intel_syntax noprefix
12 ???? EA170008 00 jmp 0x8:start32 # objdump disassembly syntax, but only works after a .intel_syntax noprefix directive
13
14 .code32
15 start32:
16 ???? 90 nop
17
(GAS 确实在“words”中列出了左列的字段宽度,这显然意味着 32 位块。这就是为什么段选择器的 00
最高有效字节由 [= 分隔的原因95=].)
在 jmp 0x8:label
之前 加上标签 没有帮助;这不是前向与后向参考的问题。即使 jmp 0x8:23
也无法 assemble.
disassemblers 从工作构建中“推荐”的语法:
objdump -drwC -Mintel -mi8086 foo.o
:
foo.o: file format elf32-i386
Disassembly of section .text:
00000000 <start32-0x17>:
0: 0f b6 c0 movzx ax,al
3: ea 17 00 08 00 jmp 0x8:0x17 4: R_386_16 .text
8: ea 17 00 08 00 jmp 0x8:0x17 9: R_386_16 .text
d: ea 17 00 08 00 jmp 0x8:0x17 e: R_386_16 .text
12: ea 17 00 08 00 jmp 0x8:0x17 13: R_386_16 .text
00000017 <start32>:
17: 90 nop
llvm-objdump --mattr=+16bit-mode --x86-asm-syntax=intel -d foo.o
:
00000000 <.text>:
0: 0f b6 c0 movzx ax, al
3: ea 17 00 08 00 ljmp 8, 23
8: ea 17 00 08 00 ljmp 8, 23
d: ea 17 00 08 00 ljmp 8, 23
12: ea 17 00 08 00 ljmp 8, 23
00000017 <start32>:
17: 90 nop
顺便说一句,我没有得到 clang 11.0 到 assemble 任何带有符号名称的英特尔语法版本。 ljmp 8, 12
assemble 有 clang,但甚至没有 ljmp 8, start32
。只有切换到 AT&T 语法并返回才能让 clang 的内置 assembler (clang -m32 -masm=intel -c
) 发出 16 位模式 far jmp.
.att_syntax
ljmp [=14=]x8, $start32 # working everywhere, even with clang
.intel_syntax noprefix
请记住,这种直接形式的 far JMP 在 64 位模式下不可用;也许这就是为什么 LLVM 的内置 assembler 似乎花费较少的精力。
脚注 1:实际上 .intel_syntax prefix
也有效,但永远不要使用它。没有人想看到 mov %eax, [%eax]
的科学怪人,尤其是 add %edx, %eax
使用 dst, src
命令,但带有 AT&T 修饰的寄存器名称。