x86 어셈블리 언어

x86 assembly language


x86 어셈블리 언어는 1972년 4월에 출시된 인텔 8008 마이크로프로세서에 CPU와 어느 정도 역호환성을 제공하는 어셈블리 언어 제품군의 이름이다.[1][2]그것은 x86 프로세서 클래스의 객체 코드를 생산하는데 사용된다.

프로그래밍 언어로 간주되는 어셈블리는 기계마다 다르며 레벨이 낮다.모든 어셈블리 언어와 마찬가지로 x86 어셈블리는 기본 CPU 명령 또는 기계 코드를 나타내기 위해 니모닉을 사용한다.[3]어셈블리 언어는 소형 실시간 임베디드 시스템, 운영체제 커널, 장치 드라이버와 같은 상세하고 시간상 중요한 애플리케이션에 가장 많이 사용되지만, 게임 롤러코스터 타이쿤과 같은 다른 애플리케이션에도 사용될 수 있는데, 이 중 99%가 x86 어셈블리로 작성되었다.[4]컴파일러는 때때로 높은 수준의 프로그램을 기계 코드로 변환할 때 중간 단계로 어셈블리 코드를 생성한다.

니모닉 및 opcode

각 x86 어셈블리 지침은 종종 하나 이상의 피연산자와 결합되어 하나 이상의 바이트로 번역되는 니모닉으로 표현된다. 를 들어 NOP 명령은 0x90으로, HLT 명령은 0xF4로 번역된다.[3]서로 다른 프로세서가 다르게 해석할 수 있는 문서화된 니모닉이 없는 잠재적인 opcode가 있으며, 이를 사용하는 프로그램이 불일치하게 동작하거나 심지어 일부 프로세서에서 예외를 발생시키기도 한다.이러한 opcode는 종종 코드 쓰기 대회에서 코드를 더 작고, 더 빠르고, 더 우아하게 만들거나, 저자의 기량을 과시하기 위한 방법으로 나타난다.

구문

x86 어셈블리 언어에는 두 가지 주요 구문 분기가 있다.Intel 구문AT&T 구문.[5]인텔 구문DOS윈도 세계에서는 우세하고 AT&T 구문은 AT&T 에서 유닉스가 탄생했기 때문에 유닉스 세계에서는 AT&T 구문이 우세하다.[6]다음은 인텔 구문AT&T 구문 간의 주요 차이점을 요약한 것이다.

AT&T 인텔
매개 변수 순서 대상 이전의 소스.
영화를 찍다 $5, %eax 
소스 이전의 대상.
영화를 찍다 이삭스, 5 
매개 변수 크기 니모닉은 qword의 경우 q, l 긴(dword)의 경우 l, w의 경우 w, 바이트의 경우 b 등 피연산자의 크기를 나타내는 문자로 접미사를 이룬다.[5]
덧칠하다 $4, %esp 
사용되는 레지스터 이름(예: rax, eax, ax, ax, al 암시 q, l, w, b)에서 파생된다.
덧셈을 esp, 4 
시길스 "$"로 접두사 에 붙은 즉시 값, 레지스터 앞에 "%"[5]가 붙는다. 조립자는 자동으로 기호의 유형, 즉 등록부, 상수 또는 기타를 감지한다.
유효 주소 DISP(BASE,INDEX,Scale)의 일반 구문.예:
영화를 찍다 mem_location(%ebx,%ecx,4), %eax 
대괄호 안의 산술 식. 또한 피연산자에서 크기를 결정할 수 없는 경우 바이트, 워드 또는 드워드와 같은 크기 키워드를 사용해야 한다.[5]예:
영화를 찍다 이삭스, [이벡스 + ecx*4 + mem_location] 

원래 AT&T 구문을 사용하던 NASM, FASM, MASM, TASM, YASM. GASM 등 많은 x86 조립자가 인텔 구문을 사용하는데, AT&T 구문을 사용한 GASM은 .intel_syntax 지시어를 통해 버전 2.10부터 두 구문을 모두 지원해왔다.[5][7][8]x86용 AT&T 구문에서의 이상한 점은 원래의 AT&T 조립자로부터 물려받은 버그인 x87 피연산자가 역전된다는 것이다.[9]

AT&T 구문은 거의 모든 다른 아키텍처에 공통적이다.mov순서; 원래 PDP-11 어셈블리의 구문이었다.인텔 구문은 x86 아키텍처에 특수하며, x86 플랫폼 설명서에서 사용되는 구문이다.

레지스터

x86 프로세서는 이진 데이터의 저장소로 사용할 수 있는 레지스터 컬렉션을 가지고 있다.데이터 및 주소 레지스터를 총칭하여 일반 레지스터라고 한다.각 등록부에는 그들이 모두 할 수 있는 것 외에 특별한 목적이 있다.

  • AX 곱하기/divide, 문자열 로드 & 저장
  • MOVE용 BX 인덱스 레지스터
  • 문자열 작업 및 교대조의 CX 개수
  • IN 및 OUT의 DX 포트 주소
  • SP가 스택의 맨 위를 가리킴
  • BP가 스택 프레임의 밑면을 가리킴
  • SI가 스트림 작업의 소스를 가리킴
  • 스트림 작업에서 대상을 가리키는 DI

일반 레지스터와 함께 다음과 같은 추가 항목이 있다.

  • IP 명령 포인터
  • 플래그
  • 64k 세그먼트가 시작되는 위치를 결정하는 세그먼트 레지스터(CS, DS, ES, FS, GS, SS)
  • 추가 확장 레지스터(MMX, 3DNow!, SSE 등) (펜티엄 & 이후에만 해당).

IP 레지스터는 코드 세그먼트에서 다음 명령의 메모리 오프셋을 가리킨다(명령의 첫 번째 바이트를 가리킨다).IP 레지스터는 프로그래머가 직접 접속할 수 없다.

