책/CSAPP

CSAPP 3.1 - 3.3

에린_1 2024. 1. 20. 20:30
728x90

CSAPP

  • 어셈블리 코드를 짤때 저급 인스트럭션을 명시해야 하는데, 대개의 경우 고급 언어가 제공하는 높은 수준의 추상화를 사용하는 것이 보다 더 생산적이고 안정적이다.

그렇다면 왜 WHY 기계어를 사용하고 공부할까?

  • 컴파일러를 적절한 커맨드라인 인자와 함께 호출하면 컴파일러는 어셈블리 코드 형태의 파일로 출력을 생성한다.
  • 이 코드를 이해하면 컴파일러의 최적화 성능을 알 수 있고, 코드에 내재된 비효율성을 분석할 수 있다.
  • 프로그램이 얼마나 효율적으로 실행될지 이해하기 위해서 생성된 어셈블리 코드를 컴파일하고 분석해 보곤한다.
  • 더욱이 고급언어에서 제공하는 추상화 계층 때문에 이해 가능한 프로그램의 런타임 동작이 감춰지는 경우도 종종 있다.
  • 또 다른 예로 악성 프로그램이 시스템을 감염 시킬 수 있도록 프로그램을 공격하는 방법 중 상당수가 프로그램이 런타임 제어정보를 저장하는 방식의 미묘한 차이와 관련이 있다. 이런 취약성이 어디서 발생하는지, 이런 공격을 어떻게 막을 수 있는지 이해하려면 프로그램의 기계수준 표현에 대한 지식이 필요하다.

상세한 내용을 완벽히 이해하면 보다 심오하고 보다 근본적인 개념들을 이해할 수 있게 된다. ‘ 일반적인 원리는 이해해도 자세한 내용은 배우고 싶지 않다.’ 고 말하는 사람들은 자신을 속이는 것이다.

  • 좀 많이 찔릴 수 있는 문장. 나태해질 때마다 자주 보자.

프로그램 인코딩

  • 일반적으로 최적화 수준을 올리게 되면 최종 프로그램은 더 빨리 동작하게 되지만, 컴파일 시간이 증가하고 디버깅 도구를 실행하기가 어려워질 위험이 있다.
  • 높은 수준 최적화를 적용하면 만들어진 코드가 너무 많이 변경되어서 본래의 코드와 생성된 기계어 코드 간 관계를 이해하기 어렵다.

GCC 컴파일러

  • GCC명령은 소스코드를 실행코드로 변환하기 위해 일련의 프로그램 호출
  • C전처리기 #include 명시된 파일을 코드에 삽입, #define 선언된 매크로 확장
  • 컴파일러가 어셈블리로 변환
  • 어셈블러가 어셈블리 코드를 바이너리 목적 코드로 변환
    • 바이너리 목적코드 - 기계어 코드의 한 유형
  • 링커가 목적코드 파일을 라이브러리 함수를 구현한 코드와 합쳐 실행 파일 생성

기계수준코드

  • 컴퓨터 시스템은 보다 간단한 추상화 모델을 이용해서 세부 구현 내용을 감추면서 추상화의 여러가지 다른 형태를 사용하고 있다. 이들 중 두가지가 기계 수준 프로그래밍에서 특히 중요하다.
  1. 기계수준 프로그램의 형식과 동작은 인스트럭션 집합구조(instruction set architecture) 즉 ‘ISA’에 의해 정의된다. 이 ISA는 프로세서의 상태, 인스트럭션의 형식, 프로세서 상태에 대한 각 인스트럭션들의 영향을 정의한다. X86-64를 포함해 대부분의 ISA는 인스트럭션이 순차적인 실행을 하는것처럼 프로그램 동작을 설명한다. 프로세서 하드웨어는 훨씬 정교해서 여러 인스트럭션을 동시에 실행하지만 ISA에 의한 순차적 동작과 일치하는 전체동작을 보이도록 해주는 안전장치를 사용한다.
  2. 기계수준 프로그램이 사용하는 주소는 가상주소이며, 메모리가 매우 큰 바이트 배열인 것처럼 보이게 하는 메모리 모델을 제공한다.

