jit.go
· 1.9 KiB · Go
Originalformat
package main
import (
"github.com/edsrzf/mmap-go"
"reflect"
"unsafe"
)
// Addr returns the address in memory of a byte slice, as a uintptr
func Addr(b []byte) uintptr {
hdr := (*reflect.SliceHeader)(unsafe.Pointer(&b))
return hdr.Data
}
// BuildTo converts a byte-slice into an arbitrary-signatured
// function. The out argument should be a pointer to a variable of
// `func' type.
//
// Arguments to the resulting function will be passed to code using a
// hybrid of the GCC and 6c ABIs: The compiled code will receive, via
// the GCC ABI, a single argument, a void* pointing at the beginning
// of the 6c argument frame. For concreteness, on amd64, a
// func([]byte) int would result in %rdi pointing at the 6c stack
// frame, like so:
//
// 24(%rdi) [ return value ]
// 16(%rdi) [ cap(slice) ]
// 8(%rdi) [ len(slice) ]
// 0(%rdi) [ uint8* data ]
func BuildTo(b []byte, out interface{}) {
buildToInternal(b, out, Build)
}
func buildToInternal(b []byte, out interface{}, build func([]byte) func()) {
v := reflect.ValueOf(out)
if v.Type().Kind() != reflect.Ptr {
panic("BuildTo: must pass a pointer")
}
if v.Elem().Type().Kind() != reflect.Func {
panic("BuildTo: must pass a pointer to func")
}
f := build(b)
ival := *(*struct {
typ uintptr
val uintptr
})(unsafe.Pointer(&out))
// Since we know that out has concrete type of *func(..) ...,
// we know it fits into a word, and thus `val' is just the
// pointer itself (http://research.swtch.com/interfaces)
*(*func())(unsafe.Pointer(ival.val)) = f
}
func Build(b []byte) func() {
dummy := jitcall
fn := &struct {
trampoline uintptr
jitcode uintptr
}{**(**uintptr)(unsafe.Pointer(&dummy)), Addr(b)}
return *(*func())(unsafe.Pointer(&fn))
}
func ToExecutableMemory(data []byte) []byte {
b, _ := mmap.MapRegion(nil, len(data), mmap.EXEC|mmap.RDWR, mmap.ANON, int64(0))
copy(b, data)
return b
}
func jitcall() {}
| 1 | package main |
| 2 | |
| 3 | import ( |
| 4 | "github.com/edsrzf/mmap-go" |
| 5 | "reflect" |
| 6 | "unsafe" |
| 7 | ) |
| 8 | |
| 9 | // Addr returns the address in memory of a byte slice, as a uintptr |
| 10 | func Addr(b []byte) uintptr { |
| 11 | hdr := (*reflect.SliceHeader)(unsafe.Pointer(&b)) |
| 12 | return hdr.Data |
| 13 | } |
| 14 | |
| 15 | // BuildTo converts a byte-slice into an arbitrary-signatured |
| 16 | // function. The out argument should be a pointer to a variable of |
| 17 | // `func' type. |
| 18 | // |
| 19 | // Arguments to the resulting function will be passed to code using a |
| 20 | // hybrid of the GCC and 6c ABIs: The compiled code will receive, via |
| 21 | // the GCC ABI, a single argument, a void* pointing at the beginning |
| 22 | // of the 6c argument frame. For concreteness, on amd64, a |
| 23 | // func([]byte) int would result in %rdi pointing at the 6c stack |
| 24 | // frame, like so: |
| 25 | // |
| 26 | // 24(%rdi) [ return value ] |
| 27 | // 16(%rdi) [ cap(slice) ] |
| 28 | // 8(%rdi) [ len(slice) ] |
| 29 | // 0(%rdi) [ uint8* data ] |
| 30 | func BuildTo(b []byte, out interface{}) { |
| 31 | buildToInternal(b, out, Build) |
| 32 | } |
| 33 | |
| 34 | func buildToInternal(b []byte, out interface{}, build func([]byte) func()) { |
| 35 | v := reflect.ValueOf(out) |
| 36 | if v.Type().Kind() != reflect.Ptr { |
| 37 | panic("BuildTo: must pass a pointer") |
| 38 | } |
| 39 | if v.Elem().Type().Kind() != reflect.Func { |
| 40 | panic("BuildTo: must pass a pointer to func") |
| 41 | } |
| 42 | |
| 43 | f := build(b) |
| 44 | |
| 45 | ival := *(*struct { |
| 46 | typ uintptr |
| 47 | val uintptr |
| 48 | })(unsafe.Pointer(&out)) |
| 49 | |
| 50 | // Since we know that out has concrete type of *func(..) ..., |
| 51 | // we know it fits into a word, and thus `val' is just the |
| 52 | // pointer itself (http://research.swtch.com/interfaces) |
| 53 | |
| 54 | *(*func())(unsafe.Pointer(ival.val)) = f |
| 55 | } |
| 56 | |
| 57 | func Build(b []byte) func() { |
| 58 | dummy := jitcall |
| 59 | fn := &struct { |
| 60 | trampoline uintptr |
| 61 | jitcode uintptr |
| 62 | }{**(**uintptr)(unsafe.Pointer(&dummy)), Addr(b)} |
| 63 | |
| 64 | return *(*func())(unsafe.Pointer(&fn)) |
| 65 | } |
| 66 | |
| 67 | func ToExecutableMemory(data []byte) []byte { |
| 68 | b, _ := mmap.MapRegion(nil, len(data), mmap.EXEC|mmap.RDWR, mmap.ANON, int64(0)) |
| 69 | copy(b, data) |
| 70 | return b |
| 71 | } |
| 72 | |
| 73 | func jitcall() {} |
| 74 |
main.go
· 2.7 KiB · Go
Originalformat
package main
import (
asm "github.com/twitchyliquid64/golang-asm"
"github.com/twitchyliquid64/golang-asm/obj"
"github.com/twitchyliquid64/golang-asm/obj/x86"
"os"
)
func clearReg(builder *asm.Builder, reg int16) *obj.Prog {
prog := builder.NewProg()
prog.As = x86.AXORQ
prog.From.Type = obj.TYPE_REG
prog.From.Reg = reg
prog.To.Type = obj.TYPE_REG
prog.To.Reg = reg
return prog
}
func movRegToReg(builder *asm.Builder, src, dst int16) *obj.Prog {
prog := builder.NewProg()
prog.As = x86.AMOVQ
prog.From.Type = obj.TYPE_REG
prog.From.Reg = src
prog.To.Type = obj.TYPE_REG
prog.To.Reg = dst
return prog
}
func noop(builder *asm.Builder) *obj.Prog {
prog := builder.NewProg()
prog.As = x86.ANOPL
prog.From.Type = obj.TYPE_REG
prog.From.Reg = x86.REG_AX
return prog
}
func addImmediateByte(builder *asm.Builder, in int32) *obj.Prog {
prog := builder.NewProg()
prog.As = x86.AADDB
prog.To.Type = obj.TYPE_REG
prog.To.Reg = x86.REG_AL
prog.From.Type = obj.TYPE_CONST
prog.From.Offset = int64(in)
return prog
}
func movImmediateByte(builder *asm.Builder, reg int16, in int32) *obj.Prog {
prog := builder.NewProg()
prog.As = x86.AMOVQ
prog.To.Type = obj.TYPE_REG
prog.To.Reg = reg
prog.From.Type = obj.TYPE_CONST
prog.From.Offset = int64(in)
return prog
}
func movMemoryByReg(builder *asm.Builder, reg, memoryReg int16, offset int64) *obj.Prog {
prog := builder.NewProg()
prog.As = x86.AMOVQ
prog.To.Type = obj.TYPE_REG
prog.To.Reg = reg
prog.From.Type = obj.TYPE_MEM
prog.From.Reg = memoryReg
prog.From.Offset = offset
return prog
}
func lea(builder *asm.Builder, reg int16, addr int32) *obj.Prog {
prog := builder.NewProg()
prog.As = x86.ALEAQ
prog.To.Type = obj.TYPE_REG
prog.To.Reg = reg
prog.From.Type = obj.TYPE_MEM
prog.From.Offset = int64(addr)
return prog
}
func syscall(builder *asm.Builder) *obj.Prog {
prog := builder.NewProg()
prog.As = x86.ASYSCALL
prog.To.Type = obj.TYPE_NONE
prog.From.Type = obj.TYPE_NONE
return prog
}
func addRet(builder *asm.Builder) *obj.Prog {
prog := builder.NewProg()
prog.As = obj.ARET
return prog
}
func main() {
huebr := "huebr\n"
b, _ := asm.NewBuilder("amd64", 64)
b.AddInstruction(movRegToReg(b, x86.REG_DI, x86.REG_CX))
b.AddInstruction(movImmediateByte(b, x86.REG_AX, 1))
b.AddInstruction(movImmediateByte(b, x86.REG_DI, int32(os.Stdout.Fd())))
b.AddInstruction(movMemoryByReg(b, x86.REG_SI, x86.REG_CX, 0))
b.AddInstruction(movMemoryByReg(b, x86.REG_DX, x86.REG_CX, 8))
b.AddInstruction(syscall(b))
b.AddInstruction(addRet(b))
insts := ToExecutableMemory(b.Assemble())
var myCall func(data string)
BuildTo(insts, &myCall)
myCall(huebr)
//fmt.Printf("Bin: %x\n", b.Assemble())
}
| 1 | package main |
| 2 | |
| 3 | import ( |
| 4 | asm "github.com/twitchyliquid64/golang-asm" |
| 5 | "github.com/twitchyliquid64/golang-asm/obj" |
| 6 | "github.com/twitchyliquid64/golang-asm/obj/x86" |
| 7 | "os" |
| 8 | ) |
| 9 | |
| 10 | func clearReg(builder *asm.Builder, reg int16) *obj.Prog { |
| 11 | prog := builder.NewProg() |
| 12 | prog.As = x86.AXORQ |
| 13 | prog.From.Type = obj.TYPE_REG |
| 14 | prog.From.Reg = reg |
| 15 | prog.To.Type = obj.TYPE_REG |
| 16 | prog.To.Reg = reg |
| 17 | return prog |
| 18 | } |
| 19 | |
| 20 | func movRegToReg(builder *asm.Builder, src, dst int16) *obj.Prog { |
| 21 | prog := builder.NewProg() |
| 22 | |
| 23 | prog.As = x86.AMOVQ |
| 24 | prog.From.Type = obj.TYPE_REG |
| 25 | prog.From.Reg = src |
| 26 | prog.To.Type = obj.TYPE_REG |
| 27 | prog.To.Reg = dst |
| 28 | return prog |
| 29 | } |
| 30 | |
| 31 | func noop(builder *asm.Builder) *obj.Prog { |
| 32 | prog := builder.NewProg() |
| 33 | prog.As = x86.ANOPL |
| 34 | prog.From.Type = obj.TYPE_REG |
| 35 | prog.From.Reg = x86.REG_AX |
| 36 | return prog |
| 37 | } |
| 38 | |
| 39 | func addImmediateByte(builder *asm.Builder, in int32) *obj.Prog { |
| 40 | prog := builder.NewProg() |
| 41 | prog.As = x86.AADDB |
| 42 | prog.To.Type = obj.TYPE_REG |
| 43 | prog.To.Reg = x86.REG_AL |
| 44 | prog.From.Type = obj.TYPE_CONST |
| 45 | prog.From.Offset = int64(in) |
| 46 | return prog |
| 47 | } |
| 48 | |
| 49 | func movImmediateByte(builder *asm.Builder, reg int16, in int32) *obj.Prog { |
| 50 | prog := builder.NewProg() |
| 51 | prog.As = x86.AMOVQ |
| 52 | prog.To.Type = obj.TYPE_REG |
| 53 | prog.To.Reg = reg |
| 54 | prog.From.Type = obj.TYPE_CONST |
| 55 | prog.From.Offset = int64(in) |
| 56 | return prog |
| 57 | } |
| 58 | |
| 59 | func movMemoryByReg(builder *asm.Builder, reg, memoryReg int16, offset int64) *obj.Prog { |
| 60 | prog := builder.NewProg() |
| 61 | prog.As = x86.AMOVQ |
| 62 | prog.To.Type = obj.TYPE_REG |
| 63 | prog.To.Reg = reg |
| 64 | prog.From.Type = obj.TYPE_MEM |
| 65 | prog.From.Reg = memoryReg |
| 66 | prog.From.Offset = offset |
| 67 | return prog |
| 68 | } |
| 69 | |
| 70 | func lea(builder *asm.Builder, reg int16, addr int32) *obj.Prog { |
| 71 | prog := builder.NewProg() |
| 72 | prog.As = x86.ALEAQ |
| 73 | prog.To.Type = obj.TYPE_REG |
| 74 | prog.To.Reg = reg |
| 75 | prog.From.Type = obj.TYPE_MEM |
| 76 | prog.From.Offset = int64(addr) |
| 77 | |
| 78 | return prog |
| 79 | } |
| 80 | |
| 81 | func syscall(builder *asm.Builder) *obj.Prog { |
| 82 | prog := builder.NewProg() |
| 83 | |
| 84 | prog.As = x86.ASYSCALL |
| 85 | prog.To.Type = obj.TYPE_NONE |
| 86 | prog.From.Type = obj.TYPE_NONE |
| 87 | |
| 88 | return prog |
| 89 | } |
| 90 | |
| 91 | func addRet(builder *asm.Builder) *obj.Prog { |
| 92 | prog := builder.NewProg() |
| 93 | |
| 94 | prog.As = obj.ARET |
| 95 | |
| 96 | return prog |
| 97 | } |
| 98 | func main() { |
| 99 | huebr := "huebr\n" |
| 100 | b, _ := asm.NewBuilder("amd64", 64) |
| 101 | |
| 102 | b.AddInstruction(movRegToReg(b, x86.REG_DI, x86.REG_CX)) |
| 103 | b.AddInstruction(movImmediateByte(b, x86.REG_AX, 1)) |
| 104 | b.AddInstruction(movImmediateByte(b, x86.REG_DI, int32(os.Stdout.Fd()))) |
| 105 | b.AddInstruction(movMemoryByReg(b, x86.REG_SI, x86.REG_CX, 0)) |
| 106 | b.AddInstruction(movMemoryByReg(b, x86.REG_DX, x86.REG_CX, 8)) |
| 107 | b.AddInstruction(syscall(b)) |
| 108 | b.AddInstruction(addRet(b)) |
| 109 | |
| 110 | insts := ToExecutableMemory(b.Assemble()) |
| 111 | |
| 112 | var myCall func(data string) |
| 113 | |
| 114 | BuildTo(insts, &myCall) |
| 115 | |
| 116 | myCall(huebr) |
| 117 | |
| 118 | //fmt.Printf("Bin: %x\n", b.Assemble()) |
| 119 | } |
| 120 |