x86 레지스터는 MOV 지침을 사용하여 사용할 수 있다.예를 들어 Intel 구문에서는:

영화를 찍다 도끼, 1234시간 ; 값 1234헥스(4660d)를 레지스터 AX에 복사 
영화를 찍다 bx, 도끼    ; AX 레지스터 값을 BX 레지스터에 복사 

세그먼트 주소 지정

실제가상의 8086 모드에서 x86 아키텍처는 다른 많은 환경에서 사용되는 플랫 메모리 모델이 아니라 메모리를 처리하기 위해 분할이라고 알려진 프로세스를 사용한다.분할에는 두 부분, 즉 세그먼트오프셋에서 메모리 주소를 구성하는 것이 포함된다. 세그먼트는 64KB(64×210) 주소 그룹의 시작을 가리키며 오프셋은 이 시작 주소에서 원하는 주소까지의 거리를 결정한다.분할된 주소 지정에서 전체 메모리 주소에 대해 두 개의 레지스터가 필요하다.하나는 세그먼트를 고정하고 다른 하나는 오프셋을 고정한다.다시 플랫 어드레스로 변환하기 위해 세그먼트 값은 4비트를 남겨두고( 곱하기 2나4 16과 같음) 오프셋에 더하여 전체 어드레스를 형성하는데, 프로그래밍을 상당히 복잡하게 만들기는 하지만, 교묘한 어드레스를 통해 64k 장벽을 깨뜨릴 수 있다.

실제 모드/보호된 경우에만, 예를 들어 DS가 16진수 숫자 0xDEAD를 포함하고 DX가 숫자 0xCAFE를 포함하는 경우 이들은 함께 메모리 주소 0xDEED * 0x10 + 0xCA를 가리킨다.FE = 0xEB5따라서 CPU는 실제 모드에서 최대 104만8,576바이트(1MB)까지 처리할 수 있다.세그먼트오프셋 값을 결합하면 20비트 주소를 찾을 수 있다.

원래 IBM PC는 프로그램을 640KB로 제한했지만 확장 메모리 규격을 사용하여 윈도우즈와 같은 이후 운영 체제가 더 큰 주소 범위의 새로운 프로세서를 사용하고 자체적인 가상 메모리 체계를 구현했을 때 사용하지 못하게 되는 은행 전환 방식을 구현했다.

보호 모드는 인텔 80286부터 OS/2에 의해 활용되었다.BIOS에 접근할 수 없는 점, 프로세서를 재설정하지 않고 리얼 모드로 되돌릴 수 없는 점 등 몇 가지 단점이 널리 사용되는 것을 막았다.[10]80286은 16비트 세그먼트에서 메모리를 다루는 것으로 여전히 제한되어 있어 한 번에 2바이트16(64킬로바이트)만 액세스할 수 있었다.80286의 확장된 기능에 액세스하기 위해 운영 체제는 프로세서를 보호 모드로 설정하여 24비트 주소 지정을 가능하게 하고 따라서24 2바이트의 메모리(16MB)를 사용할 수 있게 된다.

보호 모드에서 세그먼트 선택기는 항목이 GDT 또는 LDT인지 여부를 결정하는 테이블 표시기 비트 및 2비트 요청된 권한 수준의 세 부분으로 나눌 수 있다. x86 메모리 분할을 참조하십시오.

세그먼트와 오프셋이 있는 주소를 언급할 때 segment:offset의 표기법이 사용되므로 위의 예에서 flat address 0xEB5CE는 0xDE로 쓸 수 있다.AD:0xCAFE 또는 세그먼트 및 오프셋 레지스터 쌍, DS:DX.

중요한 주소를 가리키는 세그먼트 레지스터와 일반 레지스터의 몇 가지 특별한 조합이 있다.

  • CS:IP(CS는 코드 세그먼트, IP는 명령 포인터)는 프로세서가 다음 바이트의 코드를 가져올 주소를 가리킨다.
  • SS:SP(SS는 Stack Segment, SP는 Stack Pointer)는 스택 상단의 주소(즉, 가장 최근에 누른 바이트)를 가리킨다.
  • DS:SI(DS는 데이터 세그먼트, SI는 소스 인덱스)는 ES에 복사되려고 하는 문자열 데이터를 가리키는 데 자주 사용된다.DI
  • ES:DI(ES는 Extra Segment, DI는 Destination Index)는 일반적으로 위에서 언급한 문자열 복사본의 대상을 가리키는 데 사용된다.

인텔 80386은 실제 모드, 보호 모드, 가상 모드 등 3가지 작동 모드를 특징으로 했다.80286년에 처음 도입된 보호 모드는 80386이 최대 4GB의 메모리를 처리할 수 있도록 확장되었으며, 완전히 새로운 가상 8086 모드(VM86)는 일부 프로그램은 호환되지 않지만(일반적으로 메모리 주소 지정의 결과로) 대부분 에뮬레이트된 보호 환경에서 하나 이상의 리얼 모드 프로그램을 실행할 수 있도록 했다.트릭 또는 지정되지 않은 op-properties 사용).

80386의 확장 보호 모드의 32비트 플랫 메모리 모델2003년에 AMD가 x86-64를 출시할 때까지 x86 프로세서 제품군에 가장 중요한 기능 변화일 수 있는데, 이는 Windows가 이제 usi에 의해 DOS 애플리케이션을 포함한 많은 애플리케이션을 한 번에 실행할 수 있기 때문에 (보호 모드에 의존한) Windows 3.1의 대규모 채택을 촉진했기 때문이다.ng 가상 메모리 및 간단한 멀티태스킹.

실행 모드

