Синтаксис Intel Assembly ljmp из синтаксиса AT&T
Я пытаюсь преобразовать загрузочный код xv6 из синтаксиса At & t в синтаксис Intel, и у меня проблема с инструкцией ljmp. Я пытаюсь изучить процесс загрузки компьютеров Intel, и я не особо силен в сборке Intel.
Исходный синтаксис AT&T - .ljmp $0x8, $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 as и 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
Ответы
Ты можешь использовать jmp 0x08, start32
По какой-то причине jmp 0x8:start32
работает только после .intel_syntax noprefix
, даже с аргументами командной строки, которые должны быть эквивалентными. Это синтаксис, используемый Binutils objdump -d -Mintel -mi8086
, например ea 16 00 08 00 jmp 0x8:0x16
, возможно, это ошибка GAS, которая иногда не принимается.
Я отредактировал ваш вопрос, чтобы создать небольшой воспроизводимый пример с as
2.35.1 (который у меня есть в Arch GNU / Linux), основываясь на ваших комментариях в ответ на Jester. Я включил параметры командной строки: я предполагаю, что вы, должно быть, использовали их, потому что .intel_syntax noprefix
в вашем файле нет директивы.
Это , как представляется, проблема: -msyntax=intel -mnaked-reg
делает другую Intel синтаксиса работают вещи, как xor ax,ax
, но не делает jmp 0x8:start32
работу (или другие способы написания его). Только директива .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 $0x8, $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
.
Список GAS из as -32 -msyntax=intel -mmnemonic=intel -mnaked-reg -o foo.o foo.s -al --listing-lhs-width=2 --listing-rhs-width=140
(я почти уверен, что -mmnemonic=intel
он не имеет отношения и подразумевается синтаксисом = 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 $0x8, $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 перечисляет ширину полей для левого столбца в «словах», что, по-видимому, означает 32-битные блоки. Вот почему 00
старший байт селектора сегмента разделен пробелом.)
Поставив метку передjmp 0x8:label
тем не поможет; это не проблема прямой и обратной ссылки. Даже jmp 0x8:23
собрать не получается.
Синтаксис, "рекомендованный" дизассемблерами, из рабочей сборки:
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 для сборки каких-либо версий синтаксиса Intel с именем символа. ljmp 8, 12
собирается с лязгом, но даже не ljmp 8, start32
. Только переключившись на синтаксис AT&T и обратно, я смог заставить встроенный ассемблер clang ( clang -m32 -masm=intel -c
) выдавать 16-битный режим far jmp.
.att_syntax
ljmp $0x8, $start32 # working everywhere, even with clang
.intel_syntax noprefix
Имейте в виду, что эта прямая форма дальнего JMP недоступна в 64-битном режиме; возможно, поэтому встроенный ассемблер LLVM, похоже, потратил на это меньше усилий.
Сноска 1: На самом деле .intel_syntax prefix
тоже работает, но никогда не используйте это. Никто не хочет видеть франкен-монстра mov %eax, [%eax]
, add %edx, %eax
который использует dst, src
порядок, но с именами регистров, украшенными AT&T.
В представленном вами полном переведенном коде эта строка неверна:
ljmp start32, 0x8
Правильный синтаксис для FAR JMP в синтаксисе Intel GNU Assembler:
ljmp 0x08, start32
Значение селектора будет первым, а смещение - вторым. Похоже, что при переводе из синтаксиса AT&T вы изменили эти 2 значения на противоположные, хотя порядок должен был остаться прежним. Если бы значения поменялись местами, вы бы получили ошибку Error: can't handle non absolute segment in 'ljmp'
. В синтаксисе Intel GNU Assembler вы также можете заменить его ljmp
на, jmp
так jmp 0x08, start32
что тоже будет работать.
Есть разные варианты синтаксиса Intel. jmp 0x8:start32
- это синтаксис Intel NASM, который отличается от синтаксиса Intel GNU Assembler, в котором символы :
и ,
отличаются. Если вы использовали a :
для разделения двух значений, вы бы получили ошибку Error: junk ':start32' after expression
в GNU Assembler.
Заметки
- Если код
bootmain
не работает, вероятно, проблема не связана с кодом загрузчика, который вы указали в этом вопросе. Если вы также создаете весь код C с синтаксисом Intel, а не с синтаксисом AT&T, убедитесь, что вся встроенная сборка была правильно преобразована как исходный код, и операнд также был бы перевернут. xv6 вероятно , имеет встроенный узел в количестве файлов , включаяxv6-public/x86.h
,xv6-public/spinlock.c
,xv6-public/usertests.c
иxv6-public/stressfs.c