수색…


비고

어셈블리는 인간이 읽을 수있는 많은 형태의 기계어에 사용되는 일반적인 이름입니다. 그것은 자연스럽게 다른 CPU (중앙 처리 장치) 사이에 많이 다르지만 또한 단일 CPU에서 서로 다른 어셈블러로 컴파일 된 여러 가지 호환되지 않는 어셈블리 다이어그램이 CPU 작성자가 정의한 동일한 시스템 코드에 존재할 수 있습니다.

자신의 Assembly 문제에 대해 질문하고 싶다면, 항상 HW와 어떤 어셈블러를 사용하고 있는지 명시하십시오. 그렇지 않으면 질문에 자세히 대답하기가 어려울 것입니다.

하나의 특정 CPU를 학습하면 다른 CPU의 기초를 배우는 데 도움이되지만 모든 HW 아키텍처는 세부 사항에 상당한 차이가 있으므로 새로운 플랫폼을위한 ASM 학습은 처음부터 배우는 것에 가깝습니다.

모래밭:

X86 어셈블리 위키 책

소개

어셈블리 언어는 사람이 읽을 수있는 형태의 기계어 또는 머신 코드로, 프로세서 로직이 작동하는 비트 및 바이트의 실제 시퀀스입니다. 일반적으로 인간은 바이너리, 8 진수 또는 16 진수보다 니모닉을 읽고 프로그래밍하기가 쉽기 때문에 인간은 일반적으로 어셈블리 언어로 코드를 작성한 다음 하나 이상의 프로그램을 사용하여이를 프로세서가 이해하는 기계어 형식으로 변환합니다.

예:

mov eax, 4
cmp eax, 5
je point

어셈블러는 어셈블리 언어 프로그램을 읽고 구문 분석하고 해당 컴퓨터 언어를 생성하는 프로그램입니다. 표준 문서에 정의 된 단일 언어 인 C ++와는 달리 다양한 어셈블리 언어가 있다는 것을 이해하는 것이 중요합니다. 각 프로세서 아키텍처, ARM, MIPS, x86 등은 다른 기계어 코드를 가지고 있으므로 다른 어셈블리 언어를 사용합니다. 또한 동일한 프로세서 아키텍처에 대해 여러 어셈블리 언어가있는 경우가 있습니다. 특히 x86 프로세서 제품군에는 gas 구문 ( gas 는 GNU Assembler의 실행 파일 이름)과 Intel 구문 (x86 프로세서 제품군의 이름을 따서 명명 한)이라는 두 가지 일반적인 형식이 있습니다. 그것들은 다르기는하지만 일반적으로 두 프로그램 모두에서 주어진 프로그램을 작성할 수 있습니다.

일반적으로 프로세서의 발명가는 프로세서와 그 기계 코드를 문서화하고 어셈블리 언어를 생성합니다. 특정 어셈블리 언어가 유일하게 사용되는 것은 흔한 일이지만 컴파일러 작성자가 언어 표준을 준수하려고 시도하는 것과 달리 프로세서 발명가가 정의한 어셈블리 언어는 대개 어셈블러를 작성하는 사람들이 사용하는 버전은 아니지만 항상 그렇습니다 .

두 가지 일반적인 유형의 프로세서가 있습니다.

  • CISC (복합 명령어 집합 컴퓨터) : 매우 다양하고 종종 복잡한 기계 언어 명령어가 있습니다.

  • RISC (Reduced Instruction Set Computers) : 대조적으로, 더 간단하고 간단한 지침이 있습니다.

어셈블리 언어 프로그래머의 경우 차이점은 CISC 프로세서는 배울 수있는 많은 명령어가있을 수 있지만 특정 태스크에 적합한 명령어가있는 반면 RISC 프로세서는 더 작고 간단한 명령어가 있지만 주어진 작업에는 어셈블리 언어 프로그래머가 필요할 수 있다는 점입니다 같은 일을하기 위해 더 많은 지시 사항을 작성하십시오.

