2011-07-26 3 views
19

저는 학교 프로젝트에서 오래된 코드를보고 있는데, 노트북에서 컴파일하려고 할 때 몇 가지 문제가있었습니다. 원래 gcc의 32 비트 버전 용으로 작성되었습니다. 어쨌든 어셈블리의 일부를 64 비트 호환 코드로 변환하려고 시도하고 몇 가지 문제가 발생했습니다.인터럽트 서비스 루틴에 대한 x86_64의 레지스터를 저장하는 방법은 무엇입니까?

pusha 
pushl %ds 
pushl %es 
pushl %fs 
pushl %gs 
pushl %ss 

pusha 64 비트 모드에서 유효하지 :

여기서 일본어 코드이다. 그렇다면 64 비트 모드에서 x86_64 어셈블리에서이 작업을 수행하는 올바른 방법은 무엇입니까?

pusha이 64 비트 모드에서 유효하지 않은 이유가있어서 수동으로 모든 레지스터를 밀어 넣는 것이 좋은 생각이 아닐 수 있습니다.

답변

7

이런 종류의 일을하는 기존 코드에 대해 알아보십시오. 예를 들어 :

(유사 NetBSD에서의 vector.S입니다)

PUSHA이 존재하지 않기 때문에 실제로 regs를 "수동으로 푸시"하는 것이 AMD64에서 유일한 방법입니다. AMD64는이 측면에서 고유하지 않습니다. 대부분의 x86이 아닌 CPU는 어느 시점에서 레지스터 별 저장/복원이 필요합니다.

그러나 참조 된 소스 코드를 면밀히 살펴보면 모든 인터럽트 처리기가 전체 레지스터 세트를 저장/복원해야하는 것은 아니므로 최적화 할 여지가 있음을 알 수 있습니다.

+0