x86 프로세서는 x86 코드, 리얼 모드, 보호 모드, 롱 모드, 가상 86 모드, 시스템 관리 모드에 대해 5가지 모드를 지원하며, 이 모드에서는 일부 지침을 사용할 수 있고 다른 모드에서는 사용할 수 없다.16비트 x86 프로세서(8086, 8088, 80186, 80188 및 80286)에서 16비트 하위 집합의 지침을 사용할 수 있다.이러한 지침은 모든 x86 프로세서에서 실제 모드에서 사용할 수 있으며, 16비트 보호 모드(80286 이후)에서는 보호 모드와 관련된 추가 지침을 사용할 수 있다.80386 이상에서는 리얼 모드를 포함한 모든 모드에서 32비트 명령(후기 확장 포함)을 사용할 수 있으며, 이러한 CPU에서는 V86 모드와 32비트 보호 모드가 추가되어 기능을 관리할 수 있다.SMM은 자체 특별 지침과 함께 일부 인텔 i386SL, i486 이상 CPU에서 사용할 수 있다.마지막으로 롱 모드(AMD Opteron 이후)에서는 64비트 명령어 및 더 많은 레지스터도 이용할 수 있다.명령 집합은 각 모드에서 유사하지만 메모리 주소 지정과 단어 크기가 달라 프로그래밍 전략이 달라진다.

x86 코드를 실행할 수 있는 모드는 다음과 같다.

  • 리얼 모드(16비트)
    • 20비트 세그먼트 메모리 주소 공간(실제로 1MB의 메모리만 처리할 수 있다는 의미, 사실 조금 더 많은 메모리)은 주변 하드웨어에 대한 소프트웨어 직접 액세스, 하드웨어 레벨에서 메모리 보호 또는 멀티태스킹의 개념이 없다.BIOS를 사용하는 시스템은 이 모드에서 시작되며,
  • 보호 모드(16비트 및 32비트)
    • 주소 지정 가능한 물리적 메모리16MB로, 주소 지정 가능한 가상 메모리1GB로 확장프로그램이 서로 손상되지 않도록 하는 권한 수준과 보호된 메모리 제공. 16비트 보호 모드(DOS 시대 말기 사용)는 복잡한 다중 세그먼트 메모리 모델을 사용했다. 32비트 보호 모드는 단순하고 평평한 메모리 모델을 사용한다.
  • 롱 모드(64비트)
    • 대부분 32비트(보호 모드) 명령 집합의 확장이지만, 16-32비트 전환과는 달리 64비트 모드에서는 많은 명령이 삭제되었다.AMD가 개척했다.
  • 가상 8086 모드(16비트)
    • 보호 모드 감독자 운영 체제의 통제 하에 있는 동안 실제 모드 프로그램과 운영 체제를 실행할 수 있는 특수 하이브리드 운영 모드
  • 시스템 관리 모드(16비트)
    • 전원 관리, 시스템 하드웨어 제어 및 독점 OEM 설계 코드와 같은 시스템 전반의 기능을 처리한다.이것은 시스템 펌웨어에서만 사용하도록 되어 있다.운영 체제를 포함한 모든 정상 실행이 중단된다.그런 다음 대체 소프트웨어 시스템(일반적으로 컴퓨터의 펌웨어 또는 하드웨어 지원 디버거에 있음)을 높은 권한으로 실행한다.

전환 모드

프로세서는 전원을 켠 직후에 실제 모드로 실행되므로 운영 체제 커널 또는 다른 프로그램이 실제 모드를 제외한 어떤 모드로 실행되려면 명시적으로 다른 모드로 전환해야 한다.스위칭 모드는 약간의 준비 후에 프로세서의 제어 레지스터의 특정 비트를 수정함으로써 수행되며, 스위치 후에 약간의 추가 설정이 필요할 수 있다.

레거시 BIOS를 실행하는 컴퓨터에서 BIOS와 부트 로더가 Real 모드로 실행되면 64비트 운영 체제 커널이 CPU를 검사하여 Long 모드로 전환한 다음 64비트 코드를 실행하는 새로운 커널 모드 스레드를 시작한다.

UEFI를 실행하는 컴퓨터, UEFI 펌웨어(CSM 및 레거시 옵션 ROM 제외), UEFI 부트 로더 및 UEFI 운영 체제 커널이 모두 롱 모드로 실행된다.

지시 유형

