【问题标题】:What is the second `r2` return value in Go's Syscall() for?Go 的 Syscall() 中的第二个 `r2` 返回值是什么?
【发布时间】:2016-12-08 21:02:11
【问题描述】:

这里是Go's undocumented Syscall function

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

这里是the C definition:

long syscall(long number, ...);

完全不同。所以很明显trapnumber,而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。为什么?

【问题讨论】:

    标签: go system-calls


    【解决方案1】:

    我认为它们有多个返回值以保持一致性。正如您从该表中看到的那样,一些架构返回多个值,如果您检查该目录中的一些其他汇编文件,您会看到它们将寄存器值移动到 r2。


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

    %edx 暂存寄存器;也用于返回某些的高 32 位 64位返回类型

    后来它继续说:

    在 %edx 中返回最重要的 32 位。最少未签名的 在 %eax 中返回 long 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 包含 系统调用。 介于 -4095 和 -1 之间的值表示错误, 它是-errno。

    没有提到系统调用的 RDX。


    我不会为此(或一般情况下)投入火中,但我怀疑 Linux 没有必要使用 DX,因为它不使用从 AX 溢出的如此大的返回值。

    【讨论】:

      猜你喜欢
      • 2013-05-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-08-27
      • 1970-01-01
      • 2011-11-22
      • 2014-12-17
      相关资源
      最近更新 更多