Skip to content

Commit

Permalink
dwarf/op,proc: output register name when printing location exprs (go-…
Browse files Browse the repository at this point in the history
…delve#3052)

Output the architecture specific register name when printing location
expression after DW_OP_regN and DW_OP_bregN operators.
  • Loading branch information
aarzilli authored Jul 15, 2022
1 parent 059406e commit 5660f9a
Show file tree
Hide file tree
Showing 10 changed files with 52 additions and 12 deletions.
21 changes: 18 additions & 3 deletions pkg/dwarf/op/op.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,27 +103,39 @@ func ExecuteStackProgram(regs DwarfRegisters, instructions []byte, ptrSize int,
}

// PrettyPrint prints the DWARF stack program instructions to `out`.
func PrettyPrint(out io.Writer, instructions []byte) {
func PrettyPrint(out io.Writer, instructions []byte, regnumToName func(uint64) string) {
in := bytes.NewBuffer(instructions)

for {
opcode, err := in.ReadByte()
if err != nil {
break
}
if name, hasname := opcodeName[Opcode(opcode)]; hasname {
op := Opcode(opcode)
if name, hasname := opcodeName[op]; hasname {
io.WriteString(out, name)
if regnumToName != nil {
if op >= DW_OP_reg0 && op <= DW_OP_reg31 {
fmt.Fprintf(out, "(%s)", regnumToName(uint64(op-DW_OP_reg0)))
} else if op >= DW_OP_breg0 && op <= DW_OP_breg31 {
fmt.Fprintf(out, "(%s)", regnumToName(uint64(op-DW_OP_breg0)))
}
}
out.Write([]byte{' '})
} else {
fmt.Fprintf(out, "%#x ", opcode)
}
for _, arg := range opcodeArgs[Opcode(opcode)] {

for i, arg := range opcodeArgs[Opcode(opcode)] {
var regnum uint64
switch arg {
case 's':
n, _ := util.DecodeSLEB128(in)
regnum = uint64(n)
fmt.Fprintf(out, "%#x ", n)
case 'u':
n, _ := util.DecodeULEB128(in)
regnum = n
fmt.Fprintf(out, "%#x ", n)
case '1':
var x uint8
Expand All @@ -148,6 +160,9 @@ func PrettyPrint(out io.Writer, instructions []byte) {
data = data[:sz2]
fmt.Fprintf(out, "%d [%x] ", sz, data)
}
if regnumToName != nil && i == 0 && (op == DW_OP_regx || op == DW_OP_bregx) {
fmt.Fprintf(out, "(%s)", regnumToName(regnum))
}
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/dwarf/op/op_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ func assertExprResult(t *testing.T, expected int64, instructions []byte) {
}
if actual != expected {
buf := new(strings.Builder)
PrettyPrint(buf, instructions)
PrettyPrint(buf, instructions, nil)
t.Errorf("actual %d != expected %d (in %s)", actual, expected, buf.String())
}
}
Expand Down
4 changes: 2 additions & 2 deletions pkg/dwarf/regnum/i386.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,8 +93,8 @@ func I386MaxRegNum() int {
return max
}

func I386ToName(num int) string {
name, ok := i386DwarfToName[num]
func I386ToName(num uint64) string {
name, ok := i386DwarfToName[int(num)]
if ok {
return name
}
Expand Down
1 change: 1 addition & 0 deletions pkg/proc/amd64_arch.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ func AMD64Arch(goos string) *Arch {
ContextRegNum: regnum.AMD64_Rdx,
asmRegisters: amd64AsmRegisters,
RegisterNameToDwarf: nameToDwarfFunc(regnum.AMD64NameToDwarf),
RegnumToString: regnum.AMD64ToName,
debugCallMinStackSize: 256,
maxRegArgBytes: 9*8 + 15*8,
}
Expand Down
1 change: 1 addition & 0 deletions pkg/proc/arch.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ type Arch struct {
// inhibitStepInto returns whether StepBreakpoint can be set at pc.
inhibitStepInto func(bi *BinaryInfo, pc uint64) bool
RegisterNameToDwarf func(s string) (int, bool)
RegnumToString func(uint64) string
// debugCallMinStackSize is the minimum stack size for call injection on this architecture.
debugCallMinStackSize uint64
// maxRegArgBytes is extra padding for ABI1 call injections, equivalent to
Expand Down
1 change: 1 addition & 0 deletions pkg/proc/arm64_arch.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ func ARM64Arch(goos string) *Arch {
LRRegNum: regnum.ARM64_LR,
asmRegisters: arm64AsmRegisters,
RegisterNameToDwarf: nameToDwarfFunc(regnum.ARM64NameToDwarf),
RegnumToString: regnum.ARM64ToName,
debugCallMinStackSize: 288,
maxRegArgBytes: 16*8 + 16*8, // 16 int argument registers plus 16 float argument registers
}
Expand Down
10 changes: 6 additions & 4 deletions pkg/proc/bininfo.go
Original file line number Diff line number Diff line change
Expand Up @@ -975,7 +975,7 @@ func (bi *BinaryInfo) locationExpr(entry godwarf.Entry, attr dwarf.Attr, pc uint
return nil, nil, fmt.Errorf("no location attribute %s", attr)
}
if instr, ok := a.([]byte); ok {
return instr, &locationExpr{isBlock: true, instr: instr}, nil
return instr, &locationExpr{isBlock: true, instr: instr, regnumToName: bi.Arch.RegnumToString}, nil
}
off, ok := a.(int64)
if !ok {
Expand All @@ -985,7 +985,7 @@ func (bi *BinaryInfo) locationExpr(entry godwarf.Entry, attr dwarf.Attr, pc uint
if instr == nil {
return nil, nil, fmt.Errorf("could not find loclist entry at %#x for address %#x", off, pc)
}
return instr, &locationExpr{pc: pc, off: off, instr: instr}, nil
return instr, &locationExpr{pc: pc, off: off, instr: instr, regnumToName: bi.Arch.RegnumToString}, nil
}

type locationExpr struct {
Expand All @@ -994,6 +994,8 @@ type locationExpr struct {
off int64
pc uint64
instr []byte

regnumToName func(uint64) string
}

func (le *locationExpr) String() string {
Expand All @@ -1004,10 +1006,10 @@ func (le *locationExpr) String() string {

if le.isBlock {
fmt.Fprintf(&descr, "[block] ")
op.PrettyPrint(&descr, le.instr)
op.PrettyPrint(&descr, le.instr, le.regnumToName)
} else {
fmt.Fprintf(&descr, "[%#x:%#x] ", le.off, le.pc)
op.PrettyPrint(&descr, le.instr)
op.PrettyPrint(&descr, le.instr, le.regnumToName)
}

if le.isEscaped {
Expand Down
19 changes: 19 additions & 0 deletions pkg/proc/dwarf_expr_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,18 @@ func dwarfExprCheck(t *testing.T, scope *proc.EvalScope, testCases map[string]ui
}
}

func exprToStringCheck(t *testing.T, scope *proc.EvalScope, exprToStringCheck map[string]string) {
for name, expr := range exprToStringCheck {
thevar, err := scope.EvalExpression(name, normalLoadConfig)
assertNoError(err, t, fmt.Sprintf("EvalExpression(%s)", name))
out := thevar.LocationExpr.String()
t.Logf("%q -> %q\n", name, out)
if out != expr {
t.Errorf("%q expected expression: %q got %q", name, expr, out)
}
}
}

func dwarfRegisters(bi *proc.BinaryInfo, regs *linutil.AMD64Registers) *op.DwarfRegisters {
a := proc.AMD64Arch("linux")
so := bi.PCToImage(regs.PC())
Expand Down Expand Up @@ -159,6 +171,12 @@ func TestDwarfExprComposite(t *testing.T) {
"pair2.v": 0,
}

testCasesExprToString := map[string]string{
"pair": "[block] DW_OP_reg2(Rcx) DW_OP_piece 0x2 DW_OP_call_frame_cfa DW_OP_consts 0x10 DW_OP_plus DW_OP_piece 0x2 ",
"n": "[block] DW_OP_reg3(Rbx) ",
"pair2": "[block] DW_OP_reg2(Rcx) DW_OP_piece 0x2 DW_OP_piece 0x2 ",
}

const stringVal = "this is a string"

dwb := dwarfbuilder.New()
Expand Down Expand Up @@ -218,6 +236,7 @@ func TestDwarfExprComposite(t *testing.T) {
scope := fakeScope(mem, dwarfRegs, bi, mainfn)

dwarfExprCheck(t, scope, testCases)
exprToStringCheck(t, scope, testCasesExprToString)

thevar, err := scope.EvalExpression("s", normalLoadConfig)
assertNoError(err, t, fmt.Sprintf("EvalExpression(%s)", "s"))
Expand Down
3 changes: 2 additions & 1 deletion pkg/proc/i386_arch.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ func I386Arch(goos string) *Arch {
SPRegNum: regnum.I386_Esp,
asmRegisters: i386AsmRegisters,
RegisterNameToDwarf: nameToDwarfFunc(regnum.I386NameToDwarf),
RegnumToString: regnum.I386ToName,
}
}

Expand Down Expand Up @@ -208,7 +209,7 @@ func i386AddrAndStackRegsToDwarfRegisters(staticBase, pc, sp, bp, lr uint64) op.
}

func i386DwarfRegisterToString(j int, reg *op.DwarfRegister) (name string, floatingPoint bool, repr string) {
name = regnum.I386ToName(j)
name = regnum.I386ToName(uint64(j))

if reg == nil {
return name, false, ""
Expand Down
2 changes: 1 addition & 1 deletion pkg/proc/target.go
Original file line number Diff line number Diff line change
Expand Up @@ -510,7 +510,7 @@ func (t *Target) newCompositeMemory(mem MemoryReadWriter, regs op.DwarfRegisters
// this combination is guaranteed to be unique between resumes.
buf := new(strings.Builder)
fmt.Fprintf(buf, "%#x ", regs.CFA)
op.PrettyPrint(buf, descr.instr)
op.PrettyPrint(buf, descr.instr, t.BinInfo().Arch.RegnumToString)
key = buf.String()

if cmem := t.fakeMemoryRegistryMap[key]; cmem != nil {
Expand Down

0 comments on commit 5660f9a

Please sign in to comment.