hwan2story
article thumbnail

출처 : 드림핵

 

리눅스에서는 프로세스의 메모리를 크게 5가지의 세그먼트로 구분합니다.

 

 

 

 1. 코드 세그먼트(Code Segment)

실행 가능한 기계 코드가 위치하는 영역으로 텍스트 세그먼트(Text Segment)라고도 불림.

 

 프로그램이 동작하려면 코드를 실행할 수 있어야 하므로 이 세그먼트에는 읽기 권한과 실행 권한이 부여됩니다. 반면 쓰기 권한이 있으면 공격자가 악의적인 코드를 삽입하기가 쉬워지므로, 대부분의 현대 운영체제는 이 세그먼트에 쓰기 권한을 제거합니다.

 

 아래에서 정수 31337을 반환하는 main함수가 컴파일되면 554889e5b8697a00005dc3라는 기계 코드로 변환되는데, 이 기계 코드가 코드 세그먼트에 위치하게 됩니다.

int main() { return 31337; }

 

 

 2. 데이터 세그먼트(Data Segment)

컴파일 시점에 값이 정해진 전역 변수 및 전역 상수들이 위치합니다. CPU가 이 세그먼트의 데이터를 읽을 수 있어야 하므로, 읽기 권한이 부여됩니다.


 데이터 세그먼트는 쓰기가 가능한 세그먼트와 쓰기가 불가능한 세그먼트로 다시 분류되는데, 쓰기가 가능한 세그먼트는 전역 변수와 같이 프로그램이 실행되면서 값이 변할 수 있는 데이터들이 위치합니다. 이런 세그먼트는 data 세그먼트라고 부릅니다.

 반면 쓰기가 불가능한 세그먼트에는 프로그램이 실행되면서 값이 변하면 안되는 데이터들이 위치합니다. 전역으로 선언된 상수가 여기에 포함됩니다. 이런 세그먼트를 rodata(read-only data)세그먼트 라고 부릅니다.

 

 아래는 데이터 세그먼트에 포함되는 여러 데이터의 유형입니다. 주의 깊게 살펴봐야 할 변수는 str_ptr입니다. str_ptr은 “readonly”라는 문자열을 가리키고 있는데, 이 문자열은 상수 문자열로 취급되어 rodata에 위치하며, 이를 가리키는 str_ptr은 전역 변수로서 data에 위치합니다.

int data_num = 31337;                       // data
char data_rwstr[] = "writable_data";        // data
const char data_rostr[] = "readonly_data";  // rodata
char *str_ptr = "readonly";  // str_ptr은 data, 문자열은 rodata
int main() { ... }

 

 

3. BSS 세그먼트(BSS Segment, Block Started By Symbol Segment)

컴파일 시점에 값이 정해지지 않은 전역 변수가 위치하는 메모리 영역입니다. 

 

 여기에는 개발자가 선언만 하고 초기화하지 않은 전역변수 등이 포함됩니다. 이 세그먼트의 메모리 영역은 프로그램이 시작될 때, 모두 0으로 값이 초기화됩니다. 이런 특성 때문에 C 코드를 작성할 때, 초기화되지 않은 전역 변수의 값은 0이 됩니다.

 이 세그먼트에는 읽기 권한 및 쓰기 권한이 부여됩니다.

 

아래 코드에서 초기화되지 않은 전역 변수인 bss_data가 BSS 세그먼트에 위치하게 됩니다.

int bss_data;
int main() {
  printf("%d\n", bss_data);  // 0
  return 0;
}

 

4. 스택 세그먼트(Stack Segment)

 

 프로세스의 스택이 위치하는 영역입니다. 함수의 인자나 지역 변수와 같은 임시 변수들이 실행중에 여기에 저장됩니다.

 

 스택 세그먼트는 스택 프레임(Stack Frame)이라는 단위로 사용됩니다. 스택 프레임은 함수가 호출될 때 생성되고, 반환될 때 해제됩니다. 그런데 프로그램의 전체 실행 흐름은 사용자의 입력을 비롯한 여러 요인에 영향을 받습니다.

 

 어떤 프로세스가 실행될 때, 이 프로세스가 얼마 만큼의 스택 프레임을 사용하게 될지를 미리 계산하는 것은 일반적으로 불가능합니다. 그래서 운영체제는 프로세스를 시작할 때 작은 크기의 스택 세그먼트를 먼저 할당해 주고, 부족해질 때마다 이를 확장해 줍니다. 스택에 대해서 ‘아래로 자란다'라는 표현을 종종 사용하는데, 이는 스택이 확장될 때, 기존 주소보다 낮은 주소로 확장되기 때문입니다.

 

이 영역에는 CPU가 자유롭게 값을 읽고 쓸 수 있어야 하므로, 읽기쓰기 권한이 부여됨. 

 

아래의 코드에서 유저가 입력한 choice에 따라 call_true()가 호출될 수도, call_false()가 호출될 수도 있습니다.

void func() {
  int choice = 0;
  scanf("%d", &choice);
  if (choice)
    call_true();
  else
    call_false();
  return 0;
}

 

 

5. 힙 세그먼트(Heap Segment)

 

 힙 데이터가 위치하는 세그먼트입니다. 스택과 마찬가지로 실행중에 동적으로 할당될 수 있으며, 리눅스에서는 스택 세그먼트와 반대 방향으로 자랍니다.


C언어에서 malloc(), calloc() 등을 호출해서 할당받는 메모리가 이 세그먼트에 위치하게 되며, 일반적으로 읽기와 쓰기 권한이 부여됩니다.

 아래 예제 코드는 heap_data_ptr에 malloc()으로 동적 할당한 영역의 주소를 대입하고, 이 영역에 값을 씁니다. heap_data_ptr은 지역변수이므로 스택에 위치하며, malloc으로 할당받은 힙 세그먼트의 주소를 가리킵니다.

int main() {
  int *heap_data_ptr =
      malloc(sizeof(*heap_data_ptr));  // 동적 할당한 힙 영역의 주소를 가리킴
  *heap_data_ptr = 31337;              // 힙 영역에 값을 씀
  printf("%d\n", *heap_data_ptr);  // 힙 영역의 값을 사용함
  return 0;
}

 

6. 결론 요약

 


출처 :  드림핵https://learn.dreamhack.io/52#11

 

로그인 | Dreamhack

 

dreamhack.io

 

'이론 > [System 이론]' 카테고리의 다른 글

컴퓨터 구조  (0) 2023.05.01
단축키키  (0) 2023.04.02
x86, x64 레지스터  (0) 2023.03.19
BOF(Buffer OverFlow) - 버퍼 오버 플로우  (0) 2023.03.19
올리디버거 주요단축키  (0) 2023.03.17
profile on loading

Loading...

검색 태그