Krafton Jungle/4. CSAPP

[Computer System] ③ 프로그램의 기계수준 표현 (4)

munsik22 2025. 4. 7. 00:46

3.9 이기종Heterogeneous 자료구조

  • C는 서로 다른 유형의 객체를 연결해서 자료형을 만드는 두 가지 방법을 제공한다.
    • struct 키워드를 사용해서 선언하는 구조체: 다수의 객체를 하나의 단위로 연결한다.
    • union으로 선언하는 공용체: 하나의 객체를 여러 개의 다른 자료형으로 참조될 수 있도록 한다.

3.9.1 구조체

C struct 선언은 서로 다른 유형의 객체들을 하나의 객체로 묶어주는 자료형을 생성한다.

  • 구조체의 구현은 구조체의 모든 컴포넌트들이 메모리의 연속된 영역에 저장된다.
  • 구조체의 포인터가 첫 번째 바이트의 주소라는 점에서 배열과 유사하다.
struct rec {
    int i;
    int j;
    int a[2];
    int *p;
};

위 구조체는 4개의 필드를 포함하고 있으며, 2개의 int 형을 가진 4바이트 값, int 형을 가진 두 개의 요소를 가진 배열, 8바이트 정수 포인터 총 24바이트를 가지고 있다.

3.9.2 공용체

공용체Union은 C언어의 자료형 체제를 회피해서 하나의 객체가 다수의 자료형에 따라 참조될 수 있도록 해준다.

  • 공용체를 선언하는 문법은 구조체와 동일하나 그 의미는 매우 다르다.
  • 다른 필드들이 메모리의 다른 블록을 참조하는 것이 아니라 동일한 블록을 참조한다.
struct S3 {
    char c;
    int i[2];
    double v;
}
union U3 {
    char c;
    int i[2];
    double v;
}

위의 코드가 x86-64 리눅스 컴퓨터에서 컴파일될 때 S3, U3 필드의 오프셋 크기는 다음과 같다.

Type c i v Size
S3 0 4 16 24
U3 0 0 0 8

공용체는 다양한 컨텍스트에서 유용할 수 있지만, C 타입 시스템에서 제공되는 안정성을 우회하기 때문에 오히려 치명적인 버그를 만들 수 있다.

3.9.3 데이터의 정렬

  • 많은 컴퓨터 시스템들은 기본 자료형들에 대해 사용 가능한 주소를 제한하고 있어서 어떤 객체의 주소는 어떤 값 K(일반적으로 2, 4, 8)의 배수가 되도록 요구한다.
  • 이러한 정렬제한은 프로세서와 메모리 시스템 간의 인터페이스를 구성하는 HW의 설계를 단순화한다.
  • 이들의 정렬 규칙은 모든 K의 원시 객체들은 K의 배수를 주소로 가져야 한다는 원칙에 기초한다.
  • 정렬은 자료형 내의 모든 객체들이 각각의 정렬 제한사항을 만족하는 방법으로 조직되고 할당되도록 강요된다.
  • 아래의 그림처럼 컴파일러는 c와 j 사이에 3바이트 공간을 삽입한다.

  • 그 결과 j는 오프셋 8을 가지게 되고, 전체 구조체는 12바이트가 된다.
  • 추가로, 컴파일러는 구조체의 마지막에 0을 채워서 구조체 배열에서 각 원소가 각각의 정렬 요건을 만족하도록 해준다.

3.10 기계수준 프로그램에서 제어와 데이터의 결합

3.10.1 포인터 이해하기

포인터는 C 프로그래밍 언어에서 핵심 특징으로, 다른 자료구조 내 원소들에 대한 참조를 생성하는 통일된 방법으로서의 역할을 수행한다.

  • 포인터는 연관된 자료형을 갖는다: 이 자료형은 어떤 종류의 객체를 이 포인터가 가리키는가를 의미한다.
  • 모든 포인터는 특정 값을 가진다: 이 값은 특정 자료형을 가지는 어떤 객체의 주소다.
  • 포인터는 & 연산자를 사용해서 만든다.
  • 포인터는 * 연산자를 사용해서 역참조한다: 그 결과는 포인터와 연관된 자료형을 가지는 값을 가져온다.
  • 배열과 포인터는 밀접한 관련이 있다: 배열의 이름은 마치 포인터 변수처럼 참조될 수 있다.
  • 한 종류의 포인터에서 다른 종류로의 자료형 변환은 그 종류만 바뀔 뿐 값은 변화가 없다.
  • 포인터는 함수를 가리킬 수도 있다: 이것은 프로그램의 다른 부분에서 호출할 수 있는 코드에 대한 참조를 저장하거나 넘겨줄 수 있는 강력한 기능을 제공한다.

3.10.2 실제 적용하기

