기계코드

Machine code
W65C816S 싱글보드 컴퓨터의 기계어 모니터, 코드 분해 및 프로세서 레지스터 및 메모리 덤프 표시

컴퓨터 프로그래밍에서 머신 코드(machine code)는 컴퓨터의 중앙 처리 장치(CPU)를 제어하는 데 사용되는 기계어 명령어로 구성된 컴퓨터 코드입니다.한때 십진법 컴퓨터가 일반적이었지만, 현대의 시장은 이진법 컴퓨터에 의해 지배되고 있습니다. 이 컴퓨터들에게 머신 코드는 "컴퓨터가 실제로 읽고 해석하는 컴퓨터 프로그램의 이진법 표현"입니다.기계 코드의 프로그램은 일련의 기계 명령으로 구성되어 있습니다(아마도 데이터가 산재되어 있을 수도 있습니다)."[1]

각 명령어는 CPU가 CPU레지스터 또는 메모리에 있는 하나 이상의 단위의 데이터에 대해 로드, 저장소, 점프 또는 ALU(산술 논리 장치) 작업과 같은 매우 특정한 작업을 수행하도록 합니다.

초기 CPU들은 출시된 각 새 CPU와의 역호환성을 깨뜨릴 수 있는 특정 머신 코드를 가지고 있었습니다.ISA(Instruction Set Architecture)의 개념은 시스템의 명령어 세트를 기억하는 동작과 인코딩을 정의하고, 정확한 구현을 명시하지 않습니다.이는 추상화 계층의 역할을 하여 동일한 CPU 제품군 내에서 호환성을 가능하게 하여 제품군용 ISA에 따라 작성 또는 생성된 머신 코드가 향후 CPU를 포함한 제품군 내의 모든 CPU에서 실행되도록 합니다.

일반적으로, 각 아키텍처 제품군(예: x86, ARM)은 고유한 ISA를 가지고 있으며, 따라서 고유한 특정 머신 코드 언어를 가지고 있습니다.예외도 있습니다. 예를 들어 IA-64는 x86을 에뮬레이트할 수 있습니다.

머신 코드는 엄밀하게 수치화된 언어이며 프로그래머용 CPU에 대한 가장 낮은 수준의 인터페이스입니다.어셈블리 언어는 숫자 기계 코드와 피연산자가 판독 가능한 문자열로 대체되는 인간이 읽을 수 있는 버전과 수치 기계 코드 간의 직접적인 매핑을 제공합니다(예: 0x90은 x86의 NOP 명령어입니다).프로그램을 기계 코드로 직접 작성하는 것은 가능하지만 개별 비트를 관리하고 수동으로 숫자 주소와 상수를 계산하는 것은 지루하고 오류가 발생하기 쉽습니다.이러한 이유로 현대적인 환경에서 프로그램은 기계 코드로 직접 작성되는 경우는 매우 드물지만 낮은 수준의 디버깅, 프로그램 패치(특히 어셈블러 소스를 사용할 수 없는 경우), 어셈블리 언어 분해를 위해 수행될 수 있습니다.

오늘날 대부분의 실용적인 프로그램은 고급 언어로 작성됩니다.이러한 프로그램은 컴파일러에 의해 기계 코드로 번역되거나, 인터프리터에 의해 해석됩니다. 보통 바이트코드와 같은 중간 코드로 번역된 후 해석됩니다.[nb 1]

머신 코드는 정의상 프로그래머가 볼 수 있는 가장 낮은 수준의 프로그래밍 세부 사항이지만 내부적으로 많은 프로세서가 마이크로 코드를 사용하거나 머신 코드 명령을 최적화하고 마이크로옵의 시퀀스로 변환합니다.마이크로코드와 마이크로옵스는 일반적으로 기계 코드로 간주되지 않으며, 일부 기계를 제외하고는 사용자가 마이크로코드나 마이크로옵스를 작성할 수 없으며, 마이크로코드의 작동과 기계 코드 명령의 마이크로옵스로의 변환은 성능과 관련된 부작용을 제외하고는 프로그래머에게 투명하게 발생합니다.

명령어세트

