IT

Windows64가 x86-64의 다른 모든 OS와 다른 호출 규칙을 사용하는 이유는 무엇입니까?

lottoking 2020. 8. 28. 19:37
반응형

Windows64가 x86-64의 다른 모든 OS와 다른 호출 규칙을 사용하는 이유는 무엇입니까?


AMD는 x86-64에서 사용할 수있는 호출 규칙을 설명하는 ABI 사양이 있습니다. 자체 x86-64 호출 규칙이있는 Windows를 제외하고 모든 OS 가이를 사용합니다. 왜?

이 차이에 대한 서술, 또는 사용자가 아는 사람이 있습니까? 아니면 순전히 NIHsyndrome의 문제입니까?

나는 OS마다 더 높은 요구 사항이 다를 수 있음을 이해하지만, 예를 들어 Windows에서 전달 순서가 rcx - rdx - r8 - r9 - rest on stack다른 모든 사람들이 전달 rdi - rsi - rdx - rcx - r8 - r9 - rest on stack합니다.

추신 : 이러한 호출 규칙이 일반적으로 어떻게 다른지 알고 있으며 필요한 경우 세부 정보를 찾을 수있는 위치를 알고 있습니다. 내가 알고 싶은 것은 이유 입니다.

편집 : 방법은 wikipedia 항목 과 링크를 참조하십시오 .


x64에서 4 개의 인수 비교 선택 -UN * X / Win64에 공통점

x86에 대해 기억해야 할 사항 중 하나는 "등록 번호"를 명확하게하지 않습니다. 명령어 인코딩 측면에서 ( MOD R / M 바이트, http://www.c-jump.com/CIS77/CPU/x86/X77_0060_mod_reg_r_m_byte.htm 참조 ) 레지스터 번호 0 ... 7은 - 순서대로 - ?AX, ?CX, ?DX, ?BX, ?SP, ?BP, ?SI, ?DI.

따라서 반환 값으로 A / C / D (regs 0..2)를 선택하고 처음 두 인수 ( "고전적인"32 비트 __fastcall규칙)를 선택하는 것은 어디에서 선택합니다. 64 비트로가는 장치에서 "상위"regs가 주문되고 Microsoft와 UN * X / Linux 다 첫 번째로 R8/ R9를 사용했습니다.

염두에 유지, 마이크로 소프트의 선택 RAX(반환 값) 및 RCX, RDX, R8, R9(인수 [0..3]) 선택하면 이해할를 수있는 선택입니다 사 개 인수 레지스터.

AMD64 UN * X ABI가 RDX이전에 선택한 이유를 모르겠습니다 RCX.

64에서 6 개의 인수 레지스터 선택 -UN * X 특정

RISC 아키텍처에서 UN * X는 전통적으로 인수 전달을 수행했습니다. 특히 처음 6 개의 인수 (최소한 PPC, SPARC, MIPS에서 그렇습니다)에 대해 설명합니다. AMD64 (UN * X) ABI 디자이너가 해당 아키텍처 6 개의 유명한 이유를 사용하기로 선택한 주요 중 하나 일 수 있습니다.

당신이 원하는 그래서 경우 여섯 개 에 인수를 전달하는 레지스터, 그리고 그것을 선택하는 논리이다 RCX, RDX, R8그리고 R9그들 중 네, 당신은 두 개의 다른 어떤을 선택 해야합니까 ?

"높은"regs는 선택하기 위해 추가 급 접두사 바이트가 필요하기 크기가 더 커지 옵션이있는 경우 선택하고 싶지 않을 것입니다. 에 고전 레지스터 때문에의 암시 의 의미 RBPRSP이 사용할 수 없으며, RBX전통적으로 겉으로 AMD64 ABI 디자이너가 불필요하게 호환되고 싶지 않았다 UN * X (전역 옵셋 테이블)에 대한 특별 사용이 있습니다.
Ergo, 유일한 선택RSI/ RDI.

따라서 RSI/ RDI를 인수로 가져와야한다면 어떤 인수를 인수합니까?

필요한 arg[0]arg[1]가지 장점이 있습니다. cHao의 의견을 참조하십시오.
?SI그리고 ?DI문자열 명령어 소스 / 대상 피연산자이며 차오가 언급했듯이 인수 레지스터로 사용한다는 것은 AMD64 UN * X 규칙에서 추론 호출 간단한 strcpy()함수가 예를 들어 repz movsb; ret소스 / 대상 이 두 개의 CPU로만 구성 명령어 된다는 것을 의미합니다. 호출하는 주소를 올바른 방법에 있습니다. 저수준 및 컴파일러 생성 "접착제"코드에 있습니다 (예를 들어 일부 C ++ 힙 할당자는 개체를 0으로 채우거나 특히 힙 페이지를 0으로 채우는 경우). sbrk(), 또는 쓰기시 복사 페이지 오류) 막대한 양의 블록 복사 / 채우기를 수행 할 때 수행 할 소스 / 대상 주소 "올바른"레지스터.

