위치 독립 코드

위치 독립 코드(PIC: position-independent code) 또는 위치 독립 실행 파일(PIE: position-independent executable)은 메모리의 어딘가에 위치한 기계어 코드의 몸체로서 절대 주소와 관계 없이 적절히 실행된다. PIC는 주로 공유 라이브러리에 사용돼서, 같은 라이브러리 코드도 (사용되는 메모리 공간에 겹쳐써지지 않으면서) 각 프로그램의 주소 공간에 로드될 수 있게 된다. PIC는 또한 메모리 관리 장치(MMU)가 없는 오래된 컴퓨터 시스템에서도 사용되며, 운영 체제가 애플리케이션을 단일 주소 공간 내에서 서로 떨어져 있게 해줄 수 있다.
위치 독립 코드는 수정 없이 어느 메모리 주소에서도 실행될 수 있다. 이것은 재배치 가능 코드와의 다른 점인데, 재배치 가능 코드는 링커 또는 로더가 실행 전에 프로그램을 수정해서 단지 특정한 메모리 위치에서만 실행된다. 위치 독립 코드를 생성하는 것은 종종 컴파일러의 기본 옵션이지만, 컴파일러는 절대 주소의 사용을 불허하는 것 같이 몇몇 언어들에서의 사용을 제한한다(위치 독립 코드는 상대 주소를 사용해야 한다). 비록 현대 프로세서들은 실질적으로 무시해도 될 만한 차이점을 보이지만, 직접적으로 특정한 메모리 주소들을 참조하는 명령어들은 동등한 상대 주소 명령어들보다 가끔은 빨리 실행되는 특징을 갖는다.
역사.
초기의 컴퓨터들에서 코드는 위치 의존적이었다: 각 프로그램은 로드되기 위해 빌트되었고 특정한 주소에서 실행되었다. 동시에 독립된 프로그램들을 사용하는 여러 작업들을 실행하기 위해서 작업들은 어느 두 작업들도 같은 주소에 위치해서는 안되었다. 예를 들면 주소 32K에서 실행되게 빌트된 급여 프로그램과 회계 프로그램 모두는 동시에 실행될 수 없다. 가끔은 프로그램의 여러 버전들은 각각 다른 로드 주소에 빌트된다. 이것은 IBM DOS/360(1966)에서의 상황이다.
이 상황을 개선할 수 있는 것은 이들이 메모리에 로드될 때 실행 가능 프로그램들을 재배치하는 능력이다. 단지 프로그램의 한 복사본만 요구되지만, 한 번 로드되면 프로그램은 움직일 수 없다. IBM OS/360 (1966) 프로그램들은 이 모델이었다.
멀틱스 (1964) 같은 세그먼트된 시스템들에서는, 프로그램의 주소들이 현재 세그먼트에 상대적이기 때문에 코드는 본질적으로 위치 독립적이다.
위치 독립 코드는 비 세그먼트 시스템들을 위해 이러한 제한들을 제거하기 위해 개발되었다. 위치 독립 프로그램은 메모리의 어느 주소에도 로드될 수 있다.
모든 프로세스가 자신의 독립적인 주소 공간을 가질 수 있기 때문에, 동적 주소 변환의 발명(MMU에 의해 제공되는 기능인)은 근본적으로 위치 독립 코드의 필요를 줄였다. 그러나 같은 코드의 동시 다중적인 작업들은 가상 공간의 낭비를 만들었다. 만약 두 작업들이 전체적으로 똑같은 프로그램이라면, 동적 주소 변환은 시스템이 간단하게 두 다른 작업의 주소 32K를 실제 메모리의 같은 메모리로 매핑하는(즉, 프로그램의 단일 사본만을 포함하는) 해결책을 제공한다.
다른 프로그램들이 같은 코드를 공유할 수 있다. 예를 들면 급여 프로그램과 회계 프로그램은 모두 똑같은 서브루틴들을 포함할 수 있다. 공유된 모듈(공유 라이브러리는 공유된 모듈의 한 형태이다)은 한 번 로드되며 두 주소 공간들에 매핑된다.
기술적 사항들.
공유 라이브러리 내에 위치한 프로시저 호출들은 일반적으로 작은 프로시저 링크 테이블 스텁을 통해 만들어지며, 이후 최종적인 함수를 호출하게 된다. 이것은 공유 라이브러리가 자신의 것을 사용하는 대신에 이전에 로드된 라이브러리에서 특정한 함수를 호출할 수 있게 해준다.
위치 독립 코드에서 데이터 참조는 일반적으로 모든 접근되는 전역 변수들의 주소들을 저장하는 전역 오프셋 테이블들(GOTs)을 통해서 만들어진다. 컴파일 유닛 또는 객체 모듈 당 하나의 GOT가 존재하며, 코드에서 고정된 오프셋에 위치한다(비록 이 오프셋이 라이브러리가 링크될 때까지 알려지지 않더라도). 링커가 공유 라이브러리를 생성하기 위해 모듈을 링크하면, 이것은 GOT들을 합병하고 코드에서 최종 오프셋들을 설정한다. 이후에 공유 라이브러리를 로딩할 때 오프셋들을 조정하는 것은 필요치 않게 된다.
전역 변수에 접근하는 위치 독립 함수들은 주어진 그들의 현재 프로그램 카운터 값을 통해 GOT의 절대 주소를 결정함으로써 시작한다. 이것은 종종 허위 함수 호출의 형태를 갖는데 이것은 스택(x86)에서 또는 특별한 레지스터(파워PC, SPARC, MIPS 등)에서 반환 값을 얻기 위해서이며, 이후 미리 정의된 표준 레지스터에 저장될 수 있다. 몇몇 프로세서 아키텍처(모토로라 68000, Motorola 6809, WDC 65C816, Knuth's MMIX, ARM 그리고 x86-64)는 프로그램 카운터의 오프셋을 통해 데이터를 참조한다. 이것은 특히 위치 독립 코드를 더 작게 그리고 레지스터 요구를 줄여서 더 효율적으로 하기 위한 것이다.
윈도우 DLL.
마이크로소프트 윈도우의 동적 링크 라이브러리들(DLLs)은 주어진 DLL이나 실행 파일 내부의 루틴들 사이에 링킹을 위한 전역 오프셋 테이블을 사용하지 않으며, 일반적으로 위치 독립 코드를 사용하지 않는다. 결과적으로 그 루틴들은 이전에 로드된 DLL들에 의해 겹쳐써지지않으며, 코드는 메모리에 로드된 이후에 운영 체제에 의해서 재배치되어야 한다.
윈도우 비스타 이후의 버전에서, DLL과 실행 파일들의 재배치는 일명 커널 메모리 매니저에 의해 수행되는데, 이것은 여러 프로세스들 사이에 재배치된 바이너리들을 공유한다. 이미지들은 항상 그들의 선호되는 베이스 주소에서 재배치되면서 주소 공간 배치 난수화(ASLR)를 달성한다.
윈도우 비스타 이전 버전들은 링크 타임 시에 이미지들의 런타임 재배치를 피하기 위해서, 고정된 주소에서 미리 매핑될 수 있도록 시스템 DLL들을 요구한다. 이러한 오래된 버전들에서 런타임 재배치는 각 프로세스의 문맥 내에서 DLL 로더에 의해서 수행되었으며, 각 이미지의 재배치된 결과는 더 이상 프로세스들 사이에서 공유될 수 없다.
위치 독립 실행 파일.
위치 독립 실행 파일(PIE: Position-independent executables)은 전체가 위치 독립 코드로 이루어진 실행 가능한 바이너리이다. 몇몇 시스템들이 오직 PIC 실행 파일만 실행할 수 있다는 것 외에도 이것들이 사용되는 다른 이유들이 존재한다. PIE 바이너리는 몇몇 보안 중심 리눅스 배포판들에서 PaX나 Exec Shield가 주소 공간 배치 난수화를 사용할 수 있도록 해서, (Return-to-libc 공격 같이 바이너리에서 실행 가능한 코드의 오프셋을 아는 것에 의지하는) 익스플로잇을 사용하는 공격 동안에 공격자가 보안 공격 시에 어디에 실행 가능한 코드가 존재하는지를 알 수 없게 한다.
애플의 맥 OS X와 iOS는 각각 버전 10.7과 4.3에서 완전하게 PIE 실행 파일을 지원한다. 비-PIE iOS 실행 파일들이 애플의 앱 스토어에 제출될 시에는 경고가 생기지만 아직까지는 비-PIE 애플리케이션들이 완전하게 거부되지는 않는다.
OpenBSD는 2013년 5월 1일 이후로 릴리즈된 5.3 이후로 대부분의 아키텍처에서 기본값으로 PIE를 활성화 시킨다. Support for PIE in statically linked binaries, such as the executables in codice_1 와 codice_2 디렉토리에 위치한 실행 파일들 같은 정적 링크 바이너리들에서의 PIE 지원은 2014년이 끝나갈 즈음에 추가되었다. 페도라 23부터 시작해서, 페도라도 PIE 활성화를 기본값으로 한 빌드 패키지를 제공한다.