모든 프로세서 또는 프로세서 제품군에는 고유한 명령어 세트가 있습니다.명령어는 기계 명령에 해당하는 비트, 숫자 또는 문자의 패턴입니다.따라서 명령어 집합은 (대부분) 동일한 아키텍처를 사용하는 프로세서 클래스에 한정됩니다.후속 프로세서 또는 파생 프로세서 설계에는 종종 이전 프로세서의 명령이 포함되어 있으며 새로운 추가 명령이 추가될 수도 있습니다.때때로 후속 설계는 (일반적으로 새로운 목적에 필요하기 때문에) 일부 명령어 코드의 의미를 중단하거나 변경하여 코드 호환성에 어느 정도 영향을 미칠 수 있습니다. 호환되는 프로세서라도 일부 명령어에 대해 약간 다른 동작을 보일 수 있지만 이는 거의 문제가 되지 않습니다.또한 시스템은 메모리 배열, 운영 체제 또는 주변 장치와 같은 다른 세부 정보에 있어서도 다를 수 있습니다.일반적으로 프로그램은 이러한 요소에 의존하기 때문에, 같은 유형의 프로세서를 사용하더라도 다른 시스템에서는 일반적으로 동일한 머신 코드를 실행하지 않습니다.

프로세서의 명령어 집합에는 고정 길이 또는 가변 길이 명령어가 있을 수 있습니다.패턴을 구성하는 방법은 특정 아키텍처와 명령 유형에 따라 달라집니다.대부분의 명령어에는 기본 명령어 유형(산술, 논리, 점프 등), 연산(추가 또는 비교 등)을 지정하는 하나 이상의 opcode 필드와 피연산자 유형, 어드레싱 모드, 어드레싱 오프셋 또는 인덱스를 지정할 수 있는 기타 필드가 있습니다.또는 피연산자 값 자체(명령어에 포함된 일정한 피연산자를 즉시라고 함).[2]

모든 컴퓨터나 개별 명령에 명시적 피연산자가 있는 것은 아닙니다.단일 누적기가 있는 기계에서 누적기는 암시적으로 대부분의 산술 명령의 왼쪽 피연산자이자 결과입니다.x86 아키텍처와 같은 일부 다른 아키텍처는 일반적인 명령어의 누산기 버전을 가지고 있으며, 누산기는 더 긴 명령어에 의해 일반 레지스터 중 하나로 간주됩니다.스택 시스템은 암시적 스택에 피연산자의 대부분 또는 전부를 가지고 있습니다.특수 목적 명령에는 명시적인 피연산자가 없는 경우가 많습니다. 예를 들어, x86 아키텍처의 CPUID는 4개의 암묵적 대상 레지스터에 값을 씁니다.명시적 피연산자와 암시적 피연산자의 이러한 구분은 코드 생성기, 특히 레지스터 할당 및 라이브 레인지 추적 부분에서 중요합니다.좋은 코드 최적화기는 암시적 피연산자와 명시적 피연산자를 추적할 수 있으며, 이를 통해 보다 빈번한 상수 전파, 레지스터의 상수 접기(상수식을 그 상수로 대체함으로써 자유화된 결과를 할당받은 레지스터) 및 기타 코드 향상을 허용할 수 있습니다.

프로그램

컴퓨터 프로그램은 중앙 처리 장치(CPU)에 의해 실행될 수 있는 명령어의 목록입니다.프로그램을 실행하고 있는 CPU가 문제를 해결하여 결과를 얻기 위해서 프로그램의 실행이 이루어집니다.단순한 프로세서들이 차례로 명령들을 실행할 수 있는 반면, 수퍼스칼라 프로세서들은 (파이프라인이 꽉 찬 경우) 특정 상황들 하에서 두 개 이상의 명령들을 동시에 실행할 수 있습니다.예를 들어 1993년 인텔 펜티엄의 오리지널 제품은 파이프라인이 가득 차면 클럭 주기당 최대 2개의 명령어를 실행할 수 있습니다.

프로그램 흐름은 실행을 다음 숫자 순서의 주소가 아닌 다른 주소(따라서 명령어)로 전달하는 특수한 '점프' 명령에 의해 영향을 받을 수 있습니다.이러한 조건부 점프의 발생 여부는 값이 다른 값보다 크거나 작거나 같은 조건에 따라 달라집니다.

