mirror of
				https://github.com/go-delve/delve.git
				synced 2025-11-01 03:42:59 +08:00 
			
		
		
		
	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:
		
							
								
								
									
										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 | ||||
| } | ||||
		Reference in New Issue
	
	Block a user
	 aarzilli
					aarzilli