Skip to content

Commit

Permalink
bugfix, Issue #163: offset of g struct in TLS picked based on the val…
Browse files Browse the repository at this point in the history
…ue of runtime.iscgo flag and runtime.buildVersion, instead of being fixed to -16
  • Loading branch information
aarzilli committed Jul 23, 2015
1 parent 1727df4 commit 8b78a52
Show file tree
Hide file tree
Showing 7 changed files with 181 additions and 22 deletions.
12 changes: 12 additions & 0 deletions _fixtures/cgotest.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package main

/*
char* foo(void) { return "hello, world!"; }
*/
import "C"

import "fmt"

func main() {
fmt.Println(C.GoString(C.foo()))
}
9 changes: 6 additions & 3 deletions dwarf/op/op.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,10 @@ func ExecuteStackProgram(cfa int64, instructions []byte) (int64, error) {
stack := make([]int64, 0, 3)
buf := bytes.NewBuffer(instructions)

for ocfaode, err := buf.ReadByte(); err == nil; ocfaode, err = buf.ReadByte() {
fn, ok := oplut[ocfaode]
for opcode, err := buf.ReadByte(); err == nil; opcode, err = buf.ReadByte() {
fn, ok := oplut[opcode]
if !ok {
return 0, fmt.Errorf("invalid instruction %#v", ocfaode)
return 0, fmt.Errorf("invalid instruction %#v", opcode)
}

stack, err = fn(buf, stack, cfa)
Expand All @@ -51,6 +51,9 @@ func ExecuteStackProgram(cfa int64, instructions []byte) (int64, error) {
}

func callframecfa(buf *bytes.Buffer, stack []int64, cfa int64) ([]int64, error) {
if cfa == 0 {
return stack, fmt.Errorf("Could not retrieve CFA for current PC")
}
return append(stack, int64(cfa)), nil
}

Expand Down
37 changes: 23 additions & 14 deletions proc/arch.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package proc
import "runtime"

type Arch interface {
SetCurGInstructions(ver GoVersion, iscgo bool)
PtrSize() int
BreakpointInstruction() []byte
BreakpointSize() int
Expand All @@ -20,10 +21,18 @@ type AMD64 struct {
}

func AMD64Arch() *AMD64 {
var (
curg []byte
breakInstr = []byte{0xCC}
)
var breakInstr = []byte{0xCC}

return &AMD64{
ptrSize: 8,
breakInstruction: breakInstr,
breakInstructionLen: len(breakInstr),
hardwareBreakpointUsage: make([]bool, 4),
}
}

func (a *AMD64) SetCurGInstructions(ver GoVersion, iscgo bool) {
var curg []byte

switch runtime.GOOS {
case "darwin":
Expand All @@ -32,19 +41,19 @@ func AMD64Arch() *AMD64 {
0x0, 0x0,
}
case "linux":
curg = []byte{
0x64, 0x48, 0x8b, 0x0c, 0x25, 0xf0, 0xff, 0xff, 0xff, // mov %fs:0xfffffffffffffff0,%rcx
if iscgo || ver.After(GoVersion{1, 5, 0}) {
curg = []byte{
0x64, 0x48, 0x8b, 0x0c, 0x25, 0xf8, 0xff, 0xff, 0xff, // mov %fs:0xfffffffffffffff8,%rcx
}
} else {
curg = []byte{
0x64, 0x48, 0x8b, 0x0c, 0x25, 0xf0, 0xff, 0xff, 0xff, // mov %fs:0xfffffffffffffff0,%rcx
}
}
}
curg = append(curg, breakInstr[0])
curg = append(curg, a.breakInstruction...)

return &AMD64{
ptrSize: 8,
breakInstruction: breakInstr,
breakInstructionLen: 1,
curgInstructions: curg,
hardwareBreakpointUsage: make([]bool, 4),
}
a.curgInstructions = curg
}

func (a *AMD64) PtrSize() int {
Expand Down
31 changes: 31 additions & 0 deletions proc/proc.go
Original file line number Diff line number Diff line change
Expand Up @@ -596,6 +596,13 @@ func initializeDebugProcess(dbp *Process, path string, attach bool) (*Process, e
dbp.arch = AMD64Arch()
}

ver, iscgo, err := dbp.getGoInformation()
if err != nil {
return nil, err
}

dbp.arch.SetCurGInstructions(ver, iscgo)

return dbp, nil
}

Expand Down Expand Up @@ -674,3 +681,27 @@ func (dbp *Process) execPtraceFunc(fn func()) {
dbp.ptraceChan <- fn
<-dbp.ptraceDoneChan
}

func (dbp *Process) getGoInformation() (ver GoVersion, iscgo bool, err error) {
th := dbp.Threads[dbp.Pid]

vv, err := th.EvalPackageVariable("runtime.buildVersion")
if err != nil {
err = fmt.Errorf("Could not determine version number: %v\n", err)
return
}

ver, ok := parseVersionString(vv.Value)
if !ok {
err = fmt.Errorf("Could not parse version number: %s\n", vv.Value)
}

cv, err := th.EvalPackageVariable("runtime.iscgo")
if err != nil {
err = fmt.Errorf("Could not determine cgo usage: %v\n", err)
return
}
iscgo = (cv.Value == "true")

return
}
29 changes: 29 additions & 0 deletions proc/proc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -592,3 +592,32 @@ func TestKill(t *testing.T) {
}
})
}

func testGSupportFunc(name string, t *testing.T, p *Process, fixture protest.Fixture) {
bp, err := p.SetBreakpointByLocation("main.main")
assertNoError(err, t, name+": BreakByLocation()")

assertNoError(p.Continue(), t, name+": Continue()")

g, err := p.CurrentThread.GetG()
assertNoError(err, t, name+": GetG()")

if g == nil {
t.Fatal(name + ": g was nil")
}

t.Logf(name+": g is: %v", g)

p.ClearBreakpoint(bp.Addr)
p.Kill()
}

func TestGetG(t *testing.T) {
withTestProcess("testprog", t, func(p *Process, fixture protest.Fixture) {
testGSupportFunc("nocgo", t, p, fixture)
})

withTestProcess("cgotest", t, func(p *Process, fixture protest.Fixture) {
testGSupportFunc("cgo", t, p, fixture)
})
}
31 changes: 26 additions & 5 deletions proc/variables.go
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,27 @@ func (thread *Thread) PackageVariables() ([]*Variable, error) {
return vars, nil
}

func (thread *Thread) EvalPackageVariable(name string) (*Variable, error) {
reader := thread.dbp.DwarfReader()

for entry, err := reader.NextPackageVariable(); entry != nil; entry, err = reader.NextPackageVariable() {
if err != nil {
return nil, err
}

n, ok := entry.Val(dwarf.AttrName).(string)
if !ok {
continue
}

if n == name {
return thread.extractVariableFromEntry(entry)
}
}

return nil, fmt.Errorf("could not find symbol value for %s", name)
}

func (thread *Thread) evaluateStructMember(parentEntry *dwarf.Entry, rdr *reader.Reader, memberName string) (*Variable, error) {
parentAddr, err := thread.extractVariableDataAddress(parentEntry, rdr)
if err != nil {
Expand Down Expand Up @@ -387,13 +408,13 @@ func (thread *Thread) executeStackProgram(instructions []byte) (int64, error) {
return 0, err
}

var cfa int64 = 0

fde, err := thread.dbp.frameEntries.FDEForPC(regs.PC())
if err != nil {
return 0, err
if err == nil {
fctx := fde.EstablishFrame(regs.PC())
cfa = fctx.CFAOffset() + int64(regs.SP())
}

fctx := fde.EstablishFrame(regs.PC())
cfa := fctx.CFAOffset() + int64(regs.SP())
address, err := op.ExecuteStackProgram(cfa, instructions)
if err != nil {
return 0, err
Expand Down
54 changes: 54 additions & 0 deletions proc/version.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package proc

import (
"strconv"
"strings"
)

type GoVersion struct {
Major int
Minor int
Rev int
}

func parseVersionString(ver string) (GoVersion, bool) {
if ver[:2] != "go" {
return GoVersion{}, false
}
v := strings.SplitN(ver[2:], ".", 3)
if len(v) != 3 {
return GoVersion{}, false
}

var r GoVersion
var err1, err2, err3 error

r.Major, err1 = strconv.Atoi(v[0])
r.Minor, err2 = strconv.Atoi(v[1])
r.Rev, err3 = strconv.Atoi(v[2])
if err1 != nil || err2 != nil || err3 != nil {
return GoVersion{}, false
}

return r, true
}

func (a *GoVersion) After(b GoVersion) bool {
if a.Major < b.Major {
return false
} else if a.Major > b.Major {
return true
}

if a.Minor < b.Minor {
return false
} else if a.Minor > b.Minor {
return true
}

if a.Rev < b.Rev {
return false
}

return true
}

0 comments on commit 8b78a52

Please sign in to comment.