어셈블리어

어셈블리 언어라고 불리는 훨씬 더 인간 친화적인 기계 언어 표현은 기계 코드 명령을 직접적으로 사용하는 것이 아니라 기계 코드 명령을 참조하기 위해 니모닉 코드를 사용하고, 저장 위치와 때때로 레지스터를 나타내기 위해 기호 이름을 사용합니다.[3]예를 들어, Zilog Z80 프로세서에서는 기계 코드가00000101, 이것은 CPU가 감소하게 합니다.B 범용 레지스터는 다음과 같이 어셈블리 언어로 표시됩니다.DEC B.[4]

MIPS 아키텍처는 명령어가 항상 32비트인 머신 코드에 대한 특정한 예를 제공합니다.[5]: 299 명령의 일반적인 유형은 op(operation) 필드에서 가장 높은 6비트로 제공됩니다.J-type(점프) 및 I-type(즉시) 명령은 op에 의해 완전히 지정됩니다.R-type(레지스터) 명령에는 정확한 동작을 결정하기 위한 추가적인 필드 기능이 포함되어 있습니다.다음 유형에 사용되는 필드는 다음과 같습니다.

65 5 5 5 5 6비트 [oprt rd shamt funct] R형 [oprt address/즉시] I형 [optarget address] J형

rs, rt, rd는 레지스터 피연산자를 나타내고 shamt는 시프트 양을 나타내며 주소 또는 즉시 필드는 피연산자를 직접 포함합니다.[5]: 299–301

예를 들어 레지스터 1과 2를 추가하고 결과를 레지스터 6에 배치하면 다음과 같이 인코딩됩니다.[5]: 554

[ oprrd shamt funct] 012 6 0 32 decimal 0000000000001 00010000100000 100000 바이너리

레지스터 3에 나열된 위치 뒤에 메모리 셀 68 셀에서 가져온 값을 레지스터 8에 로드합니다.[5]: 552

[지적주소/immediate] 35 3 8 68 10진수 100011 000110 0000 0000100 이진수

주소 1024로 이동:[5]: 552

[ op target address ] 2 1024 decimal 000010000000000000000 바이너리

중복되는 지시사항

가변 길이 명령어 세트[6](인텔의 x86 프로세서 제품군과 같은)를 가진 프로세서 아키텍처에서, 그것은 크러스칼 카운트라고 알려진 제어 흐름 재동기화 현상의 한계 내에 있습니다.[7][6][8][9][10]때때로 opcode 수준의 프로그래밍을 통해 두 개의 코드 경로가 opcode 시퀀스의 공통 단편을 공유하도록 의도적으로 코드를 배열하는 것이 가능합니다.이를 겹침 명령어, 겹침 명령어, 겹침 명령어, 겹침 코드, 겹침 코드,[11][12][13] 명령어 명령어 명령어 명령어 명령어 명령어 명령어 명령어 명령어 명령어 명령어 명령어 명령어 명령어 명령어 명령어 명령어 명령어 명령어 명령어 명령어 명령어 명령어 명령어 명령어 한 가운데로 점프하는 명령어 명령어 명령어 명령어 명령어

1970년대와 1980년대에는 메모리 공간을 보존하기 위해 중복되는 명령어가 사용되기도 했습니다.한 예로 마이크로소프트의 Altair BASIC에서 인터리브된 명령어가 명령어 바이트를 상호 공유하는 오류 테이블 구현이 있습니다.[14][6][11]이 기법은 오늘날 거의 사용되지 않지만 부팅 섹터에 맞게 부팅 로더를 구현하는 등 바이트 수준에서 크기에 대한 극도의 최적화가 필요한 분야에서는 여전히 필요할 수 있습니다.[nb 2]

분해 및 변조에 대한 대책으로 코드 난독화 기법으로 사용되기도 합니다.[6][9]

이 원리는 여러 명령어 세트 호환되지 않는 프로세서 플랫폼에서 실행되어야 하는 fat 바이너리의 공유 코드 시퀀스에서도 사용됩니다.

