-
Notifications
You must be signed in to change notification settings - Fork 0
/
parser.go
286 lines (240 loc) · 8.56 KB
/
parser.go
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
// 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/undoio/delve/pkg/dwarf/util"
)
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 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
// 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, _ = 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
ctx.common.CodeAlignmentFactor, _ = util.DecodeULEB128(buf)
// parse data alignment factor
ctx.common.DataAlignmentFactor, _ = util.DecodeSLEB128(buf)
// 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.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
// 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 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
// 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
}
}