일반적으로 최신 x86 명령 집합의 특징은 다음과 같다.

  • 컴팩트 인코딩
    • 가변 길이 및 정렬 독립형(x86 아키텍처의 모든 데이터와 마찬가지로 리틀 엔디안으로 인코딩됨)
    • 주로 1주소 및 2주소 지시, 즉 첫 번째 피연산자도 목적지다.
    • 소스와 대상 모두로 메모리 피연산자가 지원된다(흔히 작은 즉시 오프셋을 사용하여 처리된 스택 요소를 읽기/쓰기하는 데 사용됨).
    • 일반 및 암묵적 레지스터 사용 모두, 7개 모두(카운트ebp) 32비트 모드의 일반 레지스터 및 15개(전체 레지스터)rbp) 64비트 모드의 일반 레지스터는 축전지 또는 어드레싱용으로 자유롭게 사용될 수 있으며, 대부분은 특정(이상 또는 이하) 특별 지침에서도 암시적으로 사용된다. 따라서 해당 레지스터는 그러한 명령 순서 중에 활성화되어 있는 경우 일시적으로 보존되어야 한다(쌓여 있어야 한다.
  • 대부분의 정수 ALU 명령을 통해 암시적으로 조건부 플래그를 생성한다.
  • 점프(x86-64 아키텍처의 개선으로 도입됨)를 제외하고 PC 상대는 아니지만 즉각적인, 오프셋 및 확장 인덱스를 포함한 다양한 어드레싱 모드를 지원한다.
  • 레지스터 스택에 대한 부동 소수점 포함.
  • 원자성 읽기-수정-쓰기 지침에 대한 특별 지원 포함(xchg,cmpxchg/cmpxchg8b,xadd, 및 와 결합되는 정수 명령lock접두사)
  • SIMD 지침(더 넓은 레지스터의 인접 셀로 인코딩된 많은 피연산자에 대해 병렬로 단일 지시를 수행하는 지침).

스택 명령

x86 아키텍처는 실행 스택 메커니즘에 대한 하드웨어 지원을 가지고 있다.다음과 같은 지시사항push,pop,call그리고ret파라미터를 통과하도록 적절하게 설정된 스택과 함께 사용되며, 로컬 데이터에 대한 공간을 할당하고, 호출 반환 지점을 저장 및 복원하는 데 사용됨.ret 크기 지침은 칼리어가 매개변수에 의해 점유된 스택 공간을 재확보하는 공간 효율적인(그리고 빠른) 호출 규칙을 구현하는 데 매우 유용하다.

재귀 절차의 로컬 데이터를 보관하도록 스택 프레임을 설정할 때 몇 가지 선택 사항이 있다. 높은 수준enter지시(80186과 함께 설명됨)는 로컬 크기 인수뿐만 아니라 절차 둥지 심층 논증을 취하며 레지스터의 명시적 조작(예:push bp;mov bp, sp;sub sp, size더 빠르거나 느린 것은 컴파일러, 프로그래머 또는 특정 프로그램 코드에 의해 사용되는 호출 규칙뿐만 아니라 특정한 x86 프로세서 구현에 달려 있다. 대부분의 x86 코드는 여러 제조업체의 x86 프로세서와 프로세서의 다른 기술 세대에서 실행되도록 의도되어 있으며, 이는 매우 다양함을 의미한다.다양한 게이트 및 트랜지스터 수준의 설계 선택뿐만 아니라 마이크로아키텍처마이크로코드 솔루션.

다음과 같은 지침에도 주소 지정 모드의 전체 범위(즉각 base+offset 포함)push그리고pop는 일부 RISC 아키텍처에 비해 ABI 규격과 메커니즘을 비교적 단순하게 유지할 뿐만 아니라 정수, 부동소수점 및 주소 데이터에 대한 스택의 직접 사용을 단순화한다(더 명시적인 콜 스택 세부사항 필요).

정수 ALU 지침

x86 어셈블리는 표준 수학 연산을 가지고 있으며,add,sub,mul와 함께idiv; 논리 연산자 and,or,xor,neg; 약간의 산술과 논리적,sal/sar,shl/shr; 운반이 있거나 없는 상태로 회전한다.rcl/rcr,rol/ror, BCD 산술 지침의 보충판,aaa,aad,daa기타 등등.

부동 소수점 지시사항

x86 어셈블리 언어에는 스택 기반 부동 소수점 단위(FPU)에 대한 지침이 포함되어 있다.FPU는 8086부터 80386까지의 선택적 개별 코프로세서였고, 80486 시리즈의 온칩 옵션이었으며, 80486년부터 펜티엄을 시작으로 모든 인텔 x86 CPU의 표준 기능이다.FPU 지침은 덧셈, 뺄셈, 부정, 곱셈, 나누기, 나머지, 제곱근, 정수 잘리기, 분수 잘리기, 2의 힘에 의한 척도를 포함한다.또한 연산에는 이진법 코드화된 소수점, 32비트 정수, 64비트 정수, 32비트 부동소수점, 64비트 부동소수점 또는 80비트 부동소수점(적재 시 이 값은 현재 사용되는 부동소수점 모드로 변환됨)의 어떤 형식으로든 메모리에서 값을 로드하거나 저장할 수 있는 변환 지침도 포함된다.x86에는 사인, 코사인, 탄젠트, 아크탄젠트, 베이스 2의 지수 및 베이스 2, 10 또는 e에 대한 로그 등 다수의 초월 함수도 포함된다.

스택 레지스터에서 명령의 스택 레지스터 형식은 일반적으로fop st, st(n)또는fop st(n), st어디에st와 같다st(0)그리고st(n)8개의 스택 레지스터 중 하나임.st(0),st(1), ...,st(7)) 정수처럼 첫 번째 피연산자는 첫 번째 소스 피연산자와 대상 피연산자 둘 다다. fsubr그리고fdivr뺄셈 또는 분할을 수행하기 전에 먼저 소스 피연산자를 스와핑하는 것으로 선택되어야 한다.덧셈, 뺄셈, 곱셈, 나누기, 저장 및 비교지침에는 연산이 완료된 후 스택의 상단을 튀기는 지시 모드가 포함된다.그래서 예를 들면.faddp st(1), st계산을 하다st(1) = st(1) + st(0), 그런 다음 제거st(0)처음부터 끝까지, 그래서 어떤 결과를 낳았는지.st(1)의 맨 위.st(0).

SIMD 지침

현대의 x86 CPU에는 SIMD 명령이 포함되어 있는데, 이는 넓은 SIMD 레지스터로 인코딩된 많은 값에 대해 대체로 같은 작업을 병렬로 수행한다.다양한 명령 기술은 서로 다른 레지스터 집합에서 서로 다른 연산을 지원하지만 전체(MMX에서 SSE4.2까지) 전체로 취합하여 정수 또는 부동소수 산술(추가, 뺄셈, 곱셈, 시프트, 최소화, 최대화, 비교, 분할 또는 제곱근)에 대한 일반 연산을 포함한다.예를 들어,paddw mm0, mm14개의 병렬 16비트 수행(에 의해 수행됨)w) 정수 추가(에 따라 달라짐padd)의mm0에 대한 가치관.mm1그리고 그 결과를 에 저장한다.mm0. 스트리밍 SIMD Extensions 또는 SSE는 레지스터의 첫 번째 값만 실제로 수정되는 부동 소수점 모드도 포함한다(SSE2로 확장).절대 차이의 합계(MPEG에서 행해지는 것과 같은 비디오 압축에서 모션 추정에 사용)와 16비트 곱셈 누적 명령(소프트웨어 기반 알파 블렌딩 및 디지털 필터링에 유용)을 포함한 몇 가지 다른 특이한 지침이 추가되었다.SSE(SSE3 이후) 및 3DNow! 확장은 쌍을 이룬 부동소수 값을 복잡한 숫자와 같이 처리하기 위한 추가 및 뺄셈 지침을 포함한다.

