Sintaxe Intel Assembly ljmp da sintaxe AT&T

Dec 18 2020

Estou tentando converter o código de inicialização xv6 da sintaxe At & t para a sintaxe Intel e tenho um problema com a instrução ljmp. Estou tentando aprender o processo de inicialização de computadores Intel e não sou particularmente forte com montagem Intel.

A sintaxe original da AT&T é .ljmp $0x8, $start32

Exemplo mínimo:

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

.code32
start32:
   nop

Usar as -32 -msyntax=intel -mnaked-reg foo.scom GNU Binutils 2.35.1 produz
Error: junk ':start32' after expressionpara a linha far jmp.

Estou usando GNU as e ferramentas gcc.
Também pode haver outros problemas com a montagem, como gdtdesc e gdt.

O código completo transferido para a sintaxe 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

Respostas

3 PeterCordes Dec 18 2020 at 11:58

Você pode usar jmp 0x08, start32

Por algum motivo, jmp 0x8:start32só funciona depois .intel_syntax noprefix, mesmo com argumentos de linha de comando que devem ser equivalentes. Esta é a sintaxe usada pelo Binutils objdump -d -Mintel -mi8086, por exemplo ea 16 00 08 00 jmp 0x8:0x16, provavelmente é um bug GAS que às vezes não é aceito.


asEditei sua pergunta para criar um pequeno exemplo reproduzível com 2.35.1 (que tenho no Arch GNU / Linux) com base em seus comentários respondendo a Jester. Incluí opções de linha de comando: presumo que você as esteja usando porque não há .intel_syntax noprefixdiretiva em seu arquivo.

Esse parece ser o problema: -msyntax=intel -mnaked-regdá outras Intel sintaxe coisas funcionam, como xor ax,ax, mas não não fazer jmp 0x8:start32o trabalho (ou outras formas de escrevê-lo). Apenas uma diretiva .intel_syntax noprefix1 faz com que a sintaxe para far jmp funcione.

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

Verifiquei esse -msyntax=intel -mnaked-regtrabalho para outras instruções onde seu efeito é necessário: movzx ax, alfunciona. Mas sem -mnaked-regnós obteríamos "muitas referências de memória" porque "ax" e "al" seriam considerados nomes de símbolos. Sem ou "incompatibilidade de tamanho de operando" sem -msyntax=intel.

A GAS lista de as -32 -msyntax=intel -mmnemonic=intel -mnaked-reg -o foo.o foo.s -al --listing-lhs-width=2 --listing-rhs-width=140
(eu tenho certeza que -mmnemonic=intelé irrelevante, e implícito sintaxe = intel.)

Observe que você pode ver quais instruções funcionaram porque têm código de máquina e quais não (a primeira jmp 0x8:start32) porque a coluna da esquerda está vazia para isso. A primeira coluna normalmente seria endereços, mas é ???? porque a montagem falhou. (Porque eu descomentei o jmp 0x8:start32para mostrar que falhou na primeira vez, trabalhando na 2ª vez.)

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 lista larguras de campo para a coluna esquerda em "palavras", o que aparentemente significa pedaços de 32 bits. É por isso que o 00byte mais significativo do seletor de segmento é separado por um espaço.)

Colocar um rótulo antes do jmp 0x8:labelnão ajudou; não é uma questão de referência para a frente ou para trás. Mesmo jmp 0x8:23não consegue montar.


Sintaxe "recomendada" por desmontadores, de uma construção funcional:

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, por falar nisso, não consegui o clang 11.0 para montar nenhuma versão da sintaxe da Intel com um nome de símbolo. ljmp 8, 12monta com clang, mas nem mesmo ljmp 8, start32. Somente alternando para a sintaxe AT&T e de volta eu poderia fazer com que o assembler ( clang -m32 -masm=intel -c) embutido do clang emitisse um far jmp no modo de 16 bits.

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

Lembre-se de que essa forma direta de JMP distante não está disponível no modo de 64 bits; talvez seja por isso que o assembler embutido do LLVM parece ter despendido menos esforço nisso.


Nota de rodapé 1: Na verdade também .intel_syntax prefixfunciona, mas nunca use isso. Ninguém quer ver o monstro Franken que está mov %eax, [%eax], ou especialmente add %edx, %eaxque está usando a dst, srcordem, mas com nomes de registro decorados pela AT&T.

3 MichaelPetch Dec 18 2020 at 10:55

No código traduzido completo que você apresentou, esta linha está incorreta:

ljmp start32, 0x8

A sintaxe adequada para um FAR JMP na sintaxe Intel do GNU Assembler é:

ljmp 0x08, start32

O valor do seletor seria o primeiro e o deslocamento em segundo. Parece que, ao traduzir da sintaxe da AT&T, você reverteu esses 2 valores quando a ordem deveria ter permanecido a mesma. Com os valores invertidos, você teria obtido o erro Error: can't handle non absolute segment in 'ljmp'. Na sintaxe Intel do GNU Assembler, você também pode substituí-la ljmppor jmpisso jmp 0x08, start32também funcionaria.

Existem diferentes tipos de sintaxe Intel. jmp 0x8:start32é a sintaxe Intel do NASM e difere da sintaxe Intel do GNU Assembler onde o :e ,diferem. Se você usasse a :para separar os dois valores, obteria o erro Error: junk ':start32' after expressionno GNU Assembler.


Notas

  • Se o código bootmainnão funcionar, provavelmente é um problema não relacionado ao código do carregador de inicialização que você apresentou nesta pergunta. Se você também estiver construindo todo o código C com a sintaxe Intel em vez da sintaxe AT&T, certifique-se de que todo o assembly inline foi convertido corretamente como fonte e o operando também foi revertido. xv6 provavelmente tem assembly embutido em uma série de arquivos xv6-public/x86.h, incluindo xv6-public/spinlock.c, xv6-public/usertests.cexv6-public/stressfs.c