이 속성은 또한 기존 코드 저장소에서 가젯이라고 하는 의도하지 않은 명령어를 찾는 데 사용되며 libc로 돌아가기 공격과 같은 공격에 대한 코드 주입의 대안으로 반환 지향 프로그래밍에서 사용됩니다.[15][6]

마이크로코드와의 관계

일부 컴퓨터에서, 아키텍처의 머신 코드는 마이크로코드(microcode)라고 불리는 훨씬 더 기본적인 기본 계층에 의해 구현되며, 라인 또는 기본적인 데이터 흐름이 매우 다른 다양한 모델의 컴퓨터 제품군에 걸쳐 공통적인 머신 언어 인터페이스를 제공합니다.이는 서로 다른 모델 간의 기계어 프로그램 포팅을 용이하게 하기 위한 것입니다.이러한 용도의 예로는 IBM System/360 제품군과 그 후속 제품군이 있습니다.8비트에서 64비트 이상의 데이터 흐름 경로 폭을 가진 이들은 그럼에도 불구하고 전체 라인에 걸쳐 기계 언어 수준에서 공통 아키텍처를 제공합니다.

마이크로코드를 사용하여 에뮬레이터를 구현하면 컴퓨터가 완전히 다른 컴퓨터의 아키텍처를 제시할 수 있습니다.System/360 제품군은 이를 사용하여 이전 IBM 컴퓨터에서 새로운 컴퓨터 계열(예: IBM S/360 모델 40의 IBM 1401/1440/1460 에뮬레이터)로 프로그램을 포팅할 수 있었습니다.

바이트코드와의 관계

기계 코드는 일반적으로 인터프리터에 의해 실행되거나 더 빠른 (직접적인) 실행을 위해 기계 코드로 컴파일되는 바이트 코드(p-code라고도 함)와 다릅니다.예외는 자바 프로세서의 경우처럼 특정 바이트 코드를 기계 코드로 직접 사용하도록 프로세서가 설계된 경우입니다.

컴퓨터 코드와 어셈블리 코드는 언어 기능이나 라이브러리의 플랫폼 의존적인 부분을 지칭할 때 네이티브 코드라고 불리기도 합니다.[16]

메모리에 저장

CPU의 관점에서 머신 코드는 RAM에 저장되지만 일반적으로 성능상의 이유로 캐시 집합에 저장됩니다.아키텍처에 따라 명령 및 데이터에 대한 캐시가 다를 수 있습니다.

CPU는 내부 프로그램 카운터에 따라 실행할 컴퓨터 코드를 알고 있습니다.프로그램 카운터는 메모리 주소를 가리키며 프로그램 분기를 유발할 수 있는 특수한 명령에 따라 변경됩니다.프로그램 카운터는 일반적으로 CPU의 전원을 처음 켤 때 하드 코딩된 값으로 설정되므로 이 주소에서 발생하는 머신 코드가 무엇이든 실행됩니다.

마찬가지로 프로그램 카운터는 유효한 컴퓨터 코드가 아니더라도 임의의 주소에 있는 컴퓨터 코드가 무엇이든 실행하도록 설정할 수 있습니다.이로 인해 일반적으로 아키텍처별 보호 오류가 발생합니다.

CPU는 페이징 기반 시스템에서 페이지 권한을 통해 현재 페이지가 실제로 실행 비트로 머신 코드를 보유하고 있는 경우 다양한 하우스 키핑 기능을 위해 페이지에 여러 개의 권한 비트(읽기, 쓰기 등)가 있습니다.예를 들어 유닉스 계열 시스템에서 메모리 페이지는 다음과 같이 실행 가능하도록 토글될 수 있습니다.mprotect()시스템 호출, 그리고 윈도우에서,VirtualProtect()유사한 결과를 얻기 위해 사용할 수 있습니다.실행 불가능한 페이지에서 컴퓨터 코드를 실행하려고 하면 일반적으로 아키텍처별 오류가 발생합니다.데이터를 기계 코드로 취급하거나 다양한 기술을 통해 기존 기계 코드를 사용하는 새로운 방법을 찾는 것은 일부 보안 취약점의 근본입니다.