다른 프로그래밍 언어 컴파일러는 어셈블러를 먼저 생성 한 다음 어셈블러를 호출하여 컴퓨터 코드로 컴파일합니다. 예를 들어 gcc 는 컴파일의 최종 단계에서 자체 가스 어셈블러를 사용합니다. 생성 된 기계 코드는 종종 링커 프로그램에 의해 실행 가능하게 링크 될 수있는 오브젝트 파일에 저장됩니다.

완벽한 "도구 체인"은 종종 컴파일러, 어셈블러 및 링커로 구성됩니다. 그런 다음 해당 어셈블러와 링커를 직접 사용하여 어셈블리 언어로 프로그램을 작성할 수 있습니다. GNU 세상에서 binutils 패키지에는 어셈블러, 링커 및 관련 도구가 포함되어 있습니다. 어셈블리 언어 프로그래밍에만 관심이있는 사람들은 gcc 나 다른 컴파일러 패키지가 필요하지 않습니다.

소형 마이크로 컨트롤러는 종종 어셈블리 언어로만 프로그래밍되거나 어셈블리 언어와 C 또는 C ++와 같은 하나 이상의 고급 언어로 프로그래밍됩니다. 이는 그러한 장치에 대한 명령 세트 아키텍처 의 특정 측면을 종종 고급 언어에서 가능할 수있는 것보다 작고 효율적인 코드를 작성하기 위해 사용할 수 있고 그러한 장치는 종종 제한된 메모리 및 레지스터를 가지기 때문에 수행됩니다. 많은 마이크로 프로세서는 내부에 마이크로 프로세서를 가지고있는 범용 컴퓨터 이외의 장치 인 임베디드 시스템에 사용됩니다. 그러한 임베디드 시스템의 예는 텔레비전, 전자 레인지 및 현대 자동차의 엔진 제어 유닛이다. 이러한 장치에는 키보드 나 화면이 없기 때문에 일반적으로 프로그래머는 범용 컴퓨터에 프로그램을 작성하고 크로스 어셈블러를 실행합니다 (이 종류의 어셈블러는 실행되는 프로세서와 다른 종류의 프로세서 용 코드를 생성하기 때문에 호출됩니다). ) 및 / 또는 크로스 컴파일러크로스 링커 를 사용하여 기계 코드를 생성 할 수 있습니다.

이러한 도구에는 많은 벤더가 있으며, 이들은 코드를 생성하는 프로세서만큼 다양합니다. 많은 프로세서가 GNU, sdcc, llvm 또는 기타와 같은 오픈 소스 솔루션을 가지고 있습니다.

기계 코드

기계어 코드는 컴퓨터가 직접 처리하는 특정 기본 컴퓨터 형식의 데이터를 말하며 대개 CPU (Central Processing Unit)라고하는 프로세서에 의해 처리됩니다.

일반적인 컴퓨터 아키텍처 ( 폰 노이만 아키텍처 )는 범용 프로세서 (CPU), 범용 메모리 - 프로그램 (ROM / RAM)과 처리 된 데이터 및 입력 및 출력 장치 (I / O 장치)를 모두 저장하는 것으로 구성됩니다.

이 아키텍처의 가장 큰 장점은 이전의 컴퓨터 시스템 (기계 구성에서 하드 와이어 된 프로그램 사용) 또는 경쟁 아키텍처 (예 : 하버드 아키텍처 가 프로그램 메모리를 메모리에서 분리하는 것)와 비교할 때 각 구성 요소의 상대적 단순성과 보편성입니다. 데이터). 단점은 좀 더 일반적인 성능입니다. 오래 동안 보편성은 일반적으로 성능 비용보다 중요한 유연한 사용을 허용했습니다.

이것은 기계 코드와 어떤 관련이 있습니까?

