2017-12-06 75 views
1

저에게 문제가되는 질문이 하나 있습니다.왜 어셈블리 x86_64 syscall 매개 변수가 i386과 같은 알파벳 순서가 아닙니다.

그래서 ... x86_32 내 기분이 레지스터에 전달되는 매개 변수에 에있는 알파벳 (eax, ecx, edx, esi) 및 위해 위 왜 (esi, ediebp)

+---------+------+------+------+------+------+------+ 
| syscall | arg0 | arg1 | arg2 | arg3 | arg4 | arg5 | 
+---------+------+------+------+------+------+------+ 
| %eax | %ebx | %ecx | %edx | %esi | %edi | %ebp | 
+---------+------+------+------+------+------+------+ 

section .text 
    global _start 
_start: 
    mov eax, 1  ; x86_64 opcode for sys_exit 
    mov ebx, 0  ; first argument 
    int 0x80 

동안 x86_64에 에서 콜의 매개 변수를 통과하다 그들은 특정 이유로 그렇게

+---------+------+------+------+------+------+------+ 
| syscall | arg0 | arg1 | arg2 | arg3 | arg4 | arg5 | 
+---------+------+------+------+------+------+------+ 
| %rax | %rdi | %rsi | %rdx | %r10 | %r8 | %r9 | 
+---------+------+------+------+------+------+------+ 

section .text 
    global _start 
_start: 
    mov eax, 1  ; x86_64 opcode for sys_exit 
    mov edi, 0  ; first argument 
    syscall 

이나요 : 조금 보이는 레지스터 에드 무작위 배열 ? 내가 여기서 뭔가 못 보는거야?

+1

x86-64에서는 syscall 래퍼 함수가 경량이므로 함수 호출 규약과 일치합니다. i386에서는 IDK가 불편한 설정을 사용하는 이유는 무엇입니까 ('ebx'는 호출 보존이므로 거의 모든 syscall 래퍼가 ebx를 저장/복원해야합니다). –

+0

@PeterCordes 그리고 함수 호출 규칙은 다음과 같은 경우에 레지스터 셔플의 양을 줄 이도록 설계되었습니다. 'memcpy'를'rep movsb'와 함께 구현합니다. – fuz

+0

함수 호출 규칙? 어쩌면 나는 신참처럼 들릴지도 모르겠다. 어쩌면 나는 ...이기는하지만 함수 호출 규칙은 무엇일까요? 그게 어셈블리 일인가요? –

답변

3

x86-64 System V ABI는 최초 AMD64 CPU가 판매되기 전에 있었던 gcc 버전으로 컴파일 된 SPECint의 명령어 카운트 (그리고 어느 정도 코드 크기)를 최소화하도록 설계되었습니다. this answer for some history and list-archive links을 참조하십시오.

5 분 전에 모든 레지스터가 같았지만 규칙 때문에 다르게 사용되었습니다. 이제 모든 것이 나를 위해 변했습니다.

86-64는 완전히 직교하지 않습니다. 일부 명령어는 암시 적으로 특정 레지스터를 사용합니다. 예 : push은 암시 적으로 rsp을 스택 포인터로 사용하고 mul rdirdx:rax = rax*rdi입니다. shl edx, clcl (BMI2 shlx까지)의 시프트 수로 만 사용할 수 있습니다. rep-string 명령어는 암시 적으로 RDI, RSI 및 RCX를 사용합니다. (그리고 rep movs/rep stos 중간 대형 방어 적이기/memset 함수에 대한 예를 들어, 어떤 경우에 사용하여 실제로 가치가있다.)

그것은 ARG-통과 레지스터를 선택하는 것이 방어 적이기에 자신의 인수를 전달 기능을 rep movs로 인라인 할 수 있도록 밝혀 Jan Hubicka가 사용하고있는 메트릭스에서 유용했기 때문에 rdirsi이 처음 두 arg로 선택되었습니다. 그러나 rcx은 변수 카운트 이동에 cl이 필요하기 때문에 4 번째 인수가 좋을 때까지 사용되지 않습니다. (그리고 대부분의 기능은 전환 수로 자신의 세번째 인수를 사용하는 일이 없습니다.)


시스템 V ABI는 기능에 거의 동일한 호출 규칙을 사용하는 - 64을 시스템를 호출하는 것이 수행한다. 이것은 우연의 일치가 아니다 : 그것은 될 수 mmap 같은 libc의 래퍼 함수의 구현을 의미

mmap: 
    mov r10, rcx  ; syscall destroys rcx and r11; 4th arg passed in r10 for syscalls 
    mov eax, __NR_mmap 
    syscall 

    cmp rax, -4096 
    ja .set_errno_and_stuff 
    ret 

이 작은 장점이지만,이 작업을 수행 할 이유 하지 정말 없습니다. 또한 커널 내부에 시스템 호출의 C 구현에 디스패치하기 전에 arg-passing 레지스터를 설정하는 커널 내부에 몇 가지 명령어를 저장합니다. (시스템 호출 처리의 커널 측면을 살펴 보려면 this answer을 참조하십시오.대부분 int 0x80 핸들러에 대해,하지만 난 64 비트 syscall 핸들러를 언급하고 직접 ASM에서 기능의 테이블에 전달한다고 생각합니다.)

함수 호출 및 시스템 호출 규칙에 대한 What are the calling conventions for UNIX & Linux system calls on i386 and x86-64를 참조하십시오.


I386 시스템 호출 규칙

은 투박 하나 불편 : ebx 호출 보존, 그래서 거의 모든 콜 래퍼 getpid 같은 어떤 인수와 통화를 제외하고 저장/ ebx을 복원 할 필요가있다. (커널을 입력 할 필요조차 없어도 vDSO를 호출하십시오 : vDSO 및 기타 많은 것들에 대한 자세한 내용은 The Definitive Guide to Linux System Calls (on x86)을 참조하십시오.)

그러나 i386 함수 호출 규칙은 그래서 glibc 래퍼 함수는 여전히 모든 인수를 mov 어쨌든해야합니다.

x86 레지스터의 "자연적인"순서는 기계 코드의 숫자 코드에 따라 EAX, ECX, EDX, EBX이고 또한 pusha/popa의 순서입니다. Why are first four x86 GPRs named in such unintuitive order?을 참조하십시오.

+0

'syscall' 컨벤션이 사용자 랜드 컨벤션 ('r10' vs'rcx')에서 4 번째 arg와 다른 이유는 무엇입니까? 간단한 래퍼에 대해 좋은 점을 알았지 만, 4, 5 또는 6 개의 인수를 가진 호출에 대해 1, 2 또는 3 인수를 셔플 링해야하는 이러한 불일치를 제외하고는 더 빠를 수 있습니다. – BeeOnRope

+2

@BeeOnRope :'syscall' 명령어 자체는 말 그대로'rcx'와'r11'을 저장된'RIP'와'RFLAGS' 값으로 닫습니다. 호출 규칙을 선택하는 데있어 매우 작은 요소이기 때문에 REX에 필요한 높은 regs와 ecx와 같은 REX가없는 REX 레지스터와 같은 고려 사항이 중요합니다. 그리고 컨벤션이 arg-passing/call-clobbered "low"reg로'rcx' 대신에'rbx'를 사용했다면, 변수 이동은 그것을 안전/복원해야 할 것입니다. 또한 통화 보존 저전력 (RBP 및 RBX는 "최소 특수"저수준이기 때문에 주로 CPUID/CMPXCHG16B (원래 존재하지도 않았 음)이기 때문에)가 필요합니다. –