[wiki 기사] (http://en.wikipedia.org/wiki/X86-64)에서 x86-64는 x86과 하위 호환이 가능합니다. 명령을 제거해도 호환성이 깨지지 않습니까? –

+0

@Ciro Santilli 六 四 事件 法輪 호환성 모드가 켜져 있어야하며, 그렇지 않으면 이전 버전과 호환되지 않습니다. –

3

pusha은 중복되어 있기 때문에 64 비트 모드에서는 유효하지 않습니다. 개별적으로 각 레지스터를 밀어 넣는 것은 정확하게해야 할 일입니다.

+4

또한 세그먼트 레지스터를 64 비트 모드로 푸시 할 수 없습니다. 먼저 다른 레지스터로 복사해야합니다. 'mov % ds, % eax; 푸시 % rax'. – ughoavgfhw

+0

@ughoavgfhw 세그먼트 레지스터는 긴 모드에서 의미있는 값을 보유하지 않습니다. 모든 세그먼트는베이스가 0이고 제한이 없으며 FS 및 GS는 제외됩니다. 기본 주소는 MSR 0xC0000100 및 0xC0000101에 의해 제어되며 편리한 스레드 로컬 저장소 포인터로 사용됩니다. – doug65536

+1

@ doug65536 때때로 이들을 보존해야하는 경우가 있습니다. 예를 들어, 32 비트 프로그램을 실행하는 64 비트 OS는 세그먼트 화가 해당 프로그램에 사용되므로 인터럽트를 얻으면 세그먼트 레지스터를 저장해야합니다. – ughoavgfhw

15

AMD는 REX 접두어에 새로운 opcode를 추가하고 64 비트 x86 확장을 개발할 때 새로운 지침을 추가해야했습니다. 그들은 일부 연산 코드의 의미를 새로운 명령어로 변경했습니다.

지침 중 일부는 기존 지침의 단순한 형식이거나 그렇지 않으면 필요하지 않았습니다. PUSHA이 희생자 중 하나였습니다. 그것은 왜 그들이 PUSHA을 금지했는지 명확하지 않지만, 새로운 명령어 opcode와 겹치지 않는 것 같습니다. 아마도 그들은 완전히 중복되어 더 빠르지 않고 문제가되는 코드에서 자주 발생하지 않기 때문에 이후 사용을 위해 PUSHAPOPA opcode가 예약되어있을 수 있습니다. eax, ecx, edx, ebx, esp, ebp, esi, edi :

PUSHA 순서

는 명령어 인코딩의 순서였다. 중복하여 esp을 밀어 넣었습니다. 푸시 한 데이터를 찾으려면 esp을 알아야합니다!

코드를 변환 할 때 PUSHA 코드가 좋지 않으면 새 레지스터 r8을 통해 r15을 푸시하도록 코드를 업데이트해야합니다.훨씬 더 큰 SSE 상태 인 xmm8부터 xmm15까지 저장하고 복원해야합니다. 당신이 그들을 clobber 것이라고 가정합니다.

인터럽트 처리기 코드가 단순히 C 코드로 전달되는 스텁 일 경우 모든 레지스터를 저장할 필요가 없습니다. C 컴파일러가 rbx, rbp, rsi, rdir12을 통해 r15을 유지하는 코드를 생성한다고 가정 할 수 있습니다. rax, rcx, rdxr8부터 r11까지만 저장하고 복원하면됩니다. - (주 리눅스 나 다른 시스템 V ABI 플랫폼에서 컴파일러는 rbx, rbp, r12 보존됩니다 r15을, 당신은 rsi 및 사방 rdi을 예상 할 수있다).

세그먼트 레지스터는 긴 모드에서 값을 유지하지 않습니다. 인터럽트 된 스레드가 32 비트 호환 모드에서 실행 중이면 세그먼트 레지스터를 보존해야합니다 (감사합니다 ughoavgfhw). 사실, 그들은 긴 모드에서 대부분의 세분화를 없애지 만, FS은 여전히 ​​운영 체제가 스레드 로컬 데이터의 기본 주소로 사용하도록 예약되어 있습니다. 레지스터 값 자체는 중요하지 않으므로 FSGS의 밑수는 MSR 0xC00001000xC0000101을 통해 설정됩니다. FS을 사용하지 않는다고 가정하면 걱정할 필요가 없습니다. C 코드로 액세스되는 모든 스레드 로컬 데이터는 임의 스레드의 TLS를 사용할 수 있습니다. C 런타임 라이브러리는 일부 기능에 TLS를 사용하기 때문에주의하십시오 (예 : strtok은 일반적으로 TLS를 사용합니다).

FS 또는 GS (사용자 모드에서도) 값을로드하면 FSBASE 또는 GSBASE MSR을 덮어 씁니다. 일부 운영 체제는 GS을 "프로세서 로컬"저장소로 사용하기 때문에 (사용자가 각 CPU의 구조에 대한 포인터를 가질 수있는 방법이 필요합니다) 사용자 모드에서 GS을로드하여 어쩔 수없이 어딘가에 보관해야합니다. 이 문제를 해결하기 위해 GSBASE 레지스터에 예약 된 2 개의 MSR이 있습니다 : 활성 1 개와 숨겨진 1 개. 커널 모드에서 커널의 GSBASE은 보통 GSBASE MSR에 있고 사용자 모드는 다른 (숨겨진) GSBASE MSR에 있습니다. 커널 모드에서 사용자 모드 컨텍스트로 컨텍스트를 전환 할 때 및 사용자 모드 컨텍스트를 저장하고 커널 모드로 전환 할 때 컨텍스트 스위치 코드는 표시되고 숨겨진 GSBASE MSR의 값을 바꿔주는 SWAPGS 명령을 실행해야합니다. 커널의 GSBASE은 사용자 모드에서 다른 MSR에 안전하게 숨겨져 있기 때문에 사용자 모드 코드는 GS에 값을로드하여 커널의 GSBASE을 훼손 할 수 없습니다. CPU가 커널 모드로 다시 들어가면 컨텍스트 저장 코드는 SWAPGS을 실행하고 커널의 GSBASE을 복원합니다.

0

안녕 그것을 할 수있는 올바른 방법을하지 않을 수 있지만, 하나

할 필요가있는 경우 하나는 다른 r8-15 레지스터를 추가 결국

.macro pushaq 
    push %rax 
    push %rcx 
    push %rdx 
    push %rbx 
    push %rbp 
    push %rsi 
    push %rdi 
.endm # pushaq 

.macro popaq 
    pop %rdi  
    pop %rsi  
    pop %rbp  
    pop %rbx  
    pop %rdx  
    pop %rcx 
    pop %rax 
.endm # popaq 

과 같은 매크로를 만들 수 있습니다

관련 문제