AT & T 구문의 인텔 어셈블리 ljmp 구문

Dec 18 2020

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

답변

3 PeterCordes Dec 18 2020 at 11:58

당신이 사용할 수있는 jmp 0x08, start32

어떤 이유로, 동등해야하는 명령 줄 인수가 있더라도 jmp 0x8:start32이후에만 작동 .intel_syntax noprefix합니다. 이것은 Binutils objdump -d -Mintel -mi8086에서 사용하는 구문입니다 . 예를 들어 ea 16 00 08 00 jmp 0x8:0x16때때로 허용되지 않는 GAS 버그 일 수 있습니다.


asJester에 대한 귀하의 의견을 기반으로 2.35.1 (Arch GNU / Linux에 있음)으로 재현 가능한 작은 예제를 만들기 위해 귀하의 질문을 편집했습니다 . 명령 줄 옵션을 포함했습니다 .intel_syntax noprefix. 파일에 지시문 이 없기 때문에이 옵션을 사용 했어야한다고 가정 합니다.

이것이 문제인 것 같습니다. -msyntax=intel -mnaked-reg다른 인텔 구문을, 같은 작동 하게 xor ax,ax하지만 작동하지는 않습니다jmp 0x8:start32 (또는 다른 작성 방법). 단 .intel_syntax noprefix1 지시문 만이 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.

목록 가스 에서 as -32 -msyntax=intel -mmnemonic=intel -mnaked-reg -o foo.o foo.s -al --listing-lhs-width=2 --listing-rhs-width=140
(나는 확신는 -mmnemonic=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

그리고 BTW, 나는 기호 이름으로 이것의 Intel 구문 버전을 어셈블하기 위해 clang 11.0을 얻지 못했습니다. ljmp 8, 12clang으로 어셈블되지만 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작동하지만 절대 사용하지 마십시오. 누구도 franken-monster mov %eax, [%eax], 또는 특히 주문을 add %edx, %eax사용 dst, src하지만 AT & T 장식 레지스터 이름 을보고 싶어하지 않습니다 .

3 MichaelPetch Dec 18 2020 at 10:55

귀하가 제시 한 전체 번역 코드에서이 행은 올바르지 않습니다.

ljmp start32, 0x8

GNU Assembler의 Intel 구문에서 FAR JMP에 대한 적절한 구문은 다음과 같습니다.

ljmp 0x08, start32

선택기 값은 첫 번째이고 오프셋은 두 번째입니다. AT & T 구문에서 번역 할 때 순서가 동일해야 할 때이 두 값을 뒤집은 것 같습니다. 값이 반전되면 오류가 발생했을 것 Error: can't handle non absolute segment in 'ljmp'입니다. GNU 어셈블러의 인텔은 또한 대체 할 수있는 구문 ljmpjmp매우 jmp 0x08, start32잘 작동.

Intel 구문에는 다양한 종류가 있습니다. jmp 0x8:start32NASM의 Intel 구문이며과가 다른 GNU Assembler의 Intel 구문 :,다릅니다. a를 사용 :하여 두 값을 분리하면 Error: junk ':start32' after expressionGNU Assembler 에서 오류가 발생 합니다.


메모

  • 코드 bootmain가 작동하지 않으면이 질문에서 제시 한 부트 로더 코드와 관련이없는 문제 일 수 있습니다. AT & T 구문이 아닌 Intel 구문을 사용하여 모든 C 코드를 빌드하는 경우 모든 인라인 어셈블리가 소스로 올바르게 변환되었는지 확인하고 피연산자도 반전되었는지 확인합니다. xv6 가능성을 포함하여 파일의 숫자에 인라인 어셈블리가 xv6-public/x86.h, xv6-public/spinlock.c, xv6-public/usertests.cxv6-public/stressfs.c