컴파일러가 스택을 관리하는 방법과 스택의 값에 액세스하는 방법에 대한 구체적인 예를 제공하기 위해 시각적 GCC
아키텍처와 대상 아키텍처가 i386 인 Linux 환경에서 생성 된 코드를 볼 수 있습니다 .
1. 스택 프레임
아시다시피 스택은 함수 또는 프로 시저 에서 사용하는 실행중인 프로세스의 주소 공간에있는 위치입니다. 즉, 공간이 로컬에 선언 된 변수 및 함수에 전달 된 인수에 대해 스택에 할당된다는 의미입니다 ( 함수 외부에서 선언 된 변수 (예 : 전역 변수)를위한 공간은 가상 메모리의 다른 영역에 할당됩니다. 함수의 모든 데이터에 할당 된 공간을 스택 프레임 이라고합니다 . 다음은 여러 스택 프레임을 시각적으로 묘사 한 것입니다 ( 컴퓨터 시스템 : 프로그래머의 관점 ).
2. 스택 프레임 관리 및 가변 위치
특정 스택 프레임 내에서 스택에 기록 된 값이 컴파일러에 의해 관리되고 프로그램에 의해 읽히려면 이러한 값의 위치를 계산하고 메모리 주소를 검색하는 방법이 있어야합니다. 스택 포인터라고하는 CPU의 레지스터와 기본 포인터가이를 도와줍니다.
기본적으로 기본 포인터 ebp
에는 스택의 하단 또는 기본 메모리 주소가 포함됩니다. 스택 프레임 내 모든 값의 위치는 기준 포인터의 주소를 참조로 사용하여 계산할 수 있습니다. 이것은 위 그림에 묘사되어 %ebp + 4
있습니다. 예를 들어베이스 포인터에 4를 더한 메모리 주소가 저장되어 있습니다.
3. 컴파일러 생성 코드
그러나 내가 얻지 못하는 것은 응용 프로그램에서 스택의 변수를 읽는 방법입니다 .x를 정수로 선언하고 x = 3과 같이 할당하고 스토리지가 스택에 예약 된 다음 3의 값이 저장되는 경우 같은 함수에서 y를 선언하고 4와 같이 할당 한 다음 x를 다른 식 (예 : z = 5 + x)에 사용하여 프로그램이 z를 평가하기 위해 어떻게 x를 읽을 수 있습니까? 스택에서 y 아래입니까?
이것이 어떻게 작동하는지보기 위해 C로 작성된 간단한 예제 프로그램을 사용하자 :
int main(void)
{
int x = 3;
int y = 4;
int z = 5 + x;
return 0;
}
이 C 소스 텍스트에 대해 GCC에서 생성 한 어셈블리 텍스트를 살펴 보겠습니다 (명확성을 위해 약간 정리했습니다).
main:
pushl %ebp # save previous frame's base address on stack
movl %esp, %ebp # use current address of stack pointer as new frame base address
subl $16, %esp # allocate 16 bytes of space on stack for function data
movl $3, -12(%ebp) # variable x at address %ebp - 12
movl $4, -8(%ebp) # variable y at address %ebp - 8
movl -12(%ebp), %eax # write x to register %eax
addl $5, %eax # x + 5 = 9
movl %eax, -4(%ebp) # write 9 to address %ebp - 4 - this is z
movl $0, %eax
leave
우리가 관찰하는 변수는 X, Y 및 Z는 어드레스에 위치된다는 점이다 %ebp - 12
, %ebp -8
그리고 %ebp - 4
각각. 즉, 스택 프레임 내의 변수 위치 main()
는 CPU 레지스터에 저장된 메모리 주소를 사용하여 계산됩니다 %ebp
.
4. 스택 포인터 이외의 메모리에있는 데이터가 범위를 벗어남
나는 분명히 무언가를 놓치고있다. 스택의 위치는 변수의 수명 / 범위에 불과하며 전체 스택은 실제로 프로그램에 항상 액세스 할 수 있습니까? 그렇다면 값을 검색 할 수 있도록 스택에 변수의 주소 만 보유하는 다른 인덱스가 있음을 의미합니까? 그러나 스택의 요점은 변수 주소와 같은 위치에 값이 저장되었다는 것입니다.
스택은 가상 메모리의 영역으로, 컴파일러에서 사용을 관리합니다. 컴파일러는 스택 포인터 이상의 값 (스택 상단의 값)이 절대 참조되지 않는 방식으로 코드를 생성합니다. 함수가 호출 될 때 스택 포인터의 위치가 변경되어 스택에서 "범위를 벗어난"것으로 간주되는 공간을 생성합니다.
함수가 호출되고 리턴되면 스택 포인터가 감소하고 증가합니다. 이 범위를 벗어난 후 스택에 기록 된 데이터는 사라지지 않지만, 컴파일러는 사용하여 이러한 데이터의 주소를 계산할 수있는 방법이 없기 때문에 컴파일러는이 데이터를 참조하는 지침을 생성하지 않습니다 %ebp
나 %esp
.
5. 요약
CPU에 의해 직접 실행될 수있는 코드는 컴파일러에 의해 생성됩니다. 컴파일러는 스택, 함수의 스택 프레임 및 CPU 레지스터를 관리합니다. i386 아키텍처에서 실행되도록 코드에서 스택 프레임의 변수 위치를 추적하기 위해 GCC에서 사용하는 전략 중 하나는 스택 프레임 기본 포인터의 메모리 주소를 %ebp
참조로 사용하고 변수의 값을 스택 프레임의 위치에 쓰는 것입니다. 에서의 주소로 오프셋합니다 %ebp
.