프로세스의 관점에서 코드 공간은 실행 중인 코드가 저장되는 주소 공간의 부분입니다.멀티태스킹 시스템에서 이것은 프로그램의 코드 세그먼트일반적으로 공유 라이브러리로 구성됩니다.멀티스레딩 환경에서는 하나의 프로세스의 서로 다른 스레드가 데이터 공간과 함께 코드 공간을 공유하므로 프로세스 전환에 비해 컨텍스트 전환의 오버헤드가 상당히 줄어듭니다.

인간의 가독성

파멜라 새뮤얼슨(Pamela Samuelson)은 기계 코드가 너무 읽을 수 없기 때문에 미국 저작권청은 특정 인코딩된 프로그램이 저작물의 원본인지 확인할 수 없다고 썼습니다.[17]미국 저작권 사무소는 컴퓨터[18] 프로그램의 저작권 등록을 허용하며, 사람들이 프로그램의 기능을 쉽게 이해할 수 있도록 프로그램의 기계 코드를 때때로 해독할 수 있습니다.[19]그러나 디컴파일러 또는 디어셈블러의 출력에는 주석과 기호 참조가 누락되므로 출력이 개체 코드보다 읽기 쉬울 수 있지만 원래 소스 코드보다 더 어려울 수 있습니다.소스 코드가 파일에 포함되어 있는 SQUOZE와 같은 개체 코드 형식에서는 이 문제가 발생하지 않습니다.

인지과학 교수 더글러스 호프스태터는 "기계어로 쓰인 프로그램을 보는 것은 DNA 분자를 원자 단위로 보는 것과 모호하게 비교할 수 있다"[20]며 기계어와 유전자 코드를 비교했습니다.

참고 항목

메모들

  1. ^ BASIC의 여러 버전, 특히 초기 버전뿐만 아니라 Smalltalk, MATLAB, Perl, Python, Ruby 및 기타 특수 목적 또는 스크립팅 언어 등이 있습니다.
  2. ^ 예를 들어, DR-DOS MBR부트 섹터(파티션 테이블BIOS Parameter Block도 보유하고 코드에 대해 각각 446바이트 미만으로 남음)는 MS-DOS/PCD의 경우와 달리, 기존에는 자체적으로 FAT12 또는 FAT16 파일 시스템에서 부트 파일을 찾아 전체 메모리에 로드할 수 있었습니다.OS는 대신 시스템 파일에 의존하여 파일 시스템의 처음 두 디렉터리 항목IBMB의 처음 세 섹터를 차지했습니다.나머지 파일을 메모리에 로드하기 위한 보조 로더가 포함된 연속 섹터에 데이터 영역을 시작할 때 저장해야 하는 IO.COM.FAT32LBA 지원이 추가되었을 때, 마이크로소프트는 코드 크기를 이유로 386개의 명령어가 필요하도록 전환하고 부트 코드를 두 개의 섹터로 분할했는데, DR-DOS의 경우 멀티부트체인 로드 시나리오의 다른 운영 체제와 역호환성 및 상호호환성이 끊겼기 때문에 선택 사항이 아니었습니다.대신 DR-DOS 7.07 부트 섹터는 자체 수정 코드, 기계어로 된 opcode 수준 프로그래밍, (문서화된) 부작용의 통제된 활용,확장된 기능을 포기하지 않으면서도 512바이트에 불과한 물리적 섹터에 모든 것을 맞출 수 있는 다단계 데이터/코드 중복 및 알고리즘 폴딩 기술.

