From b1640238ced65d64b76be27aaace915298195b60 Mon Sep 17 00:00:00 2001 From: aarzilli Date: Sun, 24 Jan 2016 09:47:22 +0100 Subject: [PATCH] dwarf/frame: detecting dwarf section endianness --- dwarf/frame/entries.go | 2 ++ dwarf/frame/entries_test.go | 3 ++- dwarf/frame/parser.go | 25 ++++++++++++++++++++++++- dwarf/frame/parser_test.go | 3 ++- dwarf/frame/table.go | 8 +++++--- proc/proc_darwin.go | 12 ++++++++++-- proc/proc_linux.go | 12 ++++++++++-- proc/proc_windows.go | 20 ++++++++++++++------ 8 files changed, 69 insertions(+), 16 deletions(-) diff --git a/dwarf/frame/entries.go b/dwarf/frame/entries.go index 66c3277d..6e52169b 100644 --- a/dwarf/frame/entries.go +++ b/dwarf/frame/entries.go @@ -1,6 +1,7 @@ package frame import ( + "encoding/binary" "fmt" "sort" ) @@ -25,6 +26,7 @@ type FrameDescriptionEntry struct { CIE *CommonInformationEntry Instructions []byte begin, end uint64 + order binary.ByteOrder } // Returns whether or not the given address is within the diff --git a/dwarf/frame/entries_test.go b/dwarf/frame/entries_test.go index 68250e1c..577ad165 100644 --- a/dwarf/frame/entries_test.go +++ b/dwarf/frame/entries_test.go @@ -1,6 +1,7 @@ package frame import ( + "encoding/binary" "io/ioutil" "os" "testing" @@ -39,7 +40,7 @@ func BenchmarkFDEForPC(b *testing.B) { if err != nil { b.Fatal(err) } - fdes := Parse(data) + fdes := Parse(data, binary.BigEndian) for i := 0; i < b.N; i++ { // bench worst case, exhaustive search diff --git a/dwarf/frame/parser.go b/dwarf/frame/parser.go index c4f36c9e..08018a64 100644 --- a/dwarf/frame/parser.go +++ b/dwarf/frame/parser.go @@ -23,7 +23,7 @@ type parseContext struct { // Parse takes in data (a byte slice) and returns a slice of // commonInformationEntry structures. Each commonInformationEntry // has a slice of frameDescriptionEntry structures. -func Parse(data []byte) FrameDescriptionEntries { +func Parse(data []byte, order binary.ByteOrder) FrameDescriptionEntries { var ( buf = bytes.NewBuffer(data) pctx = &parseContext{buf: buf, entries: NewFrameIndex()} @@ -33,6 +33,10 @@ func Parse(data []byte) FrameDescriptionEntries { fn = fn(pctx) } + for i := range pctx.entries { + pctx.entries[i].order = order + } + return pctx.entries } @@ -100,3 +104,22 @@ func parseCIE(ctx *parseContext) parsefunc { return parselength } + +// DwarfEndian determines the endianness of the DWARF by using the version number field in the debug_info section +// Trick borrowed from "debug/dwarf".New() +func DwarfEndian(infoSec []byte) binary.ByteOrder { + if len(infoSec) < 6 { + return binary.BigEndian + } + x, y := infoSec[4], infoSec[5] + switch { + case x == 0 && y == 0: + return binary.BigEndian + case x == 0: + return binary.BigEndian + case y == 0: + return binary.LittleEndian + default: + return binary.BigEndian + } +} diff --git a/dwarf/frame/parser_test.go b/dwarf/frame/parser_test.go index 53b571ce..a39202f2 100644 --- a/dwarf/frame/parser_test.go +++ b/dwarf/frame/parser_test.go @@ -1,6 +1,7 @@ package frame_test import ( + "encoding/binary" "io/ioutil" "os" "testing" @@ -24,6 +25,6 @@ func BenchmarkParse(b *testing.B) { b.ResetTimer() for i := 0; i < b.N; i++ { - frame.Parse(data) + frame.Parse(data, binary.BigEndian) } } diff --git a/dwarf/frame/table.go b/dwarf/frame/table.go index 22fcde30..6b999652 100644 --- a/dwarf/frame/table.go +++ b/dwarf/frame/table.go @@ -24,6 +24,7 @@ type DWRule struct { type FrameContext struct { loc uint64 + order binary.ByteOrder address uint64 cfa CurrentFrameAddress regs map[uint64]DWRule @@ -138,6 +139,7 @@ func executeCIEInstructions(cie *CommonInformationEntry) *FrameContext { // Unwind the stack to find the return address register. func executeDwarfProgramUntilPC(fde *FrameDescriptionEntry, pc uint64) *FrameContext { frame := executeCIEInstructions(fde.CIE) + frame.order = fde.order frame.loc = fde.Begin() frame.address = pc fdeInstructions := make([]byte, len(fde.Instructions)) @@ -237,14 +239,14 @@ func advanceloc1(frame *FrameContext) { func advanceloc2(frame *FrameContext) { var delta uint16 - binary.Read(frame.buf, binary.BigEndian, &delta) + binary.Read(frame.buf, frame.order, &delta) frame.loc += uint64(delta) * frame.codeAlignment } func advanceloc4(frame *FrameContext) { var delta uint32 - binary.Read(frame.buf, binary.BigEndian, &delta) + binary.Read(frame.buf, frame.order, &delta) frame.loc += uint64(delta) * frame.codeAlignment } @@ -280,7 +282,7 @@ func restore(frame *FrameContext) { func setloc(frame *FrameContext) { var loc uint64 - binary.Read(frame.buf, binary.BigEndian, &loc) + binary.Read(frame.buf, frame.order, &loc) frame.loc = loc } diff --git a/proc/proc_darwin.go b/proc/proc_darwin.go index db72d284..321a247a 100644 --- a/proc/proc_darwin.go +++ b/proc/proc_darwin.go @@ -193,13 +193,21 @@ func (dbp *Process) addThread(port int, attach bool) (*Thread, error) { func (dbp *Process) parseDebugFrame(exe *macho.File, wg *sync.WaitGroup) { defer wg.Done() - if sec := exe.Section("__debug_frame"); sec != nil { + debugFrameSec := exe.Section("__debug_frame") + debugInfoSec := exe.Section("__debug_info") + + if debugFrameSec != nil && debugInfoSec != nil { debugFrame, err := exe.Section("__debug_frame").Data() if err != nil { fmt.Println("could not get __debug_frame section", err) os.Exit(1) } - dbp.frameEntries = frame.Parse(debugFrame) + dat, err := debugInfoSec.Data() + if err != nil { + fmt.Println("could not get .debug_info section", err) + os.Exit(1) + } + dbp.frameEntries = frame.Parse(debugFrame, frame.DwarfEndian(dat)) } else { fmt.Println("could not find __debug_frame section in binary") os.Exit(1) diff --git a/proc/proc_linux.go b/proc/proc_linux.go index 42d4bf82..21c82a88 100644 --- a/proc/proc_linux.go +++ b/proc/proc_linux.go @@ -179,13 +179,21 @@ func (dbp *Process) findExecutable(path string) (*elf.File, error) { func (dbp *Process) parseDebugFrame(exe *elf.File, wg *sync.WaitGroup) { defer wg.Done() - if sec := exe.Section(".debug_frame"); sec != nil { + debugFrameSec := exe.Section(".debug_frame") + debugInfoSec := exe.Section(".debug_info") + + if debugFrameSec != nil && debugInfoSec != nil { debugFrame, err := exe.Section(".debug_frame").Data() if err != nil { fmt.Println("could not get .debug_frame section", err) os.Exit(1) } - dbp.frameEntries = frame.Parse(debugFrame) + dat, err := debugInfoSec.Data() + if err != nil { + fmt.Println("could not get .debug_info section", err) + os.Exit(1) + } + dbp.frameEntries = frame.Parse(debugFrame, frame.DwarfEndian(dat)) } else { fmt.Println("could not find .debug_frame section in binary") os.Exit(1) diff --git a/proc/proc_windows.go b/proc/proc_windows.go index 8a095f3a..d99c6b76 100644 --- a/proc/proc_windows.go +++ b/proc/proc_windows.go @@ -158,16 +158,24 @@ func (dbp *Process) addThread(hThread sys.Handle, threadID int, attach bool) (*T func (dbp *Process) parseDebugFrame(exe *pe.File, wg *sync.WaitGroup) { defer wg.Done() - if sec := exe.Section(".debug_frame"); sec != nil { - debugFrame, err := sec.Data() - if err != nil && uint32(len(debugFrame)) < sec.Size { + debugFrameSec := exe.Section(".debug_frame") + debugInfoSec := exe.Section(".debug_info") + + if debugFrameSec != nil && debugInfoSec != nil { + debugFrame, err := debugFrameSec.Data() + if err != nil && uint32(len(debugFrame)) < debugFrameSec.Size { fmt.Println("could not get .debug_frame section", err) os.Exit(1) } - if 0 < sec.VirtualSize && sec.VirtualSize < sec.Size { - debugFrame = debugFrame[:sec.VirtualSize] + if 0 < debugFrameSec.VirtualSize && debugFrameSec.VirtualSize < debugFrameSec.Size { + debugFrame = debugFrame[:debugFrameSec.VirtualSize] } - dbp.frameEntries = frame.Parse(debugFrame) + dat, err := debugInfoSec.Data() + if err != nil { + fmt.Println("could not get .debug_info section", err) + os.Exit(1) + } + dbp.frameEntries = frame.Parse(debugFrame, frame.DwarfEndian(dat)) } else { fmt.Println("could not find .debug_frame section in binary") os.Exit(1)