Vous avez rencontré une zone vraiment poilue de code asm.
Ma première suggestion n'est pas d'essayer d'appeler l'assembleur dans Go. - Ian Lance Taylor
Tant que votre code assembleur fait quelque chose de simple, tout semble bien.
Dès que vous avez une tâche pour appeler une fonction à partir du code assembleur de Go, un des premiers conseils qui vous sera donné: ne faites pas ça.
Et si vous en avez vraiment, vraiment besoin? Dans ce cas, s'il vous plaît, sous cat.

Convention d'appel
Tout commence par le fait que vous devez comprendre comment passer des arguments aux fonctions et comment accepter ses résultats .
Je vous recommande de vous familiariser avec les fonctions Go en langage assembleur , où la plupart des informations dont nous avons besoin sont clairement décrites.
En règle générale, la convention d'appel varie d'une plateforme à l'autre, car l'ensemble des registres disponibles peut varier. Nous ne considérerons que GOARCH=amd64
, mais dans le cas de Go, les conventions ne diffèrent pas de manière significative.
Voici quelques fonctionnalités de la convention d'appel de fonction dans Go:
- Tous les arguments sont passés à travers la pile, à l'exception du "contexte" dans les fermetures, il est accessible via le registre
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-
, . , , , . .