3.10.3 범위를 벗어난 메모리 참조와 오버플로우

  • C에서는 배열참조 시 범위를 체크하지 않으며, 지역변수들이 스택에 보존용 레지스터들과 리턴 주소 같은 상태정보와 함께 스택에 저장된다.
  • 이러한 조합 때문에, 스택에 저장된 상태정보가 범위를 벗어난 배열의 원소에 대한 쓰기 작업에 의해 변경되면서 심각한 프로그램 에러가 발생할 수 있다.
  • 일반적인 상태손실의 원인은 버퍼 오버플로우라고 알려져 있다.

buf로 범위를 초과해서 쓰게 되면 프로그램의 상태가 손상된다.

  • 버퍼 오버플로우의 보다 치명적인 사용은 일반적으로 프로그램이 하지 않을 기능들을 실행하도록 하는 것이다.
  • 일반적으로 탐색코드exploit code라고 하는 실행코드를 바이트 인코딩한 탐색코드를 가리키는 포인터 리턴 주소를 덮어쓰는 약간의 추가적인 바이트들을 포함하는 스트링을 입력한다. ret 인스트럭션을 실행하면 탐색코드로 점프하게된다.

3.10.4 버퍼 오버플로우 공격 대응 기법

  • 스택 랜덤화: 스택의 위치를 프로그램의 매 실행마다 다르게 해주는 것이다.
  • 스택 손상 검출

특별 “canary” 값이 배열 buf와 저장된 상태 값 사이에 위치한다. 코드는 스택 상태가 손상되었는지 여부를 결정하기 위해 canary 값을 체크한다.

  • 실행코드 영역 제한하기: 어느 메모리 영역이 실행코드를 저장할지를 제한하는 것이다. 다른 부분들은 읽기와 쓰기만 허용하도록 제한할 수 있다.

3.10.5 가변크기 스택 프레임 지원하기

프레임 포인터를 사용해야 하는 함수: 가변길이 배열은 스택 프레임의 크기가 컴파일 시에 결정될 수 없다는 것을 의미한다.


3.11 부동소수점 코드

프로세서의 부동소수점 아키텍처는 부동소수점 데이터로 연산하는 방법이 기계에 매핑되는 방법에 영향을 주는 다음과 같은 여러 가지 개념들로 구성된다.

  • 부동소수점 값들이 저장되고 접근되는 방법: 이것은 대개 레지스터들의 일부 형태로 이뤄진다.
  • 부동소수점 데이터로 연산하는 인스트럭션
  • 함수들의 인자와 리턴 값으로 부동소수점 값들을 전달하기 위해 이용되는 관례들
  • 함수를 호출하는 동안에 레지스터들을 보존하는 관례들

미디어 레지스터: 이 레지스터들은 부동소수점 데이터를 보관하기 위해 사용된다. 각 YMM 레지스터는 32바이트를 보관한다. 하위 16바이트는 하나의 XMM 레지스터처럼 접근될 수 있다.

3.11.1 부동소수점 이동 및 변환 연산

부동소수점 이동 명령: 이 연산들은 레지스터들 간, 레지스터와 메모리 간에 값을 이동한다.

[X: XMM 레지스터 (%xmm3), M32: 32비트 메모리 범위, M64: 64비트 메모리 범위]

두 개의 오퍼랜드를 갖는 부동소수점 변환 연산: 이 연산들은 부동소수점 데이터를 정수로 변환한다.

[X: XMM 레지스터 (%xmm3), R32: 32비트 범용 레지스터 (%eax), R64: 64비트 범용 레지스터 (%rax), M32: 32비트 메모리 범위, M64: 64비트 메모리 범위]

세 개의 오퍼랜드를 갖는 부동소수점 변환 연산: 이 연산들은 첫번째 소스의 자료형으로부터 목적지의 자료형으로 변환한다. 두번째 소스 값은 결과의 하위 바이트에는 영향을 주지 않는다.

[X: XMM 레지스터 (%xmm3), M32: 32비트 메모리 범위, M64: 64비트 메모리 범위]

3.11.2 프로시저에서 부동소수점 코드

3.11.3 부동소수점 산술연산

스칼라 부동소수점 산술연산: 이 인스트럭션들은 한 개 또는 두 개의 소스 오퍼랜드와 하나의 목적지 오퍼랜드를 갖는다.

3.11.4 부동소수점 상수의 정의 및 이용

3.11.5 부동소수점 코드에서 비트연산 사용하기

통합데이터에 대한 비트 연산: 이 인스트럭션들은 XMM 레지스터에 들어 있는 128비트들에 대한 부울boolean연산을 수행한다.

3.11.6 부동소수점 비교 연산

  • ucomiss S₁, S₂ : Compare single precision
  • ucomids S₁, S₂ : Compare double precision

[사진 출처] [CS:APP] Chapter 03. 프로그램의 기계수준 표현 (3.11 부동소수점 코드 ~ 3.12 요약)