따라서 UN * X와 Win64는 UN * X가 의도적으로 인수 RSI/ RDI예약 에서 두 개의 추가 인수 RCXRDX, R8에서 4 개의 인수를 자연스럽게 선택하는 "앞에 추가"점에서만 R9.

그 너머 ...

UN * X와 Windows x64 ABI 사이에는 인수를 많은 많은에 매핑하는 것보다 더 차이점이 있습니다. Win64에 대한 개요는 다음을 확인하십시오.

http://msdn.microsoft.com/en-us/library/7kcdt6fy.aspx

Win64 및 AMD64 UN * X는 스택이 사용되는 방식으로 많이 사용됩니다. 예를 들어 Win64에서 호출자 인수 0 ... 3이 전달되는 전달 함수 인수에 대한 스택 공간을 해야합니다 . 반면에 UN * X에서는 리프 함수 (즉, 다른 함수를 호출하지 않는 함수)가 128 바이트 이하의 스택 스페이스를 할당 할 필요조차 없습니다 (예, 소유하고 사용할 수 있습니다. 할당하지 않고 일정량의 스택 ... 음, 코드가 아니라면 멋진 버그의 원인). 이 모든 것은 특정 특정 최적화 선택이며, 그에 대한 대부분의 근거는 원본 포스터의 위키 백과 참조가 전체 ABI 참조에 설명되어 있습니다.


Windows가 그들이 한 일을 한 이유 IDK. 추측에 답변이 답변의 끝을 참조하십시오. SysV 호출 규칙이 어떻게 결정 하는지 궁금 해서 메일 링리스트 아카이브를 파헤쳐 서 깔끔한 것을 찾았습니다.

AMD 아키텍트가 활발하게 활동하고 있었기 때문에 AMD64 메일 링리스트에있는 오래된 쓰레드를 읽는 것은 흥미 롭습니다. 예를 들어 있는지 이름을 선택하는 것은 어려운 부분 중 하나였습니다. AMD 는 예정된 8 예정인 r0-r7의 이름을 바꾸거나UAX .

또한, 원래의 설계했다 확인 된 것들 DEVS syscallswapgs사용할 수 있습니다. 이것이 AMD가 실제-chip을 출시하기 전에 이것을 분류하기 위해 지침업데이트 한 방법 입니다. 2000 년 후반 인텔이 AMD64를 채택하지 않을 가정도 흥미 롭습니다.


SysV (Linux) 호출 규칙 및 호출자 보존 대 호출자 저장에 대한 예측 수에 대한 결정은 2000 년 11 월 Jan Hubicka (gcc 개발자)에 처음에 이루어졌습니다 . 그는 SPEC2000 컴파일을 하고 코드 크기와 명령어 수를 조사했습니다. 그 토론 연설은이 SO 질문에 대한 답변 및 의견과 아이디어 중 일부를 중심으로 튀어 나옵니다. 두 번째 인스턴스 생성에서 현재 시퀀스를 최적이며 최종적으로 제안하여 대안보다 작은 코드를 제안 합니다.

그는 "글로벌"이라는 용어를 사용하여 푸시 / 팝해야하는 보존 호출을 의미합니다.

