Golang怎么通过mmap执行汇编代码
#go
# A Quick Guide to Go's Assembler
Is it possible to include inline assembly in Go code?
原理是通过 mmap
将待执行的汇编代码的二进制映射到可执行的虚拟内存中。
在使用 mmap
时,需要设置 PROT_EXEC
标志,mmap
会返回一个虚拟地址。接着,将二进制代码复制到这个地址,并将其当作函数地址进行转换和执行。
怎么得到 go 代码的汇编代码二进制?
package inc
func inc(n int) int {
return n + 1
}
编译 go tool compile -S -N inc.go
╭─ultraman@ultraman-desktop ~/Desktop/workspace/jitbf ‹node-› ‹› (main*)
╰─$ go tool compile -S -N inc/inc.go
<unlinkable>.inc STEXT nosplit size=34 args=0x8 locals=0x10 funcid=0x0 align=0x0
0x0000 00000 (/home/ultraman/Desktop/workspace/jitbf/inc/inc.go:3) TEXT <unlinkable>.inc(SB), NOSPLIT|ABIInternal, $16-8
0x0000 00000 (/home/ultraman/Desktop/workspace/jitbf/inc/inc.go:3) PUSHQ BP
0x0001 00001 (/home/ultraman/Desktop/workspace/jitbf/inc/inc.go:3) MOVQ SP, BP
0x0004 00004 (/home/ultraman/Desktop/workspace/jitbf/inc/inc.go:3) SUBQ $8, SP
0x0008 00008 (/home/ultraman/Desktop/workspace/jitbf/inc/inc.go:3) FUNCDATA $0, gclocals·g2BeySu+wFnoycgXfElmcg==(SB)
0x0008 00008 (/home/ultraman/Desktop/workspace/jitbf/inc/inc.go:3) FUNCDATA $1, gclocals·g2BeySu+wFnoycgXfElmcg==(SB)
0x0008 00008 (/home/ultraman/Desktop/workspace/jitbf/inc/inc.go:3) FUNCDATA $5, <unlinkable>.inc.arginfo1(SB)
0x0008 00008 (/home/ultraman/Desktop/workspace/jitbf/inc/inc.go:3) MOVQ AX, <unlinkable>.n+24(SP)
0x000d 00013 (/home/ultraman/Desktop/workspace/jitbf/inc/inc.go:3) MOVQ $0, <unlinkable>.~r0(SP)
0x0015 00021 (/home/ultraman/Desktop/workspace/jitbf/inc/inc.go:4) INCQ AX
0x0018 00024 (/home/ultraman/Desktop/workspace/jitbf/inc/inc.go:4) MOVQ AX, <unlinkable>.~r0(SP)
0x001c 00028 (/home/ultraman/Desktop/workspace/jitbf/inc/inc.go:4) ADDQ $8, SP
0x0020 00032 (/home/ultraman/Desktop/workspace/jitbf/inc/inc.go:4) POPQ BP
0x0021 00033 (/home/ultraman/Desktop/workspace/jitbf/inc/inc.go:4) RET
0x0000 55 48 89 e5 48 83 ec 08 48 89 44 24 18 48 c7 04 UH..H...H.D$.H..
0x0010 24 00 00 00 00 48 ff c0 48 89 04 24 48 83 c4 08 $....H..H..$H...
0x0020 5d c3 ].
go:cuinfo.producer.<unlinkable> SDWARFCUINFO dupok size=0
0x0000 2d 4e 20 72 65 67 61 62 69 -N regabi
go:cuinfo.packagename.<unlinkable> SDWARFCUINFO dupok size=0
0x0000 69 6e 63 inc
gclocals·g2BeySu+wFnoycgXfElmcg== SRODATA dupok size=8
0x0000 01 00 00 00 00 00 00 00 ........
<unlinkable>.inc.arginfo1 SRODATA static dupok size=3
0x0000 00 08 ff
获得 assemble code go tool objdump -S inc.o
╰─$ go tool objdump -S inc.o
TEXT <unlinkable>.inc(SB) /home/ultraman/Desktop/workspace/jitbf/inc/inc.go
func inc(n int) int {
0x4f0 55 PUSHQ BP
0x4f1 4889e5 MOVQ SP, BP
0x4f4 4883ec08 SUBQ $0x8, SP
0x4f8 4889442418 MOVQ AX, 0x18(SP)
0x4fd 48c7042400000000 MOVQ $0x0, 0(SP)
return n + 1
0x505 48ffc0 INCQ AX
0x508 48890424 MOVQ AX, 0(SP)
0x50c 4883c408 ADDQ $0x8, SP
0x510 5d POPQ BP
0x511 c3 RET
执行
package main
import (
"fmt"
"syscall"
"unsafe"
)
func main() {
code := []byte{
0x55, 0x48, 0x89, 0xe5, 0x48, 0x83, 0xec, 0x08,
0x48, 0x89, 0x44, 0x24, 0x18, 0x48, 0xc7, 0x04,
0x24, 0x00, 0x00, 0x00, 0x00, 0x48, 0xff, 0xc0,
0x48, 0x89, 0x04, 0x24, 0x48, 0x83, 0xc4, 0x08,
0x5d, 0xc3,
}
memory, err := syscall.Mmap(-1, 0, len(code), syscall.PROT_READ|syscall.PROT_WRITE|syscall.PROT_EXEC, syscall.MAP_ANON|syscall.MAP_PRIVATE)
if err != nil {
panic(err)
}
defer syscall.Munmap(memory)
copy(memory, code)
memory_ptr := &memory
ptr := unsafe.Pointer(&memory_ptr)
inc := *(*func(int) int)(ptr)
fmt.Println(inc(13)) // 14
}
go run main.go
14
怎么运行由原生的汇编代码的机器码?
按理说已经是根据上面的思路是可以的,比如我试了下 x86_64 格式的汇编指定(由 FASM 生成)是不行的(see Code)
GPT:
以下是一些可能导致在Go中执行失败的原因:
内存安全性:
Go语言对内存访问有严格的限制,特别是在涉及指针操作和内存映射时。Go的内存模型设计旨在提供更高的安全性和避免常见的内存错误。因此,直接操作内存映射可能会受到Go的限制。
系统调用:
Go语言的标准库提供了对系统调用的封装,但是在某些情况下,直接调用系统调用可能会受到限制。在您的代码中,您使用了syscall.Mmap和syscall.Munmap来进行内存映射和解除映射,这可能会受到Go运行时的限制。
指针操作:
Go语言对指针操作有一定的限制,尤其是在涉及不安全操作时。在您的代码中,您使用了unsafe.Pointer和指针解引用操作,这可能会受到Go的安全性检查。
虽然Go语言提供了unsafe包来处理不安全操作,但是在处理系统级操作和直接内存访问时,Go的安全性机制可能会限制某些操作。相比之下,Rust更加灵活,允许更细粒度的控制和更多的系统级操作,同时通过所有权系统和借用检查器提供了更强大的内存安全性保证。
因此,尽管您的代码在Rust中可以成功执行,但在Go中可能会受到一些限制。如果您需要在Go中执行类似的操作,可能需要考虑更多的安全性和Go语言的限制,或者尝试使用更适合Go的方式来实现相同的功能。
由于 Golang 中的机制限制,同样的逻辑用 rust 或 C 都是 Ok 的 Rust Code。(是否可以使用 C,然后在 go 中用 cgo 调用 C?)