프로그램 및 데이터는이 컴퓨터에 숫자로 메모리에 저장됩니다. 데이터에서 코드를 구분할 수있는 진정한 방법은 없으므로 운영 체제 및 컴퓨터 운영자는 모든 숫자를 메모리에로드 한 후 메모리 시작 지점에서 프로그램을 시작하는 CPU 힌트를 제공합니다. CPU는 엔트리 포인트에 저장된 명령 (번호)을 읽고 엄격하게 처리하여 프로그램 자체가 다른 곳에서 실행을 계속하도록 CPU에 지시하지 않는 한 다음 번호를 추가 지침으로 순차적으로 읽습니다.

예를 들어 2 개의 8 비트 숫자 (8 비트는 함께 그룹화되며 1 바이트와 동일합니다. 0-255 범위의 부호없는 정수) : 60 201 , Zilog Z80 CPU에서 코드로 실행될 때 두 가지 명령으로 처리됩니다 : INC a (레지스터 a 값을 하나씩 증가 시킴) 및 RET (서브 루틴에서 복귀하여 CPU를 지시하여 메모리의 다른 부분에서 명령을 실행).

: 인간의 두 바이트로 헥스 편집기에서 예를 들어, 일부 메모리 / 파일 에디터에 의해 그 숫자를 입력 할 수있는 프로그램을 정의하기 위해 3C C9 (60 진수 201을베이스 16 인코딩을 기록). 그것은 기계어프로그래밍하는 것 입니다 .

인간을위한 CPU 프로그래밍 작업을 쉽게하기 위해 어셈블러 프로그램이 만들어져 다음과 같은 텍스트 파일을 읽을 수 있습니다.

subroutineIncrementA:
    INC   a
    RET

dataValueDefinedInAssemblerSource:
    DB    60          ; define byte with value 60 right after the ret

바이트 16 진수 시퀀스 출력 3C C9 3C , 대상 플랫폼에 특정한 선택적 추가 번호로 둘러 3C C9 3C : 해당 이진 파일의 어느 부분이 실행 코드인지 표시, 프로그램 시작 지점 (첫 번째 명령), 인코딩 된 부분 데이터 (실행 불가능) 등

프로그래머가 값 60을 가진 마지막 바이트를 "data"로 어떻게 지정했는지 주목하십시오. 그러나 CPU 관점에서 INC a 와는 전혀 다르지 않습니다. 명령으로 준비된 바이트에 대해 CPU를 올바르게 탐색하고 실행을 위해 데이터를 처리하는 것은 실행 프로그램에 달려 있습니다.

이러한 출력은 대개 저장 장치의 파일에 저장되고 나중에 운영 체제 ( 운영 체제 - 컴퓨터에서 이미 실행중인 컴퓨터 코드, 컴퓨터 조작을 돕는 )가 실행되기 전에 메모리에로드되며 마지막으로 CPU를 프로그램의 진입 점.

CPU는 기계 코드 만 처리하고 실행할 수 있지만 임의의 메모리 컨텐트라도 무작위로 처리 할 수 ​​있습니다. 결과는 무작위적일 수 있습니다. 예를 들어 OS에서 감지하고 처리 한 " 충돌 "부터 I / O 장치 또는 컴퓨터에 연결된 민감한 장비의 손상 (가정용 컴퓨터의 일반적인 경우는 아닙니다 :).

비슷한 과정을 거친 후에 많은 소스 코드 (프로그램의 사람이 읽을 수있는 텍스트 형식)를 컴퓨터 코드 (CPU의 원시 명령어)를 나타내는 숫자 나 일반적인 / 인터프리터 / 하이브리드 언어를 나타내는 숫자로 컴파일하는 많은 다른 고급 프로그래밍 언어가 뒤 따릅니다 언어 별 가상 머신 코드는 인터프리터 또는 가상 머신에 의한 실행 중에 네이티브 머신 코드로 디코딩됩니다.

