Machine-level representation basics(4)

G·2022년 10월 9일
0

2-2 System programming

목록 보기
2/15

Machine-level representation basics

Compliation System중 Compliation 단계에 생기는 Aseembly program인 source.s의 기본 연산자들과 피연산자들을 알아본다.
피연산자들과 Addressing Mode를 알아본다.
C언어에서 객체를 생성하고 값을 assignment하는 과정에서 더 최적화된 코드를 짤 수 있도록 Progrmmer 시점에서 알아본다.

Instruction Set Architecture

수업을 듣고 이해한 내용

Instruction은 명령어를 뜻 한다. ISA는 명령어 집합구조로 CPU가 인식하여 기능을 이해하고 실행하는 기계어 명령어를 뜻 한다. 만약 C언어로 코딩을 했을 때 디스크에 있던 soucr code가 Memory와 register를 오고가며 CPU가 연산을 하는데, ISA가 소프트웨어와 하드웨어의 중재 역할을 한다고 생각하면 된다. 이 명령어의 집합은 자료형, 명령어, 레지스터, 어드레싱 모드, 메모리 구조, 인터럽트, 예외 처리, 외부 입출력을 포함한 프로그래밍을 포함한 프로그래밍 관련 컴퓨터 아키텍쳐이다.
프로그래머가 이 기호화된 ISA를 확인할 수 있는 곳이 Assembly(.s) 파일이다.

Abstraction of computer architecture

컴퓨터의 추상화를 뜻하며 progrmmer에게 visible하게 만든 Assembly 파일을 뜻 한다.

ADT(adstract data type)

자료구조 시간에 배운 ADT를 상기해보자. 이는 자료구조의 데이터 타입, 동작원리, 함수(기능) 등을 정의해 놓은 것으로 구현과는 관련이 없다. 즉, 무엇을 하는지 정의해놓은 것이다.

ISA as an ADT

"_...the attributes of a [computing] system as seen by the programmer, i.e. the conceptual structure (state) and functional behavior (operations), as distinct from the organization of the data flow and controls, the logical design, and the physical implementation." - Amdahl, Blaauw, and Brooks, 1994
한 번 읽어보자. 무언가 느껴지긴 하지만 아직도 추상적이다..

소프트웨어와 하드웨어 사이의 인터페이스를 제공한다. 즉, ADT의 개념으로 다가갔을 때 data type은 오늘 배울 register와 memory가 될 것이고 이는 피연산자이다. 그리고 함수는 instruction이 될 것이다.

Opcode(연산코드)

내부적으로 일어나는 연산자의 코드이다(mov, add, jump) 이 코드와 피연산자의 주소를 알아야 연산이 가능하다.

  • memory와 register 사이의 data 이동이 일어난다.(피연산자)
  • 산술적 연산이 일어난다.
  • conditional branch and unconditional branch(jump)

Operands(피연산자)

  • 입력과 출력의 데이터와 데이터의 주소를 명시한다.

Machine state

  • register file: 밑에서 알아보자
  • PC: rip register: 다음 연산의 주소를 저장한다.
  • PSW: 최근의 실행된 산술, 논리 연산의 상태가 저장된 곳이다. CPU의 상태가 저장되어 있다.

What is register?

프로세서에 위치한 고속 메모리로 극히 소량의 데이터나 처리 중인 중간 결과와도 같은 프로세서가 바로 사용할 수 있는 데이터를 담고 있는 영역을 레지스터라고 한다.

만약 register가 존재하지 않는다면 CPU가 산술 연산을 할때 Memory에서 값을 가져오고, 산술 결과를 Memory에 저장한다. 이 행동을 반복하기 때문에 효율이 떨어진다. register는 이 과정을 생략하고 효율적으로 연산하기 위해 사용된다. 레지스터는 처리 중에 사용되는 데이터를 저장한다.

x86-64 Integer Register

정수 데이터를 저장하는 레지스터의 집합이며 이는 16개의 레지스터를 가지고 있다.

  • Integer register의 이름은 r로 시작한다.
  • rsp를 제외하고 정수 데이터 타입과 포인터를 저장한다.
  • rip는 다음에 실행될 연산의 주소값을 가지고 있다.
  • Floating-point 타입의 레지스터 집합은 따로 존재한다.

x86-64 Linux calling convention

Caller가 함수를 호출했을 때 Callee(함수 내부)는 argument가 어디에 저장되어 있는지 알아야 한다. Caller는 6개의 인자를 callee에게 전달하기 위해 register를 사용한다. 6개의 인자까지만 register 사용이 가능하다.
그 register 집합은 %rdi, %rsi, %rdx, %rcx, %r8, %r9이다.

7개 이상의 인자가 존재한다면 Stack에 저장된다.
사실 함수의 매개변수가 5개 이상이면 잘못 짠 코드일 것이다.
Callee는 리턴값을 rax에 저장한다.

$ gcc –S ex.c -O1 -fcf-protection=none

C complier를 사용해서 assembly language를 보자.

Intermediate-level code which is more human-friendly

위의 -S는 어셈블리 언어를 보여주는 옵션이다.
군더더기가 많다. gdb 디버거를 사용하자.

$ gcc -c -O1 ex.c -fcf-protection=none
$ gdb ex.o
(gdb) disas func
gdb 디버거를 사용해도 동적분석은 할 수 없다.

