aboutsummaryrefslogtreecommitdiffstats
path: root/Godeps/_workspace/src/github.com/obscuren/qml/testing.go
blob: 39a2a691d00c255b9bbbffec4ad8e54e86e99703 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
package qml

// #include <stdlib.h>
// int mprotect(void *addr, size_t len, int prot);
import "C"

import (
    "bytes"
    "encoding/binary"
    "gopkg.in/qml.v1/cdata"
    "reflect"
    "unsafe"
)

const pageSize = 4096

func qmain() {
    Run(func() error { tmain(); return nil })
}

func tmain() { tstub() }
func tstub() { tstub() }

func SetupTesting() {
    ptr := func(f func()) uintptr { return reflect.ValueOf(f).Pointer() }
    rmain, mmain := cdata.Addrs()
    fset(rmain, mmain, ptr(qmain))
    fset(ptr(tmain), ptr(tstub), mmain)
}

const (
    protREAD  = 1
    protWRITE = 2
    protEXEC  = 4
)

func fset(target, old, new uintptr) {
    pageOffset := target % pageSize
    pageAddr := target - pageOffset

    var mem []byte
    memh := (*reflect.SliceHeader)(unsafe.Pointer(&mem))
    memh.Data = pageAddr
    memh.Len = pageSize * 2
    memh.Cap = pageSize * 2

    oldAddr := make([]byte, 8)
    newAddr := make([]byte, 8)

    binary.LittleEndian.PutUint64(oldAddr, uint64(old))
    binary.LittleEndian.PutUint64(newAddr, uint64(new))

    // BSD's syscall package misses Mprotect. Use cgo instead.
    C.mprotect(unsafe.Pointer(pageAddr), C.size_t(len(mem)), protEXEC|protREAD|protWRITE)
    defer C.mprotect(unsafe.Pointer(pageAddr), C.size_t(len(mem)), protEXEC|protREAD)

    delta := make([]byte, 4)
    for i, c := range mem[pageOffset:] {
        if c == 0xe8 && int(pageOffset)+i+5 < len(mem) {
            instrAddr := pageAddr + pageOffset + uintptr(i)
            binary.LittleEndian.PutUint32(delta, uint32(old-instrAddr-5))
            if bytes.Equal(mem[int(pageOffset)+i+1:int(pageOffset)+i+5], delta) {
                binary.LittleEndian.PutUint32(mem[int(pageOffset)+i+1:], uint32(new-instrAddr-5))
                return
            }
        }
    }
    panic("cannot setup qml package for testing")
}