Składnia ljmp zespołu Intel ze składni AT&T
Próbuję przekonwertować kod rozruchowy xv6 ze składni At & t na składnię Intel i mam problem z instrukcją ljmp. Próbuję nauczyć się procesu uruchamiania komputerów Intela i nie jestem szczególnie silny w montażu Intela.
Oryginalna składnia AT&T to .ljmp $0x8, $start32
Minimalny przykład:
.code16
jmp 0x8:start32 # won't assemble
.code32
start32:
nop
Używanie as -32 -msyntax=intel -mnaked-reg foo.s
z GNU Binutils 2.35.1 tworzy
Error: junk ':start32' after expression
dla linii far jmp.
Używam narzędzi GNU as i gcc.
Mogą również występować inne problemy z asemblacją, takie jak gdtdesc i gdt.
Pełny kod przeniesiony do składni Intela to:
# 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
Odpowiedzi
Możesz użyć jmp 0x08, start32
Z jakiegoś powodu jmp 0x8:start32
działa tylko później .intel_syntax noprefix
, nawet z argumentami wiersza poleceń, które powinny być równoważne. Jest to składnia używana przez Binutils objdump -d -Mintel -mi8086
, np ea 16 00 08 00 jmp 0x8:0x16
. Prawdopodobnie jest to błąd GAS, który czasami nie jest akceptowany.
Zredagowałem twoje pytanie, aby stworzyć mały odtwarzalny przykład w wersji as
2.35.1 (którą mam na Arch GNU / Linux) na podstawie twoich komentarzy, które odpowiedziały na Jester. Dołączyłem opcje wiersza poleceń: zakładam, że musiałeś ich używać, ponieważ .intel_syntax noprefix
w Twoim pliku nie ma dyrektywy.
To wydaje się być problemem: -msyntax=intel -mnaked-reg
sprawia, że inne rzeczy Intel składnia działa, jak xor ax,ax
, lecz nie uczynić jmp 0x8:start32
pracę (lub inne sposoby pisania). Tylko dyrektywa .intel_syntax noprefix
1 sprawia, że taka składnia działa dla 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
Sprawdziłem, że -msyntax=intel -mnaked-reg
działają dla innych instrukcji, gdzie ich efekt jest konieczny: movzx ax, al
działa. Ale bez nich -mnaked-reg
otrzymalibyśmy „zbyt wiele odniesień do pamięci”, ponieważ „ax” i „al” byłyby traktowane jako nazwy symboli. Bez lub „niezgodność rozmiaru operandu” bez -msyntax=intel
.
Lista GAS z as -32 -msyntax=intel -mmnemonic=intel -mnaked-reg -o foo.o foo.s -al --listing-lhs-width=2 --listing-rhs-width=140
(jestem prawie pewien, że -mmnemonic=intel
jest nieistotna i wynika ze składni = intel).
Zauważ, że możesz zobaczyć, które instrukcje zadziałały, ponieważ mają kod maszynowy, a które nie (pierwsza jmp 0x8:start32
), ponieważ lewa kolumna jest dla nich pusta. Pierwsza kolumna normalnie zawierałaby adresy, ale jest to ???? ponieważ montaż nie powiódł się. (Ponieważ odkomentowałem, jmp 0x8:start32
aby pokazać, że zawodzi za pierwszym razem, pracując za drugim razem.)
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 podaje szerokości pól dla lewej kolumny w „słowach”, co najwyraźniej oznacza fragmenty 32-bitowe. Dlatego 00
najbardziej znaczący bajt selektora segmentu jest oddzielony spacją).
Umieszczenie etykiety przed tym jmp 0x8:label
nie pomogło; nie jest to kwestia odniesienia do przodu i do tyłu. Nawet się jmp 0x8:23
nie składa.
Składnia „zalecana” przez dezasemblery z działającej kompilacji:
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
A tak przy okazji, nie dostałem clang 11.0, aby zebrać jakiekolwiek wersje składni Intela tego z nazwą symbolu. ljmp 8, 12
zbiera się z brzękiem, ale nie równo ljmp 8, start32
. Tylko przełączając się na składnię AT&T iz powrotem, mogłem uzyskać wbudowany assembler ( clang -m32 -masm=intel -c
) clang do emitowania 16-bitowego trybu far jmp.
.att_syntax
ljmp $0x8, $start32 # working everywhere, even with clang
.intel_syntax noprefix
Należy pamiętać, że ta bezpośrednia forma dalekiego JMP nie jest dostępna w trybie 64-bitowym; być może dlatego wydaje się, że wbudowany asembler LLVM poświęcił na to mniej wysiłku.
Przypis 1: Właściwie też .intel_syntax prefix
działa, ale nigdy tego nie używaj. Nikt nie chce widzieć tego frankena-potwora mov %eax, [%eax]
, a zwłaszcza add %edx, %eax
używającego dst, src
porządku, ale z nazwami rejestrowymi zdobionymi AT&T.
W przedstawionym kompletnym przetłumaczonym kodzie ta linia jest nieprawidłowa:
ljmp start32, 0x8
Prawidłowa składnia dla FAR JMP w składni Intel GNU Assemblera to:
ljmp 0x08, start32
Wartość selektora byłaby pierwsza, a przesunięcie druga. Wygląda na to, że tłumacząc ze składni AT&T, odwróciłeś te 2 wartości, gdy kolejność powinna pozostać taka sama. Po odwróceniu wartości wystąpiłby błąd Error: can't handle non absolute segment in 'ljmp'
. W składni Intela GNU Assemblera możesz również podstawić ljmp
przez, jmp
więc też jmp 0x08, start32
zadziała.
Istnieją różne odmiany składni Intela. jmp 0x8:start32
to składnia NASM Intel i różni się od składni GNU Assembler Intel, gdzie różnią się :
i ,
. Jeśli użyjesz a :
do oddzielenia dwóch wartości, otrzymasz błąd Error: junk ':start32' after expression
w GNU Assembler.
Uwagi
- Jeśli kod
bootmain
nie działa, prawdopodobnie jest to problem niezwiązany z kodem programu ładującego, który przedstawiłeś w tym pytaniu. Jeśli budujesz również cały kod C za pomocą składni Intel, a nie składni AT&T, upewnij się, że cały zestaw wbudowany został poprawnie przekonwertowany, ponieważ źródło i operand również zostałyby odwrócone. xv6 prawdopodobnie ma inline montaż w wielu plików w tymxv6-public/x86.h
,xv6-public/spinlock.c
,xv6-public/usertests.c
ixv6-public/stressfs.c