또한 이 지침 집합에는 레지스터 내에서 값을 섞고 삽입 및 추출하는 고정된 하위 단어 지침이 많이 포함되어 있다.또한 정수 레지스터와 XMM(SSE에서 사용됨)/FPU(MMX에서 사용됨) 레지스터 간에 데이터를 이동하는 지침이 있다.

메모리 지침

x86 프로세서는 또한 즉시 오프셋이 있는 메모리, 레지스터, 오프셋이 있는 레지스터, 오프셋이 있거나 없는 스케일링 레지스터, 옵션 오프셋과 다른 스케일링 레지스터를 처리하는 복잡한 주소 지정 모드를 포함한다.예를 들어, 인코딩을 할 수 있다.mov eax, [Table + ebx + esi*4]다음과 같이 계산된 주소로부터 32비트의 데이터를 로드하는 단일 명령으로서(Table + ebx + esi * 4)에서 상쇄하다.ds선택기를 사용하여eax등록하다일반적으로 x86 프로세서는 작동 중인 모든 레지스터의 크기와 일치하는 메모리를 로드하고 사용할 수 있다. (SIMD 지침에는 반로드 지침도 포함되어 있다.)

정수 ALU 지침을 포함한 대부분의 2-oper 및 x86 지침은 MOD-REG-R/M 바이트라고 불리는 표준 "어드레싱 모드 바이트"[11]를 사용한다.[12][13][14]또한 많은 32비트 x86 지침에는 MOD-REG-R/M 바이트를 따르는 SIB 주소 지정 모드 바이트가 있다.[15][16][17][18][19]

원칙적으로 명령어 opcode는 어드레싱 모드 바이트와 분리되어 있기 때문에, 그 명령어들은 직교한다. 왜냐하면 그 중 어떤 opcode도 어드레싱 모드와 혼용될 수 있기 때문이다.그러나, x86 명령 집합은 일반적으로 비직교적인 것으로 간주된다. 왜냐하면 많은 다른 opcode는 일정한 어드레싱 모드를 가지고 있고(주소 모드 바이트가 없으며), 모든 레지스터는 특별하기 때문이다.[19][20]

x86 명령 집합에는 문자열 로드, 저장, 이동, 스캔 및 비교 지침이 포함된다.lods,stos,movs,scas그리고cmps지정된 크기로 각 작업을 수행하는 ()b8비트 바이트의 경우,w16비트 단어로d32비트 더블 워드용) 다음 증분/감소(DF, 방향 플래그에 따라 다름) 암묵적 주소 레지스터(Address Register)si을 위해lods,di을 위해stos그리고scas, 그리고 둘 다.movs그리고cmps. 로드, 저장 및 스캔 작업의 경우, 암시적 대상/소스/비교 레지스터는al,ax또는eax등록(크기에 따라 다름)사용된 암시적 세그먼트 레지스터는ds을 위해si그리고es을 위해di. Thecx또는ecx레지스터는 감소 카운터로 사용되며, 카운터가 0에 도달하면 작동이 중지되거나(스캔 및 비교용) 불평등이 감지되면 작동이 중지된다.

스택은 암시적으로 감소(푸시) 및 증가(팝) 스택 포인터를 사용하여 구현된다.16비트 모드에서는 이 암시적 스택 포인터가 SS:[SP]로, 32비트 모드에서는 SS:[ESP]로, 64비트 모드에서는 [RSP]로 지정된다.스택 포인터(stack pointer)는 크기가 프로세서의 작동 모드(즉, 16, 32 또는 64비트)와 일치한다는 가정 하에 실제로 저장된 마지막 값을 가리킨다.push/pop/call/ret지침들또한 지침이 포함되어 있다.enter그리고leave스택 프레임 포인터를 설정하는 동안 스택 상단에서 데이터를 예약하고 제거bp/ebp/rbp. 단, 에 대한 직접 설정 또는 추가 및 뺄셈.sp/esp/rsp레지스터도 지원되므로enter/leave지시사항은 일반적으로 불필요하다.

함수 시작 부분의 이 코드:

 밀다    에브       ; 호출 기능의 스택 프레임 저장(ebp)  영화를 찍다     에브, esp  ; 발신자 스택 위에 새 스택 프레임을 만드십시오.  후보선수     esp, 4    ; 이 함수의 로컬 변수에 대해 4바이트의 스택 공간 할당 

...기능상 다음과 같다.

 입장하다   4, 0 

스택을 조작하기 위한 기타 지침은pushf/popf(E)FLAGs 레지스터 저장 및 검색용.pusha/popa명령어는 스택에 대한 전체 정수 레지스터 상태를 저장하고 검색한다.

SIMD 부하 또는 저장소의 값은 SIMD 레지스터를 위해 인접한 위치에 포장된 것으로 가정하고 순차적으로 작은 엔디안 순서로 정렬한다.일부 SSE 로드 및 저장 지침은 16바이트 정렬이 있어야 제대로 작동할 수 있다.또한 SIMD 명령 집합에는 로드를 수행하지만 캐시 로딩에 사용되는 레지스터를 대상으로 하지 않는 "prefetch" 명령이 포함되어 있다.SSE 명령 집합에는 대상이 아직 캐시되지 않은 경우 캐시 할당을 수행하지 않고 바로 메모리로 저장소를 수행하는 비임시 저장소 지침도 포함되어 있다(그렇지 않으면 일반 저장소처럼 동작함).