참고문헌

  1. ^ Stallings, William. Computer Organization and Architecture 10th edition. p. 776. ISBN 9789332570405.
  2. ^ Kjell, Bradley. "Immediate Operand".
  3. ^ Dourish, Paul (2004). Where the Action is: The Foundations of Embodied Interaction. MIT Press. p. 7. ISBN 0-262-54178-5. Retrieved 2023-03-05.
  4. ^ Zaks, Rodnay (1982). Programming the Z80 (Third Revised ed.). Sybex. pp. 67, 120, 609. ISBN 0-89588-094-6. Retrieved 2023-03-05.
  5. ^ a b c d e Harris, David; Harris, Sarah L. (2007). Digital Design and Computer Architecture. Morgan Kaufmann Publishers. ISBN 978-0-12-370497-9. Retrieved 2023-03-05.
  6. ^ a b c d e Jacob, Matthias; Jakubowski, Mariusz H.; Venkatesan, Ramarathnam [at Wikidata] (20–21 September 2007). Towards Integral Binary Execution: Implementing Oblivious Hashing Using Overlapped Instruction Encodings (PDF). Proceedings of the 9th workshop on Multimedia & Security (MM&Sec '07). Dallas, Texas, USA: Association for Computing Machinery. pp. 129–140. CiteSeerX 10.1.1.69.5258. doi:10.1145/1288869.1288887. ISBN 978-1-59593-857-2. S2CID 14174680. Archived (PDF) from the original on 2018-09-04. Retrieved 2021-12-25. (12페이지)
  7. ^ Lagarias, Jeffrey "Jeff" Clark; Rains, Eric Michael; Vanderbei, Robert J. (2009) [2001-10-13]. Brams, Stephen; Gehrlein, William V.; Roberts, Fred S. (eds.). "The Kruskal Count". The Mathematics of Preference, Choice and Order. Essays in Honor of Peter J. Fishburn. Berlin / Heidelberg, Germany: Springer-Verlag: 371–391. arXiv:math/0110143. ISBN 978-3-540-79127-0. (22페이지)
  8. ^ Andriesse, Dennis; Bos, Herbert [at Wikidata] (2014-07-10). Written at Vrije Universiteit Amsterdam, Amsterdam, Netherlands. Dietrich, Sven (ed.). Instruction-Level Steganography for Covert Trigger-Based Malware (PDF). 11th International Conference on Detection of Intrusions and Malware, and Vulnerability Assessment (DIMVA). Lecture Notes in Computer Science. Egham, UK; Switzerland: Springer International Publishing. pp. 41–50 [45]. doi:10.1007/978-3-319-08509-8_3. eISSN 1611-3349. ISBN 978-3-31908508-1. ISSN 0302-9743. S2CID 4634611. LNCS 8550. Archived (PDF) from the original on 2023-08-26. Retrieved 2023-08-26. (10페이지)
  9. ^ a b Jakubowski, Mariusz H. (February 2016). "Graph Based Model for Software Tamper Protection". Microsoft. Archived from the original on 2019-10-31. Retrieved 2023-08-19.
  10. ^ (1+xvii+1+152페이지)
  11. ^ a b "Unintended Instructions on x86". Hacker News. 2021. Archived from the original on 2021-12-25. Retrieved 2021-12-24.
  12. ^ Kinder, Johannes (2010-09-24). Static Analysis of x86 Executables [Statische Analyse von Programmen in x86 Maschinensprache] (PDF) (Dissertation). Munich, Germany: Technische Universität Darmstadt. D17. Archived from the original on 2020-11-12. Retrieved 2021-12-25. (199페이지)
  13. ^ "What is "overlapping instructions" obfuscation?". Reverse Engineering Stack Exchange. 2013-04-07. Archived from the original on 2021-12-25. Retrieved 2021-12-25.
  14. ^ Gates, William "Bill" Henry, Personal communication (NB. Jacob et al.에 의하면.)
  15. ^ Shacham, Hovav (2007). The Geometry of Innocent Flesh on the Bone: Return-into-libc without Function Calls (on the x86) (PDF). Proceedings of the ACM, CCS 2007. ACM Press. Archived (PDF) from the original on 2021-12-15. Retrieved 2021-12-24.
  16. ^ "Managed, Unmanaged, Native: What Kind of Code Is This?". developer.com. 2003-04-28. Retrieved 2008-09-02.
  17. ^ Samuelson, Pamela (September 1984). "CONTU Revisited: The Case against Copyright Protection for Computer Programs in Machine-Readable Form". Duke Law Journal. 1984 (4): 663–769. doi:10.2307/1372418. JSTOR 1372418. PMID 10268940.
  18. ^ "Copyright Registration for Computer Programs" (PDF). US Copyright Office. August 2008. Retrieved 2014-02-23.
  19. ^ "What is decompile? - Definition from WhatIs.com". WhatIs.com. Retrieved 2016-12-26.
  20. ^ Hofstadter, Douglas R. (1980). Gödel, Escher, Bach: An Eternal Golden Braid. p. 290.

추가열람