2017-04-12 15 views
4

64 비트 Linux는 기본적으로 작은 메모리 모델을 사용하므로 모든 코드와 정적 데이터를 2GB 주소 한도 이하로 유지합니다. 이렇게하면 32 비트 절대 주소를 사용할 수 있습니다. 이전 버전의 gcc는 상대 주소 계산을위한 추가 명령어를 저장하기 위해 정적 배열에 32 비트 절대 주소를 사용합니다. 그러나 이것은 더 이상 작동하지 않습니다. 어셈블리에서 32 비트 절대 주소를 만들려고하면 링커 오류가 발생합니다. "공유 객체를 만들 때`.data '에 대한 재배치 R_X86_64_32S를 사용할 수 없으며 -fPIC로 다시 컴파일하십시오. 공유 객체를 만들지 않아서 -fPIC이 도움이되지 않기 때문에이 오류 메시지는 오해의 소지가 있습니다. 지금까지 내가 알아 낸 점은 gcc 버전 4.8.5는 정적 배열에 32 비트 절대 주소를 사용하고 gcc 버전 6.3.0은 사용하지 않는다는 것입니다. 버전 5는 아마도 그렇지 않습니다. binutils 2.24의 링커는 32 비트 절대 주소를 허용하고, 2.28 절은 그렇지 않습니다.x86-64 Linux에서 32 비트 절대 주소가 더 이상 허용되지 않습니까?

이 변경으로 인해 오래된 라이브러리를 다시 컴파일해야하고 기존 어셈블리 코드가 손상 될 수 있습니다.

이제 저는 묻고 싶습니다. 언제이 변경이 이루어 졌습니까? 그것은 어딘가에 문서화되어 있습니까? 32 비트 절대 주소를 허용하는 링커 옵션이 있습니까?

답변

7

배포판에 gcc가 --enable-default-pie으로 구성되어 있으므로 기본적으로 위치 독립적 실행 파일을 만들고 있습니다 (실행 파일과 라이브러리의 ASLR 허용). 해당 재배치는 PIC/PIE에서 허용되지 않습니다.

gcc -fno-pie -no-pie을 사용하면이 동작을 이전 동작으로 다시 정의 할 수 있습니다.-no-pie은 링커 옵션 인 -fno-pie is the code-gen option입니다. -fno-pie 만 있으면 gcc는 여전히 -pie과 연결되지 않는 mov eax, offset .LC0과 같은 코드를 만듭니다.


:

(A July 2017 patch는 GCC와의 compat를 들어, -nopie에 대한 -no-pie 별칭을 만들어 clang -fno-pie -nopie,하지만 clang4.0.1는 없습니다. 그 소리는 PIE도 기본적으로 활성화 할 수 있습니다.)

-no-pie 만 있으면 (여전히 -fpie) 컴파일러 생성 코드는보다 느리지 만 ASLR의 혜택을받지 않는 위치 종속적 실행 파일로 연결됩니다. 패널티는 주로 정적 저장소 클래스가있는 파일 범위 변수 static 대신 전역에 액세스 할 때 발생합니다. -fPIE은 64 비트 코드에서는 -fPIC보다 훨씬 나쁘지 만 32 비트에서는 여전히 나쁘다는 점에 유의하십시오. some examples on the Godbolt compiler explorer을 참조하십시오. The sorry state of dynamic libraries on Linux을 참조하십시오.

gcc가 이렇게 구성된 경우 gcc -v |& grep -o -e '[^ ]*pie'--enable-default-pie을 인쇄합니다. 이 구성 옵션에 대한 지원이 early 2015의 gcc에 추가되었습니다. 우분투는 16.10에서, 데비안은 gcc 6.2.0-7에서 (커널 빌드 오류 : https://lkml.org/lkml/2016/10/21/904) 같은 시간대에 사용 가능하게했습니다. 관련 항목 : Build compressed x86 kernels as PIE.


그 자체는 기본값을 변경하지 않았습니다. 여전히 정상적으로 작동합니다 (최소한 binutils 2.28을 사용하는 아치 리눅스에서). 변경 사항은 이 명시 적으로 -static 또는 -no-pie을 사용하지 않는 한 기본적으로 -pie을 링커 옵션으로 전달한다는 것입니다.

NASM 소스 파일에서 절대 주소를 얻으려면 a32 mov eax, [abs buf]을 사용했습니다. 관련

nasm -felf64 -Worphan-labels -g -Fdwarf testloop.asm && 
ld -o testloop testloop.o    # works 

gcc -v -nostdlib testloop.o   # doesn't work 
... 
..../collect2 ... -pie ... 
/usr/bin/ld: testloop.o: relocation R_X86_64_32 against `.bss' can not be used when making a shared object; recompile with -fPIC 
/usr/bin/ld: final link failed: Nonrepresentable section on output 
collect2: error: ld returned 1 exit status 

gcc -v -no-pie -nostdlib testloop.o # works 
gcc -v -static -nostdlib testloop.o # also works: -static implies -no-pie 

(67 a1 40 f1 60 00 인텔의 CPU에 LCP 스톨)했다 It does을 6 바이트 방법 작은 절대 주소 (주소 크기 + MOV의 EAX, moffs를 인코딩하는 경우 I 테스트 하였다..) building static/dynamic executables with/without libc, defining _start or main한다.


filereadelf은 파이가 아니라 실행 "객체를 공유"하는 것을 말한다.

$ gcc -fno-pie -no-pie -O3 hello.c 
$ file a.out 
a.out: ELF 64-bit LSB executable, ... 

$ gcc -O3 hello.c 
$ file a.out 
a.out: ELF 64-bit LSB shared object, ... 

세미는 관련 : 또 다른 최근의 GCC 기능은 gcc -fno-plt입니다. 마지막으로 공유 라이브러리에 대한 호출은 PLT 트램펄린이없는 call [rip + [email protected]] (AT & T call *[email protected](%rip)) 일 수 있습니다.

Distros는 쓰기가 가능한 + 실행 메모리 페이지가 필요 없기 때문에 조만간 지원을 시작할 것입니다. 라이브러리 공유 호출을 많이하는 프로그램의 경우 상당히 빠릅니다. x86-64 clang -O2 -g tramp3d 컴파일은 하드웨어에 관계없이 41.6s에서 36.8s로 변경됩니다. the patch author tested on. (clang은 공유 라이브러리 호출의 최악의 시나리오 일 수 있습니다.)

지연 연결 대신 초기 바인딩이 필요하므로 즉시 종료되는 큰 프로그램의 경우 속도가 느립니다. (예 : clang --version 또는 hello.c을 컴파일). 이 속도 저하는 사전 링크를 통해 줄일 수 있습니다.

그러나 PIC 코드의 외부 변수에 대한 GOT 오버 헤드는 제거되지 않습니다. (위의 godbolt 링크 참조).

+1

'-m32'의 경우 이것은 PC를 보관할 소중한 레지스터를 낭비하기 때문에 매우 나쁩니다. 내 64 비트 배포판에서도 플래그가 활성화되어 있으므로'-m32 '도이 플래그를 사용합니다. –