لقد واجهت منطقة مشعرة حقًا من كود ASM.
اقتراحي الأول ليس محاولة الاتصال من المجمّع في Go. - إيان لانس تايلور
طالما أن رمز المجمع الخاص بك يفعل شيئًا بسيطًا ، فإن كل شيء يبدو جيدًا.
بمجرد أن تكون لديك مهمة لاستدعاء وظيفة من كود المجمّع لـ Go ، فإن إحدى النصائح الأولى التي ستحصل عليها: لا تفعل ذلك.
ولكن ماذا لو كنت حقا بحاجة إليها؟ في هذه الحالة ، من فضلك ، تحت القط.

اصطلاح الدعوة
يبدأ كل شيء بحقيقة أنك بحاجة إلى فهم كيفية تمرير الحجج إلى الوظائف وكيفية قبول نتائجها .
أوصي بأن تتعرف على وظائف Go بلغة التجميع ، حيث يتم وصف معظم المعلومات التي نحتاجها بوضوح.
عادة ، تختلف اصطلاح الاتصال من نظام إلى آخر ، حيث قد تختلف مجموعة السجلات المتاحة. سننظر فقط GOARCH=amd64، ولكن في حالة Go ، تختلف الاتفاقيات بشكل كبير.
فيما يلي بعض ميزات اصطلاح استدعاء الوظيفة في Go:
- يتم تمرير كافة الوسائط من خلال المكدس ، باستثناء "السياق" في عمليات الإغلاق ، ويمكن الوصول إليها من خلال السجل DX(٪ rdx).
- ( arguments).
- .
- . , .
, . .

, register-based calling convention. Go.
, :
- , .
- 0(SP),- 8(SP)( 8 ), .
- n(SP),- n— .- int64,- 16(SP).
.
package main
func asmfunc(x int32) (int32, int32)
func gofunc(a1 int64, a2, a3 int32) (int32, int32) {
    return int32(a1) + a2, int32(a1) + a3
}
func main() {
    v1, v2 := asmfunc(10)
    println(v1, v2) 
}
// func asmfunc(x int32) (int32, int32)
TEXT ·asmfunc(SB), 0, $24-12
  MOVL x+0(FP), AX
  MOVQ $1, 0(SP)  //   (a1 int64)
  MOVL $2, 8(SP)  //   (a2 int32)
  MOVL AX, 12(SP) //   (a3 int32)
  CALL ·gofunc(SB)
  MOVL 16(SP), AX //   
  MOVL 20(SP), CX //   
  MOVL AX, ret+8(FP)  //   
  MOVL CX, ret+12(FP) //   
  RET
$24-16 (locals=24 bytes, args=16 bytes)
          0     8     12    16     20     SP
locals=24 [a1:8][a2:4][a3:4][ret:4][ret:4]
(ret    asmfunc,   gofunc)
        0    4          8      12     FP
args=16 [x:4][padding:4][ret:4][ret:4]
(ret    main,   asmfunc)
, 4 . , , (8 amd64).
, . — int32, — int64, offset 8, 4, reflect.TypeOf(int64(0)).Align() 8.
, FP go vet.
stackmap
-.
package foo
import (
    "fmt"
    "testing"
)
func foo(ptr *object)
type object struct {
    x, y, z int64
}
func printPtr(ptr *object) {
    fmt.Println(*ptr)
}
func TestFoo(t *testing.T) {
    foo(&object{x: 11, y: 22, z: 33})
}
TEXT ·foo(SB), 0, $8-8
        MOVQ ptr+0(FP), AX
        MOVQ AX, 0(SP)
        CALL ·printPtr(SB)
        RET
, - stackmap:
=== RUN   TestFoo
runtime: frame <censored> untyped locals 0xc00008ff38+0x8
fatal error: missing stackmap
, , GC stackmaps. Go , .
stub ( Go ). , stackmap , .
, stackmap (), NO_LOCAL_POINTERS ( ).
NO_LOCAL_POINTERS
TestFoo :
#include "funcdata.h"
TEXT ·foo(SB), 0, $8-8
        NO_LOCAL_POINTERS
        MOVQ ptr+0(FP), AX
        MOVQ AX, 0(SP)
        CALL ·printPtr(SB)
        RET
, .
, ? , , "" , , , , ?
, heap, , . , , . GC "" , , .
, , "" (escapes to heap) escape analysis, , .
NO_LOCAL_POINTERS: , , , GC . .
Go non-cooperative preemption, , .
Go. , go:nosplit, , , NO_LOCAL_POINTERS .
GO_ARGS
, Go prototype, GO_ARGS.
GO_ARGS — funcdata.h, NO_LOCAL_POINTERS. , stackmap Go .
, stackmap . args_stackmap . : , stackmap.
GO_RESULTS_INITIALIZED
Go , ( ) GO_RESULTS_INITIALIZED .
:
// func getg() interface{}
TEXT ·getg(SB), NOSPLIT, $32-16
  //     .
  //      .
  MOVQ $0, ret_type+0(FP)
  MOVQ $0, ret_data+8(FP)
  GO_RESULTS_INITIALIZED
  //    ...
  RET
