Lucas Teske revidoval tento gist 3 years ago. Přejít na revizi
2 files changed, 192 insertions
jit.go(vytvořil soubor)
| @@ -0,0 +1,73 @@ | |||
| 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() {} | |
main.go(vytvořil soubor)
| @@ -0,0 +1,119 @@ | |||
| 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 | + | } | |
Novější
Starší