컴퓨터 구조 CS:APP 3장 - 1 -
프로그램의 기계수준 표현
컴퓨터는 데이터를 처리하고, 메모리를 관리하고, 저장장치에 데이터를 읽거나 쓰고,
네트워크를 통해 통신하는 등의 하위 동작들을 인코딩한 연속된 바이트인 기계어 코드(machine code)를 실행한다
GCC는 어셈블러와 링커를 호출하여 어셈블리 코드로부터 실행 가능한 기계어 코드를 생성한다
컴파일러가 어셈블리어 코드를 만들어 내는 대부분의 일을 하고 있지만,
그 코드들을 읽고 이해하는 능력은 프로그래머들에게 매우 중요한 기술이다
-
코드들을 이해하면 컴파일러의 최적화 성능을 알 수 있으며,
-
코드에 내재된 비효율성을 분석 할 수도 있다
-
프로그램의 취약성이 어떻게 발생하는지, 공격을 어떻게 막을 수 있을지 이해하려면
프로그램의 기계수준 표현에 대한 지식이 필요하다
C 코드로 표현된 계산에 대해서 최적화 컴파일러는 실행 순서를 조정하고
불필요한 계산을 제거하고, 느린 연산을 빠른 연산으로 교체하고
재귀적 연산들을 반복 연산으로 바꾸기도 한다
기계수준 코드
-
기계 수준 프로그램의 형식과 동작은 인스트럭션 집합구조(ISA)에 의해 정의된다
-
기계 수준 프로그램이 사용하는 주소는 가상 주소이며,
메모리가 매우 큰 바이트 배열인 것처럼 보이게 하는 메모리 모델을 제공 한다
-
프로그램 카운터(PC)는 실행할 다음 인스트럭션의 메모리 주소를 가리키는 역할을 한다
-
정수 레지스터 파일은 64비트 값을 저장하기 위한 16개의 이름을 붙인 위치를 갖는다
레지스터는 주소나 정수 데이터를 저장할 수 있다
-
조건코드 레지스터들은 산술 또는 논리 인스트럭션에 관한 상태 정보를 저장한다
-
벡터 레지스터들의 집합 하나 이상의 정수나 부동소수점 값들을 각각 저장할 수 있다
기계어 코드는 메모리를 단순히 바이트 주소지정이 가능한 큰 배열로 본다
프로그램 메모리는 프로그램의 실행 기계어 코드, 운영체제를 위한 일부 정보,
프로시저 호출과 리턴을 관리하는 런타임 스택, 사용자에 의해 할당된(malloc) 메모리 블록들을 포함하고 있다
운영체제는 이 가상 주소공간을 관리해서 가상주소를 실제 프로세서 메모리 상의 물리적 주소 값으로 번역
왼쪽은 어셈블리 인스트럭션에 대응되는 목적코드이다
중요한 교훈은 실제 실행된 프로그램은 인스트럭션을 인코딩한 바이트라는 점이다
-
인스트럭션들과 오퍼랜드가 적은 것들이 짧은 길이를 갖도록 하고 그 반대의 경우에는 좀 더 긴 인스트럭션 길이를 갖도록 인코딩한다
-
인스트럭션들의 형식은 주어진 시작 위치에서부터 바이트들을 기계어 인스트럭션으로 유일하게 디코딩할 수 있도록 설계한다
-
역어셈블러는 기계어 코드 파일의 바이트 순서에만 전적으로 의존해서 어셈블리 코드를 결정한다
-
역어셈블러는 GCC가 생성한 어셈블리 코드와는 약간 다른 명명법을 인스트럭션에 사용한다
데이터의 형식
16비트 구조를 사용하다가 추후에 32비트로 확장했기 때문에
인텔은 “워드”라는 단어를 16비트 데이터 타입을 말할 때 사용한다
이것을 기초해서 32비트의 양을 더블 워드라고 부른다
64비트의 양은 쿼드 워드라고 부른다
표준 int 값들은 더블 워드로 저장된다(32비트)
포인터들은(여기서 char*로 나타낸 것처럼) 64비트 머신에서 8바이트 쿼드 워드로 저장된다
부동 소수점 숫자
-
float 타입 : 단일 정밀도, 단정도(4바이트)
-
double 타입 : 이중 정밀도, 배정도(8바이트)
-
long double 타입 : 80비트, 확장 정밀도(10바이트)
정보 접근하기
x86-64 주처리장치 CPU는 64비트 값을 저장할 수 있는 16개의 범용 레지스터를 보유하고 있다
이들 레지스터는 정수 데이터와 포인터를 저장하는 데 사용한다
원조인 8086에서 레지스터들은 %ax에서 %sp까지 나타낸 것처럼 16비트 레지스터를 가지고 있다
인스트럭션들은 16개의 레지스터 하위 바이트들에 저장된 다양한 크기의 데이터에 대해 연산할 수 있다
바이트 수준 연산들은 가장 덜 중요한 바이트에 대해 연산 할 수 있으며
16비트 연산들은 가장 덜 중요한 2바이트에 접근하고, 32비트 연산은 덜 중요한 4바이트에, 64비트 연산은 레지스터 전체에 접근 할 수 있다
가장 특이한 점은 스택 포인터 %rsp로 런타임 스택의 끝 부분을 가리키기 위해 사용된다