, , -.
GitHub.
Go JIT-
Go .
Garbage collector Go , , , , , Go JIT'.
, . , , .
calljit-v1
package main
import (
    "log"
    "reflect"
    "syscall"
    "unsafe"
)
func main() {
    a := funcAddr(goFunc)
    code := []byte{
        
        0xb8, byte(a), byte(a >> 8), byte(a >> 16), byte(a >> 24),
        
        0xff, 0xd0,
        
        0xc3,
    }
    executable, err := mmapExecutable(len(code))
    if err != nil {
        log.Panicf("mmap: %v", err)
    }
    copy(executable, code)
    calljit(&executable[0])
}
func calljit(code *byte)
func goFunc() {
    println("called from JIT")
}
func mmapExecutable(length int) ([]byte, error) {
    const prot = syscall.PROT_READ | syscall.PROT_WRITE | syscall.PROT_EXEC
    const flags = syscall.MAP_PRIVATE | syscall.MAP_ANON
    return mmapLinux(0, uintptr(length), prot, flags, 0, 0)
}
func mmapLinux(addr, length, prot, flags, fd, off uintptr) ([]byte, error) {
    ptr, _, err := syscall.Syscall6(
        syscall.SYS_MMAP,
        addr, length, prot, flags, fd, offset)
    if err != 0 {
        return nil, err
    }
    slice := *(*[]byte)(unsafe.Pointer(&reflect.SliceHeader{
        Data: ptr,
        Len:  int(length),
        Cap:  int(length),
    }))
    return slice, nil
}
func funcAddr(fn interface{}) uintptr {
    type emptyInterface struct {
        typ   uintptr
        value *uintptr
    }
    e := (*emptyInterface)(unsafe.Pointer(&fn))
    return *e.value
}
// file jit_amd64.s
TEXT ·calljit(SB), 0, $0-8
        MOVQ code+0(FP), AX
        JMP AX
 , ( ):
$ go build -o jit . && ./jit
called from JIT
goFunc, JIT-:
 func goFunc() {
    println("called from JIT")
+   runtime.GC()
 }
:
$ go build -o jit . && ./jit
called from JIT
runtime: unexpected return pc for main.goFunc called from 0x7f9465f7c007
stack: frame={sp:0xc00008ced0, fp:0xc00008cef0} stack=[0xc00008c000,0xc00008d000)
000000c00008cdd0:  0000000000000000  00007f94681f7558 
000000c00008cde0:  000000c000029270  000000000000000b 
... (+ more)
: unexpected return pc for main.goFunc called from 0x7f9465f7c007, 0x7f9465f7c007 — JIT-. , runtime.
, , FP BP return address , .
Go runtime , JIT-, , .
calljit , , Go . , Go , , Go (calljit).
#include "funcdata.h"
TEXT ·calljit(SB), 0, $8-8
        NO_LOCAL_POINTERS
        MOVQ code+0(FP), AX
        JMP AX
callgo:
        CALL CX
        JMP (SP)
:
- 8 , return address JIT .
- NO_LOCAL_POINTERS- ,- CALL.
calljit , , , Go . , CX , [rsp] — , .
, callgo. , . main():
a := funcAddr(goFunc)
j := funcAddr(calljit) + 36
code := []byte{
    
    0x48, 0xc7, 0xc1, byte(a), byte(a >> 8), byte(a >> 16), byte(a >> 24),
    
    0x48, 0xc7, 0xc7, byte(j), byte(j >> 8), byte(j >> 16), byte(j >> 24),
    
    0x48, 0x8d, 0x35, (4 + 2), 0, 0, 0,
    
    0x48, 0x89, 0x34, 0x24,
    
    0xff, 0xe7,
    
    0x48, 0x83, 0xc4, (8 + 8),
    
    0xc3,
}
CX, callgo DI, SI , [rsp]. 4+2 — LEAQ .
, , ADDQ. 8 8 BP.
calljit:
   return address  callgo
|
|       Go    BP   
|       |
0(SP)   8(SP)    16(SP)    24(SP)
[empty] [prevBP] [retaddr] [arg1:code]
|             /  |         |
|            /   |           calljit (caller frame)
|           /    |      
|          /       CALL   calljit
|         /
calljit frame, 16 bytes     
runtime.GC():
$ go build -o jit . && ./jit
called from JIT
Go . , — , .
, , . Go.
Go Internal ABI
Go Internal ABI — .
Go , , . ABI, , .
:
- .
- .
calling convention ABI0, ABIInternal.
Go -S, , ABIInternal , ABI0:

ABIInternal , ABI1, . ABIInternal calling convention .
, .
, Go . , .

Hub-
, . , , , . .