대부분의 일반 정수 및 부동소수(SIMD는 없음) 명령어는 하나의 파라미터를 두 번째 소스 파라미터로 복합 주소로 사용할 수 있다.정수 명령도 하나의 메모리 파라미터를 대상 피연산자로 받아들일 수 있다.

프로그램 흐름

x86 어셈블리는 무조건 점프 작동을 가지고 있는데, 이 동작은 즉석 주소, 레지스터 또는 간접 주소를 매개 변수로 사용할 수 있다(대부분의 RISC 프로세서는 링크 레지스터 또는 점프에 대한 짧은 즉시 변위만을 지원한다).

또한 다음과 같은 몇 가지 조건부 점프가 지원된다.jz(0에 표시됨),jnz(0이 아닌 곳에 표시),jg(보다 큼, 서명됨)jl(미만으로 표시, 서명됨)ja(위/아래에 표시됨, 서명되지 않음)jb(아래에 표시/이하, 서명되지 않음)이러한 조건부 연산은 (E)FLAGs 레지스터의 특정 비트 상태를 기반으로 한다.많은 산술 및 논리 연산은 결과에 따라 이러한 플래그를 설정, 삭제 또는 보완한다.비교cmp(compare) 및 지침은 피연산자의 값을 변경하지 않고 각각 감산 또는 비트 AND 연산을 수행한 것처럼 플래그를 설정한다.다음과 같은 지침도 있다.clc(국기 지우기) 및cmc깃발에 직접 작용하는 깃발부동 소수점 비교는 다음을 통해 수행된다.fcom또는ficom결국 정수 플래그로 변환해야 하는 지시사항

각 점프 연산은 피연산자의 크기에 따라 세 가지 형태가 있다.짧은 점프는 8비트 부호 피연산자를 사용하며, 이는 현재 명령과 상대적인 오프셋이다.근접 점프는 짧은 점프와 유사하지만 16비트 서명 피연산자(실제 또는 보호 모드) 또는 32비트 서명 피연산자(32비트 보호 모드에서만)를 사용한다.원점프는 전체 세그먼트 베이스:오프셋 값을 절대 어드레스로 사용하는 것이다.이것들 각각에 대한 간접적이고 지수화된 형태도 있다.

간단한 점프 조작 외에도, 이 있다.call(서브루틴을 호출) 및ret(서브루틴에서 반환) 지침.서브루틴으로 컨트롤을 전송하기 전에call다음 명령의 세그먼트 오프셋 주소를 푸시하십시오.call겹겹이 쌓이는 곳에,ret이 값을 스택에서 튀기고, 스택으로 점프하여 프로그램의 해당 부분에 대한 제어 흐름을 효과적으로 되돌린다.a의 경우far call, 오프셋 다음에 세그먼트 베이스가 푸시된다.far ret오프셋을 팝업한 다음 반환할 세그먼트 베이스.

또한 (중복) 두 개의 유사한 지침이 있는데, 이 지침은 현재 (E)FLAGs 레지스터 값을 스택에 저장한 다음far call, 주소 대신 인터럽트 벡터, 즉 인덱스를 인터럽트 핸들러 주소의 표로 사용하는 것을 제외한다.일반적으로 인터럽트 핸들러는 작동 결과를 호출 프로그램(인터럽트라고 하는 소프트웨어에서)으로 되돌리는 데 사용되지 않는 한 사용하는 다른 모든 CPU 레지스터를 저장한다.인터럽트 명령으로부터의 매칭 리턴은iret돌아온 후에 깃발을 복원하는 것.위에서 설명한 유형의 소프트 인터럽트는 시스템 호출에 일부 운영 체제에서 사용되며 하드 인터럽트 핸들러를 디버깅할 때도 사용할 수 있다.하드 인터럽트는 외부 하드웨어 이벤트에 의해 트리거되며, 현재 실행 중인 프로그램의 상태를 알 수 없으므로 모든 레지스터 값을 보존해야 한다.보호 모드에서 OS는 작업 스위치를 트리거하도록 인터럽트를 설정할 수 있으며, 이 인터럽트는 활성 작업의 모든 레지스터를 자동으로 저장할 수 있다.

MASM 스타일 어셈블리의 DOS용 "Hello world!" 프로그램

출력 시 인터럽트 21시간 사용 - 다른 샘플은 libc 프린트f를 사용하여 stdout에 인쇄한다.[21]

.model 작다 .stack 100시간  .data 음스그 db '헬로 월드!
                
.code 시작: 영화를 찍다 아., 09시 ; 메시지 표시 리아 dx, 음스그 인트로 21시간 영화를 찍다 도끼, 4C00h ; 실행 파일 종료 인트로 21시간 종지부를 찍다 출발하다

MASM 스타일 어셈블리의 Windows용 "Hello world!" 프로그램

; 6.15 및 이전 버전에서 /vmx 스위치 필요 .386 .model 작다,c .stack 1000h  .data 음스그     db "헬로 월드!",0  .code 포함시키다 libcmt.lib 포함시키다 libvcruntime.lib 포함시키다 libucrt.lib.lib 포함시키다 legacy_stdio_reason.lib  돌출시키다 활자화하다:근처에 돌출시키다 퇴장하다:근처에  공중의 본래의 본래의 생식을 하다         밀다    상쇄하다 음스그         부르다    활자화하다         밀다    0         부르다    퇴장하다 본래의 끝을 맺다  종지부를 찍다 

NASM 스타일 어셈블리의 Windows용 "Hello World!" 프로그램

; 이미지 기준 = 0x00400000 %define RVA(x) (x-0x00400000) 단면.text 밀다 드워드 안녕 부르다 드워드 [활자화하다] 밀다 바이트 +0 부르다 드워드 [퇴장하다] 되받아치다  단면.data 안녕 db "헬로 월드!"  단면.idata dd RVA(msvcrt_LookupTable) dd -1 dd 0 dd RVA(msvcrt_string) dd RVA(msvcrt_message) 시대 5 dd 0 ; 설명자 테이블 종료  msvcrt_string dd "msvcrt.propert", 0 msvcrt_LookupTable: dd RVA(msvcrt_printf) dd RVA(msvcrt_message) dd 0  msvcrt_messages: 활자화하다 dd RVA(msvcrt_printf) 퇴장하다 dd RVA(msvcrt_message) dd 0  msvcrt_printf: 드와 1 드와 "인쇄", 0 msvcrt_messages: 드와 2 드와 "exit", 0 dd 0 

