[컴퓨터 밑바닥의 비밀 내용 정리] 1.7 링커의 재배치와 가상메모리
0. 들어가며
- 굉장히 감명깊게 읽었던 [컴퓨터 밑바닥의 비밀] 책의 내용을 돌아보기 위해 정리글을 작성한다.
- 전공자로 어느정도 안다고 생각한 내용들에서도 부족한 부분이 보여 책을 다시 읽어보며 정리하고자 한다.
https://link.coupang.com/a/cdaqFv
컴퓨터 밑바닥의 비밀:컴퓨터 시스템의 본질을 알면 코드의 실마리가 보인다 - 보안과 해킹 | 쿠
쿠팡에서 4.3 구매하고 더 많은 혜택을 받으세요! 지금 할인중인 다른 3 제품도 바로 쿠팡에서 확인할 수 있습니다.
www.coupang.com
"이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다."
(본문 소개 링크로 도서 구입 시 일정액의 수수료를 제공받습니다.)
1. 주제
모든 변수나 함수에 메모리 주소가 있다.
어셈블리어 코드를 보면 명령어에 변수 정보는 없고 전부 메모리 주소를 쓴다.
foo 함수 호출 → call 0x4004d6
→ foo 함수의 첫번째 기계 명령어가 0x4004d6 주소에 위치한다는 뜻.
그러면, 실행 파일의 함수를 실행하기 위해서는
이 함수의 메모리 주소를 알아야 한다는 전제가 자동으로 성립된다.
그런데, 링커는 실행파일을 생성할 때 프로그램이 실행되는 시점에
함수가 적재될 메모리 주소를 확정해야 한다.
아직 실행되지도 않은 함수의 적재될 메모리 주소를 어떻게 확정할 수 있을까?
(이 이유는 추후 설명할 가상메모리 덕분에 알 수 있다.)
우선 컴파일러의 동작을 먼저 살펴보자.
컴파일러의 동작
컴파일러는 foo함수가 어느 메모리 주소에 적재될지
즉, call 뒤에 어떤 메모리 주소를 적을지 몰라서 ‘call 0x00’ 이라고만 적고
foo함수를 호출한다는 사실만 기록한다
→ 이처럼 메모리 주소를 확정할 수 없는 변수나 함수를 발견했을 때
컴파일러는 대상 파일에 .relo.text에는 해당 명령어를,
.relo.data에는 명령어와 관련된 데이터를 저장한다.
예를 들어 foo함수의 경우, 컴파일러는 call 명령어를 생성하면서
.relo.text에 “코드 영역의 시작 주소 기준 오프셋이 60바이트인 위치에 foo 심벌을 발견했지만,
실행시에 어느 주소에서 실행해야 할지 알 수 없습니다.
링커가 실행파일을 생성할 때 이 명령어를 수정해야 합니다.” 라고 기록한다.
→ 그러면 대상 파일의 각 영역들이 실행 파일 생성시에 모두 결합되고 나면
모든 기계 명령어와 전역 변수들이 프로그램 실행시간에 위치할 메모리 주소를 알게 된다.
(이 이유는 가상 메모리 때문인데, 추후 설명)
링커의 동작
이어서 링커는 .relo.text 파일을 한 줄씩 읽으며 새로 알게된 주소로
심벌의 메모리 주소를 알맞게 적어준다. 이를 ’재배치‘라고 한다.
링커가 프로그램이 실행된 후의 변수나 기계 명령어의 메모리 주소를
실행 전에 알 수 있는 방법은? 앞서 말했듯이 가상 메모리 덕분이다.
그럼 가상 메모리의 구조를 먼저 살펴보자.
2. 가상메모리

스택 영역은 로컬 변수나 컴파일 시 할당할 주소
힙 영역은 malloc처럼 동적으로 할당할 메모리
코드 영역, 데이터 영역은 실행 파일의 내용이 메모리에 적재되는 것은 맞다.
정말 흥미로운건, 모든 프로그램은 실행된 후에 예외 없이 코드 영역이 0x400000에서 시작한다.
그럼 프로그램 A와 B가 동시에 실행중일 때
CPU가 메모리 주소 0x400000에서 가져오는 기계 명령어는 A의 코드일까? B의 코드일까?
→ CPU가 프로그램 A를 실행할 때 메모리 주소 0x400000에서 가져오는 명령어는 A의 코드이고,
프로그램 B를 실행할 때 메모리 주소 0x400000에서 가져오는 명령어는 B의 코드이다.
말 그대로 가상 메모리는 말 그래도 물리적으로 존재하지 않는 가상의 메모리라
각각의 프로그램이 실행중일 때, 자기 자신이 모든 메모리를 모두 독점적으로 쓰고 있다고 착각한다.
32비트 시스템이면 2의 32승 즉, 4GB 메모리를 독점한다고 생각하는 것이다.
→ 따라서, 모든 프로그램은 동일한 표준적인 메모리 구조를 갖고,
프로그래머는 코드 작성 시 이 표준 메모리 구조를 기반으로 프로그램을 작성할 수 있으며,
링커도 실행파일을 생성하자마자 실행 시 심벌의 메모리 주소를 미리 알 수 있다.
가상 메모리 상의 주소와 물리 메모리 주소 간의 매핑 정보는 메모리 페이지 단위로 관리되며,
이 매핑 관계를 기록한 표를 페이지 테이블이라 한다.
3. CPU의 가상 메모리 접근 과정

각각의 프로세스는 자신만의 페이지 테이블이 있고
CPU가 프로그램 A를 실행하고 0x400000에 접근하면
특별히 설계된 하드웨어가 페이지 테이블을 참고해 물리 메모리 주소로 변환 후 접근한다.
- 모든 프로세스의 가상 메모리는 표준화되어 있고, 크기가 동일하다. (프로세스마다 각 영역의 크기가 다를 수 있지만, 영역이 배치되는 순서는 동일)
- 실제 물리메모리의 크기는 가상 메모리의 크기와는 무관하며 물리 메모리에는 힙 영역, 스택 영역 등 구분조차 존재하지 않는다.
- 모든 프로세스는 자신만의 페이지 테이블을 가지며, 같은 가상 메모리 주소여도 페이지 테이블을 통해 서로 다른 물리 메모리 주소를 확인한다. (이를 통해 CPU는 같은 가상 메모리 주소에서 서로 다른 내용을 가져온다.)