2017-03-25 1 views
7

Compilers: Understanding assembly code generated from small programs에서 컴파일러는 스택 포인터를 조정하지 않고 두 개의 로컬 변수를 사용합니다. 지역 변수의 사용을 RSP을 조정하지RSP를 조정하지 않고 로컬 변수를 사용하는 컴파일러

그래서 컴파일러는 인터럽트가 발생할 때 자동으로 시스템 스택으로 전환 하드웨어에 의존하는 것 같다 안전 방해하지 을 보인다. 그렇지 않으면 첫 번째 인터럽트는 명령 포인터를 스택으로 밀어 넣어 로컬 변수를 덮어 씁니다.

질문의 코드이다 :이 컴파일러에 의해 생성

#include <stdio.h> 

int main() 
{ 
    for(int i=0;i<10;i++){ 
     int k=0; 
    } 
} 

어셈블리 코드 :

00000000004004d6 <main>: 
    4004d6:  55      push rbp 
    4004d7:  48 89 e5    mov rbp,rsp 
    4004da:  c7 45 f8 00 00 00 00 mov DWORD PTR [rbp-0x8],0x0 
    4004e1:  eb 0b     jmp 4004ee <main+0x18> 
    4004e3:  c7 45 fc 00 00 00 00 mov DWORD PTR [rbp-0x4],0x0 
    4004ea:  83 45 f8 01    add DWORD PTR [rbp-0x8],0x1 
    4004ee:  83 7d f8 09    cmp DWORD PTR [rbp-0x8],0x9 
    4004f2:  7e ef     jle 4004e3 <main+0xd> 
    4004f4:  b8 00 00 00 00   mov eax,0x0 
    4004f9:  5d      pop rbp 
    4004fa:  c3      ret  

로컬 변수 [rbp-0x4]에서 [rbp-0x8]에서 ik이다.

누구든지이 인터럽트 문제에 불을 붙일 수 있습니까? 하드웨어가 실제로 시스템 스택으로 전환합니까? 방법? 내 이해가 틀렸어?

+0

아마도이 부분이 흥미로울 수 있습니다. http://stackoverflow.com/questions/28759227/which-stack-is-used-by-interrupt-handler-linux – 4386427

답변

8

이것은 x86-64 ABI의 소위 "빨간색 영역"입니다. wikipedia에서 요약 : 컴퓨팅

은 적색 영역은 기능에 의해 유지되지 않고, 현재의 스택 포인터를 넘어서 함수의 스택 프레임에서 고정 된 크기의 영역이다. 호출 수신자 함수는 스택 포인터을 수정하는 추가 오버 헤드없이 로컬 변수 을 저장하기 위해 빨간색 영역을 사용할 수 있습니다. 이 메모리 영역은 인터럽트/예외/신호 핸들러에 의해 수정되지 않아야합니다. System V에서 사용하는 x86-64 ABI는 스택 포인터의 현재 값 바로 아래에서 시작되는 128 바이트의 빨간색 영역을 요구합니다.

64 비트 Linux 사용자 코드에서는 128 바이트를 초과하지 않는 한 괜찮습니다. 그것은 잎 기능, 다른 함수를 호출하지 않는 즉, 함수에서 가장 눈에 띄게 사용 최적화, 당신이 GCC와 64 비트 리눅스 프로그램으로 예제 프로그램을 컴파일한다면


(또는 이 같은 코드를 볼하려는 -mno-red-zone 옵션을 사용하여 호환 컴파일러) 생성 :

main: 
     push rbp 
     mov  rbp, rsp 
     sub  rsp, 16;  <<============ Observe RSP is now being adjusted. 
     mov  DWORD PTR [rbp-4], 0 
.L3: 
     cmp  DWORD PTR [rbp-4], 9 
     jg  .L2 
     mov  DWORD PTR [rbp-8], 0 
     add  DWORD PTR [rbp-4], 1 
     jmp  .L3 
.L2: 
     mov  eax, 0 
     leave 
     ret 

이 코드 생성이 godbolt.org 링크에서 관찰 할 수있다.


32 비트 Linux 사용자 프로그램의 경우 스택 포인터를 조정하지 않는 것이 좋습니다.

main: 
     push ebp 
     mov  ebp, esp 
     sub  esp, 16;  <<============ Observe ESP is being adjusted. 
     mov  DWORD PTR [ebp-4], 0 
.L3: 
     cmp  DWORD PTR [ebp-4], 9 
     jg  .L2 
     mov  DWORD PTR [ebp-8], 0 
     add  DWORD PTR [ebp-4], 1 
     jmp  .L3 
.L2: 
     mov  eax, 0 
     leave 
     ret 

이 코드 생성이 gotbolt.org 링크에서 볼 수있다 : 당신이 (-m32 옵션을 사용하여) 32 비트 코드와 문제의 코드를 컴파일한다면 main 다음 코드 같은 것을 나타납니다.

+0

답해 주셔서 감사합니다.그것은 모든 것을 설명합니다. 따라서 인터럽트가 발생하면 하드웨어는 명령어 포인터를 스택 아래로 128 밀리므로 로컬 변수를 덮어 쓰지 않습니다. 함수가 로컬 저장소에 128 바이트 이상을 요구하면 스택 포인터를 조정해야합니다. –

+1

@Paul - [커널은 별도의 스택을 사용합니다] (http://stackoverflow.com/questions/15168822/intel-x86-vs-x64-system-call/15169141#15169141), 권한이 부여 된 인터럽트는 사용자 코드의 스택. –

+0

@Bo Persson, 스택이 인터럽트의 권한 수준에 따라 전환되는 것을 알고 있습니다. 따라서 동일한 권한 수준의 인터럽트는 현재 스택을 사용할 것이고 _and_는 처음 128 바이트를 사용해서는 안됩니다. 그 맞습니까? –