1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288
|
// Package frame contains data structures and
// related functions for parsing and searching
// through Dwarf .debug_frame data.
package frame
import (
"bytes"
"encoding/binary"
"fmt"
"io"
"github.com/go-delve/delve/pkg/dwarf"
"github.com/go-delve/delve/pkg/dwarf/leb128"
)
type parsefunc func(*parseContext) parsefunc
type parseContext struct {
staticBase uint64
buf *bytes.Buffer
totalLen int
entries FrameDescriptionEntries
ciemap map[int]*CommonInformationEntry
common *CommonInformationEntry
frame *FrameDescriptionEntry
length uint32
ptrSize int
ehFrameAddr uint64
err error
}
// Parse takes in data (a byte slice) and returns FrameDescriptionEntries,
// which is a slice of FrameDescriptionEntry. Each FrameDescriptionEntry
// has a pointer to CommonInformationEntry.
// 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 (
buf = bytes.NewBuffer(data)
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; {
fn = fn(pctx)
if pctx.err != nil {
return nil, pctx.err
}
}
for i := range pctx.entries {
pctx.entries[i].order = order
}
return pctx.entries, nil
}
func (ctx *parseContext) parsingEHFrame() bool {
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 {
start := ctx.offset()
binary.Read(ctx.buf, binary.LittleEndian, &ctx.length) //TODO(aarzilli): this does not support 64bit DWARF
if ctx.length == 0 {
// ZERO terminator
return parselength
}
var cieid uint32
binary.Read(ctx.buf, binary.LittleEndian, &cieid)
ctx.length -= 4 // take off the length of the CIE id / CIE pointer.
if ctx.cieEntry(cieid) {
ctx.common = &CommonInformationEntry{Length: ctx.length, staticBase: ctx.staticBase, CIE_id: cieid}
ctx.ciemap[start] = ctx.common
return parseCIE
}
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
}
func parseFDE(ctx *parseContext) parsefunc {
startOff := ctx.offset()
r := ctx.buf.Next(int(ctx.length))
reader := bytes.NewReader(r)
num := ctx.readEncodedPtr(addrSum(ctx.ehFrameAddr+uint64(startOff), reader), reader, ctx.frame.CIE.ptrEncAddr)
ctx.frame.begin = num + ctx.staticBase
// 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
// otherwise compares won't work.
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 augmentation string then we
// need to read the augmentation data, which are encoded as a ULEB128
// size followed by 'size' bytes.
n, _ := leb128.DecodeUnsigned(reader)
reader.Seek(int64(n), io.SeekCurrent)
}
// The rest of this entry consists of the instructions
// so we can just grab all of the data from the buffer
// cursor to length.
off, _ := reader.Seek(0, io.SeekCurrent)
ctx.frame.Instructions = r[off:]
ctx.length = 0
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 {
data := ctx.buf.Next(int(ctx.length))
buf := bytes.NewBuffer(data)
// parse version
ctx.common.Version, _ = buf.ReadByte()
// parse augmentation
ctx.common.Augmentation, _ = dwarf.ReadString(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
ctx.common.CodeAlignmentFactor, _ = leb128.DecodeUnsigned(buf)
// parse data alignment factor
ctx.common.DataAlignmentFactor, _ = leb128.DecodeSigned(buf)
// parse return address register
if ctx.parsingEHFrame() && ctx.common.Version == 1 {
b, _ := buf.ReadByte()
ctx.common.ReturnAddressRegister = uint64(b)
} else {
ctx.common.ReturnAddressRegister, _ = leb128.DecodeUnsigned(buf)
}
ctx.common.ptrEncAddr = ptrEncAbs
if ctx.parsingEHFrame() && len(ctx.common.Augmentation) > 0 {
_, _ = leb128.DecodeUnsigned(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.
b, _ := buf.ReadByte()
e := ptrEnc(b) &^ ptrEncIndirect
if !e.Supported() {
ctx.err = fmt.Errorf("pointer encoding not supported %#x at %#x", e, ctx.offset())
return nil
}
ctx.readEncodedPtr(0, buf, e)
default:
ctx.err = fmt.Errorf("unsupported augmentation character %c at %#x", ctx.common.Augmentation[i], ctx.offset())
return nil
}
}
}
// parse initial instructions
// The rest of this entry consists of the instructions
// so we can just grab all of the data from the buffer
// cursor to length.
ctx.common.InitialInstructions = buf.Bytes() //ctx.buf.Next(int(ctx.length))
ctx.length = 0
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 leb128.Reader, ptrEnc ptrEnc) uint64 {
if ptrEnc == ptrEncOmit {
return 0
}
var ptr uint64
switch ptrEnc & 0xf {
case ptrEncAbs, ptrEncSigned:
ptr, _ = dwarf.ReadUintRaw(buf, binary.LittleEndian, ctx.ptrSize)
case ptrEncUleb:
ptr, _ = leb128.DecodeUnsigned(buf)
case ptrEncUdata2:
ptr, _ = dwarf.ReadUintRaw(buf, binary.LittleEndian, 2)
case ptrEncSdata2:
ptr, _ = dwarf.ReadUintRaw(buf, binary.LittleEndian, 2)
ptr = uint64(int16(ptr))
case ptrEncUdata4:
ptr, _ = dwarf.ReadUintRaw(buf, binary.LittleEndian, 4)
case ptrEncSdata4:
ptr, _ = dwarf.ReadUintRaw(buf, binary.LittleEndian, 4)
ptr = uint64(int32(ptr))
case ptrEncUdata8, ptrEncSdata8:
ptr, _ = dwarf.ReadUintRaw(buf, binary.LittleEndian, 8)
case ptrEncSleb:
n, _ := leb128.DecodeSigned(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
// 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
}
}
|