일부 컴파일러는 어셈블러를 컴파일의 중간 단계로 사용하여 소스를 먼저 어셈블러 형식으로 변환 한 다음 어셈블러 도구를 실행하여 최종 머신 코드 를 얻습니다 (GCC 예제 : gcc -S helloworld.c 를 실행하여 어셈블러 버전의 C 프로그램을 gcc -S helloworld.c helloworld.c ).

Linux x86_64 (Intel 64 비트) 용 Hello world

section .data
    msg db "Hello world!",10      ; 10 is the ASCII code for a new line (LF)

section .text
    global _start

_start:
    mov rax, 1
    mov rdi, 1
    mov rsi, msg
    mov rdx, 13
    syscall
    
    mov rax, 60
    mov rdi, 0
    syscall

이 프로그램을 실행하려면 Netwide Assembler , nasm 필요합니다. 왜냐하면이 코드는 구문을 사용하기 때문입니다. 그런 다음 다음 명령을 사용하십시오 (코드가 helloworld.asm 파일에 있다고 가정). 그것들은 각각 조립, 연결 및 실행에 필요합니다.

  • nasm -felf64 helloworld.asm
  • ld helloworld.o -o helloworld
  • ./helloworld

이 코드는 Linux의 sys_write 호출을 사용합니다. 여기서 x86_64 아키텍처의 모든 syscalls 목록을 볼 수 있습니다. 쓰기종료 의 메뉴얼 페이지를 고려해 보면 위의 프로그램을 C와 동일하게 번역 할 수 있으며 훨씬 더 읽기 쉽습니다.

#include <unistd.h>

#define STDOUT 1

int main()
{
    write(STDOUT, "Hello world!\n", 13);
    _exit(0);
}

컴파일과 링크 (첫 번째)와 실행을 위해 여기에 두 개의 명령 만 있으면됩니다.

  • gcc helloworld_c.c -o helloworld_c .
  • ./helloworld_c

OS X 용 Hello World (x86_64, 인텔 문법 가스)

.intel_syntax noprefix

.data

.align 16
hello_msg:
    .asciz "Hello, World!"

.text

.global _main
_main:
    push rbp
    mov rbp, rsp

    lea rdi, [rip+hello_msg]
    call _puts

    xor rax, rax
    leave
    ret

모으다:

clang main.s -o hello
./hello

노트:

  • OS X의 시스템 호출 API는 안정적이지 않은 것으로 간주되므로 시스템 호출을 사용하지 않는 것이 좋습니다. 대신 C 라이브러리를 사용하십시오. ( 스택 오버 플로우 문제에 대한 참조 )
  • 인텔은 단어보다 큰 구조가 16 바이트 경계에서 시작하는 것이 좋습니다. ( Intel 설명서 참조 )
  • 주문 데이터는 레지스터를 통해 함수로 전달됩니다 : rdi, rsi, rdx, rcx, r8 및 r9. ( System V ABI 참조 )

Visual Studio 2015에서 x86 어셈블리 실행

1 단계 : 파일 -> 새 프로젝트를 통해 빈 프로젝트를 만듭니다.

여기에 이미지 설명을 입력하십시오.

2 단계 : 프로젝트 솔루션을 마우스 오른쪽 단추로 클릭하고 종속성 빌드 -> 사용자 정의 빌드를 선택 하십시오 .

3 단계 : ".masm" 확인란을 선택하십시오.

여기에 이미지 설명을 입력하십시오.

4 단계 : " 확인 " 버튼을 누르십시오.

5 단계 : 어셈블리 파일을 만들고 다음을 입력하십시오.

.386
    .model small
        .code

            public main
                main proc

                    ; Ends program normally

                    ret
                main endp
            end main

6 단계 : 컴파일!

여기에 이미지 설명을 입력하십시오.



Modified text is an extract of the original Stack Overflow Documentation
아래 라이선스 CC BY-SA 3.0
와 제휴하지 않음 Stack Overflow