의 선택 rdi, rsi, rdx처음 세 인수에 의해 동기가되었을 때 :

  • memset인수에 대한 다른 C 많은 함수를 호출하는 함수에서 약간의 절약 합니다 (여기서 gcc는 rep 작업을 인라인하고 있습니까?).
  • rbxREX 접두사 (rbx 및 rbp)없이 액세스 할 수있는 두 개의 호출 보존 된 reg를 사용하는 것이 승리이기 때문에 호출 보존됩니다. 어떤 명령에서도 거의 사용되지 않는 것입니다. (rep string, shift count, mul / div outputs / inputs touch all other all).
  • 특수 목적의 예언은 호출 보존되지 않은 것 (이전 포인트 참조) reps "또는" "" "" "" "" "" "" "" "" " 발신자의 가치를 회복하십시오.
  • RCX는 EAX와 같은 특수한 용도로 일반적으로 사용되는 레지스터이므로 시퀀스에서 누락되는 것과 동일한 목적을 가지고 있으므로 시퀀스 초기에 RCX를 피하려고합니다. 또한 syscall에 사용할 수 없으며 가능한 한 함수 호출 시퀀스와 일치하도록 syscall 시퀀스를 만들고 싶습니다.

    (배경 : syscall/ sysret불가피하게 rcx(with rip) 및 r11(with RFLAGS) 파괴 하므로 커널이 실행 rcx되었을 때 원래 있던 내용을 볼 수 없습니다 syscall.)

커널 시스템 호출 ABI는 r10대신을 제외하고 함수 호출 ABI와 일치하도록 선택 rcx되었으므로 libc 래퍼 mmap(2)mov %rcx, %r10/ mov $0x9, %eax/ 와 같은 기능을 수행 할 수 있습니다 syscall.


i386 Linux에서 사용하는 SysV 호출 규칙은 Window의 32 비트 __vectorcall에 비해 짜증이납니다. 스택의 모든 것을 전달 edx:eax하고 작은 구조체가 아닌 int64에 대해서만 반환 합니다 . 호환성을 유지하기 위해 약간의 노력을 기울인 것은 놀라운 일이 아닙니다. 그렇게하지 않을 이유가 없을 때, 그들은 rbx원래 8 (REX 접두사가 필요하지 않음)에 다른 것을 갖는 것이 좋다고 결정했기 때문에 통화 보존을 유지하는 것과 같은 일 을했습니다.

ABI를 최적으로 만드는 것은 다른 고려 사항보다 장기적으로 훨씬 더 중요합니다. 나는 그들이 꽤 잘했다고 생각합니다. 다른 regs의 다른 필드 대신 레지스터에 압축 된 구조체를 반환하는 것에 대해 완전히 확신하지 못합니다. 필드에서 실제로 작동하지 않고 값으로 전달하는 코드가이 방법으로이기는 것 같지만, 압축을 푸는 추가 작업은 어리석은 것 같습니다. 그것들은 단지보다 더 많은 정수 리턴 레지스터를 가질 수 있었기 rdx:rax때문에 4 개의 멤버가있는 구조체를 리턴하면 rdi, rsi, rdx, rax 등으로 리턴 될 수 있습니다.

SSE2는 정수에서 작동 할 수 있기 때문에 벡터 regs에서 정수 전달을 고려했습니다. 다행히 그들은 그렇게하지 않았습니다. 정수는 포인터 오프셋으로 자주 사용되며 스택 메모리로의 왕복 비용은 매우 저렴 합니다. 또한 SSE2 명령어는 정수 명령어보다 더 많은 코드 바이트를 사용합니다.


나는 Windows ABI 디자이너가 asm을 한 곳에서 다른 곳으로 포팅해야하는 사람들의 이익을 위해 32 비트와 64 비트 사이의 차이를 최소화하는 것을 목표로했을 수도 #ifdef있고, 같은 소스를 더 쉽게 구축 할 수 있도록 일부 ASM에서 s를 사용할 수 있다고 생각합니다. 32 비트 또는 64 비트 버전의 함수.

툴체인의 변경을 최소화하는 것은 불가능 해 보입니다. x86-64 컴파일러에는 레지스터가 무엇에 사용되며 호출 규칙이 무엇인지에 대한 별도의 테이블이 필요합니다. 32 비트와 약간 겹치는 것은 도구 체인 코드 크기 / 복잡성을 크게 절감 할 수 없습니다.


