Go的Syscall()中的第二个`r2`返回值是多少?

时间:2016-08-03 08:38:06

标签: go system-calls

以下是Go's undocumented Syscall function

func Syscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno)

这是the C definition

long syscall(long number, ...);

非常不同。因此trap number a1a2a3允许三个参数是相当明显的。我还得出结论:r1是返回值,errerrno。但是什么是r2?系统调用手册页未提及多个返回值。

它确实给出了实际的调用约定(仍然只有一个retval):

       arch/ABI    instruction           syscall #  retval  error    Notes
       ────────────────────────────────────────────────────────────────────
       alpha       callsys               v0         a0      a3       [1]
       arc         trap0                 r8         r0      -
       arm/OABI    swi NR                -          a1      -        [2]
       arm/EABI    swi 0x0               r7         r0      -
       arm64       svc #0                x8         x0      -
       blackfin    excpt 0x0             P0         R0      -
       i386        int $0x80             eax        eax     -
       ia64        break 0x100000        r15        r8      r10      [1]
       m68k        trap #0               d0         d0      -
       microblaze  brki r14,8            r12        r3      -
       mips        syscall               v0         v0      a3       [1]
       nios2       trap                  r2         r2      r7
       parisc      ble 0x100(%sr2, %r0)  r20        r28     -
       powerpc     sc                    r0         r3      r0       [1]
       s390        svc 0                 r1         r2      -        [3]
       s390x       svc 0                 r1         r2      -        [3]
       superh      trap #0x17            r3         r0      -        [4]
       sparc/32    t 0x10                g1         o0      psr/csr  [1]
       sparc/64    t 0x6d                g1         o0      psr/csr  [1]
       tile        swint1                R10        R00     R01      [1]
       x86_64      syscall               rax        rax     -        [5]
       x32         syscall               rax        rax     -        [5]
       xtensa      syscall               a2         a2      -

但是在x86上,这是the implementation

    #define INVOKE_SYSCALL  INT $0x80

    TEXT    ·Syscall(SB),NOSPLIT,$0-28
        CALL    runtime·entersyscall(SB)
        MOVL    trap+0(FP), AX  // syscall entry
        MOVL    a1+4(FP), BX
        MOVL    a2+8(FP), CX
        MOVL    a3+12(FP), DX
        MOVL    $0, SI
        MOVL    $0,  DI
        INVOKE_SYSCALL
        CMPL    AX, $0xfffff001
        JLS ok
        MOVL    $-1, r1+16(FP)
        MOVL    $0, r2+20(FP)
        NEGL    AX
        MOVL    AX, err+24(FP)
        CALL    runtime·exitsyscall(SB)
        RET
    ok:
        MOVL    AX, r1+16(FP)
        MOVL    DX, r2+20(FP)
        MOVL    $0, err+24(FP)
        CALL    runtime·exitsyscall(SB)
        RET

现在,我没有很好地阅读汇编,但我很确定它会在r2中返回EDX。为什么呢?

1 个答案:

答案 0 :(得分:5)

我认为它们具有多个返回值以保持一致性。从该表中可以看出,某些体系结构返回多个值,如果从该目录中检查一些其他程序集文件,您将看到它们将寄存器值移动到r2。

但为什么是DX?这部分仍然令人费解。分散在网络上的是i386上提到的文档,允许函数使用EAX和EDX作为返回值。例如System V Application Binary Interface Intel386 Architecture Processor Supplement

  

%edx 暂记寄存器;还用来返回一些上面的32位   64位返回类型

后来继续说:

  

%edx中返回最重要的32位。最没有签名的   在%eax中返回长long有效32位。

我们试试这个:

uint64_t some_function() {
  return 18446744073709551614LLU;
}

Clang最终制作:

pushl   %ebp
movl    %esp, %ebp
movl    $-2, %eax
movl    $-1, %edx
popl    %ebp
ret

有趣的是,asm_linux_amd64.s似乎做同样的事情,给我们一个借口来看System V ABI for AMD64。这也是文件中提到的关于RDX:

  

用于将第三个参数传递给函数; 第二次返回登记

但附录A具体涉及Linux公约。

  

C库和Linux内核之间的接口是相同的   对于具有以下差异的用户级应用程序:

     

从系统调用返回,寄存器%rax包含结果   system-call。介于-4095和-1之间的值表示错误,   它是-errno。

系统调用没有提及RDX。

我不会为了这个(或者一般情况下)把我的手放在火中但是我怀疑对于Linux来说没有必要使用DX,因为Linux没有使用它们从AX溢出的如此大的返回值。