$ objdump –d ex.o (Disassembler 디스어셈블러는 기계어를 어셈블리어로 바꿔주는 프로그램이다.)

왼쪽은 binary이고 오른쪽은 symbolic이다. 개발자는 오른쪽을 읽고 코드를 이해할 수 있다.
왼쪽은 기계언어로 사람이 읽기 굉장히 어렵다.

ATT formant

gcc, objdump 등을 위한 기본 format이다. linux format이라고 보면 된다.
Intel format이 아니라 linux의 format을 확인한다.
둘이 별 차이도 없다.

mov Instruction: Moving data

mov 연산은 src의 값을 dst로 copy 해주는 명령어이다.
Operand Types

Immediate: Constant integer data

  • 상수 값이다.
  • src에만 올 수 있다. 예시로 메모리 주소가 있다.
  • src에 $ 기호가 붙는다.

    src $0x104: 주소를 참조하는게 아니라 0x104라는 수이다.

Register

  • 예시로 %rax, %r13가 있다.
  • 다른 레지스터들은 mov 연산 말고 특별하게 사용된다. 예시로 %rsp가 있다.
  • src, dst 둘 다 가능하다.

    src %rax: %rax가 가지고 있는 값
    dst %rax: %rax의 공간

Memory

  • 예시로 (%rax), 0x400이 있다.
  • src, dst 둘 다 가능하다.

    src

    • (%rax)는 레지스터가 가지고 있는 값의 메모리 주소이다.
    • 0x104의 절대주소는 src일 때 메모리에 있는 값, dst일 때 메모리 공간이다.

Optimization


-01 옵션을 줘서 최적화를 하고 assembly file을 보면 최적화가 이뤄져 x의 인자에 -1이 들어가는 과정이 생략된채 return 값에 -1이 들어간 모습을 볼 수 있다.
x의 인자를 찾고 x의 인자에 -1을 할당하는 두 단계가 사라졌다.

Copy the value into the memory at address 0x104


메모리 주소에 값을 할당하는 코드와 어셈블리 파일이다.

Copy the value at address 0x104 into A return register


왼쪽의 함수의 어셈블리 파일을 보면 리턴값을 저장하는 %rax에 할당이 된다. 최적화가 이뤄진 것이다.
오른쪽의함수는 스코프를 지나면 없어지기 때문에 최적화가 이루어져 mov가 작동하지 않는다.

Operand Forms

(%rxx) 값 = (%rxx)에 저장되어 있는 값이 저장된 메모리를 참조한다.

  1. src일 경우 메모리의 값이 된다. 이를 copy
  2. dst의 경우 메모리 공간이 된다. 여기에 copy

Indirect addressing mode

  • mov (%rbx), ____: (%rbx)는 rbx 레지스터에 저장된 메모리에 저장된 값 copy
  • mov ____,(%rbx): rbx 값이 저장된 메모리 공간에 copy

Indirect addressing mode는 src인 경우 메모리의 저장된 값(src) 또는 메모리 공간(dst)이다. src일 경우 값을 copy하여 우측 피연산자에 값을 copy하고 dst일 경우 좌측 피연산자의 값을 copy하여 저장한다.
()안에서 레지스터에 저장된 값의 산술연산을 통해 특정 수를 만든다면 그 수가 주소인 메모리를 참조하여 값을 가져오거나 공간에 값을 복사한다.

Displacement addressing mode

  • mov 0x10(%rax), ____: [0x10+(%rax에 저장된 값)]의 메모리 주소에 저장된 값 copy
  • mov ____,0x10(%rax): [0x10+(%rax에 저장된 값)]의 메모리 공간에 copy

Indexed addressing mode

  • mov (%rax,%rdx),____: [%rax 값 + %rdx 값 copy]의 메모리에 저장된 값 copy
  • mov ____,(%rax,%rdx): [%rax 값 + %rdx 값 copy]의 메모리에 저장된 값 copy
  • mov 0x10(%rax,%rdx),__: [0x10+(%rax 값 + %rdx 값)]의 메모리에 저장된 값 copy
  • mov ___,0x10(%rax,%rdx): [0x10+(%rax 값 + %rdx 값)] 메모리 공간에 값 copy

caled Indexed addressing mode

  • mov 0x4(%rax,%rdx,2), _: [0x4 + %rax + %rdx * 2)] 메모리에 저장된 값 copy
  • mov __,0x4(%rax,%rdx,2): [0x4 + %rax + %rdx * 2)] 메모리 공간에 값 copy
  • mov ___,0x4(,%rdx,4): [0x4 + %rdx * 4)] 메모리 공간에 값 copy

Table of examples

Memory to Memory

위의 사진을 보면 배열이 존재하는 Memory에 포인터가 존재하는 Memory에 존재하는 값을 할당해주는 연산을 볼 수 있다.
Memory에의 값을 Memory에 복사하는 연산은 한 번에 하지 못하고 리턴 값을 저장하면서 임시기억공간 역할을 하는 %rax 레지스터에 저장된 후 다시 Memory로 복사되는 것을 확인할 수 있다
Memory -> Register -> Memory 형식으로 값이 복사된다.

profile
열심히 안 사는 사람

0개의 댓글