Win32는 ESI 및 EDI를 자체적으로 사용하며 수정하지 않아야합니다 (또는 최소한 API를 호출하기 전에 복원해야 함). 64 비트 코드가 RSI 및 RDI에서 동일한 작업을 수행한다고 생각합니다. 따라서 함수 인수를 전달하는 데 사용되지 않는 이유가 설명됩니다.

그래도 RCX와 RDX가 왜 바뀌 었는지 말할 수 없었습니다.


Microsoft는 IA64 아키텍처에서 Intel과 강력한 파트너 였기 때문에 처음에는 "초기 AMD64 노력에 대해 공식적으로 헌신하지 않았습니다"( Matthew Kerner와 Neil Padgett의 "현대 64 비트 컴퓨팅의 역사" 에서 )를 기억하십시오. 이것은 그들이 ABI에서 GCC 엔지니어들과 함께 Unix와 Windows에서 모두 사용하기 위해 열려 있더라도 그렇게하지 않았을 것임을 의미한다고 생각합니다. 그렇지 않았을 때 AMD64 노력을 공개적으로 지원한다는 의미이기 때문입니다. 아직 공식적으로 그렇게했습니다 (그리고 아마도 인텔을 화나게했을 것입니다).

게다가 그 당시 마이크로 소프트는 오픈 소스 프로젝트에 대해 전혀 관심이 없었습니다. 확실히 Linux 나 GCC는 아닙니다.

그렇다면 왜 그들은 ABI에 협력했을까요? 나는 ABI가 거의 동시에 그리고 분리되어 설계 되었기 때문에 단순히 다르다고 생각합니다.

"현대 64 비트 컴퓨팅의 역사"의 또 다른 인용문 :

마이크로 소프트 협업과 병행하여 AMD는 칩을 준비하기 위해 오픈 소스 커뮤니티에 참여했습니다. AMD는 도구 체인 작업을 위해 Code Sorcery 및 SuSE와 계약했습니다 (Red Hat은 이미 IA64 도구 체인 포트에서 Intel과 계약을 맺었습니다). Russell은 SuSE가 C 및 FORTRAN 컴파일러를 생산하고 Code Sorcery가 Pascal 컴파일러를 생산했다고 설명했습니다. Weber는 회사가 Linux 포트를 준비하기 위해 Linux 커뮤니티와도 협력했다고 설명했습니다. 이 노력은 매우 중요했습니다. Microsoft가 AMD64 Windows 노력에 계속 투자 할 수있는 인센티브 역할을했으며, 당시 중요한 OS가되었던 Linux가 칩이 출시되면 사용할 수 있도록 보장했습니다.

Weber는 Linux 작업이 AMD64의 성공에 절대적으로 중요하다고 말하는데, 이는 AMD가 필요한 경우 다른 회사의 도움없이 엔드-투-엔드 시스템을 생산할 수있게했기 때문입니다. 이 가능성은 다른 파트너가 물러나더라도 AMD가 최악의 생존 전략을 가지고 있음을 보장했으며, 결국 다른 파트너는 자신에게 뒤쳐 질 까봐 두려워서 계속 참여했습니다.

이것은 AMD조차도 MS와 Unix 간의 협력이 반드시 가장 중요하다고 생각하지 않았지만 Unix / Linux 지원이 매우 중요하다는 것을 나타냅니다. 타협하거나 협력하도록 한쪽 또는 양쪽을 설득하려는 시도조차도 그들 중 하나를 짜증나게하는 노력이나 위험 (?)의 가치가 없었을까요? 아마도 AMD는 공통 ABI를 제안하는 것조차 단순히 칩이 준비되었을 때 소프트웨어 지원을 준비하는 더 중요한 목표를 지연 시키거나 탈선시킬 수 있다고 생각했을 것입니다.

내 생각에는 추측이지만 ABI가 다른 주된 이유는 MS와 유닉스 / 리눅스 측이 함께 작동하지 않았고 AMD가 문제로 보지 않은 정치적 이유 때문이라고 생각합니다.

참고 URL : https://stackoverflow.com/questions/4429398/why-does-windows64-use-a-different-calling-convention-from-all-other-oses-on-x86

반응형