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