Sintassi Intel Assembly ljmp dalla sintassi AT&T

Dec 18 2020

Sto cercando di convertire il codice di avvio xv6 dalla sintassi At & t alla sintassi Intel e ho un problema con l'istruzione ljmp. Sto cercando di imparare il processo di avvio dei computer Intel e non sono particolarmente bravo con l'assemblaggio Intel.

La sintassi originale di AT&T è .ljmp $0x8, $start32

Esempio minimo:

.code16
   jmp 0x8:start32          # won't assemble

.code32
start32:
   nop

Usare as -32 -msyntax=intel -mnaked-reg foo.scon GNU Binutils 2.35.1 produce
Error: junk ':start32' after expressionper la linea jmp lontana.

Sto usando GNU come e gli strumenti gcc.
Potrebbero esserci anche altri problemi con l'assembly come gdtdesc e gdt.

Il codice completo trasferito alla sintassi 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

Risposte

3 PeterCordes Dec 18 2020 at 11:58

Puoi usare jmp 0x08, start32

Per qualche ragione, jmp 0x8:start32funziona solo dopo .intel_syntax noprefix, anche con argomenti della riga di comando che dovrebbero essere equivalenti. Questa è la sintassi usata da Binutils objdump -d -Mintel -mi8086, ea 16 00 08 00 jmp 0x8:0x16quindi è probabilmente un bug GAS che a volte non è accettato.


Ho modificato la tua domanda per creare un piccolo esempio riproducibile con as2.35.1 (che ho su Arch GNU / Linux) in base ai tuoi commenti in risposta a Jester. Ho incluso le opzioni della riga di comando: presumo che tu abbia utilizzato quelle perché non c'è alcuna .intel_syntax noprefixdirettiva nel tuo file.

Che sembra essere il problema: -msyntax=intel -mnaked-regrende il lavoro altre cose Intel sintassi, come xor ax,ax, ma lo fa senza fare jmp 0x8:start32il lavoro (o di altri modi di scriverlo). Solo una direttiva .intel_syntax noprefix1 fa funzionare quella sintassi per jmp lontano.

# .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

Ho verificato che -msyntax=intel -mnaked-regfunziona per altre istruzioni in cui il loro effetto è necessario: movzx ax, alfunziona. Ma senza -mnaked-regavremmo "troppi riferimenti di memoria" perché "ax" e "al" sarebbero stati presi come nomi di simboli. Senza o "mancata corrispondenza della dimensione dell'operando" senza -msyntax=intel.

Un elenco GAS da as -32 -msyntax=intel -mmnemonic=intel -mnaked-reg -o foo.o foo.s -al --listing-lhs-width=2 --listing-rhs-width=140
(sono abbastanza sicuro che -mmnemonic=intelsia irrilevante e implicito da syntax = intel.)

Nota che puoi vedere quali istruzioni hanno funzionato perché hanno il codice macchina e quali no (la prima jmp 0x8:start32) perché la colonna di sinistra è vuota. La primissima colonna sarebbe normalmente indirizzi, ma è ???? perché l'assemblaggio non è riuscito. (Perché ho decommentato jmp 0x8:start32per mostrare che fallisce la prima volta, lavorando la seconda volta.)

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 elenca le larghezze dei campi per la colonna di sinistra in "parole", che apparentemente significa blocchi di 32 bit. Ecco perché il 00byte più significativo del selettore di segmento è separato da uno spazio.)

Mettere un'etichetta prima del jmp 0x8:labelnon ha aiutato; non è un problema di riferimento in avanti o all'indietro. Anche jmp 0x8:23non riesce a montare.


Sintassi "consigliata" dai disassemblatori, da una build funzionante:

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

E a proposito, non ho ottenuto clang 11.0 per assemblare nessuna versione della sintassi Intel di questo con un nome di simbolo. ljmp 8, 12si assembla con fragore, ma nemmeno ljmp 8, start32. Solo passando alla sintassi AT&T e viceversa potrei far sì che l'assembler ( clang -m32 -masm=intel -c) integrato di clang emetta un jmp lontano in modalità 16 bit.

.att_syntax
   ljmp $0x8, $start32      # working everywhere, even with clang
.intel_syntax noprefix

Tieni presente che questa forma diretta di JMP lontano non è disponibile in modalità a 64 bit; forse è per questo che l'assemblatore integrato di LLVM sembra aver dedicato meno impegno.


Nota a piè di pagina 1: .intel_syntax prefixFunziona anche in realtà , ma non usarlo mai. Nessuno vuole vedere il mostro franken che è mov %eax, [%eax], o soprattutto add %edx, %eaxche usa l' dst, srcordine, ma con i nomi dei registri decorati da AT&T.

3 MichaelPetch Dec 18 2020 at 10:55

Nel codice tradotto completo che hai presentato, questa riga non è corretta:

ljmp start32, 0x8

La sintassi corretta per un FAR JMP nella sintassi Intel di GNU Assembler è:

ljmp 0x08, start32

Il valore del selettore sarebbe il primo e l'offset il secondo. Sembra che traducendo dalla sintassi AT&T tu abbia invertito questi 2 valori quando l'ordine sarebbe dovuto rimanere lo stesso. Con i valori invertiti avresti ottenuto l'errore Error: can't handle non absolute segment in 'ljmp'. Nella sintassi Intel di GNU Assembler puoi anche sostituire ljmpcon jmpcosì jmp 0x08, start32funzionerebbe anche tu .

Esistono diversi tipi di sintassi Intel. jmp 0x8:start32è la sintassi Intel di NASM e differisce dalla sintassi Intel di GNU Assembler in cui :e ,differiscono. Se si usasse a :per separare i due valori, si otterrebbe l'errore Error: junk ':start32' after expressionin GNU Assembler.


Appunti

  • Se il codice in bootmainnon funziona, è probabile che si tratti di un problema non correlato al codice del bootloader che hai presentato in questa domanda. Se stai anche compilando tutto il codice C con la sintassi Intel anziché la sintassi AT&T, assicurati che tutto l'assembly inline sia stato convertito correttamente come sorgente e che anche l'operando sarebbe stato invertito. xv6 probabilmente ha in linea di assemblaggio in un certo numero di file tra cui xv6-public/x86.h, xv6-public/spinlock.c, xv6-public/usertests.cexv6-public/stressfs.c