NASM 스타일 어셈블리의 Linux용 "Hello world!" 프로그램

; ; 이 프로그램은 32비트 보호 모드로 실행된다. ;; 빌드: nasm -f 엘프 -F 찌르는 이름.asm ; 링크: ld -o name.o ; ; 64비트 롱 모드에서는 64비트 레지스터(예: eax 대신 rax, ebx 대신 rbx 등)를 사용할 수 있다. ; 빌드 명령에서 "-f 엘프 "를 "-f 엘프 64"로 변경한다. ; 단면.data                           ; 초기화 데이터에 대한 섹션 str:     db '헬로 월드!', 오아         ; 끝에 새 줄 문자가 있는 메시지 문자열(10진수) str_len: 등수를 맞추다 $ - 발을 동동 구르다                    ; str의 시작 주소를 뺀 문자열의 계산 길이(수치)                                             ; 이 주소로부터 ($ 기호)  단면.text                           ; 여기가 코드 섹션이다. 전지구적_시작                           ; _시작은 진입점이며 글로벌 스코프가 에 의해 '보이는' 것이 필요하다.                                             ; 링커 --C++의 메인()과 동일 _시작:                                 ; 여기서 _시작 절차 정의 시작  영화를 찍다 이삭스, 4                   ; sys_write 함수 코드 지정(OS 벡터 테이블에서)  영화를 찍다 이벡스, 1                   ; 파일 설명자 stdout 지정 --gnu/messages, 모든 것이 파일로 처리됨,                                              ; 하드웨어 장치도 포함  영화를 찍다 ecx, 발을 동동 구르다                 ; 문자열 메시지의 start _address_를 ecx 레지스터로 이동  영화를 찍다 edx, str_len             ; 메시지 길이 이동(바이트)  인트로 80시간                      ; 방금 설정한 시스템 호출을 수행하기 위해 커널을 인터럽트 -                                              ; gnu/number 서비스는 커널을 통해 요청됨  영화를 찍다 이삭스, 1                   ; sys_exit 함수 코드 지정(OS 벡터 테이블에서)  영화를 찍다 이벡스, 0                   ; OS에 대한 반환 코드 지정(OS에 모든 것이 정상임을 0으로 표시)  인트로 80시간                      ; 시스템 호출을 수행하기 위해 커널을 인터럽트(종료하기 위해) 

C 표준 라이브러리를 사용하는 NASM 스타일 어셈블리의 Linux용 "Hello world!" 프로그램

; ; 이 프로그램은 32비트 보호 모드로 실행된다. ; gcc는 기본적으로 표준-C 라이브러리를 연결함  ;; 빌드: nasm -f 엘프 -F 찌르는 이름.asm ; 링크: gcc -o name.o ; ; 64비트 롱 모드에서는 64비트 레지스터(예: eax 대신 rax, ebx 대신 rbx 등)를 사용할 수 있다. ; 빌드 명령에서 "-f 엘프 "를 "-f 엘프 64"로 변경한다. ;         전지구적본래의                                ;main은 C-Standard Library에 대해 컴파일되는 대로 정의되어야 함         바깥의활자화하다                               ;인쇄f로 외부 기호를 사용할 경우 다른 객체-기호에서 선언함.                                                            ;Linker는 나중에 이 기호를 해결한다.  분절하다.data                                       ; 초기화 데이터에 대한 섹션  끈을 매다 db '헬로 월드!', 오아, 0h           ;new-line char (10진수) 및 NULL 터미네이터가 있는 메시지 문자열                                                     ;string now는 'Hello, World'가 저장되어 있는 출발 주소를 가리킨다.  분절하다.text 주:         밀다    끈을 매다                              문자열의 첫 번째 문자의 주소를 스택에 저장하십시오.이것은 인쇄할 논거가 될 것이다.         부르다    활자화하다                              ;인쇄 인쇄물         덧셈을     esp, 4                              ;밀린 문자열 인수를 4개 플러시하여 스택-프로토콜 처리         되받아치다                                         반환하다;반환하다;리턴 

NASM 스타일 어셈블리의 64비트 모드 Linux용 "Hello world!" 프로그램

; 빌드: nasm -f 엘프64 -F damant hello.asm ; 링크: ld -o hello.o  체납 REL   ; 기본적으로 RIP-상대 주소 지정 모드를 사용하므로 [foo] = [rel foo]  섹션.rodata   ; 읽기 전용 데이터는 Windows의 .rdata와 같이 GNU/Linux의 .rodata 섹션에 저장될 수 있음 여보세요:  db "헬로 월드!",10        ; 10 = '\n'. len_Hello: 등수를 맞추다 $-안녕                 ; 길이를 조립 시간 상수로 계산하도록 NASM 가져오기 ;;; write()는 길이가 걸리므로 0종단 C-스타일 문자열은 필요하지 않다.그것은 퍼트를 위한 것일 것이다.  섹션.text  전지구적_시작 _시작:  영화를 찍다 이삭스, 1    ; __NR_write syscall number from Linux asm/unistd_64.h (x86_64)  영화를 찍다 에디, 1    ; intfd = STDOUT_FILENO  리아 rsi, [다시 하다 안녕]   ;x86-64 RIP-상대 LEA를 사용하여 정적 주소를 레지스트리에 포함  영화를 찍다 rdx, len_Hello  ; size_t count = len_안녕  syscall     ; write(1, Hello, len_Hello); 커널을 호출하여 실제로 시스템 호출을 실행하십시오.      ;;; RAX의 반환 값.RCX 및 R11도 syscall에 의해 덮어쓰기됨   영화를 찍다 이삭스, 60    ; __NR_exit 호출 번호(x86_64)  xor 에디, 에디    ; 상태 = 0(정상적임)  syscall     ; _reason(0) 

