bugfix, Issue #163: offset of g struct in TLS picked based on the value of runtime.buildVersion and presence of compile units created by GNU AS, instead of being fixed to -16

This commit is contained in:
aarzilli 2015-07-23 19:08:28 +02:00
parent efb5ef97c7
commit d0f3459efb
8 changed files with 203 additions and 23 deletions

12
_fixtures/cgotest.go Normal file

@ -0,0 +1,12 @@
package main
/*
char* foo(void) { return "hello, world!"; }
*/
import "C"
import "fmt"
func main() {
fmt.Println(C.GoString(C.foo()))
}

@ -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)
@ -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
}

@ -312,3 +312,17 @@ func (reader *Reader) NextPackageVariable() (*dwarf.Entry, error) {
// No more items
return nil, nil
}
func (reader *Reader) NextCompileUnit() (*dwarf.Entry, error) {
for entry, err := reader.Next(); entry != nil; entry, err = reader.Next() {
if err != nil {
return nil, err
}
if entry.Tag == dwarf.TagCompileUnit {
return entry, nil
}
}
return nil, nil
}

@ -3,6 +3,7 @@ package proc
import "runtime"
type Arch interface {
SetCurGInstructions(ver GoVersion, iscgo bool)
PtrSize() int
BreakpointInstruction() []byte
BreakpointSize() int
@ -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, isextld bool) {
var curg []byte
switch runtime.GOOS {
case "darwin":
@ -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 isextld || 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 {

@ -596,6 +596,13 @@ func initializeDebugProcess(dbp *Process, path string, attach bool) (*Process, e
dbp.arch = AMD64Arch()
}
ver, isextld, err := dbp.getGoInformation()
if err != nil {
return nil, err
}
dbp.arch.SetCurGInstructions(ver, isextld)
return dbp, nil
}
@ -674,3 +681,34 @@ func (dbp *Process) execPtraceFunc(fn func()) {
dbp.ptraceChan <- fn
<-dbp.ptraceDoneChan
}
func (dbp *Process) getGoInformation() (ver GoVersion, isextld 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)
}
isextld = false
rdr := dbp.DwarfReader()
rdr.Seek(0)
for entry, err := rdr.NextCompileUnit(); entry != nil; entry, err = rdr.NextCompileUnit() {
if err != nil {
return ver, isextld, err
}
if prod, ok := entry.Val(dwarf.AttrProducer).(string); ok && (strings.HasPrefix(prod, "GNU AS")) {
isextld = true
break
}
}
return
}

@ -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)
})
}

@ -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 {
@ -387,13 +408,13 @@ func (thread *Thread) executeStackProgram(instructions []byte) (int64, error) {
return 0, err
}
fde, err := thread.dbp.frameEntries.FDEForPC(regs.PC())
if err != nil {
return 0, err
}
var cfa int64 = 0
fctx := fde.EstablishFrame(regs.PC())
cfa := fctx.CFAOffset() + int64(regs.SP())
fde, err := thread.dbp.frameEntries.FDEForPC(regs.PC())
if err == nil {
fctx := fde.EstablishFrame(regs.PC())
cfa = fctx.CFAOffset() + int64(regs.SP())
}
address, err := op.ExecuteStackProgram(cfa, instructions)
if err != nil {
return 0, err

54
proc/version.go Normal file

@ -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
}