X86-64 기계어코드

  • 프로그램 카운터(X86-64 %rlp) - 실행할 다음 인스트럭션 메모리 주소를 가리킨다
  • 정수 레지스터 파일 - 64비트 값을 저장하기 위한 16개의 이름을 붙인 위치를 가진다. 레지스터주소나 정수 데이터를 저장할 수 있다. 일부 레지스터는 프로그램의 중요한 상태를 추적하는데 사용할 수 있으며, 다른 레지스터들은 함수의 리턴 값 뿐만 아니라 프로시저의 지역변수와 인자 같은 임시값을 저장하는데 사용한다.
  • 조건코드 레지스터 - 가장 최근에 실행한 산술 또는 논리 인스트럭션에 관해 상태정보 저장. if나 while문 구현할 때 필요한 제어나 조건에 따른 데이터 흐름의 변경을 구현하기 위해 사용한다.
  • 벡터 레지스터 - 하나 이상의 정수나 부동소수점 값 저장
  • C가 다른 종류의 데이터 타입을 선언하고 메모리에 할당할 수 있는 모델을 제공하는 반면 기계어 코드는 메모리를 단순히 바이트 주소 지정이 가능한 큰 배열로 본다.
  • C에서 배열이나 구조체처럼 연결된 데이터 타입은 기계어 코드에서 연속적인 바이트로 표시. 심지어 포인터와 정수형 사이에도 구분을 하지않는다.
  • 프로그램 메모리 - 프로그램의 실행 기계어 코드, 운영체제를 위한 일부 정보, 프로시저 호출과 리턴을 관리하는 런타임스택, 사용자에 의해 할당된 메모리 블록(heap)
  • 운영체제는 가상주소 공간을 관리해서 가상주소를 실제 프로세서 메모리상의 물리적 주소값으로 번역해준다.
  • 하나의 기계어는 매우 기초적인 동작만을 수행한다.
    • 레지스터들에 저장된 수를 더하고, 데이터 교환, 조건 분기
  • pushq %rbx - 레지스터 rbx가 프로그램 스택에 저장 push 되어야 한다.
    • 지역변수나 데이터 타입에 관한 모든 정보는 삭제됐다.
  • .O파일 16진수 데이터
  • 컴퓨터에 의해 실제 실행된 프로그램은 단순히 일련의 인스트럭션을 인코딩한 일련의 바이트. 컴퓨터는 인스트럭션들이 생성된 코드에 대한 정보를 거의 가지고 있지않다.
  • 기계어 코드 파일을 조사하려면, 역 어셈블러라고 하는 프로그램이 중요하다.

기계어코드 몇 특징과 이들의 역어셈블된 표현

  • X86-64 인스트럭션은 1~15byte 길이를 가진다. 인스트럭션 인코딩은 자주 사용되는 인스트럭션들과 오퍼랜드가 적은것들이 짧은 길이를 갖도록 하고, 그 반대는 좀 더 긴 인스트럭션 길이를 갖도록 한다.
    • 오퍼랜드 - 연산을 수행하는데 필요한 데이터, 데이터 주소
  • 인스트럭션 형식은 주어진 시작 위치에서부터 바이트들을 기계어 인스트럭션으로 유일하게 디코딩할 수 있도록 설계한다. pushq %rbx 인스트럭션만이 바이트값 53으로 시작할 수 있다.
  • 역어셈블러는 기계어 코드 파일이 바이트 순서에만 전적으로 의존해서 어셈블리코드를 결정한다.
  • 역어셈블러는 GCC가 생성한 어셈블리 코드와는 다른 명명법을 인스트럭션에 사용한다.
728x90

' > CSAPP' 카테고리의 다른 글

CSAPP 3.6.5 - 3.7  (1) 2024.01.22
CSAPP 3.5 - 3.6.4  (0) 2024.01.20
CSAPP 3.4  (0) 2024.01.20
CSAPP 1.7 - 1완  (0) 2024.01.20
CSAPP 1.1 - 1.6  (0) 2024.01.20