로 실행하면 프로세스에서 추가 시스템 호출이 발생하지 않는지 확인하십시오.인쇄판 버전은 libc를 초기화하고 동적 연결을 하기 위해 더 많은 시스템 호출을 할 것이다.그러나 이것은 -pie 또는 공유 라이브러리 없이 ld를 사용하여 연결되었기 때문에 정적 실행 파일이다. 사용자 공간에서 실행되는 유일한 지침은 당신이 제공하는 것이다.

$ strace ./hello > /dev/properties # 리디렉션 없이, 프로그램의 stdout은 stderr에 대한 stdr가 혼합되어 있다.  보통 미세실행("/hello", ["/hello"), 0x7ffc8b0b3570 /* 51var */) = 0 쓰기(1, "Hello world!\n", 13) = 13 exit(0) = ? +++++

플래그 레지스터 사용

플래그는 x86 아키텍처에서 비교를 위해 많이 사용된다.두 데이터를 비교할 때 CPU는 관련 플래그 또는 플래그를 설정한다.이후 조건부 점프 지침을 사용하여 실행해야 하는 플래그와 코드 분기를 확인할 수 있다. 예를 들어,

 cmp 이삭스, 이벡스   do_something  ; ... do_something:  여기서 무엇을 하다 

플래그는 또한 x86 아키텍처에서 특정 기능이나 실행 모드를 켜거나 끄기 위해 사용된다.예를 들어 마스크 가능한 모든 인터럽트를 비활성화하려면 다음 명령을 사용하십시오.

 오려내다 

깃발 레지스터도 직접 접속할 수 있다.플래그 레지스터의 낮은 8비트를 로딩할 수 있음ah을 이용하여lahf교수법또한 지침에 따라 전체 플래그 레지스터를 스택에 올리거나 내릴 수 있다.pushf,popf,int(포함)into) 및iret.

명령 포인터 레지스터 사용

명령 포인터를 호출함ip16비트 모드에서,eip32비트 모드에서rip64비트 모드로.명령 포인터 레지스터는 프로세서가 다음에 실행하려고 시도할 메모리 주소를 가리킨다. 16비트 또는 32비트 모드에서는 직접 액세스할 수 없지만, 다음과 같은 시퀀스를 작성하여 주소를 입력할 수 있다.next_lineeax:

 부르다 next_line next_line:  펑펑 터지다 이삭스 

이 명령의 순서는 위치 독립 코드를 생성한다.call다음 지침(이 경우 0)에서 대상 지침의 바이트 단위의 오프셋을 설명하는 명령-직접 피연산자를 사용한다.

명령 포인터에 쓰는 것은 간단하다 —jmp명령어는 명령 포인터를 대상 주소로 설정하므로, 예를 들어 다음과 같은 시퀀스는 다음 내용을 넣는다.eaxeip:

 jmp 이삭스 

64비트 모드에서는 명령 포인터에 상대적인 데이터를 참조할 수 있으므로 명령 포인터의 값을 다른 레지스터에 복사할 필요가 없다.

참고 항목

참조

  1. ^ "Intel 8008 (i8008) microprocessor family". www.cpu-world.com. Retrieved 2021-03-25.
  2. ^ "Intel 8008". CPU MUSEUM - MUSEUM OF MICROPROCESSORS & DIE PHOTOGRAPHY. Retrieved 2021-03-25.
  3. ^ a b c "Intel 8008 OPCODES". www.pastraiser.com. Retrieved 2021-03-25.
  4. ^ "Chris Sawyer Software Development". www.chrissawyergames.com. Retrieved 2022-03-07.
  5. ^ a b c d e Narayam, Ram (2007-10-17). "Linux assemblers: A comparison of GAS and NASM". Archived from the original on October 3, 2013. Retrieved 2008-07-02.
  6. ^ "The Creation of Unix". Archived from the original on April 2, 2014.
  7. ^ Hyde, Randall. "Which Assembler is the Best?". Retrieved 2008-05-18.
  8. ^ "GNU Assembler News, v2.1 supports Intel syntax". 2008-04-04. Retrieved 2008-07-02.
  9. ^ "i386-Bugs (Using as)". Binutils documentation. Retrieved 15 January 2020.
  10. ^ Mueller, Scott (March 24, 2006). "P2 (286) Second-Generation Processors". Upgrading and Repairing PCs, 17th Edition (Book) (17 ed.). Que. ISBN 0-7897-3404-4. Retrieved 2017-12-06.
  11. ^ 커티스 메도우."8086 지침서 인코딩"
  12. ^ 이고르 홀로도프."6. x86 명령 연산자, MOD-REG-R/M 바이트 인코딩".
  13. ^ "x86 지시사항 인코딩".
  14. ^ 마이클 애브래시"집회 언어의 제인:제1권, 지식""7장: 메모리 주소 지정""mod-reg-rm 어드레싱" 섹션.
  15. ^ Intel 80386 참조 프로그래머 설명서."17.2.1 ModR/M 및 SIB 바이트"
  16. ^ "X86-64 명령 인코딩: ModR/M 및 SIB 바이트"
  17. ^ "그림 2-1. Intel 64IA-32 아키텍처 명령 형식".
  18. ^ "x86 후드 아래 주소 지정"
  19. ^ a b 스티븐 맥캐맨트"수동자동 바이너리 리버스 엔지니어링".
  20. ^ "X86 명령 위시리스트".
  21. ^ "I just started Assembly". daniweb.com. 2008.

추가 읽기

설명서

책들