dwarf/frame,proc: use eh_frame section (#2344)
The eh_frame section is similar to debug_frame but uses a slightly different format. Gcc and clang by default only emit eh_frame.
This commit is contained in:
parent
6a70d531bb
commit
314ae669a3
@ -18,6 +18,9 @@ type CommonInformationEntry struct {
|
|||||||
ReturnAddressRegister uint64
|
ReturnAddressRegister uint64
|
||||||
InitialInstructions []byte
|
InitialInstructions []byte
|
||||||
staticBase uint64
|
staticBase uint64
|
||||||
|
|
||||||
|
// eh_frame pointer encoding
|
||||||
|
ptrEncAddr ptrEnc
|
||||||
}
|
}
|
||||||
|
|
||||||
// FrameDescriptionEntry represents a Frame Descriptor Entry in the
|
// FrameDescriptionEntry represents a Frame Descriptor Entry in the
|
||||||
@ -80,8 +83,64 @@ func (fdes FrameDescriptionEntries) FDEForPC(pc uint64) (*FrameDescriptionEntry,
|
|||||||
// Append appends otherFDEs to fdes and returns the result.
|
// Append appends otherFDEs to fdes and returns the result.
|
||||||
func (fdes FrameDescriptionEntries) Append(otherFDEs FrameDescriptionEntries) FrameDescriptionEntries {
|
func (fdes FrameDescriptionEntries) Append(otherFDEs FrameDescriptionEntries) FrameDescriptionEntries {
|
||||||
r := append(fdes, otherFDEs...)
|
r := append(fdes, otherFDEs...)
|
||||||
sort.Slice(r, func(i, j int) bool {
|
sort.SliceStable(r, func(i, j int) bool {
|
||||||
return r[i].Begin() < r[j].Begin()
|
return r[i].Begin() < r[j].Begin()
|
||||||
})
|
})
|
||||||
|
// remove duplicates
|
||||||
|
uniqFDEs := fdes[:0]
|
||||||
|
for _, fde := range fdes {
|
||||||
|
if len(uniqFDEs) > 0 {
|
||||||
|
last := uniqFDEs[len(uniqFDEs)-1]
|
||||||
|
if last.Begin() == fde.Begin() && last.End() == fde.End() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
uniqFDEs = append(uniqFDEs, fde)
|
||||||
|
}
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ptrEnc represents a pointer encoding value, used during eh_frame decoding
|
||||||
|
// to determine how pointers were encoded.
|
||||||
|
// Least significant 4 (0xf) bytes encode the size as well as its
|
||||||
|
// signed-ness, most significant 4 bytes (0xf0) are flags describing how
|
||||||
|
// the value should be interpreted (absolute, relative...)
|
||||||
|
// See https://www.airs.com/blog/archives/460.
|
||||||
|
type ptrEnc uint8
|
||||||
|
|
||||||
|
const (
|
||||||
|
ptrEncAbs ptrEnc = 0x00 // pointer-sized unsigned integer
|
||||||
|
ptrEncOmit ptrEnc = 0xff // omitted
|
||||||
|
ptrEncUleb ptrEnc = 0x01 // ULEB128
|
||||||
|
ptrEncUdata2 ptrEnc = 0x02 // 2 bytes
|
||||||
|
ptrEncUdata4 ptrEnc = 0x03 // 4 bytes
|
||||||
|
ptrEncUdata8 ptrEnc = 0x04 // 8 bytes
|
||||||
|
ptrEncSigned ptrEnc = 0x08 // pointer-sized signed integer
|
||||||
|
ptrEncSleb ptrEnc = 0x09 // SLEB128
|
||||||
|
ptrEncSdata2 ptrEnc = 0x0a // 2 bytes, signed
|
||||||
|
ptrEncSdata4 ptrEnc = 0x0b // 4 bytes, signed
|
||||||
|
ptrEncSdata8 ptrEnc = 0x0c // 8 bytes, signed
|
||||||
|
|
||||||
|
ptrEncPCRel ptrEnc = 0x10 // value is relative to the memory address where it appears
|
||||||
|
ptrEncTextRel ptrEnc = 0x20 // value is relative to the address of the text section
|
||||||
|
ptrEncDataRel ptrEnc = 0x30 // value is relative to the address of the data section
|
||||||
|
ptrEncFuncRel ptrEnc = 0x40 // value is relative to the start of the function
|
||||||
|
ptrEncAligned ptrEnc = 0x50 // value should be aligned
|
||||||
|
ptrEncIndirect ptrEnc = 0x80 // value is an address where the real value of the pointer is stored
|
||||||
|
)
|
||||||
|
|
||||||
|
// Supported returns true if this pointer encoding is supported.
|
||||||
|
func (ptrEnc ptrEnc) Supported() bool {
|
||||||
|
if ptrEnc != ptrEncOmit {
|
||||||
|
szenc := ptrEnc & 0x0f
|
||||||
|
if ((szenc > ptrEncUdata8) && (szenc < ptrEncSigned)) || (szenc > ptrEncSdata8) {
|
||||||
|
// These values aren't defined at the moment
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if ptrEnc&0xf0 != ptrEncPCRel {
|
||||||
|
// Currently only the PC relative flag is supported
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|||||||
@ -67,7 +67,7 @@ func BenchmarkFDEForPC(b *testing.B) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
b.Fatal(err)
|
b.Fatal(err)
|
||||||
}
|
}
|
||||||
fdes := Parse(data, binary.BigEndian, 0, ptrSizeByRuntimeArch())
|
fdes, _ := Parse(data, binary.BigEndian, 0, ptrSizeByRuntimeArch(), 0)
|
||||||
|
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
// bench worst case, exhaustive search
|
// bench worst case, exhaustive search
|
||||||
|
|||||||
@ -6,6 +6,8 @@ package frame
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
|
||||||
"github.com/go-delve/delve/pkg/dwarf/util"
|
"github.com/go-delve/delve/pkg/dwarf/util"
|
||||||
)
|
)
|
||||||
@ -16,81 +18,134 @@ type parseContext struct {
|
|||||||
staticBase uint64
|
staticBase uint64
|
||||||
|
|
||||||
buf *bytes.Buffer
|
buf *bytes.Buffer
|
||||||
|
totalLen int
|
||||||
entries FrameDescriptionEntries
|
entries FrameDescriptionEntries
|
||||||
|
ciemap map[int]*CommonInformationEntry
|
||||||
common *CommonInformationEntry
|
common *CommonInformationEntry
|
||||||
frame *FrameDescriptionEntry
|
frame *FrameDescriptionEntry
|
||||||
length uint32
|
length uint32
|
||||||
ptrSize int
|
ptrSize int
|
||||||
|
ehFrameAddr uint64
|
||||||
|
err error
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse takes in data (a byte slice) and returns FrameDescriptionEntries,
|
// Parse takes in data (a byte slice) and returns FrameDescriptionEntries,
|
||||||
// which is a slice of FrameDescriptionEntry. Each FrameDescriptionEntry
|
// which is a slice of FrameDescriptionEntry. Each FrameDescriptionEntry
|
||||||
// has a pointer to CommonInformationEntry.
|
// has a pointer to CommonInformationEntry.
|
||||||
func Parse(data []byte, order binary.ByteOrder, staticBase uint64, ptrSize int) FrameDescriptionEntries {
|
// If ehFrameAddr is not zero the .eh_frame format will be used, a minor variant of DWARF described at https://www.airs.com/blog/archives/460.
|
||||||
|
// The value of ehFrameAddr will be used as the address at which eh_frame will be mapped into memory
|
||||||
|
func Parse(data []byte, order binary.ByteOrder, staticBase uint64, ptrSize int, ehFrameAddr uint64) (FrameDescriptionEntries, error) {
|
||||||
var (
|
var (
|
||||||
buf = bytes.NewBuffer(data)
|
buf = bytes.NewBuffer(data)
|
||||||
pctx = &parseContext{buf: buf, entries: newFrameIndex(), staticBase: staticBase, ptrSize: ptrSize}
|
pctx = &parseContext{buf: buf, totalLen: len(data), entries: newFrameIndex(), staticBase: staticBase, ptrSize: ptrSize, ehFrameAddr: ehFrameAddr, ciemap: map[int]*CommonInformationEntry{}}
|
||||||
)
|
)
|
||||||
|
|
||||||
for fn := parselength; buf.Len() != 0; {
|
for fn := parselength; buf.Len() != 0; {
|
||||||
fn = fn(pctx)
|
fn = fn(pctx)
|
||||||
|
if pctx.err != nil {
|
||||||
|
return nil, pctx.err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := range pctx.entries {
|
for i := range pctx.entries {
|
||||||
pctx.entries[i].order = order
|
pctx.entries[i].order = order
|
||||||
}
|
}
|
||||||
|
|
||||||
return pctx.entries
|
return pctx.entries, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func cieEntry(data []byte) bool {
|
func (ctx *parseContext) parsingEHFrame() bool {
|
||||||
return bytes.Equal(data, []byte{0xff, 0xff, 0xff, 0xff})
|
return ctx.ehFrameAddr > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ctx *parseContext) cieEntry(cieid uint32) bool {
|
||||||
|
if ctx.parsingEHFrame() {
|
||||||
|
return cieid == 0x00
|
||||||
|
}
|
||||||
|
return cieid == 0xffffffff
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ctx *parseContext) offset() int {
|
||||||
|
return ctx.totalLen - ctx.buf.Len()
|
||||||
}
|
}
|
||||||
|
|
||||||
func parselength(ctx *parseContext) parsefunc {
|
func parselength(ctx *parseContext) parsefunc {
|
||||||
binary.Read(ctx.buf, binary.LittleEndian, &ctx.length)
|
start := ctx.offset()
|
||||||
|
binary.Read(ctx.buf, binary.LittleEndian, &ctx.length) //TODO(aarzilli): this does not support 64bit DWARF
|
||||||
|
|
||||||
if ctx.length == 0 {
|
if ctx.length == 0 {
|
||||||
// ZERO terminator
|
// ZERO terminator
|
||||||
return parselength
|
return parselength
|
||||||
}
|
}
|
||||||
|
|
||||||
var data = ctx.buf.Next(4)
|
var cieid uint32
|
||||||
|
binary.Read(ctx.buf, binary.LittleEndian, &cieid)
|
||||||
|
|
||||||
ctx.length -= 4 // take off the length of the CIE id / CIE pointer.
|
ctx.length -= 4 // take off the length of the CIE id / CIE pointer.
|
||||||
|
|
||||||
if cieEntry(data) {
|
if ctx.cieEntry(cieid) {
|
||||||
ctx.common = &CommonInformationEntry{Length: ctx.length, staticBase: ctx.staticBase}
|
ctx.common = &CommonInformationEntry{Length: ctx.length, staticBase: ctx.staticBase}
|
||||||
|
ctx.ciemap[start] = ctx.common
|
||||||
return parseCIE
|
return parseCIE
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.frame = &FrameDescriptionEntry{Length: ctx.length, CIE: ctx.common}
|
if ctx.ehFrameAddr > 0 {
|
||||||
|
cieid = uint32(start - int(cieid) + 4)
|
||||||
|
}
|
||||||
|
|
||||||
|
common := ctx.ciemap[int(cieid)]
|
||||||
|
|
||||||
|
if common == nil {
|
||||||
|
ctx.err = fmt.Errorf("unknown CIE_id %#x at %#x", cieid, start)
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.frame = &FrameDescriptionEntry{Length: ctx.length, CIE: common}
|
||||||
return parseFDE
|
return parseFDE
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseFDE(ctx *parseContext) parsefunc {
|
func parseFDE(ctx *parseContext) parsefunc {
|
||||||
var num uint64
|
startOff := ctx.offset()
|
||||||
r := ctx.buf.Next(int(ctx.length))
|
r := ctx.buf.Next(int(ctx.length))
|
||||||
|
|
||||||
reader := bytes.NewReader(r)
|
reader := bytes.NewReader(r)
|
||||||
num, _ = util.ReadUintRaw(reader, binary.LittleEndian, ctx.ptrSize)
|
num := ctx.readEncodedPtr(addrSum(ctx.ehFrameAddr+uint64(startOff), reader), reader, ctx.frame.CIE.ptrEncAddr)
|
||||||
ctx.frame.begin = num + ctx.staticBase
|
ctx.frame.begin = num + ctx.staticBase
|
||||||
num, _ = util.ReadUintRaw(reader, binary.LittleEndian, ctx.ptrSize)
|
|
||||||
ctx.frame.size = num
|
// For the size field in .eh_frame only the size encoding portion of the
|
||||||
|
// address pointer encoding is considered.
|
||||||
|
// See decode_frame_entry_1 in gdb/dwarf2-frame.c.
|
||||||
|
// For .debug_frame ptrEncAddr is always ptrEncAbs and never has flags.
|
||||||
|
sizePtrEnc := ctx.frame.CIE.ptrEncAddr & 0x0f
|
||||||
|
ctx.frame.size = ctx.readEncodedPtr(0, reader, sizePtrEnc)
|
||||||
|
|
||||||
// Insert into the tree after setting address range begin
|
// Insert into the tree after setting address range begin
|
||||||
// otherwise compares won't work.
|
// otherwise compares won't work.
|
||||||
ctx.entries = append(ctx.entries, ctx.frame)
|
ctx.entries = append(ctx.entries, ctx.frame)
|
||||||
|
|
||||||
|
if ctx.parsingEHFrame() && len(ctx.frame.CIE.Augmentation) > 0 {
|
||||||
|
// If we are parsing a .eh_frame and we saw an agumentation string then we
|
||||||
|
// need to read the augmentation data, which are encoded as a ULEB128
|
||||||
|
// size followed by 'size' bytes.
|
||||||
|
n, _ := util.DecodeULEB128(reader)
|
||||||
|
reader.Seek(int64(n), io.SeekCurrent)
|
||||||
|
}
|
||||||
|
|
||||||
// The rest of this entry consists of the instructions
|
// The rest of this entry consists of the instructions
|
||||||
// so we can just grab all of the data from the buffer
|
// so we can just grab all of the data from the buffer
|
||||||
// cursor to length.
|
// cursor to length.
|
||||||
ctx.frame.Instructions = r[2*ctx.ptrSize:]
|
|
||||||
|
off, _ := reader.Seek(0, io.SeekCurrent)
|
||||||
|
ctx.frame.Instructions = r[off:]
|
||||||
ctx.length = 0
|
ctx.length = 0
|
||||||
|
|
||||||
return parselength
|
return parselength
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func addrSum(base uint64, buf *bytes.Reader) uint64 {
|
||||||
|
n, _ := buf.Seek(0, io.SeekCurrent)
|
||||||
|
return base + uint64(n)
|
||||||
|
}
|
||||||
|
|
||||||
func parseCIE(ctx *parseContext) parsefunc {
|
func parseCIE(ctx *parseContext) parsefunc {
|
||||||
data := ctx.buf.Next(int(ctx.length))
|
data := ctx.buf.Next(int(ctx.length))
|
||||||
buf := bytes.NewBuffer(data)
|
buf := bytes.NewBuffer(data)
|
||||||
@ -100,6 +155,15 @@ func parseCIE(ctx *parseContext) parsefunc {
|
|||||||
// parse augmentation
|
// parse augmentation
|
||||||
ctx.common.Augmentation, _ = util.ParseString(buf)
|
ctx.common.Augmentation, _ = util.ParseString(buf)
|
||||||
|
|
||||||
|
if ctx.parsingEHFrame() {
|
||||||
|
if ctx.common.Augmentation == "eh" {
|
||||||
|
ctx.err = fmt.Errorf("unsupported 'eh' augmentation at %#x", ctx.offset())
|
||||||
|
}
|
||||||
|
if len(ctx.common.Augmentation) > 0 && ctx.common.Augmentation[0] != 'z' {
|
||||||
|
ctx.err = fmt.Errorf("unsupported augmentation at %#x (does not start with 'z')", ctx.offset())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// parse code alignment factor
|
// parse code alignment factor
|
||||||
ctx.common.CodeAlignmentFactor, _ = util.DecodeULEB128(buf)
|
ctx.common.CodeAlignmentFactor, _ = util.DecodeULEB128(buf)
|
||||||
|
|
||||||
@ -107,7 +171,48 @@ func parseCIE(ctx *parseContext) parsefunc {
|
|||||||
ctx.common.DataAlignmentFactor, _ = util.DecodeSLEB128(buf)
|
ctx.common.DataAlignmentFactor, _ = util.DecodeSLEB128(buf)
|
||||||
|
|
||||||
// parse return address register
|
// parse return address register
|
||||||
|
if ctx.parsingEHFrame() && ctx.common.Version == 1 {
|
||||||
|
b, _ := buf.ReadByte()
|
||||||
|
ctx.common.ReturnAddressRegister = uint64(b)
|
||||||
|
} else {
|
||||||
ctx.common.ReturnAddressRegister, _ = util.DecodeULEB128(buf)
|
ctx.common.ReturnAddressRegister, _ = util.DecodeULEB128(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.common.ptrEncAddr = ptrEncAbs
|
||||||
|
|
||||||
|
if ctx.parsingEHFrame() && len(ctx.common.Augmentation) > 0 {
|
||||||
|
_, _ = util.DecodeULEB128(buf) // augmentation data length
|
||||||
|
for i := 1; i < len(ctx.common.Augmentation); i++ {
|
||||||
|
switch ctx.common.Augmentation[i] {
|
||||||
|
case 'L':
|
||||||
|
_, _ = buf.ReadByte() // LSDA pointer encoding, we don't support this.
|
||||||
|
case 'R':
|
||||||
|
// Pointer encoding, describes how begin and size fields of FDEs are encoded.
|
||||||
|
b, _ := buf.ReadByte()
|
||||||
|
ctx.common.ptrEncAddr = ptrEnc(b)
|
||||||
|
if !ctx.common.ptrEncAddr.Supported() {
|
||||||
|
ctx.err = fmt.Errorf("pointer encoding not supported %#x at %#x", ctx.common.ptrEncAddr, ctx.offset())
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
case 'S':
|
||||||
|
// Signal handler invocation frame, we don't support this but there is no associated data to read.
|
||||||
|
case 'P':
|
||||||
|
// Personality function encoded as a pointer encoding byte followed by
|
||||||
|
// the pointer to the personality function encoded as specified by the
|
||||||
|
// pointer encoding.
|
||||||
|
// We don't support this but have to read it anyway.
|
||||||
|
e, _ := buf.ReadByte()
|
||||||
|
if !ptrEnc(e).Supported() {
|
||||||
|
ctx.err = fmt.Errorf("pointer encoding not supported %#x at %#x", e, ctx.offset())
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
ctx.readEncodedPtr(0, buf, ptrEnc(e))
|
||||||
|
default:
|
||||||
|
ctx.err = fmt.Errorf("unsupported augmentation character %c at %#x", ctx.common.Augmentation[i], ctx.offset())
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// parse initial instructions
|
// parse initial instructions
|
||||||
// The rest of this entry consists of the instructions
|
// The rest of this entry consists of the instructions
|
||||||
@ -119,6 +224,48 @@ func parseCIE(ctx *parseContext) parsefunc {
|
|||||||
return parselength
|
return parselength
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// readEncodedPtr reads a pointer from buf encoded as specified by ptrEnc.
|
||||||
|
// This function is used to read pointers from a .eh_frame section, when
|
||||||
|
// used to parse a .debug_frame section ptrEnc will always be ptrEncAbs.
|
||||||
|
// The parameter addr is the address that the current byte of 'buf' will be
|
||||||
|
// mapped to when the executable file containing the eh_frame section being
|
||||||
|
// parse is loaded in memory.
|
||||||
|
func (ctx *parseContext) readEncodedPtr(addr uint64, buf util.ByteReaderWithLen, ptrEnc ptrEnc) uint64 {
|
||||||
|
if ptrEnc == ptrEncOmit {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
var ptr uint64
|
||||||
|
|
||||||
|
switch ptrEnc & 0xf {
|
||||||
|
case ptrEncAbs, ptrEncSigned:
|
||||||
|
ptr, _ = util.ReadUintRaw(buf, binary.LittleEndian, ctx.ptrSize)
|
||||||
|
case ptrEncUleb:
|
||||||
|
ptr, _ = util.DecodeULEB128(buf)
|
||||||
|
case ptrEncUdata2:
|
||||||
|
ptr, _ = util.ReadUintRaw(buf, binary.LittleEndian, 2)
|
||||||
|
case ptrEncSdata2:
|
||||||
|
ptr, _ = util.ReadUintRaw(buf, binary.LittleEndian, 2)
|
||||||
|
ptr = uint64(int16(ptr))
|
||||||
|
case ptrEncUdata4:
|
||||||
|
ptr, _ = util.ReadUintRaw(buf, binary.LittleEndian, 4)
|
||||||
|
case ptrEncSdata4:
|
||||||
|
ptr, _ = util.ReadUintRaw(buf, binary.LittleEndian, 4)
|
||||||
|
ptr = uint64(int32(ptr))
|
||||||
|
case ptrEncUdata8, ptrEncSdata8:
|
||||||
|
ptr, _ = util.ReadUintRaw(buf, binary.LittleEndian, 8)
|
||||||
|
case ptrEncSleb:
|
||||||
|
n, _ := util.DecodeSLEB128(buf)
|
||||||
|
ptr = uint64(n)
|
||||||
|
}
|
||||||
|
|
||||||
|
if ptrEnc&0xf0 == ptrEncPCRel {
|
||||||
|
ptr += addr
|
||||||
|
}
|
||||||
|
|
||||||
|
return ptr
|
||||||
|
}
|
||||||
|
|
||||||
// DwarfEndian determines the endianness of the DWARF by using the version number field in the debug_info section
|
// DwarfEndian determines the endianness of the DWARF by using the version number field in the debug_info section
|
||||||
// Trick borrowed from "debug/dwarf".New()
|
// Trick borrowed from "debug/dwarf".New()
|
||||||
func DwarfEndian(infoSec []byte) binary.ByteOrder {
|
func DwarfEndian(infoSec []byte) binary.ByteOrder {
|
||||||
|
|||||||
@ -14,6 +14,7 @@ func TestParseCIE(t *testing.T) {
|
|||||||
common: &CommonInformationEntry{Length: 12},
|
common: &CommonInformationEntry{Length: 12},
|
||||||
length: 12,
|
length: 12,
|
||||||
}
|
}
|
||||||
|
ctx.totalLen = ctx.buf.Len()
|
||||||
_ = parseCIE(ctx)
|
_ = parseCIE(ctx)
|
||||||
|
|
||||||
common := ctx.common
|
common := ctx.common
|
||||||
@ -53,6 +54,6 @@ func BenchmarkParse(b *testing.B) {
|
|||||||
|
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
Parse(data, binary.BigEndian, 0, ptrSizeByRuntimeArch())
|
Parse(data, binary.BigEndian, 0, ptrSizeByRuntimeArch(), 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,12 +8,20 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// ByteReaderWithLen is a io.ByteReader with a Len method. This interface is
|
||||||
|
// satisified by both bytes.Buffer and bytes.Reader.
|
||||||
|
type ByteReaderWithLen interface {
|
||||||
|
io.ByteReader
|
||||||
|
io.Reader
|
||||||
|
Len() int
|
||||||
|
}
|
||||||
|
|
||||||
// The Little Endian Base 128 format is defined in the DWARF v4 standard,
|
// The Little Endian Base 128 format is defined in the DWARF v4 standard,
|
||||||
// section 7.6, page 161 and following.
|
// section 7.6, page 161 and following.
|
||||||
|
|
||||||
// DecodeULEB128 decodes an unsigned Little Endian Base 128
|
// DecodeULEB128 decodes an unsigned Little Endian Base 128
|
||||||
// represented number.
|
// represented number.
|
||||||
func DecodeULEB128(buf *bytes.Buffer) (uint64, uint32) {
|
func DecodeULEB128(buf ByteReaderWithLen) (uint64, uint32) {
|
||||||
var (
|
var (
|
||||||
result uint64
|
result uint64
|
||||||
shift uint64
|
shift uint64
|
||||||
@ -46,7 +54,7 @@ func DecodeULEB128(buf *bytes.Buffer) (uint64, uint32) {
|
|||||||
|
|
||||||
// DecodeSLEB128 decodes a signed Little Endian Base 128
|
// DecodeSLEB128 decodes a signed Little Endian Base 128
|
||||||
// represented number.
|
// represented number.
|
||||||
func DecodeSLEB128(buf *bytes.Buffer) (int64, uint32) {
|
func DecodeSLEB128(buf ByteReaderWithLen) (int64, uint32) {
|
||||||
var (
|
var (
|
||||||
b byte
|
b byte
|
||||||
err error
|
err error
|
||||||
|
|||||||
@ -809,7 +809,7 @@ func (bi *BinaryInfo) LoadImageFromData(dwdata *dwarf.Data, debugFrameBytes, deb
|
|||||||
image.dwarfTreeCache, _ = simplelru.NewLRU(dwarfTreeCacheSize, nil)
|
image.dwarfTreeCache, _ = simplelru.NewLRU(dwarfTreeCacheSize, nil)
|
||||||
|
|
||||||
if debugFrameBytes != nil {
|
if debugFrameBytes != nil {
|
||||||
bi.frameEntries = frame.Parse(debugFrameBytes, frame.DwarfEndian(debugFrameBytes), 0, bi.Arch.PtrSize())
|
bi.frameEntries, _ = frame.Parse(debugFrameBytes, frame.DwarfEndian(debugFrameBytes), 0, bi.Arch.PtrSize(), 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
image.loclist2 = loclist.NewDwarf2Reader(debugLocBytes, bi.Arch.PtrSize())
|
image.loclist2 = loclist.NewDwarf2Reader(debugLocBytes, bi.Arch.PtrSize())
|
||||||
@ -1009,6 +1009,38 @@ func (bi *BinaryInfo) funcToImage(fn *Function) *Image {
|
|||||||
return fn.cu.image
|
return fn.cu.image
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// parseDebugFrameGeneral parses a debug_frame and a eh_frame section.
|
||||||
|
// At least one of the two must be present and parsed correctly, if
|
||||||
|
// debug_frame is present it must be parsable correctly.
|
||||||
|
func (bi *BinaryInfo) parseDebugFrameGeneral(image *Image, debugFrameBytes []byte, debugFrameName string, debugFrameErr error, ehFrameBytes []byte, ehFrameAddr uint64, ehFrameName string, byteOrder binary.ByteOrder) {
|
||||||
|
if debugFrameBytes == nil && ehFrameBytes == nil {
|
||||||
|
image.setLoadError("could not get %s section: %v", debugFrameName, debugFrameErr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if debugFrameBytes != nil {
|
||||||
|
fe, err := frame.Parse(debugFrameBytes, byteOrder, image.StaticBase, bi.Arch.PtrSize(), 0)
|
||||||
|
if err != nil {
|
||||||
|
image.setLoadError("could not parse %s section: %v", debugFrameName, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
bi.frameEntries = bi.frameEntries.Append(fe)
|
||||||
|
}
|
||||||
|
|
||||||
|
if ehFrameBytes != nil && ehFrameAddr > 0 {
|
||||||
|
fe, err := frame.Parse(ehFrameBytes, byteOrder, image.StaticBase, bi.Arch.PtrSize(), ehFrameAddr)
|
||||||
|
if err != nil {
|
||||||
|
if debugFrameBytes == nil {
|
||||||
|
image.setLoadError("could not parse %s section: %v", ehFrameName, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
bi.logger.Warnf("could not parse %s section: %v", ehFrameName, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
bi.frameEntries = bi.frameEntries.Append(fe)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ELF ///////////////////////////////////////////////////////////////
|
// ELF ///////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
// ErrNoBuildIDNote is used in openSeparateDebugInfo to signal there's no
|
// ErrNoBuildIDNote is used in openSeparateDebugInfo to signal there's no
|
||||||
@ -1207,13 +1239,16 @@ func (bi *BinaryInfo) loadSymbolName(image *Image, file *elf.File, wg *sync.Wait
|
|||||||
func (bi *BinaryInfo) parseDebugFrameElf(image *Image, exe *elf.File, debugInfoBytes []byte, wg *sync.WaitGroup) {
|
func (bi *BinaryInfo) parseDebugFrameElf(image *Image, exe *elf.File, debugInfoBytes []byte, wg *sync.WaitGroup) {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
|
|
||||||
debugFrameData, err := godwarf.GetDebugSectionElf(exe, "frame")
|
debugFrameData, debugFrameErr := godwarf.GetDebugSectionElf(exe, "frame")
|
||||||
if err != nil {
|
ehFrameSection := exe.Section(".eh_frame")
|
||||||
image.setLoadError("could not get .debug_frame section: %v", err)
|
var ehFrameData []byte
|
||||||
return
|
var ehFrameAddr uint64
|
||||||
|
if ehFrameSection != nil {
|
||||||
|
ehFrameAddr = ehFrameSection.Addr
|
||||||
|
ehFrameData, _ = ehFrameSection.Data()
|
||||||
}
|
}
|
||||||
|
|
||||||
bi.frameEntries = bi.frameEntries.Append(frame.Parse(debugFrameData, frame.DwarfEndian(debugInfoBytes), image.StaticBase, bi.Arch.PtrSize()))
|
bi.parseDebugFrameGeneral(image, debugFrameData, ".debug_frame", debugFrameErr, ehFrameData, ehFrameAddr, ".eh_frame", frame.DwarfEndian(debugInfoBytes))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (bi *BinaryInfo) setGStructOffsetElf(image *Image, exe *elf.File, wg *sync.WaitGroup) {
|
func (bi *BinaryInfo) setGStructOffsetElf(image *Image, exe *elf.File, wg *sync.WaitGroup) {
|
||||||
@ -1363,12 +1398,7 @@ func (bi *BinaryInfo) parseDebugFramePE(image *Image, exe *pe.File, debugInfoByt
|
|||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
|
|
||||||
debugFrameBytes, err := godwarf.GetDebugSectionPE(exe, "frame")
|
debugFrameBytes, err := godwarf.GetDebugSectionPE(exe, "frame")
|
||||||
if err != nil {
|
bi.parseDebugFrameGeneral(image, debugFrameBytes, ".debug_frame", err, nil, 0, "", frame.DwarfEndian(debugInfoBytes))
|
||||||
image.setLoadError("could not get .debug_frame section: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
bi.frameEntries = bi.frameEntries.Append(frame.Parse(debugFrameBytes, frame.DwarfEndian(debugInfoBytes), image.StaticBase, bi.Arch.PtrSize()))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Borrowed from https://golang.org/src/cmd/internal/objfile/pe.go
|
// Borrowed from https://golang.org/src/cmd/internal/objfile/pe.go
|
||||||
@ -1453,13 +1483,16 @@ func (bi *BinaryInfo) setGStructOffsetMacho() {
|
|||||||
func (bi *BinaryInfo) parseDebugFrameMacho(image *Image, exe *macho.File, debugInfoBytes []byte, wg *sync.WaitGroup) {
|
func (bi *BinaryInfo) parseDebugFrameMacho(image *Image, exe *macho.File, debugInfoBytes []byte, wg *sync.WaitGroup) {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
|
|
||||||
debugFrameBytes, err := godwarf.GetDebugSectionMacho(exe, "frame")
|
debugFrameBytes, debugFrameErr := godwarf.GetDebugSectionMacho(exe, "frame")
|
||||||
if err != nil {
|
ehFrameSection := exe.Section("__eh_frame")
|
||||||
image.setLoadError("could not get __debug_frame section: %v", err)
|
var ehFrameBytes []byte
|
||||||
return
|
var ehFrameAddr uint64
|
||||||
|
if ehFrameSection != nil {
|
||||||
|
ehFrameAddr = ehFrameSection.Addr
|
||||||
|
ehFrameBytes, _ = ehFrameSection.Data()
|
||||||
}
|
}
|
||||||
|
|
||||||
bi.frameEntries = bi.frameEntries.Append(frame.Parse(debugFrameBytes, frame.DwarfEndian(debugInfoBytes), image.StaticBase, bi.Arch.PtrSize()))
|
bi.parseDebugFrameGeneral(image, debugFrameBytes, "__debug_frame", debugFrameErr, ehFrameBytes, ehFrameAddr, "__eh_frame", frame.DwarfEndian(debugInfoBytes))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do not call this function directly it isn't able to deal correctly with package paths
|
// Do not call this function directly it isn't able to deal correctly with package paths
|
||||||
|
|||||||
@ -1550,10 +1550,6 @@ func TestPluginVariables(t *testing.T) {
|
|||||||
func TestCgoEval(t *testing.T) {
|
func TestCgoEval(t *testing.T) {
|
||||||
protest.MustHaveCgo(t)
|
protest.MustHaveCgo(t)
|
||||||
|
|
||||||
if runtime.GOARCH == "arm64" {
|
|
||||||
t.Skip("cgo evaluation broken on arm64")
|
|
||||||
}
|
|
||||||
|
|
||||||
testcases := []varTest{
|
testcases := []varTest{
|
||||||
{"s", true, `"a string"`, `"a string"`, "*char", nil},
|
{"s", true, `"a string"`, `"a string"`, "*char", nil},
|
||||||
{"longstring", true, `"averylongstring0123456789a0123456789b0123456789c0123456789d01234...+1 more"`, `"averylongstring0123456789a0123456789b0123456789c0123456789d01234...+1 more"`, "*const char", nil},
|
{"longstring", true, `"averylongstring0123456789a0123456789b0123456789c0123456789d01234...+1 more"`, `"averylongstring0123456789a0123456789b0123456789c0123456789d01234...+1 more"`, "*const char", nil},
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user