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