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:
parent
efb5ef97c7
commit
d0f3459efb
12
_fixtures/cgotest.go
Normal file
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
|
||||
}
|
||||
|
37
proc/arch.go
37
proc/arch.go
@ -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 {
|
||||
|
38
proc/proc.go
38
proc/proc.go
@ -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
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
|
||||
}
|
Loading…
Reference in New Issue
Block a user